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