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