• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) <2020> Mathieu Duponchelle <mathieu@centricular.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #include <gst/check/gstcheck.h>
21 #include <gst/check/gstharness.h>
22 #include <gst/rtp/gstrtpbuffer.h>
23 #include <gst/base/base.h>
24 
25 static GstBuffer *
make_fec_sample(guint16 seq,guint32 ts,guint16 seq_base,gboolean row,guint8 offset,guint8 NA,guint32 ts_recovery,guint8 * fec_payload,guint fec_payload_len,guint16 length_recovery)26 make_fec_sample (guint16 seq, guint32 ts, guint16 seq_base, gboolean row,
27     guint8 offset, guint8 NA, guint32 ts_recovery, guint8 * fec_payload,
28     guint fec_payload_len, guint16 length_recovery)
29 {
30   GstBuffer *ret;
31   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
32   GstBitWriter bits;
33   guint8 *data;
34 
35   ret = gst_rtp_buffer_new_allocate (16 + fec_payload_len, 0, 0);
36 
37   fail_unless (gst_rtp_buffer_map (ret, GST_MAP_WRITE, &rtp));
38 
39   data = gst_rtp_buffer_get_payload (&rtp);
40   memset (data, 0x00, 16);
41 
42   gst_bit_writer_init_with_data (&bits, data, 17, FALSE);
43 
44   gst_bit_writer_put_bits_uint16 (&bits, seq_base, 16); /* SNBase low bits */
45   gst_bit_writer_put_bits_uint16 (&bits, length_recovery, 16);  /* Length Recovery */
46   gst_bit_writer_put_bits_uint8 (&bits, 1, 1);  /* E */
47   gst_bit_writer_put_bits_uint8 (&bits, 0x21, 7);       /* PT recovery */
48   gst_bit_writer_put_bits_uint32 (&bits, 0, 24);        /* Mask */
49   gst_bit_writer_put_bits_uint32 (&bits, ts_recovery, 32);      /* TS recovery */
50   gst_bit_writer_put_bits_uint8 (&bits, 0, 1);  /* N */
51   gst_bit_writer_put_bits_uint8 (&bits, row ? 1 : 0, 1);        /* D */
52   gst_bit_writer_put_bits_uint8 (&bits, 0, 3);  /* type */
53   gst_bit_writer_put_bits_uint8 (&bits, 0, 3);  /* index */
54   gst_bit_writer_put_bits_uint8 (&bits, offset, 8);     /* Offset */
55   gst_bit_writer_put_bits_uint8 (&bits, NA, 8); /* NA */
56   gst_bit_writer_put_bits_uint8 (&bits, 0, 8);  /* SNBase ext bits */
57 
58   memcpy (data + 16, fec_payload, fec_payload_len);
59 
60   gst_bit_writer_reset (&bits);
61 
62   GST_MEMDUMP ("fec", data, 16 + fec_payload_len);
63 
64   gst_rtp_buffer_set_payload_type (&rtp, 96);
65   gst_rtp_buffer_set_seq (&rtp, seq);
66   gst_rtp_buffer_set_timestamp (&rtp, ts);
67   gst_rtp_buffer_unmap (&rtp);
68 
69   return ret;
70 }
71 
72 static GstBuffer *
make_media_sample(guint16 seq,guint32 ts,guint8 * payload,guint payload_len)73 make_media_sample (guint16 seq, guint32 ts, guint8 * payload, guint payload_len)
74 {
75   GstBuffer *ret;
76   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
77   guint8 *data;
78 
79   ret = gst_rtp_buffer_new_allocate (payload_len, 0, 0);
80 
81   gst_rtp_buffer_map (ret, GST_MAP_WRITE, &rtp);
82   gst_rtp_buffer_set_payload_type (&rtp, 33);
83   gst_rtp_buffer_set_seq (&rtp, seq);
84   gst_rtp_buffer_set_timestamp (&rtp, ts);
85   data = gst_rtp_buffer_get_payload (&rtp);
86   memcpy (data, payload, payload_len);
87   gst_rtp_buffer_unmap (&rtp);
88 
89   return ret;
90 }
91 
92 static void
pull_and_check(GstHarness * h,guint16 seq,guint32 ts,guint8 * payload,guint payload_len,guint n_in_queue)93 pull_and_check (GstHarness * h, guint16 seq, guint32 ts, guint8 * payload,
94     guint payload_len, guint n_in_queue)
95 {
96   GstBuffer *buffer;
97   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
98   guint8 *data;
99   guint i;
100 
101   fail_unless_equals_int (gst_harness_buffers_in_queue (h), n_in_queue);
102   buffer = gst_harness_pull (h);
103 
104   fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
105 
106   fail_unless_equals_int (gst_rtp_buffer_get_seq (&rtp), seq);
107   fail_unless_equals_int (gst_rtp_buffer_get_timestamp (&rtp), ts);
108   fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp), 33);
109   fail_unless_equals_int (gst_rtp_buffer_get_payload_len (&rtp), payload_len);
110   data = gst_rtp_buffer_get_payload (&rtp);
111 
112   for (i = 0; i < payload_len; i++)
113     fail_unless_equals_int (data[i], payload[i]);
114 
115   gst_rtp_buffer_unmap (&rtp);
116 
117   gst_buffer_unref (buffer);
118 }
119 
120 /**
121  * +--------------+
122  * | 9  | 10 |  x | l1
123  * | 12 | 13 |  x | l2
124  * | x  | x  |  x |
125  * +--------------+
126  *   x    x     x
127  *
128  * Missing values:
129  * 11: 0xc5
130  * 14: 0xb8
131  */
GST_START_TEST(test_row)132 GST_START_TEST (test_row)
133 {
134   guint8 payload;
135   GstHarness *h =
136       gst_harness_new_with_padnames ("rtpst2022-1-fecdec", NULL, "src");
137   GstHarness *h0 = gst_harness_new_with_element (h->element, "sink", NULL);
138   GstHarness *h_fec_1 =
139       gst_harness_new_with_element (h->element, "fec_1", NULL);
140 
141   gst_harness_set_src_caps_str (h0, "application/x-rtp");
142   gst_harness_set_src_caps_str (h_fec_1, "application/x-rtp");
143 
144   payload = 0x37;
145   gst_harness_push (h0, make_media_sample (9, 0, &payload, 1));
146   payload = 0x28;
147   gst_harness_push (h0, make_media_sample (10, 0, &payload, 1));
148   payload = 0xff;
149   gst_harness_push (h0, make_media_sample (12, 0, &payload, 1));
150 
151   /* We receive 9, 10 and 12 */
152   fail_unless_equals_int (gst_harness_buffers_in_queue (h), 3);
153   while (gst_harness_buffers_in_queue (h)) {
154     gst_buffer_unref (gst_harness_pull (h));
155   }
156 
157   payload = 0xda;
158   gst_harness_push (h_fec_1, make_fec_sample (0, 0, 9, TRUE, 1, 3, 0, &payload,
159           1, 1));
160 
161   /* After pushing l1, we should have enough info to reconstruct 11 */
162   payload = 0xc5;
163   pull_and_check (h, 11, 0, &payload, 1, 1);
164 
165   /* Now we try to push l2 before 13, to verify that 14 is eventually
166    * reconstructed once 13 is pushed */
167   payload = 0x02;
168   gst_harness_push (h_fec_1, make_fec_sample (1, 0, 12, TRUE, 1, 3, 0, &payload,
169           1, 1));
170   fail_unless_equals_int (gst_harness_buffers_in_queue (h), 0);
171   payload = 0x45;
172   gst_harness_push (h0, make_media_sample (13, 0, &payload, 1));
173   fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
174   payload = 0xb8;
175   pull_and_check (h, 14, 0, &payload, 1, 2);
176   payload = 0x45;
177   pull_and_check (h, 13, 0, &payload, 1, 1);
178 
179   gst_harness_teardown (h);
180   gst_harness_teardown (h0);
181   gst_harness_teardown (h_fec_1);
182 }
183 
184 GST_END_TEST;
185 
186 /**
187  * +--------------+
188  * | 7  | 8  |  x | x
189  * | 10 | 11 |  x | x
190  * | x  | x  |  x |
191  * +--------------+
192  *   d1   d2    x
193  *
194  * Missing values:
195  * 13: 0xc5
196  * 14: 0x51
197  */
GST_START_TEST(test_column)198 GST_START_TEST (test_column)
199 {
200   guint8 payload;
201   GstHarness *h =
202       gst_harness_new_with_padnames ("rtpst2022-1-fecdec", NULL, "src");
203   GstHarness *h0 = gst_harness_new_with_element (h->element, "sink", NULL);
204   GstHarness *h_fec_0 =
205       gst_harness_new_with_element (h->element, "fec_0", NULL);
206 
207   gst_harness_set_src_caps_str (h0, "application/x-rtp");
208   gst_harness_set_src_caps_str (h_fec_0, "application/x-rtp");
209 
210   payload = 0x37;
211   gst_harness_push (h0, make_media_sample (7, 0, &payload, 1));
212   payload = 0x28;
213   gst_harness_push (h0, make_media_sample (10, 0, &payload, 1));
214 
215   fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
216   while (gst_harness_buffers_in_queue (h))
217     gst_buffer_unref (gst_harness_pull (h));
218 
219   payload = 0xda;
220   gst_harness_push (h_fec_0, make_fec_sample (0, 0, 7, FALSE, 3, 3, 0, &payload,
221           1, 1));
222 
223   /* After pushing d1, we should have enough info to reconstruct 13 */
224   payload = 0xc5;
225   pull_and_check (h, 13, 0, &payload, 1, 1);
226 
227   /* Now we try to push d2 before 8 and 11, to verify that 14 is eventually
228    * reconstructed once 11 is pushed */
229   payload = 0x04;
230   gst_harness_push (h_fec_0, make_fec_sample (1, 0, 8, FALSE, 3, 3, 0, &payload,
231           1, 1));
232   payload = 0x21;
233   gst_harness_push (h0, make_media_sample (8, 0, &payload, 1));
234 
235   fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
236   while (gst_harness_buffers_in_queue (h))
237     gst_buffer_unref (gst_harness_pull (h));
238 
239   payload = 0x74;
240   gst_harness_push (h0, make_media_sample (11, 0, &payload, 1));
241   payload = 0x51;
242   pull_and_check (h, 14, 0, &payload, 1, 2);
243   payload = 0x74;
244   pull_and_check (h, 11, 0, &payload, 1, 1);
245 
246   gst_harness_teardown (h);
247   gst_harness_teardown (h0);
248   gst_harness_teardown (h_fec_0);
249 }
250 
251 GST_END_TEST;
252 
253 
254 /*
255  * +-----------+
256  * | 0 | 1 | x | x
257  * | 3 | 4 | x | l1
258  * | 6 | x | x | l2
259  * +-----------+
260  *   d0  d1  d2
261  *
262  * We should be able to retrieve 2 by retrieving 5 7 and 8 first.
263  *
264  * Missing values:
265  * 2: 0xfc
266  * 5: 0x3a
267  * 7: 0x5f
268  * 8: 0x21
269  */
270 
GST_START_TEST(test_2d)271 GST_START_TEST (test_2d)
272 {
273   guint8 payload;
274   GstHarness *h =
275       gst_harness_new_with_padnames ("rtpst2022-1-fecdec", NULL, "src");
276   GstHarness *h0 = gst_harness_new_with_element (h->element, "sink", NULL);
277   GstHarness *h_fec_0 =
278       gst_harness_new_with_element (h->element, "fec_0", NULL);
279   GstHarness *h_fec_1 =
280       gst_harness_new_with_element (h->element, "fec_1", NULL);
281 
282   gst_harness_set_src_caps_str (h0, "application/x-rtp");
283   gst_harness_set_src_caps_str (h_fec_0, "application/x-rtp");
284   gst_harness_set_src_caps_str (h_fec_1, "application/x-rtp");
285 
286   payload = 0xde;
287   gst_harness_push (h0, make_media_sample (0, 0, &payload, 1));
288   payload = 0xad;
289   gst_harness_push (h0, make_media_sample (1, 0, &payload, 1));
290   payload = 0xbe;
291   gst_harness_push (h0, make_media_sample (3, 0, &payload, 1));
292   payload = 0xef;
293   gst_harness_push (h0, make_media_sample (4, 0, &payload, 1));
294   payload = 0x42;
295   gst_harness_push (h0, make_media_sample (6, 0, &payload, 1));
296 
297   /* row FEC */
298   /* l1 0xbe ^ 0xef ^ 0x3a */
299   payload = 0x6b;
300   gst_harness_push (h_fec_1, make_fec_sample (0, 0, 3, TRUE, 1, 3, 0, &payload,
301           1, 1));
302   /* l2 0x42 ^ 0x5f ^ 0x21 */
303   payload = 0x3c;
304   gst_harness_push (h_fec_1, make_fec_sample (0, 0, 6, TRUE, 1, 3, 0, &payload,
305           1, 1));
306 
307   /* column FEC */
308   /* d0 0xde ^ 0xbe ^ 0x42 */
309   payload = 0x22;
310   gst_harness_push (h_fec_0, make_fec_sample (0, 0, 0, FALSE, 3, 3, 0, &payload,
311           1, 1));
312   /* d1 0xad ^ 0xef ^ 0x5f */
313   payload = 0x1d;
314   gst_harness_push (h_fec_0, make_fec_sample (1, 0, 1, FALSE, 3, 3, 0, &payload,
315           1, 1));
316   /* d2 0xfc ^ 0x3a ^ 0x21 */
317   payload = 0xe7;
318   gst_harness_push (h_fec_0, make_fec_sample (2, 0, 2, FALSE, 3, 3, 0, &payload,
319           1, 1));
320 
321   /* We should retrieve all 9 packets despite dropping 4! */
322   payload = 0xde;
323   pull_and_check (h, 0, 0, &payload, 1, 9);
324   payload = 0xad;
325   pull_and_check (h, 1, 0, &payload, 1, 8);
326   payload = 0xbe;
327   pull_and_check (h, 3, 0, &payload, 1, 7);
328   payload = 0xef;
329   pull_and_check (h, 4, 0, &payload, 1, 6);
330   payload = 0x42;
331   pull_and_check (h, 6, 0, &payload, 1, 5);
332   payload = 0x3a;
333   pull_and_check (h, 5, 0, &payload, 1, 4);
334   payload = 0x21;
335   pull_and_check (h, 8, 0, &payload, 1, 3);
336   payload = 0x5f;
337   pull_and_check (h, 7, 0, &payload, 1, 2);
338   payload = 0xfc;
339   pull_and_check (h, 2, 0, &payload, 1, 1);
340 
341   gst_harness_teardown (h);
342   gst_harness_teardown (h0);
343   gst_harness_teardown (h_fec_0);
344   gst_harness_teardown (h_fec_1);
345 }
346 
347 GST_END_TEST;
348 
349 static void
_xor_mem(guint8 * restrict dst,const guint8 * restrict src,gsize length)350 _xor_mem (guint8 * restrict dst, const guint8 * restrict src, gsize length)
351 {
352   guint i;
353 
354   for (i = 0; i < (length / sizeof (guint64)); ++i) {
355 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
356     GST_WRITE_UINT64_LE (dst,
357         GST_READ_UINT64_LE (dst) ^ GST_READ_UINT64_LE (src));
358 #else
359     GST_WRITE_UINT64_BE (dst,
360         GST_READ_UINT64_BE (dst) ^ GST_READ_UINT64_BE (src));
361 #endif
362     dst += sizeof (guint64);
363     src += sizeof (guint64);
364   }
365   for (i = 0; i < (length % sizeof (guint64)); ++i)
366     dst[i] ^= src[i];
367 }
368 
369 /**
370  * +-----------------+
371  * | 0-1 | 1-3 | x-4 | l1
372  * +-----------------+
373  *   x    x     x
374  *
375  * Missing values:
376  * 2: 0xc5b74108
377  */
GST_START_TEST(test_variable_length)378 GST_START_TEST (test_variable_length)
379 {
380   guint8 payload[4];
381   guint8 fec_payload[4];
382   GstHarness *h =
383       gst_harness_new_with_padnames ("rtpst2022-1-fecdec", NULL, "src");
384   GstHarness *h0 = gst_harness_new_with_element (h->element, "sink", NULL);
385   GstHarness *h_fec_1 =
386       gst_harness_new_with_element (h->element, "fec_1", NULL);
387 
388   gst_harness_set_src_caps_str (h0, "application/x-rtp");
389   gst_harness_set_src_caps_str (h_fec_1, "application/x-rtp");
390 
391   memset (fec_payload, 0x00, 4);
392 
393   payload[0] = 0x37;
394   _xor_mem (fec_payload, payload, 1);
395   gst_harness_push (h0, make_media_sample (0, 0, payload, 1));
396 
397   payload[0] = 0x28;
398   payload[1] = 0x39;
399   payload[2] = 0x56;
400   _xor_mem (fec_payload, payload, 3);
401   gst_harness_push (h0, make_media_sample (1, 0, payload, 3));
402 
403   /* We receive 0 and 1 */
404   fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
405   while (gst_harness_buffers_in_queue (h)) {
406     gst_buffer_unref (gst_harness_pull (h));
407   }
408 
409   payload[0] = 0xc5;
410   payload[1] = 0xb7;
411   payload[2] = 0x41;
412   payload[3] = 0x08;
413 
414   _xor_mem (fec_payload, payload, 4);
415   gst_harness_push (h_fec_1, make_fec_sample (0, 0, 0, TRUE, 1, 3, 0,
416           fec_payload, 4, 1 ^ 3 ^ 4));
417 
418   pull_and_check (h, 2, 0, payload, 4, 1);
419 
420   gst_harness_teardown (h);
421   gst_harness_teardown (h0);
422   gst_harness_teardown (h_fec_1);
423 }
424 
425 GST_END_TEST;
426 
427 
428 static Suite *
st2022_1_dec_suite(void)429 st2022_1_dec_suite (void)
430 {
431   Suite *s = suite_create ("rtpst2022-1-fecdec");
432   TCase *tc_chain = tcase_create ("general");
433 
434   suite_add_tcase (s, tc_chain);
435 
436   tcase_add_test (tc_chain, test_row);
437   tcase_add_test (tc_chain, test_column);
438   tcase_add_test (tc_chain, test_2d);
439   tcase_add_test (tc_chain, test_variable_length);
440 
441   return s;
442 }
443 
444 GST_CHECK_MAIN (st2022_1_dec)
445