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