• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/check/gstharness.h>
22 #include <gst/rtp/gstrtpbuffer.h>
23 #include <gst/check/gstcheck.h>
24 
25 #define PT_RED 100
26 #define PT_MEDIA 96
27 #define CLOCKRATE 8000
28 #define TIMESTAMP_BASE (1000)
29 #define TIMESTAMP_DIFF (40 * CLOCKRATE / 1000)
30 #define TIMESTAMP_NTH(i) (TIMESTAMP_BASE + (i) * TIMESTAMP_DIFF)
31 #define xstr(s) str(s)
32 #define str(s) #s
33 #define GST_RTP_RED_ENC_CAPS_STR "application/x-rtp, payload=" xstr(PT_MEDIA)
34 #define GST_RTP_RED_ENC_TWCC_CAPS_STR "application/x-rtp, extmap-1=http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01, payload=" xstr(PT_MEDIA)
35 
36 #define _check_red_received(h, expected)                     \
37   G_STMT_START {                                             \
38     guint received;                                          \
39     g_object_get ((h)->element, "received", &received, NULL);\
40     fail_unless_equals_int (expected, received);             \
41   } G_STMT_END
42 
43 #define _check_red_sent(h, expected)                 \
44   G_STMT_START {                                     \
45     guint sent;                                      \
46     g_object_get ((h)->element, "sent", &sent, NULL);\
47     fail_unless_equals_int (expected, sent);         \
48   } G_STMT_END
49 
50 #define _check_caps(_h_, _nth_, _expected_payload_)               \
51   G_STMT_START {                                                  \
52     GstEvent *_ev_;                                               \
53     gint _pt_ = -1, _i_;                                          \
54     GstCaps *_caps_ = NULL;                                       \
55                                                                   \
56     for (_i_ = 0; _i_ < _nth_; ++_i_)                             \
57       gst_event_unref (gst_harness_pull_event (_h_));             \
58                                                                   \
59     _ev_ = gst_harness_pull_event (_h_);                          \
60     fail_unless (NULL != _ev_);                                   \
61     fail_unless_equals_string ("caps", GST_EVENT_TYPE_NAME(_ev_));\
62                                                                   \
63     gst_event_parse_caps (_ev_, &_caps_);                         \
64                                                                   \
65     gst_structure_get_int (                                       \
66         gst_caps_get_structure (_caps_, 0), "payload", &_pt_);    \
67     fail_unless_equals_int (_expected_payload_, _pt_);            \
68     gst_event_unref (_ev_);                                       \
69   } G_STMT_END
70 
71 #define _check_nocaps(_h_)                                     \
72   G_STMT_START {                                               \
73     GstEvent *_ev_;                                            \
74     while (NULL != (_ev_ = gst_harness_try_pull_event (_h_))) {\
75       fail_unless (GST_EVENT_TYPE (_ev_) != GST_EVENT_CAPS,    \
76           "Don't expect to receive caps event");               \
77       gst_event_unref (_ev_);                                  \
78     }                                                          \
79   } G_STMT_END
80 
81 static GstBuffer *
_new_rtp_buffer(gboolean marker,guint8 csrc_count,guint8 pt,guint16 seqnum,guint32 timestamp,guint32 ssrc,guint payload_len)82 _new_rtp_buffer (gboolean marker, guint8 csrc_count, guint8 pt, guint16 seqnum,
83     guint32 timestamp, guint32 ssrc, guint payload_len)
84 {
85   GstBuffer *buf = gst_rtp_buffer_new_allocate (payload_len, 0, csrc_count);
86   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
87 
88   fail_unless (gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp));
89   gst_rtp_buffer_set_marker (&rtp, marker);
90   gst_rtp_buffer_set_payload_type (&rtp, pt);
91   gst_rtp_buffer_set_seq (&rtp, seqnum);
92   gst_rtp_buffer_set_timestamp (&rtp, timestamp);
93   gst_rtp_buffer_set_ssrc (&rtp, ssrc);
94   gst_rtp_buffer_unmap (&rtp);
95 
96   return buf;
97 }
98 
GST_START_TEST(rtpreddec_passthrough)99 GST_START_TEST (rtpreddec_passthrough)
100 {
101   GstBuffer *bufinp, *bufout;
102   GstHarness *h = gst_harness_new ("rtpreddec");
103   gst_harness_set_src_caps_str (h, "application/x-rtp");
104 
105   /* Passthrough when pt is not set */
106   bufinp =
107       _new_rtp_buffer (FALSE, 0, PT_RED, 0, TIMESTAMP_NTH (0), 0xabe2b0b, 0);
108   bufout = gst_harness_push_and_pull (h, bufinp);
109   fail_unless (bufout == bufinp);
110   fail_unless (gst_buffer_is_writable (bufout));
111   gst_buffer_unref (bufout);
112 
113   /* Now pt is set */
114   g_object_set (h->element, "pt", PT_RED, NULL);
115 
116   /* Passthrough when not RED. RED pt = 100, pushing pt 99 */
117   bufinp =
118       _new_rtp_buffer (FALSE, 0, PT_MEDIA, 1, TIMESTAMP_NTH (1), 0xabe2b0b, 0);
119   bufout = gst_harness_push_and_pull (h, bufinp);
120   fail_unless (bufout == bufinp);
121   fail_unless (gst_buffer_is_writable (bufout));
122   gst_buffer_unref (bufout);
123 
124   /* Passthrough when not RTP buffer */
125   bufinp = gst_buffer_new_wrapped (g_strdup ("hello"), 5);
126   bufout = gst_harness_push_and_pull (h, bufinp);
127   fail_unless (bufout == bufinp);
128   fail_unless (gst_buffer_is_writable (bufout));
129   gst_buffer_unref (bufout);
130 
131   _check_red_received (h, 0);
132   gst_harness_teardown (h);
133 }
134 
135 GST_END_TEST;
136 
GST_START_TEST(rtpreddec_main_block)137 GST_START_TEST (rtpreddec_main_block)
138 {
139   GstHarness *h = gst_harness_new ("rtpreddec");
140   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
141   guint8 out_data[] = { 0xa, 0xa, 0xa, 0xa, 0xa };
142   guint8 red_in[] = { PT_MEDIA, 0xa, 0xa, 0xa, 0xa, 0xa };
143   guint gst_ts = 3454679;
144   guint csrc_count = 2;
145   guint seq = 549;
146   GstBuffer *bufinp, *bufout;
147   guint bufinp_flags;
148 
149   g_object_set (h->element, "pt", PT_RED, NULL);
150   gst_harness_set_src_caps_str (h, "application/x-rtp");
151 
152   /* RED buffer has Marker bit set, has CSRCS and flags */
153   bufinp =
154       _new_rtp_buffer (TRUE, csrc_count, PT_RED, seq, TIMESTAMP_NTH (0),
155       0xabe2b0b, sizeof (red_in));
156   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
157   memcpy (gst_rtp_buffer_get_payload (&rtp), &red_in, sizeof (red_in));
158   gst_rtp_buffer_set_csrc (&rtp, 0, 0x1abe2b0b);
159   gst_rtp_buffer_set_csrc (&rtp, 1, 0x2abe2b0b);
160   GST_BUFFER_TIMESTAMP (bufinp) = gst_ts;
161   GST_BUFFER_FLAG_SET (bufinp, GST_RTP_BUFFER_FLAG_RETRANSMISSION);
162   GST_BUFFER_FLAG_SET (bufinp, GST_BUFFER_FLAG_DISCONT);
163   bufinp_flags = GST_BUFFER_FLAGS (bufinp);
164   gst_rtp_buffer_unmap (&rtp);
165 
166   /* Checking that pulled buffer has keeps everything from RED buffer */
167   bufout = gst_harness_push_and_pull (h, bufinp);
168   fail_unless (gst_rtp_buffer_map (bufout, GST_MAP_READ, &rtp));
169   fail_unless_equals_int (GST_BUFFER_TIMESTAMP (bufout), gst_ts);
170   fail_unless_equals_int (GST_BUFFER_FLAGS (bufout), bufinp_flags);
171   fail_unless_equals_int (gst_buffer_get_size (bufout),
172       gst_rtp_buffer_calc_packet_len (sizeof (out_data), 0, csrc_count));
173   fail_unless_equals_int (gst_rtp_buffer_get_timestamp (&rtp),
174       TIMESTAMP_NTH (0));
175   fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp), PT_MEDIA);
176   fail_unless_equals_int (gst_rtp_buffer_get_seq (&rtp), seq);
177   fail_unless_equals_int (gst_rtp_buffer_get_csrc_count (&rtp), csrc_count);
178   fail_unless_equals_int (gst_rtp_buffer_get_ssrc (&rtp), 0x0abe2b0b);
179   fail_unless_equals_int (gst_rtp_buffer_get_csrc (&rtp, 0), 0x1abe2b0b);
180   fail_unless_equals_int (gst_rtp_buffer_get_csrc (&rtp, 1), 0x2abe2b0b);
181   fail_unless (gst_rtp_buffer_get_marker (&rtp));
182   fail_unless (!memcmp (gst_rtp_buffer_get_payload (&rtp), out_data,
183           sizeof (out_data)));
184   gst_rtp_buffer_unmap (&rtp);
185   gst_buffer_unref (bufout);
186 
187   _check_red_received (h, 1);
188   gst_harness_teardown (h);
189 }
190 
191 GST_END_TEST;
192 
193 static void
_push_and_check_didnt_go_through(GstHarness * h,GstBuffer * bufinp)194 _push_and_check_didnt_go_through (GstHarness * h, GstBuffer * bufinp)
195 {
196   gst_harness_push (h, bufinp);
197   /* Making sure it didn't go through */
198   fail_unless_equals_int (gst_harness_buffers_received (h), 0);
199 }
200 
201 static void
_push_and_check_cant_pull_twice(GstHarness * h,GstBuffer * bufinp,guint buffers_received)202 _push_and_check_cant_pull_twice (GstHarness * h,
203     GstBuffer * bufinp, guint buffers_received)
204 {
205   gst_buffer_unref (gst_harness_push_and_pull (h, bufinp));
206   /* Making sure only one buffer was pushed through */
207   fail_unless_equals_int (gst_harness_buffers_received (h), buffers_received);
208 }
209 
210 static void
_push_and_check_redundant_packet(GstHarness * h,GstBuffer * bufinp,guint seq,guint timestamp,guint payload_len,gconstpointer payload)211 _push_and_check_redundant_packet (GstHarness * h, GstBuffer * bufinp,
212     guint seq, guint timestamp, guint payload_len, gconstpointer payload)
213 {
214   GstBuffer *bufout = gst_harness_push_and_pull (h, bufinp);
215   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
216 
217   fail_unless (gst_rtp_buffer_map (bufout, GST_MAP_READ, &rtp));
218   fail_unless (GST_BUFFER_FLAG_IS_SET (bufout, GST_RTP_BUFFER_FLAG_REDUNDANT));
219   fail_unless_equals_int (gst_buffer_get_size (bufout),
220       gst_rtp_buffer_calc_packet_len (payload_len, 0, 0));
221   fail_unless_equals_int (gst_rtp_buffer_get_timestamp (&rtp), timestamp);
222   fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp), PT_MEDIA);
223   fail_unless_equals_int (gst_rtp_buffer_get_seq (&rtp), seq);
224   fail_unless_equals_int (gst_rtp_buffer_get_ssrc (&rtp), 0x0abe2b0b);
225   fail_unless (!memcmp (gst_rtp_buffer_get_payload (&rtp), payload,
226           payload_len));
227   gst_rtp_buffer_unmap (&rtp);
228   gst_buffer_unref (bufout);
229   gst_buffer_unref (gst_harness_pull (h));
230 }
231 
GST_START_TEST(rtpreddec_redundant_block_not_pushed)232 GST_START_TEST (rtpreddec_redundant_block_not_pushed)
233 {
234   GstHarness *h = gst_harness_new ("rtpreddec");
235   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
236 
237   /* Redundant block has valid tsoffset but we have not seen any buffers before */
238   guint16 ts_offset = TIMESTAMP_DIFF;
239   guint8 red_in[] = {
240     0x80 | PT_MEDIA,
241     (guint8) (ts_offset >> 6),
242     (guint8) (ts_offset & 0x3f) << 2, 1,        /* Redundant block size = 1 */
243     PT_MEDIA, 0xa, 0xa          /* Main block size = 1 */
244   };
245   GstBuffer *bufinp =
246       _new_rtp_buffer (FALSE, 0, PT_RED, 2, TIMESTAMP_NTH (2), 0xabe2b0b,
247       sizeof (red_in));
248 
249   g_object_set (h->element, "pt", PT_RED, NULL);
250   gst_harness_set_src_caps_str (h, "application/x-rtp");
251 
252   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
253   memcpy (gst_rtp_buffer_get_payload (&rtp), &red_in, sizeof (red_in));
254   gst_rtp_buffer_unmap (&rtp);
255   _push_and_check_cant_pull_twice (h, bufinp, 1);
256 
257   /* Redundant block has too large tsoffset */
258   ts_offset = TIMESTAMP_DIFF * 4;
259   red_in[1] = ts_offset >> 6;
260   red_in[2] = (ts_offset & 0x3f) << 2;
261   bufinp =
262       _new_rtp_buffer (FALSE, 0, PT_RED, 3, TIMESTAMP_NTH (3), 0xabe2b0b,
263       sizeof (red_in));
264   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
265   memcpy (gst_rtp_buffer_get_payload (&rtp), &red_in, sizeof (red_in));
266   gst_rtp_buffer_unmap (&rtp);
267   _push_and_check_cant_pull_twice (h, bufinp, 2);
268 
269   /* TS offset is too small */
270   ts_offset = TIMESTAMP_DIFF / 2;
271   red_in[1] = ts_offset >> 6;
272   red_in[2] = (ts_offset & 0x3f) << 2;
273   bufinp =
274       _new_rtp_buffer (FALSE, 0, PT_RED, 4, TIMESTAMP_NTH (4), 0xabe2b0b,
275       sizeof (red_in));
276   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
277   memcpy (gst_rtp_buffer_get_payload (&rtp), &red_in, sizeof (red_in));
278   gst_rtp_buffer_unmap (&rtp);
279   _push_and_check_cant_pull_twice (h, bufinp, 3);
280 
281   /* Now we ts_offset points to the previous buffer we didn't loose */
282   ts_offset = TIMESTAMP_DIFF;
283   red_in[1] = ts_offset >> 6;
284   red_in[2] = (ts_offset & 0x3f) << 2;
285   bufinp =
286       _new_rtp_buffer (FALSE, 0, PT_RED, 5, TIMESTAMP_NTH (5), 0xabe2b0b,
287       sizeof (red_in));
288   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
289   memcpy (gst_rtp_buffer_get_payload (&rtp), &red_in, sizeof (red_in));
290   gst_rtp_buffer_unmap (&rtp);
291   _push_and_check_cant_pull_twice (h, bufinp, 4);
292 
293   _check_red_received (h, 4);
294   gst_harness_teardown (h);
295 }
296 
297 GST_END_TEST;
298 
GST_START_TEST(rtpreddec_redundant_block_pushed)299 GST_START_TEST (rtpreddec_redundant_block_pushed)
300 {
301   GstHarness *h = gst_harness_new ("rtpreddec");
302   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
303   guint16 ts_offset = TIMESTAMP_DIFF;
304   guint8 red_in[] = {
305     0x80 | PT_MEDIA,
306     (guint8) (ts_offset >> 6),
307     (guint8) (ts_offset & 0x3f) << 2, 5,        /* Redundant block size = 5 */
308     PT_MEDIA, 0x01, 0x02, 0x03, 0x4, 0x5, 0xa   /* Main block size = 1 */
309   };
310   GstBuffer *bufinp;
311 
312   g_object_set (h->element, "pt", PT_RED, NULL);
313   gst_harness_set_src_caps_str (h, "application/x-rtp");
314 
315   /* Pushing seq=0 */
316   gst_buffer_unref (gst_harness_push_and_pull (h, _new_rtp_buffer (FALSE, 0,
317               PT_MEDIA, 0, TIMESTAMP_NTH (0), 0xabe2b0b, 0)));
318 
319   /* Pushing seq=2, recovering seq=1 (fec distance 1) */
320 
321   bufinp =
322       _new_rtp_buffer (FALSE, 0, PT_RED, 2, TIMESTAMP_NTH (2), 0xabe2b0b,
323       sizeof (red_in));
324   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
325   memcpy (gst_rtp_buffer_get_payload (&rtp), &red_in, sizeof (red_in));
326   gst_rtp_buffer_unmap (&rtp);
327   _push_and_check_redundant_packet (h, bufinp, 1, TIMESTAMP_NTH (1), 5,
328       red_in + 5);
329 
330   /* Pushing seq=5, recovering seq=3 (fec distance 2) */
331   ts_offset = TIMESTAMP_DIFF * 2;
332   red_in[1] = ts_offset >> 6;
333   red_in[2] = (ts_offset & 0x3f) << 2;
334   bufinp =
335       _new_rtp_buffer (FALSE, 0, PT_RED, 5, TIMESTAMP_NTH (5), 0xabe2b0b,
336       sizeof (red_in));
337   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
338   memcpy (gst_rtp_buffer_get_payload (&rtp), &red_in, sizeof (red_in));
339   gst_rtp_buffer_unmap (&rtp);
340   _push_and_check_redundant_packet (h, bufinp, 3, TIMESTAMP_NTH (3), 5,
341       red_in + 5);
342 
343   /* Pushing seq=9, recovering seq=6 (fec distance 3) */
344   ts_offset = TIMESTAMP_DIFF * 3;
345   red_in[1] = ts_offset >> 6;
346   red_in[2] = (ts_offset & 0x3f) << 2;
347   bufinp =
348       _new_rtp_buffer (FALSE, 0, PT_RED, 9, TIMESTAMP_NTH (9), 0xabe2b0b,
349       sizeof (red_in));
350   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
351   memcpy (gst_rtp_buffer_get_payload (&rtp), &red_in, sizeof (red_in));
352   gst_rtp_buffer_unmap (&rtp);
353   _push_and_check_redundant_packet (h, bufinp, 6, TIMESTAMP_NTH (6), 5,
354       red_in + 5);
355 
356   /* Pushing seq=14, recovering seq=10 (fec distance 4) */
357   ts_offset = TIMESTAMP_DIFF * 4;
358   red_in[1] = ts_offset >> 6;
359   red_in[2] = (ts_offset & 0x3f) << 2;
360   bufinp =
361       _new_rtp_buffer (FALSE, 0, PT_RED, 14, TIMESTAMP_NTH (14), 0xabe2b0b,
362       sizeof (red_in));
363   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
364   memcpy (gst_rtp_buffer_get_payload (&rtp), &red_in, sizeof (red_in));
365   gst_rtp_buffer_unmap (&rtp);
366   _push_and_check_redundant_packet (h, bufinp, 10, TIMESTAMP_NTH (10), 5,
367       red_in + 5);
368 
369   _check_red_received (h, 4);
370   gst_harness_teardown (h);
371 }
372 
373 GST_END_TEST;
374 
GST_START_TEST(rtpreddec_invalid)375 GST_START_TEST (rtpreddec_invalid)
376 {
377   GstBuffer *bufinp;
378   GstHarness *h = gst_harness_new ("rtpreddec");
379   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
380   /* 2 block RED packets should have at least 4 bytes for redundant block
381    * header and 1 byte for the main block header. */
382   guint8 data[] = {
383     0x80 | PT_MEDIA, 0, 0, 1,   /* 1st block header (redundant block) size=1, timestmapoffset=0 */
384     PT_MEDIA,                   /* 2nd block header (main block) size=0 */
385   };
386 
387   g_object_set (h->element, "pt", PT_RED, NULL);
388   gst_harness_set_src_caps_str (h, "application/x-rtp");
389 
390   /* Single block RED packets should have at least 1 byte of payload to be
391    * considered valid. This buffer does not have any payload */
392   bufinp =
393       _new_rtp_buffer (FALSE, 0, PT_RED, 0, TIMESTAMP_NTH (0), 0xabe2b0b, 0);
394   _push_and_check_didnt_go_through (h, bufinp);
395 
396   /* Only the first byte with F bit set (indication of redundant block) */
397   bufinp =
398       _new_rtp_buffer (FALSE, 0, PT_RED, 1, TIMESTAMP_NTH (1), 0xabe2b0b, 1);
399   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
400   memcpy (gst_rtp_buffer_get_payload (&rtp), &data, sizeof (data));
401   gst_rtp_buffer_unmap (&rtp);
402   _push_and_check_didnt_go_through (h, bufinp);
403 
404   /* Full 1st block header only */
405   bufinp =
406       _new_rtp_buffer (FALSE, 0, PT_RED, 2, TIMESTAMP_NTH (2), 0xabe2b0b, 4);
407   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
408   memcpy (gst_rtp_buffer_get_payload (&rtp), &data, sizeof (data));
409   gst_rtp_buffer_unmap (&rtp);
410   _push_and_check_didnt_go_through (h, bufinp);
411 
412   /* Both blocks, missing 1 byte of payload for redundant block */
413   bufinp =
414       _new_rtp_buffer (FALSE, 0, PT_RED, 3, TIMESTAMP_NTH (3), 0xabe2b0b, 5);
415   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
416   memcpy (gst_rtp_buffer_get_payload (&rtp), &data, sizeof (data));
417   gst_rtp_buffer_unmap (&rtp);
418   _push_and_check_didnt_go_through (h, bufinp);
419 
420   _check_red_received (h, 4);
421   gst_harness_teardown (h);
422 }
423 
424 GST_END_TEST;
425 
GST_START_TEST(rtpredenc_passthrough)426 GST_START_TEST (rtpredenc_passthrough)
427 {
428   GstBuffer *bufinp, *bufout;
429   GstHarness *h = gst_harness_new ("rtpredenc");
430 
431   g_object_set (h->element, "allow-no-red-blocks", FALSE, NULL);
432   gst_harness_set_src_caps_str (h, GST_RTP_RED_ENC_CAPS_STR);
433 
434   bufinp =
435       _new_rtp_buffer (FALSE, 0, PT_MEDIA, 0, TIMESTAMP_NTH (0), 0xabe2b0b, 0);
436   bufout = gst_harness_push_and_pull (h, bufinp);
437 
438   _check_caps (h, 1, PT_MEDIA);
439   fail_unless (bufout == bufinp);
440   fail_unless (gst_buffer_is_writable (bufout));
441   gst_buffer_unref (bufout);
442 
443   /* Setting pt and allowing RED packets without redundant blocks */
444   g_object_set (h->element, "pt", PT_RED, "allow-no-red-blocks", TRUE, NULL);
445 
446   /* Passthrough when not RTP buffer */
447   bufinp = gst_buffer_new_wrapped (g_strdup ("hello"), 5);
448   bufout = gst_harness_push_and_pull (h, bufinp);
449 
450   _check_nocaps (h);
451   fail_unless (bufout == bufinp);
452   fail_unless (gst_buffer_is_writable (bufout));
453   gst_buffer_unref (bufout);
454 
455   gst_harness_teardown (h);
456 }
457 
458 GST_END_TEST;
459 
GST_START_TEST(rtpredenc_payloadless_rtp)460 GST_START_TEST (rtpredenc_payloadless_rtp)
461 {
462   GstHarness *h = gst_harness_new ("rtpredenc");
463   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
464   guint8 out_data[] = { PT_MEDIA };
465   GstBuffer *bufout;
466 
467   g_object_set (h->element, "pt", PT_RED, "allow-no-red-blocks", TRUE, NULL);
468   gst_harness_set_src_caps_str (h, GST_RTP_RED_ENC_CAPS_STR);
469 
470   bufout =
471       gst_harness_push_and_pull (h, _new_rtp_buffer (TRUE, 0, PT_MEDIA, 0,
472           TIMESTAMP_NTH (0), 0xabe2b0b, 0));
473 
474   _check_caps (h, 1, PT_RED);
475   fail_unless (gst_rtp_buffer_map (bufout, GST_MAP_READ, &rtp));
476   fail_unless_equals_int (gst_buffer_get_size (bufout),
477       gst_rtp_buffer_calc_packet_len (sizeof (out_data), 0, 0));
478   fail_unless_equals_int (gst_rtp_buffer_get_timestamp (&rtp),
479       TIMESTAMP_NTH (0));
480   fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp), PT_RED);
481   fail_unless_equals_int (gst_rtp_buffer_get_seq (&rtp), 0);
482   fail_unless_equals_int (gst_rtp_buffer_get_csrc_count (&rtp), 0);
483   fail_unless_equals_int (gst_rtp_buffer_get_ssrc (&rtp), 0x0abe2b0b);
484   fail_unless (gst_rtp_buffer_get_marker (&rtp));
485   fail_unless (!memcmp (gst_rtp_buffer_get_payload (&rtp), out_data,
486           sizeof (out_data)));
487   gst_rtp_buffer_unmap (&rtp);
488   gst_buffer_unref (bufout);
489 
490   _check_red_sent (h, 1);
491   gst_harness_teardown (h);
492 }
493 
494 GST_END_TEST;
495 
GST_START_TEST(rtpredenc_without_redundant_block)496 GST_START_TEST (rtpredenc_without_redundant_block)
497 {
498   GstHarness *h = gst_harness_new ("rtpredenc");
499   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
500   guint8 in_data[] = { 0xa, 0xa, 0xa, 0xa, 0xa };
501   guint8 out_data[] = { PT_MEDIA, 0xa, 0xa, 0xa, 0xa, 0xa };
502   guint gst_ts = 3454679;
503   guint csrc_count = 2;
504   guint seq = 549;
505   guint bufinp_flags;
506   GstBuffer *bufinp, *bufout;
507 
508   g_object_set (h->element, "pt", PT_RED, "allow-no-red-blocks", TRUE, NULL);
509   gst_harness_set_src_caps_str (h, GST_RTP_RED_ENC_CAPS_STR);
510 
511   /* Media buffer has Marker bit set, has CSRCS and flags */
512   bufinp =
513       _new_rtp_buffer (TRUE, csrc_count, PT_MEDIA, seq, TIMESTAMP_NTH (0),
514       0xabe2b0b, sizeof (in_data));
515   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
516   memcpy (gst_rtp_buffer_get_payload (&rtp), &in_data, sizeof (in_data));
517   gst_rtp_buffer_set_csrc (&rtp, 0, 0x1abe2b0b);
518   gst_rtp_buffer_set_csrc (&rtp, 1, 0x2abe2b0b);
519   gst_rtp_buffer_unmap (&rtp);
520   GST_BUFFER_TIMESTAMP (bufinp) = gst_ts;
521   GST_BUFFER_FLAG_SET (bufinp, GST_RTP_BUFFER_FLAG_RETRANSMISSION);
522   GST_BUFFER_FLAG_SET (bufinp, GST_BUFFER_FLAG_DISCONT);
523   bufinp_flags = GST_BUFFER_FLAGS (bufinp);
524   bufout = gst_harness_push_and_pull (h, bufinp);
525 
526   /* Checking that pulled buffer has keeps everything from Media buffer */
527   _check_caps (h, 1, PT_RED);
528   fail_unless (gst_rtp_buffer_map (bufout, GST_MAP_READ, &rtp));
529   fail_unless_equals_int (GST_BUFFER_TIMESTAMP (bufout), gst_ts);
530   fail_unless_equals_int (GST_BUFFER_FLAGS (bufout), bufinp_flags);
531   fail_unless_equals_int (gst_buffer_get_size (bufout),
532       gst_rtp_buffer_calc_packet_len (sizeof (out_data), 0, csrc_count));
533   fail_unless_equals_int (gst_rtp_buffer_get_timestamp (&rtp),
534       TIMESTAMP_NTH (0));
535   fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp), PT_RED);
536   fail_unless_equals_int (gst_rtp_buffer_get_seq (&rtp), seq);
537   fail_unless_equals_int (gst_rtp_buffer_get_csrc_count (&rtp), csrc_count);
538   fail_unless_equals_int (gst_rtp_buffer_get_ssrc (&rtp), 0x0abe2b0b);
539   fail_unless_equals_int (gst_rtp_buffer_get_csrc (&rtp, 0), 0x1abe2b0b);
540   fail_unless_equals_int (gst_rtp_buffer_get_csrc (&rtp, 1), 0x2abe2b0b);
541   fail_unless (gst_rtp_buffer_get_marker (&rtp));
542   fail_unless (!memcmp (gst_rtp_buffer_get_payload (&rtp), out_data,
543           sizeof (out_data)));
544   gst_rtp_buffer_unmap (&rtp);
545   gst_buffer_unref (bufout);
546 
547   _check_red_sent (h, 1);
548   gst_harness_teardown (h);
549 }
550 
551 GST_END_TEST;
552 
GST_START_TEST(rtpredenc_with_redundant_block)553 GST_START_TEST (rtpredenc_with_redundant_block)
554 {
555   GstHarness *h = gst_harness_new ("rtpredenc");
556   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
557   guint8 in_data0[] = { 0xa, 0xa, 0xa, 0xa, 0xa };
558   guint8 in_data1[] = { 0xb, 0xb, 0xb, 0xb, 0xb };
559   guint8 in_data2[] = { 0xc, 0xc, 0xc, 0xc, 0xc };
560   guint timestmapoffset0 = TIMESTAMP_NTH (1) - TIMESTAMP_NTH (0);
561   guint timestmapoffset1 = TIMESTAMP_NTH (2) - TIMESTAMP_NTH (0);
562   guint8 out_data0[] = {
563     /* Redundant block header */
564     0x80 | PT_MEDIA,            /* F=1 | pt=PT_MEDIA */
565     timestmapoffset0 >> 6,      /* timestamp hi 8 bits */
566     timestmapoffset0 & 0x3f,    /* timestamp lo 6 bits | length hi = 0 */
567     sizeof (in_data0),          /* length lo 8 bits */
568     /* Main block header */
569     PT_MEDIA,                   /* F=0 | pt=PT_MEDIA */
570     /* Redundant block data */
571     0xa, 0xa, 0xa, 0xa, 0xa,
572     /* Main block data */
573     0xb, 0xb, 0xb, 0xb, 0xb
574   };
575 
576   guint8 out_data1[] = {
577     /* Redundant block header */
578     0x80 | PT_MEDIA,            /* F=1 | pt=PT_MEDIA */
579     timestmapoffset1 >> 6,      /* timestamp hi 8 bits */
580     timestmapoffset1 & 0x3f,    /* timestamp lo 6 bits | length hi = 0 */
581     sizeof (in_data0),          /* length lo 8 bits */
582     /* Main block header */
583     PT_MEDIA,                   /* F=0 | pt=PT_MEDIA */
584     /* Redundant block data */
585     0xa, 0xa, 0xa, 0xa, 0xa,
586     /* Main block data */
587     0xc, 0xc, 0xc, 0xc, 0xc
588   };
589   guint seq = 549;
590   GstBuffer *bufinp, *bufout;
591 
592   g_object_set (h->element,
593       "pt", PT_RED, "distance", 2, "allow-no-red-blocks", FALSE, NULL);
594   gst_harness_set_src_caps_str (h, GST_RTP_RED_ENC_CAPS_STR);
595 
596   bufinp =
597       _new_rtp_buffer (TRUE, 0, PT_MEDIA, seq, TIMESTAMP_NTH (0), 0xabe2b0b,
598       sizeof (in_data0));
599   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
600   memcpy (gst_rtp_buffer_get_payload (&rtp), &in_data0, sizeof (in_data0));
601   gst_rtp_buffer_unmap (&rtp);
602   bufout = gst_harness_push_and_pull (h, bufinp);
603 
604   /* The first buffer should go through,
605    * there were no redundant data to create RED packet */
606   _check_caps (h, 1, PT_MEDIA);
607   fail_unless (bufout == bufinp);
608   fail_unless (gst_buffer_is_writable (bufout));
609   fail_unless (gst_rtp_buffer_map (bufout, GST_MAP_READ, &rtp));
610   fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp), PT_MEDIA);
611   gst_rtp_buffer_unmap (&rtp);
612   gst_buffer_unref (bufout);
613 
614   bufinp =
615       _new_rtp_buffer (TRUE, 0, PT_MEDIA, seq + 1, TIMESTAMP_NTH (1), 0xabe2b0b,
616       sizeof (in_data1));
617   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
618   memcpy (gst_rtp_buffer_get_payload (&rtp), &in_data1, sizeof (in_data1));
619   gst_rtp_buffer_unmap (&rtp);
620   bufout = gst_harness_push_and_pull (h, bufinp);
621 
622   /* The next buffer is RED referencing previous packet */
623   _check_caps (h, 1, PT_RED);
624   fail_unless (gst_rtp_buffer_map (bufout, GST_MAP_READ, &rtp));
625   fail_unless_equals_int (gst_buffer_get_size (bufout),
626       gst_rtp_buffer_calc_packet_len (sizeof (out_data0), 0, 0));
627   fail_unless_equals_int (gst_rtp_buffer_get_timestamp (&rtp),
628       TIMESTAMP_NTH (1));
629   fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp), PT_RED);
630   fail_unless_equals_int (gst_rtp_buffer_get_seq (&rtp), seq + 1);
631   fail_unless (gst_rtp_buffer_get_marker (&rtp));
632   fail_unless (!memcmp (gst_rtp_buffer_get_payload (&rtp), out_data0,
633           sizeof (out_data0)));
634   gst_rtp_buffer_unmap (&rtp);
635   gst_buffer_unref (bufout);
636 
637   bufinp =
638       _new_rtp_buffer (TRUE, 0, PT_MEDIA, seq + 2, TIMESTAMP_NTH (2), 0xabe2b0b,
639       sizeof (in_data2));
640   fail_unless (gst_rtp_buffer_map (bufinp, GST_MAP_WRITE, &rtp));
641   memcpy (gst_rtp_buffer_get_payload (&rtp), &in_data2, sizeof (in_data2));
642   gst_rtp_buffer_unmap (&rtp);
643   bufout = gst_harness_push_and_pull (h, bufinp);
644 
645   /* The next buffer is RED referencing the packet before the previous */
646   _check_nocaps (h);
647   fail_unless (gst_rtp_buffer_map (bufout, GST_MAP_READ, &rtp));
648   fail_unless_equals_int (gst_buffer_get_size (bufout),
649       gst_rtp_buffer_calc_packet_len (sizeof (out_data1), 0, 0));
650   fail_unless_equals_int (gst_rtp_buffer_get_timestamp (&rtp),
651       TIMESTAMP_NTH (2));
652   fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp), PT_RED);
653   fail_unless_equals_int (gst_rtp_buffer_get_seq (&rtp), seq + 2);
654   fail_unless (gst_rtp_buffer_get_marker (&rtp));
655   fail_unless (!memcmp (gst_rtp_buffer_get_payload (&rtp), out_data1,
656           sizeof (out_data1)));
657   gst_rtp_buffer_unmap (&rtp);
658   gst_buffer_unref (bufout);
659 
660   _check_red_sent (h, 2);
661   gst_harness_teardown (h);
662 }
663 
664 GST_END_TEST;
665 
GST_START_TEST(rtpredenc_transport_cc)666 GST_START_TEST (rtpredenc_transport_cc)
667 {
668   GstHarness *h = gst_harness_new ("rtpredenc");
669   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
670   GstBuffer *bufin;
671   GstBuffer *bufout;
672   guint16 data;
673   gpointer out_data;
674   guint out_size;
675 
676   g_object_set (h->element, "pt", PT_RED, "allow-no-red-blocks", TRUE, NULL);
677   gst_harness_set_src_caps_str (h, GST_RTP_RED_ENC_TWCC_CAPS_STR);
678 
679   /* When we push in a media buffer with a transport-cc extension, the output
680    * RED buffer must hold one too */
681 
682   bufin =
683       _new_rtp_buffer (TRUE, 0, PT_MEDIA, 0, TIMESTAMP_NTH (0), 0xabe2b0b, 0);
684   fail_unless (gst_rtp_buffer_map (bufin, GST_MAP_READ, &rtp));
685   gst_rtp_buffer_add_extension_onebyte_header (&rtp, 1, &data, sizeof (data));
686   gst_rtp_buffer_unmap (&rtp);
687 
688   bufout = gst_harness_push_and_pull (h, bufin);
689 
690   fail_unless (gst_rtp_buffer_map (bufout, GST_MAP_READ, &rtp));
691   fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp), PT_RED);
692   fail_unless (gst_rtp_buffer_get_extension_onebyte_header (&rtp, 1, 0,
693           &out_data, &out_size));
694   gst_rtp_buffer_unmap (&rtp);
695   gst_buffer_unref (bufout);
696 
697   /* And when the input media buffer doesn't hold the extension,
698    * the output buffer shouldn't either */
699 
700   bufin =
701       _new_rtp_buffer (TRUE, 0, PT_MEDIA, 1, TIMESTAMP_NTH (1), 0xabe2b0b, 0);
702   bufout = gst_harness_push_and_pull (h, bufin);
703 
704   fail_unless (gst_rtp_buffer_map (bufout, GST_MAP_READ, &rtp));
705   fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp), PT_RED);
706   fail_if (gst_rtp_buffer_get_extension_onebyte_header (&rtp, 1, 0, &out_data,
707           &out_size));
708   gst_rtp_buffer_unmap (&rtp);
709   gst_buffer_unref (bufout);
710 
711   _check_red_sent (h, 2);
712   gst_harness_teardown (h);
713 }
714 
715 GST_END_TEST;
716 
717 
718 static void
rtpredenc_cant_create_red_packet_base_test(GstBuffer * buffer0,GstBuffer * buffer1)719 rtpredenc_cant_create_red_packet_base_test (GstBuffer * buffer0,
720     GstBuffer * buffer1)
721 {
722   /* The test configures PexRtpRedEnc to produce RED packets only with redundant
723    * blocks. The first packet we pull should not be RED just because it is the
724    * very first one. The second should not be RED because it was impossible
725    * to create a RED packet for varios reasons:
726    * - too large redundant block size
727    * - too large timestamp offset
728    * - negative timestamp offset */
729   GstBuffer *bufout;
730   GstHarness *h = gst_harness_new ("rtpredenc");
731   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
732   g_object_set (h->element,
733       "pt", PT_RED, "distance", 1, "allow-no-red-blocks", FALSE, NULL);
734   gst_harness_set_src_caps_str (h, GST_RTP_RED_ENC_CAPS_STR);
735 
736   /* Checking the first pulled buffer is media packet */
737   bufout = gst_harness_push_and_pull (h, buffer0);
738   _check_caps (h, 1, PT_MEDIA);
739   fail_unless (gst_rtp_buffer_map (bufout, GST_MAP_READ, &rtp));
740   fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp), PT_MEDIA);
741   gst_rtp_buffer_unmap (&rtp);
742   gst_buffer_unref (bufout);
743 
744   /* The next buffer should be media packet too */
745   bufout = gst_harness_push_and_pull (h, buffer1);
746   _check_nocaps (h);
747   fail_unless (gst_rtp_buffer_map (bufout, GST_MAP_READ, &rtp));
748   fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp), PT_MEDIA);
749   gst_rtp_buffer_unmap (&rtp);
750   gst_buffer_unref (bufout);
751 
752   _check_red_sent (h, 0);
753   gst_harness_teardown (h);
754 }
755 
GST_START_TEST(rtpredenc_negative_timestamp_offset)756 GST_START_TEST (rtpredenc_negative_timestamp_offset)
757 {
758   gboolean with_warping;
759   guint16 seq0, seq1;
760   guint32 timestamp0, timestamp1;
761   GstBuffer *buffer0, *buffer1;
762   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
763   guint8 in_data[] = { 0xa, 0xa, 0xa, 0xa, 0xa };
764 
765   with_warping = __i__ != 0;
766   timestamp0 =
767       with_warping ? (0xffffffff - TIMESTAMP_DIFF / 2) : TIMESTAMP_BASE;
768   timestamp1 = timestamp0 + TIMESTAMP_DIFF;
769   seq0 = with_warping ? 0xffff : 0;
770   seq1 = seq0 + 1;
771 
772   /* Two buffers have negative timestamp difference */
773   buffer0 =
774       _new_rtp_buffer (TRUE, 0, PT_MEDIA, seq0, timestamp1, 0xabe2b0b,
775       sizeof (in_data));
776   buffer1 =
777       _new_rtp_buffer (TRUE, 0, PT_MEDIA, seq1, timestamp0, 0xabe2b0b,
778       sizeof (in_data));
779 
780   fail_unless (gst_rtp_buffer_map (buffer0, GST_MAP_WRITE, &rtp));
781   memcpy (gst_rtp_buffer_get_payload (&rtp), &in_data, sizeof (in_data));
782   gst_rtp_buffer_unmap (&rtp);
783 
784   fail_unless (gst_rtp_buffer_map (buffer1, GST_MAP_WRITE, &rtp));
785   memcpy (gst_rtp_buffer_get_payload (&rtp), &in_data, sizeof (in_data));
786   gst_rtp_buffer_unmap (&rtp);
787 
788   rtpredenc_cant_create_red_packet_base_test (buffer0, buffer1);
789 }
790 
791 GST_END_TEST;
792 
GST_START_TEST(rtpredenc_too_large_timestamp_offset)793 GST_START_TEST (rtpredenc_too_large_timestamp_offset)
794 {
795   gboolean with_warping;
796   guint16 seq0, seq1;
797   guint32 timestamp0, timestamp1, timestamp_diff;
798   GstBuffer *buffer0, *buffer1;
799   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
800   guint8 in_data[] = { 0xa, 0xa, 0xa, 0xa, 0xa };
801 
802   with_warping = __i__ != 0;
803   timestamp_diff = 0x4000;
804   timestamp0 =
805       with_warping ? (0xffffffff - timestamp_diff / 2) : TIMESTAMP_BASE;
806   timestamp1 = timestamp0 + timestamp_diff;
807 
808   seq0 = with_warping ? 0xffff : 0;
809   seq1 = seq0 + 1;
810 
811   /* Two buffers have timestamp difference > 14bit long */
812   buffer0 =
813       _new_rtp_buffer (TRUE, 0, PT_MEDIA, seq0, timestamp0, 0xabe2b0b,
814       sizeof (in_data));
815   buffer1 =
816       _new_rtp_buffer (TRUE, 0, PT_MEDIA, seq1, timestamp1, 0xabe2b0b,
817       sizeof (in_data));
818   fail_unless (gst_rtp_buffer_map (buffer0, GST_MAP_WRITE, &rtp));
819   memcpy (gst_rtp_buffer_get_payload (&rtp), &in_data, sizeof (in_data));
820   gst_rtp_buffer_unmap (&rtp);
821 
822   fail_unless (gst_rtp_buffer_map (buffer1, GST_MAP_WRITE, &rtp));
823   memcpy (gst_rtp_buffer_get_payload (&rtp), &in_data, sizeof (in_data));
824   gst_rtp_buffer_unmap (&rtp);
825 
826   rtpredenc_cant_create_red_packet_base_test (buffer0, buffer1);
827 }
828 
829 GST_END_TEST;
830 
GST_START_TEST(rtpredenc_too_large_length)831 GST_START_TEST (rtpredenc_too_large_length)
832 {
833   gboolean with_warping;
834   guint16 seq0, seq1;
835   guint32 timestamp0, timestamp1;
836   GstBuffer *buffer0, *buffer1;
837   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
838   guint8 in_data0[1024] = { 0, };
839   guint8 in_data1[] = { 0xa, 0xa, 0xa, 0xa, 0xa };
840 
841   with_warping = __i__ != 0;
842   timestamp0 =
843       with_warping ? (0xffffffff - TIMESTAMP_DIFF / 2) : TIMESTAMP_BASE;
844   timestamp1 = timestamp0 + TIMESTAMP_DIFF;
845   seq0 = with_warping ? 0xffff : 0;
846   seq1 = seq0 + 1;
847 
848   /* The first buffer is too large to use as a redundant block */
849   buffer0 =
850       _new_rtp_buffer (TRUE, 0, PT_MEDIA, seq0, timestamp0, 0xabe2b0b,
851       sizeof (in_data0));
852   buffer1 =
853       _new_rtp_buffer (TRUE, 0, PT_MEDIA, seq1, timestamp1, 0xabe2b0b,
854       sizeof (in_data1));
855   fail_unless (gst_rtp_buffer_map (buffer0, GST_MAP_WRITE, &rtp));
856   memcpy (gst_rtp_buffer_get_payload (&rtp), &in_data0, sizeof (in_data0));
857   gst_rtp_buffer_unmap (&rtp);
858 
859   fail_unless (gst_rtp_buffer_map (buffer1, GST_MAP_WRITE, &rtp));
860   memcpy (gst_rtp_buffer_get_payload (&rtp), &in_data1, sizeof (in_data1));
861   gst_rtp_buffer_unmap (&rtp);
862 
863   rtpredenc_cant_create_red_packet_base_test (buffer0, buffer1);
864 }
865 
866 GST_END_TEST;
867 
868 static Suite *
rtpred_suite(void)869 rtpred_suite (void)
870 {
871   Suite *s = suite_create ("rtpred");
872   TCase *tc_chain = tcase_create ("decoder");
873   suite_add_tcase (s, tc_chain);
874   tcase_add_test (tc_chain, rtpreddec_passthrough);
875   tcase_add_test (tc_chain, rtpreddec_main_block);
876   tcase_add_test (tc_chain, rtpreddec_redundant_block_not_pushed);
877   tcase_add_test (tc_chain, rtpreddec_redundant_block_pushed);
878   tcase_add_test (tc_chain, rtpreddec_invalid);
879 
880   tc_chain = tcase_create ("encoder");
881   suite_add_tcase (s, tc_chain);
882   tcase_add_test (tc_chain, rtpredenc_passthrough);
883   tcase_add_test (tc_chain, rtpredenc_payloadless_rtp);
884   tcase_add_test (tc_chain, rtpredenc_without_redundant_block);
885   tcase_add_test (tc_chain, rtpredenc_with_redundant_block);
886   tcase_add_loop_test (tc_chain, rtpredenc_negative_timestamp_offset, 0, 2);
887   tcase_add_loop_test (tc_chain, rtpredenc_too_large_timestamp_offset, 0, 2);
888   tcase_add_loop_test (tc_chain, rtpredenc_too_large_length, 0, 2);
889   tcase_add_test (tc_chain, rtpredenc_transport_cc);
890 
891   return s;
892 }
893 
894 GST_CHECK_MAIN (rtpred)
895