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