1 /* GStreamer
2 *
3 * Copyright (C) 2013 Collabora Ltd.
4 * @author Julien Isorce <julien.isorce@collabora.co.uk>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21 #include <gst/check/gstcheck.h>
22 #include <gst/check/gstharness.h>
23 #include <gst/rtp/gstrtpbuffer.h>
24
25 #define verify_buf(buf, is_rtx, expected_ssrc, expted_pt, expected_seqnum) \
26 G_STMT_START { \
27 GstRTPBuffer _rtp = GST_RTP_BUFFER_INIT; \
28 fail_unless (gst_rtp_buffer_map (buf, GST_MAP_READ, &_rtp)); \
29 fail_unless_equals_int (gst_rtp_buffer_get_ssrc (&_rtp), expected_ssrc); \
30 fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&_rtp), expted_pt); \
31 if (!(is_rtx)) { \
32 fail_unless_equals_int (gst_rtp_buffer_get_seq (&_rtp), expected_seqnum); \
33 } else { \
34 fail_unless_equals_int (GST_READ_UINT16_BE (gst_rtp_buffer_get_payload \
35 (&_rtp)), expected_seqnum); \
36 } \
37 gst_rtp_buffer_unmap (&_rtp); \
38 } G_STMT_END
39
40 #define pull_and_verify(h, is_rtx, expected_ssrc, expted_pt, expected_seqnum) \
41 G_STMT_START { \
42 GstBuffer *_buf = gst_harness_pull (h); \
43 verify_buf (_buf, is_rtx, expected_ssrc, expted_pt, expected_seqnum); \
44 gst_buffer_unref (_buf); \
45 } G_STMT_END
46
47 #define push_pull_and_verify(h, buf, is_rtx, expected_ssrc, expted_pt, expected_seqnum) \
48 G_STMT_START { \
49 gst_harness_push (h, buf); \
50 pull_and_verify (h, is_rtx, expected_ssrc, expted_pt, expected_seqnum); \
51 } G_STMT_END
52
53 static GstEvent *
create_rtx_event(guint32 ssrc,guint8 payload_type,guint16 seqnum)54 create_rtx_event (guint32 ssrc, guint8 payload_type, guint16 seqnum)
55 {
56 return gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
57 gst_structure_new ("GstRTPRetransmissionRequest",
58 "seqnum", G_TYPE_UINT, seqnum,
59 "ssrc", G_TYPE_UINT, ssrc,
60 "payload-type", G_TYPE_UINT, payload_type, NULL));
61 }
62
63 static void
compare_rtp_packets(GstBuffer * a,GstBuffer * b)64 compare_rtp_packets (GstBuffer * a, GstBuffer * b)
65 {
66 GstRTPBuffer rtp_a = GST_RTP_BUFFER_INIT;
67 GstRTPBuffer rtp_b = GST_RTP_BUFFER_INIT;
68
69 gst_rtp_buffer_map (a, GST_MAP_READ, &rtp_a);
70 gst_rtp_buffer_map (b, GST_MAP_READ, &rtp_b);
71
72 fail_unless_equals_int (gst_rtp_buffer_get_header_len (&rtp_a),
73 gst_rtp_buffer_get_header_len (&rtp_b));
74 fail_unless_equals_int (gst_rtp_buffer_get_version (&rtp_a),
75 gst_rtp_buffer_get_version (&rtp_b));
76 fail_unless_equals_int (gst_rtp_buffer_get_ssrc (&rtp_a),
77 gst_rtp_buffer_get_ssrc (&rtp_b));
78 fail_unless_equals_int (gst_rtp_buffer_get_seq (&rtp_a),
79 gst_rtp_buffer_get_seq (&rtp_b));
80 fail_unless_equals_int (gst_rtp_buffer_get_csrc_count (&rtp_a),
81 gst_rtp_buffer_get_csrc_count (&rtp_b));
82 fail_unless_equals_int (gst_rtp_buffer_get_marker (&rtp_a),
83 gst_rtp_buffer_get_marker (&rtp_b));
84 fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp_a),
85 gst_rtp_buffer_get_payload_type (&rtp_b));
86 fail_unless_equals_int (gst_rtp_buffer_get_timestamp (&rtp_a),
87 gst_rtp_buffer_get_timestamp (&rtp_b));
88 fail_unless_equals_int (gst_rtp_buffer_get_extension (&rtp_a),
89 gst_rtp_buffer_get_extension (&rtp_b));
90
91 fail_unless_equals_int (gst_rtp_buffer_get_payload_len (&rtp_a),
92 gst_rtp_buffer_get_payload_len (&rtp_b));
93 fail_unless_equals_int (memcmp (gst_rtp_buffer_get_payload (&rtp_a),
94 gst_rtp_buffer_get_payload (&rtp_b),
95 gst_rtp_buffer_get_payload_len (&rtp_a)), 0);
96
97 gst_rtp_buffer_unmap (&rtp_a);
98 gst_rtp_buffer_unmap (&rtp_b);
99 }
100
101 static GstBuffer *
create_rtp_buffer(guint32 ssrc,guint8 payload_type,guint16 seqnum)102 create_rtp_buffer (guint32 ssrc, guint8 payload_type, guint16 seqnum)
103 {
104 GstRTPBuffer rtpbuf = GST_RTP_BUFFER_INIT;
105 guint payload_size = 29;
106 guint64 timestamp = gst_util_uint64_scale_int (seqnum, 90000, 30);
107 GstBuffer *buf = gst_rtp_buffer_new_allocate (payload_size, 0, 0);
108
109 gst_rtp_buffer_map (buf, GST_MAP_WRITE, &rtpbuf);
110 gst_rtp_buffer_set_ssrc (&rtpbuf, ssrc);
111 gst_rtp_buffer_set_payload_type (&rtpbuf, payload_type);
112 gst_rtp_buffer_set_seq (&rtpbuf, seqnum);
113 gst_rtp_buffer_set_timestamp (&rtpbuf, (guint32) timestamp);
114 memset (gst_rtp_buffer_get_payload (&rtpbuf), 0x29, payload_size);
115 gst_rtp_buffer_unmap (&rtpbuf);
116 return buf;
117 }
118
119 static GstBuffer *
create_rtp_buffer_with_timestamp(guint32 ssrc,guint8 payload_type,guint16 seqnum,guint32 timestamp)120 create_rtp_buffer_with_timestamp (guint32 ssrc, guint8 payload_type,
121 guint16 seqnum, guint32 timestamp)
122 {
123 GstRTPBuffer rtpbuf = GST_RTP_BUFFER_INIT;
124 GstBuffer *buf = create_rtp_buffer (ssrc, payload_type, seqnum);
125 gst_rtp_buffer_map (buf, GST_MAP_WRITE, &rtpbuf);
126 gst_rtp_buffer_set_timestamp (&rtpbuf, timestamp);
127 gst_rtp_buffer_unmap (&rtpbuf);
128 return buf;
129 }
130
GST_START_TEST(test_rtxsend_rtxreceive)131 GST_START_TEST (test_rtxsend_rtxreceive)
132 {
133 const guint packets_num = 5;
134 guint master_ssrc = 1234567;
135 guint master_pt = 96;
136 guint rtx_pt = 99;
137 GstStructure *pt_map;
138 GstBuffer *inbufs[5];
139 GstHarness *hrecv = gst_harness_new ("rtprtxreceive");
140 GstHarness *hsend = gst_harness_new ("rtprtxsend");
141 guint i;
142
143 pt_map = gst_structure_new ("application/x-rtp-pt-map",
144 "96", G_TYPE_UINT, rtx_pt, NULL);
145 g_object_set (hrecv->element, "payload-type-map", pt_map, NULL);
146 g_object_set (hsend->element, "payload-type-map", pt_map, NULL);
147
148 gst_harness_set_src_caps_str (hsend, "application/x-rtp, "
149 "media = (string)video, payload = (int)96, "
150 "ssrc = (uint)1234567, clock-rate = (int)90000, "
151 "encoding-name = (string)RAW");
152 gst_harness_set_src_caps_str (hrecv, "application/x-rtp, "
153 "media = (string)video, payload = (int)96, "
154 "ssrc = (uint)1234567, clock-rate = (int)90000, "
155 "encoding-name = (string)RAW");
156
157 /* Push 'packets_num' packets through rtxsend to rtxreceive */
158 for (i = 0; i < packets_num; i++) {
159 inbufs[i] = create_rtp_buffer (master_ssrc, master_pt, 100 + i);
160 gst_harness_push (hsend, gst_buffer_ref (inbufs[i]));
161 gst_harness_push (hrecv, gst_harness_pull (hsend));
162 pull_and_verify (hrecv, FALSE, master_ssrc, master_pt, 100 + i);
163 }
164
165 /* Getting rid of reconfigure event. Preparation before the next step */
166 gst_event_unref (gst_harness_pull_upstream_event (hrecv));
167 fail_unless_equals_int (gst_harness_upstream_events_in_queue (hrecv), 0);
168
169 /* Push 'packets_num' RTX events through rtxreceive to rtxsend.
170 Push RTX packets from rtxsend to rtxreceive and
171 check that the packet produced out of RTX packet is the same
172 as an original packet */
173 for (i = 0; i < packets_num; i++) {
174 GstBuffer *outbuf;
175 gst_harness_push_upstream_event (hrecv,
176 create_rtx_event (master_ssrc, master_pt, 100 + i));
177 gst_harness_push_upstream_event (hsend,
178 gst_harness_pull_upstream_event (hrecv));
179 gst_harness_push (hrecv, gst_harness_pull (hsend));
180
181 outbuf = gst_harness_pull (hrecv);
182 compare_rtp_packets (inbufs[i], outbuf);
183 gst_buffer_unref (inbufs[i]);
184 gst_buffer_unref (outbuf);
185 }
186
187 /* Check RTX stats */
188 {
189 guint rtx_requests;
190 guint rtx_packets;
191 guint rtx_assoc_packets;
192 g_object_get (G_OBJECT (hsend->element),
193 "num-rtx-requests", &rtx_requests,
194 "num-rtx-packets", &rtx_packets, NULL);
195 fail_unless_equals_int (rtx_packets, packets_num);
196 fail_unless_equals_int (rtx_requests, packets_num);
197
198 g_object_get (G_OBJECT (hrecv->element),
199 "num-rtx-requests", &rtx_requests,
200 "num-rtx-packets", &rtx_packets,
201 "num-rtx-assoc-packets", &rtx_assoc_packets, NULL);
202 fail_unless_equals_int (rtx_packets, packets_num);
203 fail_unless_equals_int (rtx_requests, packets_num);
204 fail_unless_equals_int (rtx_assoc_packets, packets_num);
205 }
206
207 gst_structure_free (pt_map);
208 gst_harness_teardown (hrecv);
209 gst_harness_teardown (hsend);
210 }
211
212 GST_END_TEST;
213
GST_START_TEST(test_rtxsend_rtxreceive_with_packet_loss)214 GST_START_TEST (test_rtxsend_rtxreceive_with_packet_loss)
215 {
216 guint packets_num = 20;
217 guint master_ssrc = 1234567;
218 guint master_pt = 96;
219 guint rtx_pt = 99;
220 guint seqnum = 100;
221 guint expected_rtx_packets = 0;
222 GstStructure *pt_map;
223 GstHarness *hrecv = gst_harness_new ("rtprtxreceive");
224 GstHarness *hsend = gst_harness_new ("rtprtxsend");
225 guint drop_nth_packet, i;
226
227 pt_map = gst_structure_new ("application/x-rtp-pt-map",
228 "96", G_TYPE_UINT, rtx_pt, NULL);
229 g_object_set (hrecv->element, "payload-type-map", pt_map, NULL);
230 g_object_set (hsend->element, "payload-type-map", pt_map, NULL);
231
232 gst_harness_set_src_caps_str (hsend, "application/x-rtp, "
233 "media = (string)video, payload = (int)96, "
234 "ssrc = (uint)1234567, clock-rate = (int)90000, "
235 "encoding-name = (string)RAW");
236 gst_harness_set_src_caps_str (hrecv, "application/x-rtp, "
237 "media = (string)video, payload = (int)96, "
238 "ssrc = (uint)1234567, clock-rate = (int)90000, "
239 "encoding-name = (string)RAW");
240
241 /* Getting rid of reconfigure event. Making sure there is no upstream
242 events in the queue. Preparation step before the test. */
243 gst_event_unref (gst_harness_pull_upstream_event (hrecv));
244 fail_unless_equals_int (gst_harness_upstream_events_in_queue (hrecv), 0);
245
246 /* Push 'packets_num' packets through rtxsend to rtxreceive losing every
247 'drop_every_n_packets' packet. When we loose the packet we send RTX event
248 through rtxreceive to rtxsend, and verify the packet was retransmitted */
249 for (drop_nth_packet = 2; drop_nth_packet < 10; drop_nth_packet++) {
250 for (i = 0; i < packets_num; i++, seqnum++) {
251 GstBuffer *outbuf;
252 GstBuffer *inbuf = create_rtp_buffer (master_ssrc, master_pt, seqnum);
253 gboolean drop_this_packet = ((i + 1) % drop_nth_packet) == 0;
254
255 gst_harness_push (hsend, gst_buffer_ref (inbuf));
256 if (drop_this_packet) {
257 /* Dropping original packet */
258 gst_buffer_unref (gst_harness_pull (hsend));
259 /* Requesting retransmission */
260 gst_harness_push_upstream_event (hrecv,
261 create_rtx_event (master_ssrc, master_pt, seqnum));
262 gst_harness_push_upstream_event (hsend,
263 gst_harness_pull_upstream_event (hrecv));
264 /* Pushing RTX packet to rtxreceive */
265 gst_harness_push (hrecv, gst_harness_pull (hsend));
266 expected_rtx_packets++;
267 } else {
268 gst_harness_push (hrecv, gst_harness_pull (hsend));
269 }
270
271 /* We making sure every buffer we pull is the same as original input
272 buffer */
273 outbuf = gst_harness_pull (hrecv);
274 compare_rtp_packets (inbuf, outbuf);
275 gst_buffer_unref (inbuf);
276 gst_buffer_unref (outbuf);
277
278 /*
279 We should not have any packets in the harness queue by this point. It
280 means rtxsend didn't send more packets than RTX events and rtxreceive
281 didn't produce more than one packet per RTX packet.
282 */
283 fail_unless_equals_int (gst_harness_buffers_in_queue (hsend), 0);
284 fail_unless_equals_int (gst_harness_buffers_in_queue (hrecv), 0);
285 }
286 }
287
288 /* Check RTX stats */
289 {
290 guint rtx_requests;
291 guint rtx_packets;
292 guint rtx_assoc_packets;
293 g_object_get (G_OBJECT (hsend->element),
294 "num-rtx-requests", &rtx_requests,
295 "num-rtx-packets", &rtx_packets, NULL);
296 fail_unless_equals_int (rtx_packets, expected_rtx_packets);
297 fail_unless_equals_int (rtx_requests, expected_rtx_packets);
298
299 g_object_get (G_OBJECT (hrecv->element),
300 "num-rtx-requests", &rtx_requests,
301 "num-rtx-packets", &rtx_packets,
302 "num-rtx-assoc-packets", &rtx_assoc_packets, NULL);
303 fail_unless_equals_int (rtx_packets, expected_rtx_packets);
304 fail_unless_equals_int (rtx_requests, expected_rtx_packets);
305 fail_unless_equals_int (rtx_assoc_packets, expected_rtx_packets);
306 }
307
308 gst_structure_free (pt_map);
309 gst_harness_teardown (hrecv);
310 gst_harness_teardown (hsend);
311 }
312
313 GST_END_TEST;
314
315 typedef struct
316 {
317 GstHarness *h;
318 guint master_ssrc;
319 guint master_pt;
320 guint rtx_ssrc;
321 guint rtx_pt;
322 guint seqnum;
323 guint expected_rtx_packets;
324 } RtxSender;
325
326 static GstStructure *
create_rtxsenders(RtxSender * senders,guint senders_num)327 create_rtxsenders (RtxSender * senders, guint senders_num)
328 {
329 GstStructure *recv_pt_map =
330 gst_structure_new_empty ("application/x-rtp-pt-map");
331 guint i;
332
333 for (i = 0; i < senders_num; i++) {
334 gchar *master_pt_str;
335 gchar *master_caps_str;
336 GstStructure *send_pt_map;
337
338 senders[i].h = gst_harness_new ("rtprtxsend");
339 senders[i].master_ssrc = 1234567 + i;
340 senders[i].rtx_ssrc = 7654321 + i;
341 senders[i].master_pt = 80 + i;
342 senders[i].rtx_pt = 20 + i;
343 senders[i].seqnum = i * 1000;
344 senders[i].expected_rtx_packets = 0;
345
346 master_pt_str = g_strdup_printf ("%u", senders[i].master_pt);
347 master_caps_str = g_strdup_printf ("application/x-rtp, "
348 "media = (string)video, payload = (int)%u, "
349 "ssrc = (uint)%u, clock-rate = (int)90000, "
350 "encoding-name = (string)RAW",
351 senders[i].master_pt, senders[i].master_ssrc);
352
353 send_pt_map = gst_structure_new ("application/x-rtp-pt-map",
354 master_pt_str, G_TYPE_UINT, senders[i].rtx_pt, NULL);
355 gst_structure_set (recv_pt_map,
356 master_pt_str, G_TYPE_UINT, senders[i].rtx_pt, NULL);
357
358 g_object_set (senders[i].h->element, "payload-type-map", send_pt_map, NULL);
359 gst_harness_set_src_caps_str (senders[i].h, master_caps_str);
360
361 gst_structure_free (send_pt_map);
362 g_free (master_pt_str);
363 g_free (master_caps_str);
364 }
365 return recv_pt_map;
366 }
367
368 static guint
check_rtxsenders_stats_and_teardown(RtxSender * senders,guint senders_num)369 check_rtxsenders_stats_and_teardown (RtxSender * senders, guint senders_num)
370 {
371 guint total_pakets_num = 0;
372 guint i;
373
374 for (i = 0; i < senders_num; i++) {
375 guint rtx_requests;
376 guint rtx_packets;
377 g_object_get (G_OBJECT (senders[i].h->element),
378 "num-rtx-requests", &rtx_requests,
379 "num-rtx-packets", &rtx_packets, NULL);
380 fail_unless_equals_int (rtx_packets, senders[i].expected_rtx_packets);
381 fail_unless_equals_int (rtx_requests, senders[i].expected_rtx_packets);
382 total_pakets_num += rtx_packets;
383
384 gst_harness_teardown (senders[i].h);
385 }
386 return total_pakets_num;
387 }
388
GST_START_TEST(test_multi_rtxsend_rtxreceive_with_packet_loss)389 GST_START_TEST (test_multi_rtxsend_rtxreceive_with_packet_loss)
390 {
391 guint senders_num = 5;
392 guint packets_num = 10;
393 guint total_pakets_num = senders_num * packets_num;
394 guint total_dropped_packets = 0;
395 RtxSender senders[5];
396 GstStructure *pt_map;
397 GstHarness *hrecv = gst_harness_new ("rtprtxreceive");
398 guint drop_nth_packet, i, j;
399
400 pt_map = create_rtxsenders (senders, 5);
401 g_object_set (hrecv->element, "payload-type-map", pt_map, NULL);
402 gst_harness_set_src_caps_str (hrecv, "application/x-rtp, "
403 "media = (string)video, payload = (int)80, "
404 "ssrc = (uint)1234567, clock-rate = (int)90000, "
405 "encoding-name = (string)RAW");
406
407 /* Getting rid of reconfigure event. Making sure there is no upstream
408 events in the queue. Preparation step before the test. */
409 gst_event_unref (gst_harness_pull_upstream_event (hrecv));
410 fail_unless_equals_int (gst_harness_upstream_events_in_queue (hrecv), 0);
411
412 /* We are going to push the 1st packet from the 1st sender, 2nd from the 2nd,
413 3rd from the 3rd, etc. until all the senders will push 'packets_num' packets.
414 We will drop every 'drop_nth_packet' packet and request its retransmission
415 from all the senders. Because only one of them can produce RTX packet.
416 We need to make sure that all other senders will ignore the RTX event they
417 can't act upon.
418 */
419 for (drop_nth_packet = 2; drop_nth_packet < 5; drop_nth_packet++) {
420 for (i = 0; i < total_pakets_num; i++) {
421 RtxSender *sender = &senders[i % senders_num];
422 gboolean drop_this_packet = ((i + 1) % drop_nth_packet) == 0;
423 GstBuffer *outbuf, *inbuf;
424 inbuf =
425 create_rtp_buffer (sender->master_ssrc, sender->master_pt,
426 sender->seqnum);
427
428 gst_harness_push (sender->h, gst_buffer_ref (inbuf));
429 if (drop_this_packet) {
430 GstEvent *rtxevent;
431 /* Dropping original packet */
432 gst_buffer_unref (gst_harness_pull (sender->h));
433
434 /* Pushing RTX event through rtxreceive to all the senders */
435 gst_harness_push_upstream_event (hrecv,
436 create_rtx_event (sender->master_ssrc, sender->master_pt,
437 sender->seqnum));
438 rtxevent = gst_harness_pull_upstream_event (hrecv);
439
440 /* ... to all the senders */
441 for (j = 0; j < senders_num; j++)
442 gst_harness_push_upstream_event (senders[j].h,
443 gst_event_ref (rtxevent));
444 gst_event_unref (rtxevent);
445
446 /* Pushing RTX packet to rtxreceive */
447 gst_harness_push (hrecv, gst_harness_pull (sender->h));
448 sender->expected_rtx_packets++;
449 total_dropped_packets++;
450 } else {
451 gst_harness_push (hrecv, gst_harness_pull (sender->h));
452 }
453
454 /* It should not matter whether the buffer was dropped (and retransmitted)
455 or it went straight through rtxsend to rtxreceive. We should always pull
456 the same buffer that was pushed */
457 outbuf = gst_harness_pull (hrecv);
458 compare_rtp_packets (inbuf, outbuf);
459 gst_buffer_unref (inbuf);
460 gst_buffer_unref (outbuf);
461
462 /*
463 We should not have any packets in the harness queue by this point. It
464 means our senders didn't produce the packets for the unknown RTX event.
465 */
466 for (j = 0; j < senders_num; j++)
467 fail_unless_equals_int (gst_harness_buffers_in_queue (senders[j].h), 0);
468
469 sender->seqnum++;
470 }
471 }
472
473 /* Check RTX stats */
474 {
475 guint total_rtx_packets;
476 guint rtx_requests;
477 guint rtx_packets;
478 guint rtx_assoc_packets;
479
480 total_rtx_packets =
481 check_rtxsenders_stats_and_teardown (senders, senders_num);
482 fail_unless_equals_int (total_rtx_packets, total_dropped_packets);
483
484 g_object_get (G_OBJECT (hrecv->element),
485 "num-rtx-requests", &rtx_requests,
486 "num-rtx-packets", &rtx_packets,
487 "num-rtx-assoc-packets", &rtx_assoc_packets, NULL);
488 fail_unless_equals_int (rtx_packets, total_rtx_packets);
489 fail_unless_equals_int (rtx_requests, total_rtx_packets);
490 fail_unless_equals_int (rtx_assoc_packets, total_rtx_packets);
491 }
492
493 gst_structure_free (pt_map);
494 gst_harness_teardown (hrecv);
495 }
496
497 GST_END_TEST;
498
499 static void
test_rtxsender_packet_retention(gboolean test_with_time)500 test_rtxsender_packet_retention (gboolean test_with_time)
501 {
502 guint master_ssrc = 1234567;
503 guint master_pt = 96;
504 guint rtx_ssrc = 7654321;
505 guint rtx_pt = 99;
506 gint num_buffers = test_with_time ? 30 : 10;
507 gint half_buffers = num_buffers / 2;
508 guint timestamp_delta = 90000 / 30;
509 guint timestamp = G_MAXUINT32 - half_buffers * timestamp_delta;
510 GstHarness *h;
511 GstStructure *pt_map = gst_structure_new ("application/x-rtp-pt-map",
512 "96", G_TYPE_UINT, rtx_pt, NULL);
513 GstStructure *ssrc_map = gst_structure_new ("application/x-rtp-ssrc-map",
514 "1234567", G_TYPE_UINT, rtx_ssrc, NULL);
515 gint i, j;
516
517 h = gst_harness_new ("rtprtxsend");
518
519 /* In both cases we want the rtxsend queue to store 'half_buffers'
520 amount of buffers at most. In max-size-packets mode, it's trivial.
521 In max-size-time mode, we specify almost half a second, which is
522 the equivalent of 15 frames in a 30fps video stream.
523 */
524 g_object_set (h->element,
525 "max-size-packets", test_with_time ? 0 : half_buffers,
526 "max-size-time", test_with_time ? 499 : 0,
527 "payload-type-map", pt_map, "ssrc-map", ssrc_map, NULL);
528
529 gst_harness_set_src_caps_str (h, "application/x-rtp, "
530 "media = (string)video, payload = (int)96, "
531 "ssrc = (uint)1234567, clock-rate = (int)90000, "
532 "encoding-name = (string)RAW");
533
534 /* Now push all buffers and request retransmission every time for all of them */
535 for (i = 0; i < num_buffers; ++i, timestamp += timestamp_delta) {
536 /* Request to retransmit all the previous ones */
537 for (j = 0; j < i; ++j) {
538 guint rtx_seqnum = 0x100 + j;
539 gst_harness_push_upstream_event (h,
540 create_rtx_event (master_ssrc, master_pt, rtx_seqnum));
541
542 /* Pull only the ones supposed to be retransmitted */
543 if (j >= i - half_buffers)
544 pull_and_verify (h, TRUE, rtx_ssrc, rtx_pt, rtx_seqnum);
545 }
546 /* Check there no extra buffers in the harness queue */
547 fail_unless_equals_int (gst_harness_buffers_in_queue (h), 0);
548
549 /* We create RTP buffers with timestamps that will eventually wrap around 0
550 to be sure, rtprtxsend can handle it properly */
551 push_pull_and_verify (h,
552 create_rtp_buffer_with_timestamp (master_ssrc, master_pt, 0x100 + i,
553 timestamp), FALSE, master_ssrc, master_pt, 0x100 + i);
554 }
555
556 gst_structure_free (pt_map);
557 gst_structure_free (ssrc_map);
558 gst_harness_teardown (h);
559 }
560
GST_START_TEST(test_rtxsender_max_size_packets)561 GST_START_TEST (test_rtxsender_max_size_packets)
562 {
563 test_rtxsender_packet_retention (FALSE);
564 }
565
566 GST_END_TEST;
567
GST_START_TEST(test_rtxsender_max_size_time)568 GST_START_TEST (test_rtxsender_max_size_time)
569 {
570 test_rtxsender_packet_retention (TRUE);
571 }
572
573 GST_END_TEST;
574
575 static void
test_rtxqueue_packet_retention(gboolean test_with_time)576 test_rtxqueue_packet_retention (gboolean test_with_time)
577 {
578 guint ssrc = 1234567;
579 guint pt = 96;
580 gint num_buffers = test_with_time ? 30 : 10;
581 gint half_buffers = num_buffers / 2;
582 GstClockTime timestamp_delta = GST_SECOND / 30;
583 GstClockTime timestamp = 0;
584 GstBuffer *buf;
585 GstHarness *h;
586 gint i, j;
587
588 h = gst_harness_new ("rtprtxqueue");
589
590 /* In both cases we want the rtxqueue to store 'half_buffers'
591 amount of buffers at most. In max-size-packets mode, it's trivial.
592 In max-size-time mode, we specify almost half a second, which is
593 the equivalent of 15 frames in a 30fps video stream.
594 */
595 g_object_set (h->element,
596 "max-size-packets", test_with_time ? 0 : half_buffers,
597 "max-size-time", test_with_time ? 498 : 0, NULL);
598
599 gst_harness_set_src_caps_str (h, "application/x-rtp, "
600 "media = (string)video, payload = (int)96, "
601 "ssrc = (uint)1234567, clock-rate = (int)90000, "
602 "encoding-name = (string)RAW");
603
604 /* Now push all buffers and request retransmission every time for all of them.
605 * Note that rtprtxqueue sends retransmissions in chain(), just before
606 * pushing out the chained buffer, a differentiation from rtprtxsend above
607 */
608 for (i = 0; i < num_buffers; i++, timestamp += timestamp_delta) {
609 /* Request to retransmit all the previous ones */
610 for (j = 0; j < i; j++) {
611 guint rtx_seqnum = 0x100 + j;
612 gst_harness_push_upstream_event (h,
613 create_rtx_event (ssrc, pt, rtx_seqnum));
614 }
615
616 /* push one packet */
617 buf = create_rtp_buffer (ssrc, pt, 0x100 + i);
618 GST_BUFFER_TIMESTAMP (buf) = timestamp;
619 gst_harness_push (h, buf);
620
621 /* Pull the ones supposed to be retransmitted */
622 for (j = 0; j < i; j++) {
623 guint rtx_seqnum = 0x100 + j;
624 if (j >= i - half_buffers)
625 pull_and_verify (h, FALSE, ssrc, pt, rtx_seqnum);
626 }
627
628 /* There should be only one packet remaining in the queue now */
629 fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
630
631 /* pull the one that we just pushed (comes after the retransmitted ones) */
632 pull_and_verify (h, FALSE, ssrc, pt, 0x100 + i);
633
634 /* Check there no extra buffers in the harness queue */
635 fail_unless_equals_int (gst_harness_buffers_in_queue (h), 0);
636 }
637
638 gst_harness_teardown (h);
639 }
640
GST_START_TEST(test_rtxqueue_max_size_packets)641 GST_START_TEST (test_rtxqueue_max_size_packets)
642 {
643 test_rtxqueue_packet_retention (FALSE);
644 }
645
646 GST_END_TEST;
647
GST_START_TEST(test_rtxqueue_max_size_time)648 GST_START_TEST (test_rtxqueue_max_size_time)
649 {
650 test_rtxqueue_packet_retention (TRUE);
651 }
652
653 GST_END_TEST;
654
655 /* In this test, we verify the behaviour of rtprtxsend when
656 * generic caps are provided to its sink pad, this is useful
657 * when connected to an rtp funnel.
658 */
GST_START_TEST(test_rtxsender_clock_rate_map)659 GST_START_TEST (test_rtxsender_clock_rate_map)
660 {
661 GstBuffer *inbuf, *outbuf;
662 guint master_ssrc = 1234567;
663 guint master_pt = 96;
664 guint rtx_pt = 99;
665 guint master_clock_rate = 90000;
666 GstStructure *pt_map;
667 GstStructure *clock_rate_map;
668 GstHarness *hsend = gst_harness_new ("rtprtxsend");
669
670 pt_map = gst_structure_new ("application/x-rtp-pt-map",
671 "96", G_TYPE_UINT, rtx_pt, NULL);
672 clock_rate_map = gst_structure_new ("application/x-rtp-clock-rate-map",
673 "96", G_TYPE_UINT, master_clock_rate, NULL);
674 g_object_set (hsend->element, "payload-type-map", pt_map,
675 "clock-rate-map", clock_rate_map, "max-size-time", 1000, NULL);
676 gst_structure_free (pt_map);
677 gst_structure_free (clock_rate_map);
678
679 gst_harness_set_src_caps_str (hsend, "application/x-rtp");
680
681 inbuf = create_rtp_buffer (master_ssrc, master_pt, 100);
682 gst_harness_push (hsend, inbuf);
683
684 outbuf = gst_harness_pull (hsend);
685 fail_unless (outbuf == inbuf);
686 gst_buffer_unref (outbuf);
687
688 gst_harness_push_upstream_event (hsend, create_rtx_event (master_ssrc,
689 master_pt, 100));
690
691 outbuf = gst_harness_pull (hsend);
692 fail_unless (outbuf);
693 gst_buffer_unref (outbuf);
694
695 fail_unless_equals_int (gst_harness_buffers_in_queue (hsend), 0);
696
697 /* Thanks to the provided clock rate, rtprtxsend should be able to
698 * determine that the previously pushed buffer should be cleared from
699 * its rtx queue */
700 inbuf = create_rtp_buffer (master_ssrc, master_pt, 131);
701 gst_harness_push (hsend, inbuf);
702
703 outbuf = gst_harness_pull (hsend);
704 fail_unless (outbuf == inbuf);
705 gst_buffer_unref (outbuf);
706
707 fail_unless_equals_int (gst_harness_buffers_in_queue (hsend), 0);
708
709 gst_harness_push_upstream_event (hsend, create_rtx_event (master_ssrc,
710 master_pt, 100));
711
712 fail_unless_equals_int (gst_harness_buffers_in_queue (hsend), 0);
713
714 gst_harness_teardown (hsend);
715 }
716
717 GST_END_TEST;
718
719 static Suite *
rtprtx_suite(void)720 rtprtx_suite (void)
721 {
722 Suite *s = suite_create ("rtprtx");
723 TCase *tc_chain = tcase_create ("general");
724
725 tcase_set_timeout (tc_chain, 120);
726
727 suite_add_tcase (s, tc_chain);
728
729 tcase_add_test (tc_chain, test_rtxsend_rtxreceive);
730 tcase_add_test (tc_chain, test_rtxsend_rtxreceive_with_packet_loss);
731 tcase_add_test (tc_chain, test_multi_rtxsend_rtxreceive_with_packet_loss);
732 tcase_add_test (tc_chain, test_rtxsender_max_size_packets);
733 tcase_add_test (tc_chain, test_rtxsender_max_size_time);
734 tcase_add_test (tc_chain, test_rtxqueue_max_size_packets);
735 tcase_add_test (tc_chain, test_rtxqueue_max_size_time);
736 tcase_add_test (tc_chain, test_rtxsender_clock_rate_map);
737
738 return s;
739 }
740
741 GST_CHECK_MAIN (rtprtx);
742