1 /* GStreamer plugin for forward error correction
2  * Copyright (C) 2017 Pexip
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  * Author: Mikhail Fludkov <misha@pexip.com>
19  */
20 
21 #include <gst/rtp/gstrtpbuffer.h>
22 #include <gst/check/gstcheck.h>
23 #include <gst/check/gstharness.h>
24 
25 #include "../../../gst/rtp/gstrtpstorage.h"
26 #include "../../../gst/rtp/rtpstorage.h"
27 
28 #define RTP_CLOCK_RATE (90000)
29 #define RTP_FRAME_DUR (RTP_CLOCK_RATE / 30)
30 
31 #define RTP_TSTAMP_BASE (0x11111111)
32 #define GST_TSTAMP_BASE (0x22222222)
33 #define RTP_TSTAMP(i) (RTP_FRAME_DUR * (i) + RTP_TSTAMP_BASE)
34 #define GST_TSTAMP(i) (RTP_PACKET_DUR * (i) + GST_TSTAMP_BASE)
35 
36 #define RTP_PACKET_DUR (10 * GST_MSECOND)
37 
38 static GstBufferList *
get_packets_for_recovery(GstHarness * h,gint fec_pt,guint32 ssrc,guint16 lost_seq)39 get_packets_for_recovery (GstHarness * h, gint fec_pt, guint32 ssrc,
40     guint16 lost_seq)
41 {
42   GstBufferList *res;
43   RtpStorage *internal_storage;
44 
45   g_object_get (h->element, "internal-storage", &internal_storage, NULL);
46 
47   res =
48       rtp_storage_get_packets_for_recovery (internal_storage, fec_pt, ssrc,
49       lost_seq);
50 
51   g_object_unref (internal_storage);
52 
53   return res;
54 }
55 
56 static void
put_recovered_packet(GstHarness * h,GstBuffer * buffer,guint8 pt,guint32 ssrc,guint16 seq)57 put_recovered_packet (GstHarness * h, GstBuffer * buffer, guint8 pt,
58     guint32 ssrc, guint16 seq)
59 {
60   RtpStorage *internal_storage;
61 
62   g_object_get (h->element, "internal-storage", &internal_storage, NULL);
63 
64   rtp_storage_put_recovered_packet (internal_storage, buffer, pt, ssrc, seq);
65 
66   g_object_unref (internal_storage);
67 }
68 
69 static GstBuffer *
create_rtp_packet(guint8 pt,guint32 ssrc,guint32 timestamp,guint16 seq)70 create_rtp_packet (guint8 pt, guint32 ssrc, guint32 timestamp, guint16 seq)
71 {
72   GstBuffer *buf = gst_rtp_buffer_new_allocate (0, 0, 0);
73   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
74 
75   fail_unless (gst_rtp_buffer_map (buf, GST_MAP_WRITE, &rtp));
76   gst_rtp_buffer_set_ssrc (&rtp, ssrc);
77   gst_rtp_buffer_set_payload_type (&rtp, pt);
78   gst_rtp_buffer_set_timestamp (&rtp, timestamp);
79   gst_rtp_buffer_set_seq (&rtp, seq);
80   GST_BUFFER_DTS (buf) = GST_TSTAMP (seq);
81   gst_rtp_buffer_unmap (&rtp);
82 
83   return buf;
84 }
85 
GST_START_TEST(rtpstorage_up_and_down)86 GST_START_TEST (rtpstorage_up_and_down)
87 {
88   GstHarness *h = gst_harness_new ("rtpstorage");
89   gst_harness_set_src_caps_str (h, "application/x-rtp");
90   gst_harness_teardown (h);
91 }
92 
93 GST_END_TEST;
94 
GST_START_TEST(rtpstorage_resize)95 GST_START_TEST (rtpstorage_resize)
96 {
97   guint i, j;
98   GstBuffer *bufin, *bufout, *bufs[10];
99   GstHarness *h = gst_harness_new ("rtpstorage");
100 
101   gst_harness_set_src_caps_str (h, "application/x-rtp");
102 
103   g_object_set (h->element, "size-time", (guint64) 0, NULL);
104   bufin = create_rtp_packet (96, 0xabe2b0b, 0x111111, 0);
105   bufout = gst_harness_push_and_pull (h, bufin);
106   fail_unless (bufin == bufout);
107   fail_unless (gst_buffer_is_writable (bufout));
108 
109   g_object_set (h->element,
110       "size-time", (guint64) (G_N_ELEMENTS (bufs) - 1) * RTP_PACKET_DUR, NULL);
111 
112   // Pushing 10 buffers all of them should have ref. count =2
113   for (i = 0; i < G_N_ELEMENTS (bufs); ++i) {
114     bufs[i] =
115         gst_harness_push_and_pull (h, create_rtp_packet (96, 0xabe2b0b,
116             0x111111, i));
117     for (j = 0; j <= i; ++j)
118       fail_unless (!gst_buffer_is_writable (bufs[j]));
119   }
120 
121   // The next 10 buffers should expel the first 10
122   for (i = 0; i < G_N_ELEMENTS (bufs); ++i) {
123     gst_buffer_unref (gst_harness_push_and_pull (h, create_rtp_packet (96,
124                 0xabe2b0b, 0x111111, G_N_ELEMENTS (bufs) + i)));
125     for (j = 0; j <= i; ++j)
126       fail_unless (gst_buffer_is_writable (bufs[j]));
127   }
128 
129   for (i = 0; i < G_N_ELEMENTS (bufs); ++i)
130     gst_buffer_unref (bufs[i]);
131   gst_buffer_unref (bufout);
132   gst_harness_teardown (h);
133 }
134 
135 GST_END_TEST;
136 
GST_START_TEST(rtpstorage_stop_redundant_packets)137 GST_START_TEST (rtpstorage_stop_redundant_packets)
138 {
139   GstHarness *h = gst_harness_new ("rtpstorage");
140   GstBuffer *bufinp;
141 
142   g_object_set (h->element, "size-time", (guint64) 2 * RTP_PACKET_DUR, NULL);
143   gst_harness_set_src_caps_str (h, "application/x-rtp");
144 
145   bufinp = create_rtp_packet (96, 0xabe2b0b, 0x111111, 0);
146   GST_BUFFER_FLAG_SET (bufinp, GST_RTP_BUFFER_FLAG_REDUNDANT);
147   gst_harness_push (h, bufinp);
148 
149   gst_buffer_unref (gst_harness_push_and_pull (h, create_rtp_packet (96,
150               0xabe2b0b, 0x111111, 1)));
151 
152   fail_unless_equals_int (gst_harness_buffers_received (h), 1);
153   gst_harness_teardown (h);
154 }
155 
156 GST_END_TEST;
157 
GST_START_TEST(rtpstorage_unknown_ssrc)158 GST_START_TEST (rtpstorage_unknown_ssrc)
159 {
160   GstBufferList *bufs_out;
161   GstHarness *h = gst_harness_new ("rtpstorage");
162   g_object_set (h->element, "size-time", (guint64) RTP_PACKET_DUR, NULL);
163   gst_harness_set_src_caps_str (h, "application/x-rtp");
164 
165   /* No packets has been pushed through yet */
166   bufs_out = get_packets_for_recovery (h, 100, 0xabe2b0b, 0);
167   fail_unless (NULL == bufs_out);
168 
169   /* 1 packet with ssrc=0xabe2bob pushed. Asking for ssrc=0xdeadbeef */
170   gst_buffer_unref (gst_harness_push_and_pull (h, create_rtp_packet (96,
171               0xabe2b0b, 0x111111, 0)));
172   bufs_out = get_packets_for_recovery (h, 100, 0xdeadbeef, 0);
173   fail_unless (NULL == bufs_out);
174 
175   gst_harness_teardown (h);
176 }
177 
178 GST_END_TEST;
179 
GST_START_TEST(rtpstorage_packet_not_lost)180 GST_START_TEST (rtpstorage_packet_not_lost)
181 {
182   GstBuffer *buf;
183   GstBufferList *bufs_out;
184   GstHarness *h = gst_harness_new ("rtpstorage");
185   g_object_set (h->element, "size-time", (guint64) 10 * RTP_PACKET_DUR, NULL);
186   gst_harness_set_src_caps_str (h, "application/x-rtp");
187 
188   /* Pushing through 2 frames + 2 FEC */
189   gst_buffer_unref (gst_harness_push_and_pull (h, create_rtp_packet (96,
190               0xabe2b0b, RTP_TSTAMP (0), 0)));
191   gst_buffer_unref (gst_harness_push_and_pull (h, (buf =
192               create_rtp_packet (96, 0xabe2b0b, RTP_TSTAMP (1), 1))));
193   gst_buffer_unref (gst_harness_push_and_pull (h, create_rtp_packet (96,
194               0xabe2b0b, RTP_TSTAMP (1), 2)));
195   gst_buffer_unref (gst_harness_push_and_pull (h, create_rtp_packet (96,
196               0xabe2b0b, RTP_TSTAMP (1), 3)));
197 
198   /* Asking for a packet which was pushed before */
199   bufs_out = get_packets_for_recovery (h, 100, 0xabe2b0b, 1);
200   fail_unless (NULL != bufs_out);
201   fail_unless_equals_int (1, gst_buffer_list_length (bufs_out));
202   fail_unless (gst_buffer_list_get (bufs_out, 0) == buf);
203 
204   gst_buffer_list_unref (bufs_out);
205   gst_harness_teardown (h);
206 }
207 
208 GST_END_TEST;
209 
GST_START_TEST(test_rtpstorage_put_recovered_packet)210 GST_START_TEST (test_rtpstorage_put_recovered_packet)
211 {
212   GstBuffer *bufs_in[4];
213   GstBufferList *bufs_out;
214   GstHarness *h = gst_harness_new ("rtpstorage");
215   g_object_set (h->element, "size-time", (guint64) 10 * RTP_PACKET_DUR, NULL);
216   gst_harness_set_src_caps_str (h, "application/x-rtp");
217 
218   /* Pushing through 2 frames + 2 FEC
219    * Packets with sequence numbers 1 and 2 are lost */
220   bufs_in[0] = create_rtp_packet (96, 0xabe2b0b, RTP_TSTAMP (0), 0);
221   bufs_in[1] = NULL;
222   bufs_in[2] = NULL;
223   bufs_in[3] = create_rtp_packet (100, 0xabe2b0b, RTP_TSTAMP (1), 3);
224   gst_buffer_unref (gst_harness_push_and_pull (h, bufs_in[0]));
225   gst_buffer_unref (gst_harness_push_and_pull (h, bufs_in[3]));
226 
227   /* 1 more frame + 1 FEC */
228   gst_buffer_unref (gst_harness_push_and_pull (h, create_rtp_packet (96,
229               0xabe2b0b, RTP_TSTAMP (2), 4)));
230   gst_buffer_unref (gst_harness_push_and_pull (h, create_rtp_packet (100,
231               0xabe2b0b, RTP_TSTAMP (2), 5)));
232 
233   /* Asking for the lost packet seq=1 */
234   bufs_out = get_packets_for_recovery (h, 100, 0xabe2b0b, 1);
235   fail_unless (NULL != bufs_out);
236   fail_unless_equals_int (2, gst_buffer_list_length (bufs_out));
237   fail_unless (gst_buffer_list_get (bufs_out, 0) == bufs_in[0]);
238   fail_unless (gst_buffer_list_get (bufs_out, 1) == bufs_in[3]);
239   gst_buffer_list_unref (bufs_out);
240 
241   /* During recovery the packet of a new frame has arrived */
242   gst_buffer_unref (gst_harness_push_and_pull (h, create_rtp_packet (96,
243               0xabe2b0b, RTP_TSTAMP (3), 6)));
244 
245   /* Say we recovered packet with seq=1 and put it back in the storage */
246   bufs_in[1] = create_rtp_packet (96, 0xabe2b0b, RTP_TSTAMP (1), 1);
247   put_recovered_packet (h, bufs_in[1], 96, 0xabe2b0b, 1);
248 
249   /* Asking for the lost packet seq=2 */
250   bufs_out = get_packets_for_recovery (h, 100, 0xabe2b0b, 2);
251   fail_unless (NULL != bufs_out);
252   fail_unless_equals_int (3, gst_buffer_list_length (bufs_out));
253   fail_unless (gst_buffer_list_get (bufs_out, 0) == bufs_in[0]);
254   fail_unless (gst_buffer_list_get (bufs_out, 1) == bufs_in[1]);
255   fail_unless (gst_buffer_list_get (bufs_out, 2) == bufs_in[3]);
256   gst_buffer_list_unref (bufs_out);
257 
258   gst_harness_teardown (h);
259 }
260 
261 GST_END_TEST;
262 
263 
264 static void
_single_ssrc_test(GstHarness * h,guint32 ssrc,guint16 seq_start,guint16 nth_to_loose,gsize expected_buf_size,gsize expected_first_buffer_idx)265 _single_ssrc_test (GstHarness * h, guint32 ssrc,
266     guint16 seq_start, guint16 nth_to_loose,
267     gsize expected_buf_size, gsize expected_first_buffer_idx)
268 {
269   guint i;
270   GPtrArray *bufs_in =
271       g_ptr_array_new_with_free_func ((GDestroyNotify) gst_buffer_unref);
272   GstBufferList *bufs_out;
273 
274   /* 2 frames + 2 FEC */
275   g_ptr_array_add (bufs_in, create_rtp_packet (96, ssrc, RTP_TSTAMP (0),
276           seq_start + 0));
277   g_ptr_array_add (bufs_in, create_rtp_packet (96, ssrc, RTP_TSTAMP (1),
278           seq_start + 1));
279   g_ptr_array_add (bufs_in, create_rtp_packet (100, ssrc, RTP_TSTAMP (1),
280           seq_start + 2));
281   g_ptr_array_add (bufs_in, create_rtp_packet (100, ssrc, RTP_TSTAMP (1),
282           seq_start + 3));
283   /* 3 frames + 2 FEC */
284   g_ptr_array_add (bufs_in, create_rtp_packet (96, ssrc, RTP_TSTAMP (2),
285           seq_start + 4));
286   g_ptr_array_add (bufs_in, create_rtp_packet (96, ssrc, RTP_TSTAMP (3),
287           seq_start + 5));
288   g_ptr_array_add (bufs_in, create_rtp_packet (96, ssrc, RTP_TSTAMP (4),
289           seq_start + 6));
290   g_ptr_array_add (bufs_in, create_rtp_packet (100, ssrc, RTP_TSTAMP (4),
291           seq_start + 7));
292   g_ptr_array_add (bufs_in, create_rtp_packet (100, ssrc, RTP_TSTAMP (4),
293           seq_start + 8));
294   g_ptr_array_add (bufs_in, create_rtp_packet (100, ssrc, RTP_TSTAMP (4),
295           seq_start + 9));
296   /* 2 frames + no FEC */
297   g_ptr_array_add (bufs_in, create_rtp_packet (96, ssrc, RTP_TSTAMP (5),
298           seq_start + 10));
299   g_ptr_array_add (bufs_in, create_rtp_packet (96, ssrc, RTP_TSTAMP (6),
300           seq_start + 11));
301 
302   /* Loosing one */
303   g_ptr_array_remove_index (bufs_in, nth_to_loose);
304 
305   /* Push all of them through */
306   for (i = 0; i < bufs_in->len; ++i)
307     gst_buffer_unref (gst_harness_push_and_pull (h,
308             gst_buffer_ref (g_ptr_array_index (bufs_in, i))));
309 
310   bufs_out =
311       get_packets_for_recovery (h, 100, ssrc,
312       (guint16) (seq_start + nth_to_loose));
313   if (0 == expected_buf_size) {
314     fail_unless (NULL == bufs_out);
315   } else {
316     fail_unless (NULL != bufs_out);
317     fail_unless_equals_int (expected_buf_size,
318         gst_buffer_list_length (bufs_out));
319     for (i = 0; i < gst_buffer_list_length (bufs_out); ++i)
320       fail_unless (gst_buffer_list_get (bufs_out, i) ==
321           g_ptr_array_index (bufs_in, expected_first_buffer_idx + i));
322     gst_buffer_list_unref (bufs_out);
323   }
324   g_ptr_array_unref (bufs_in);
325 }
326 
327 static void
_multiple_ssrcs_test(guint16 nth_to_loose,gsize expected_buf_size,gsize expected_first_buffer_idx)328 _multiple_ssrcs_test (guint16 nth_to_loose,
329     gsize expected_buf_size, gsize expected_first_buffer_idx)
330 {
331   guint16 stream0_seq_start = 200;
332   guint16 stream1_seq_start = 65529;
333   GstHarness *h = gst_harness_new ("rtpstorage");
334   g_object_set (h->element, "size-time", (guint64) 12 * RTP_PACKET_DUR, NULL);
335   gst_harness_set_src_caps_str (h, "application/x-rtp");
336 
337   _single_ssrc_test (h, 0x0abe2b0b, stream0_seq_start,
338       nth_to_loose, expected_buf_size, expected_first_buffer_idx);
339   _single_ssrc_test (h, 0xdeadbeef, stream1_seq_start,
340       nth_to_loose, expected_buf_size, expected_first_buffer_idx);
341 
342   gst_harness_teardown (h);
343 }
344 
GST_START_TEST(rtpstorage_loss_pattern0)345 GST_START_TEST (rtpstorage_loss_pattern0)
346 {
347   _multiple_ssrcs_test (1, 3, 0);
348 }
349 
350 GST_END_TEST;
351 
GST_START_TEST(rtpstorage_loss_pattern1)352 GST_START_TEST (rtpstorage_loss_pattern1)
353 {
354   _multiple_ssrcs_test (2, 3, 0);
355 }
356 
357 GST_END_TEST;
358 
GST_START_TEST(rtpstorage_loss_pattern2)359 GST_START_TEST (rtpstorage_loss_pattern2)
360 {
361   _multiple_ssrcs_test (3, 6, 3);
362 }
363 
364 GST_END_TEST;
365 
GST_START_TEST(rtpstorage_loss_pattern3)366 GST_START_TEST (rtpstorage_loss_pattern3)
367 {
368   _multiple_ssrcs_test (4, 5, 4);
369 }
370 
371 GST_END_TEST;
372 
GST_START_TEST(rtpstorage_loss_pattern4)373 GST_START_TEST (rtpstorage_loss_pattern4)
374 {
375   _multiple_ssrcs_test (5, 5, 4);
376 }
377 
378 GST_END_TEST;
379 
GST_START_TEST(rtpstorage_loss_pattern5)380 GST_START_TEST (rtpstorage_loss_pattern5)
381 {
382   _multiple_ssrcs_test (6, 5, 4);
383 }
384 
385 GST_END_TEST;
386 
GST_START_TEST(rtpstorage_loss_pattern6)387 GST_START_TEST (rtpstorage_loss_pattern6)
388 {
389   _multiple_ssrcs_test (7, 5, 4);
390 }
391 
392 GST_END_TEST;
393 
GST_START_TEST(rtpstorage_loss_pattern7)394 GST_START_TEST (rtpstorage_loss_pattern7)
395 {
396   _multiple_ssrcs_test (8, 5, 4);
397 }
398 
399 GST_END_TEST;
400 
GST_START_TEST(rtpstorage_loss_pattern8)401 GST_START_TEST (rtpstorage_loss_pattern8)
402 {
403   _multiple_ssrcs_test (9, 0, 0);
404 }
405 
406 GST_END_TEST;
407 
GST_START_TEST(rtpstorage_loss_pattern9)408 GST_START_TEST (rtpstorage_loss_pattern9)
409 {
410   _multiple_ssrcs_test (10, 0, 0);
411 }
412 
413 GST_END_TEST;
414 
415 #define STRESS_TEST_SSRCS (8)
416 #define STRESS_TEST_STORAGE_DEPTH (50)
417 typedef struct _StressTestData StressTestData;
418 struct _StressTestData
419 {
420   guint16 seq[STRESS_TEST_SSRCS];
421   guint32 ssrc[STRESS_TEST_SSRCS];
422   gsize count[STRESS_TEST_SSRCS];
423   GRand *rnd;
424 };
425 
426 static GstBuffer *
rtpstorage_stress_prepare_buffer(GstHarness * h,gpointer data)427 rtpstorage_stress_prepare_buffer (GstHarness * h, gpointer data)
428 {
429   static const guint8 fec_pt = 100;
430   static const guint8 media_pt = 96;
431   StressTestData *test_data = data;
432   gsize ssrc_idx = g_rand_int_range (test_data->rnd, 0, STRESS_TEST_SSRCS);
433   guint16 seq = test_data->seq[ssrc_idx];
434   guint32 ssrc = test_data->ssrc[ssrc_idx];
435   gboolean is_fec = test_data->count[ssrc_idx] > 0 && (seq % 5 == 0
436       || seq % 5 == 1);
437   guint8 pt = is_fec ? fec_pt : media_pt;
438 
439   GstBuffer *buf = create_rtp_packet (pt, ssrc, RTP_TSTAMP (0), seq);
440 
441   ++test_data->seq[ssrc_idx];
442   ++test_data->count[ssrc_idx];
443   return buf;
444 }
445 
GST_START_TEST(rtpstorage_stress)446 GST_START_TEST (rtpstorage_stress)
447 {
448   GRand *rnd;
449   GTimer *timer;
450   GstCaps *caps;
451   GstSegment segment;
452   GstHarnessThread *ht;
453   StressTestData test_data;
454   guint seed, i, total, requested;
455   GstHarness *h = gst_harness_new ("rtpstorage");
456   g_object_set (h->element,
457       "size-time", (guint64) STRESS_TEST_STORAGE_DEPTH * RTP_PACKET_DUR, NULL);
458 
459   /* The stress test pushes buffers with STRESS_TEST_SSRCS different
460    * ssrcs from one thread and requests packets for FEC recovery from
461    * another thread.
462    * */
463   memset (&test_data, 0, sizeof (test_data));
464   seed = g_random_int ();
465   test_data.rnd = g_rand_new_with_seed (seed);
466   for (i = 0; i < STRESS_TEST_SSRCS; ++i) {
467     test_data.ssrc[i] = 0x00112233 + i * 0x01000000;
468     test_data.seq[i] = g_rand_int_range (test_data.rnd, 0, 0x10000);
469   }
470 
471   gst_segment_init (&segment, GST_FORMAT_TIME);
472   caps = gst_caps_from_string ("application/x-rtp");
473   rnd = g_rand_copy (test_data.rnd);
474 
475   GST_INFO ("%u seed", seed);
476   ht = gst_harness_stress_push_buffer_with_cb_start (h, caps, &segment,
477       rtpstorage_stress_prepare_buffer, &test_data, NULL);
478 
479   requested = 0;
480   timer = g_timer_new ();
481   while (g_timer_elapsed (timer, NULL) < 2) {
482     gsize ssrc_idx = g_rand_int_range (rnd, 0, STRESS_TEST_SSRCS);
483 
484     /* The following if statement is simply keeping the log
485      * clean from ERROR messages */
486     if (*((volatile gsize *) &test_data.count[ssrc_idx]) > 1) {
487       guint16 lost_seq = *((volatile guint16 *) &test_data.seq[ssrc_idx]) - 5;
488 
489       GstBufferList *bufs_out = get_packets_for_recovery (h, 100,
490           test_data.ssrc[ssrc_idx], lost_seq);
491       if (bufs_out) {
492         requested += gst_buffer_list_length (bufs_out);
493         gst_buffer_list_unref (bufs_out);
494       }
495     }
496 
497     /* Having sleep here makes it hard to detect the race, but we need it to
498      * allow another thread to push more buffers when running under valgrind */
499     g_usleep (G_USEC_PER_SEC / 10000);
500   }
501 
502   gst_harness_stress_thread_stop (ht);
503   for (i = 0, total = 0; i < STRESS_TEST_SSRCS; ++i) {
504     GST_INFO ("SSRC 0x%08x: %u packets", test_data.ssrc[i],
505         (guint32) test_data.count[i]);
506     total += test_data.count[i];
507   }
508   GST_INFO ("%u packets pushed through, %u requested", total, requested);
509 
510   g_rand_free (rnd);
511   g_rand_free (test_data.rnd);
512   gst_caps_unref (caps);
513   g_timer_destroy (timer);
514   gst_harness_teardown (h);
515 }
516 
517 GST_END_TEST;
518 
519 static Suite *
rtpstorage_suite(void)520 rtpstorage_suite (void)
521 {
522   Suite *s = suite_create ("rtpstorage");
523   TCase *tc_chain = tcase_create ("general");
524 
525   gst_element_register (NULL, "rtpstorage", GST_RANK_NONE,
526       GST_TYPE_RTP_STORAGE);
527 
528   suite_add_tcase (s, tc_chain);
529   tcase_add_test (tc_chain, rtpstorage_up_and_down);
530   tcase_add_test (tc_chain, rtpstorage_resize);
531   tcase_add_test (tc_chain, rtpstorage_stop_redundant_packets);
532   tcase_add_test (tc_chain, rtpstorage_unknown_ssrc);
533   tcase_add_test (tc_chain, rtpstorage_packet_not_lost);
534   tcase_add_test (tc_chain, rtpstorage_loss_pattern0);
535   tcase_add_test (tc_chain, rtpstorage_loss_pattern1);
536   tcase_add_test (tc_chain, rtpstorage_loss_pattern2);
537   tcase_add_test (tc_chain, rtpstorage_loss_pattern3);
538   tcase_add_test (tc_chain, rtpstorage_loss_pattern4);
539   tcase_add_test (tc_chain, rtpstorage_loss_pattern5);
540   tcase_add_test (tc_chain, rtpstorage_loss_pattern6);
541   tcase_add_test (tc_chain, rtpstorage_loss_pattern7);
542   tcase_add_test (tc_chain, rtpstorage_loss_pattern8);
543   tcase_add_test (tc_chain, rtpstorage_loss_pattern9);
544   tcase_add_test (tc_chain, test_rtpstorage_put_recovered_packet);
545   tcase_add_test (tc_chain, rtpstorage_stress);
546 
547   return s;
548 }
549 
550 GST_CHECK_MAIN (rtpstorage)
551