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   gint 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   gint 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 loosing 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   gint 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   gint 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   gint 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 retransmited */
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 eventualy 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 static Suite *
rtprtx_suite(void)656 rtprtx_suite (void)
657 {
658   Suite *s = suite_create ("rtprtx");
659   TCase *tc_chain = tcase_create ("general");
660 
661   tcase_set_timeout (tc_chain, 120);
662 
663   suite_add_tcase (s, tc_chain);
664 
665   tcase_add_test (tc_chain, test_rtxsend_rtxreceive);
666   tcase_add_test (tc_chain, test_rtxsend_rtxreceive_with_packet_loss);
667   tcase_add_test (tc_chain, test_multi_rtxsend_rtxreceive_with_packet_loss);
668   tcase_add_test (tc_chain, test_rtxsender_max_size_packets);
669   tcase_add_test (tc_chain, test_rtxsender_max_size_time);
670   tcase_add_test (tc_chain, test_rtxqueue_max_size_packets);
671   tcase_add_test (tc_chain, test_rtxqueue_max_size_time);
672 
673   return s;
674 }
675 
676 GST_CHECK_MAIN (rtprtx);
677