diff options
author | Mathieu Duponchelle <mathieu@centricular.com> | 2020-10-06 04:13:30 +0300 |
---|---|---|
committer | GStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org> | 2020-10-09 01:22:18 +0300 |
commit | cff42d4c26ddf268fd50973aa6d086bc18694768 (patch) | |
tree | 6622a3f65a61014ed299e366a1815bc02043c129 /tests | |
parent | 7c9a5e86fe3a504811aad9af7c86b64d7ea5a8d7 (diff) |
rtpmanager: implement SMPTE 2022-1 FEC decoder
+ improve integration of FEC decoders in rtpbin
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/753>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/check/elements/rtpst2022-1-fecdec.c | 444 | ||||
-rw-r--r-- | tests/check/meson.build | 1 |
2 files changed, 445 insertions, 0 deletions
diff --git a/tests/check/elements/rtpst2022-1-fecdec.c b/tests/check/elements/rtpst2022-1-fecdec.c new file mode 100644 index 000000000..dd1ec451a --- /dev/null +++ b/tests/check/elements/rtpst2022-1-fecdec.c @@ -0,0 +1,444 @@ +/* GStreamer + * Copyright (C) <2020> Mathieu Duponchelle <mathieu@centricular.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <gst/check/gstcheck.h> +#include <gst/check/gstharness.h> +#include <gst/rtp/gstrtpbuffer.h> +#include <gst/base/base.h> + +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) +{ + GstBuffer *ret; + GstRTPBuffer rtp = GST_RTP_BUFFER_INIT; + GstBitWriter bits; + guint8 *data; + + ret = gst_rtp_buffer_new_allocate (16 + fec_payload_len, 0, 0); + + fail_unless (gst_rtp_buffer_map (ret, GST_MAP_WRITE, &rtp)); + + data = gst_rtp_buffer_get_payload (&rtp); + memset (data, 0x00, 16); + + gst_bit_writer_init_with_data (&bits, data, 17, FALSE); + + gst_bit_writer_put_bits_uint16 (&bits, seq_base, 16); /* SNBase low bits */ + gst_bit_writer_put_bits_uint16 (&bits, length_recovery, 16); /* Length Recovery */ + gst_bit_writer_put_bits_uint8 (&bits, 1, 1); /* E */ + gst_bit_writer_put_bits_uint8 (&bits, 0x21, 7); /* PT recovery */ + gst_bit_writer_put_bits_uint32 (&bits, 0, 24); /* Mask */ + gst_bit_writer_put_bits_uint32 (&bits, ts_recovery, 32); /* TS recovery */ + gst_bit_writer_put_bits_uint8 (&bits, 0, 1); /* N */ + gst_bit_writer_put_bits_uint8 (&bits, row ? 1 : 0, 1); /* D */ + gst_bit_writer_put_bits_uint8 (&bits, 0, 3); /* type */ + gst_bit_writer_put_bits_uint8 (&bits, 0, 3); /* index */ + gst_bit_writer_put_bits_uint8 (&bits, offset, 8); /* Offset */ + gst_bit_writer_put_bits_uint8 (&bits, NA, 8); /* NA */ + gst_bit_writer_put_bits_uint8 (&bits, 0, 8); /* SNBase ext bits */ + + memcpy (data + 16, fec_payload, fec_payload_len); + + gst_bit_writer_reset (&bits); + + GST_MEMDUMP ("fec", data, 16 + fec_payload_len); + + gst_rtp_buffer_set_payload_type (&rtp, 96); + gst_rtp_buffer_set_seq (&rtp, seq); + gst_rtp_buffer_set_timestamp (&rtp, ts); + gst_rtp_buffer_unmap (&rtp); + + return ret; +} + +static GstBuffer * +make_media_sample (guint16 seq, guint32 ts, guint8 * payload, guint payload_len) +{ + GstBuffer *ret; + GstRTPBuffer rtp = GST_RTP_BUFFER_INIT; + guint8 *data; + + ret = gst_rtp_buffer_new_allocate (payload_len, 0, 0); + + gst_rtp_buffer_map (ret, GST_MAP_WRITE, &rtp); + gst_rtp_buffer_set_payload_type (&rtp, 33); + gst_rtp_buffer_set_seq (&rtp, seq); + gst_rtp_buffer_set_timestamp (&rtp, ts); + data = gst_rtp_buffer_get_payload (&rtp); + memcpy (data, payload, payload_len); + gst_rtp_buffer_unmap (&rtp); + + return ret; +} + +static void +pull_and_check (GstHarness * h, guint16 seq, guint32 ts, guint8 * payload, + guint payload_len, guint n_in_queue) +{ + GstBuffer *buffer; + GstRTPBuffer rtp = GST_RTP_BUFFER_INIT; + guint8 *data; + guint i; + + fail_unless_equals_int (gst_harness_buffers_in_queue (h), n_in_queue); + buffer = gst_harness_pull (h); + + fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp)); + + fail_unless_equals_int (gst_rtp_buffer_get_seq (&rtp), seq); + fail_unless_equals_int (gst_rtp_buffer_get_timestamp (&rtp), ts); + fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp), 33); + fail_unless_equals_int (gst_rtp_buffer_get_payload_len (&rtp), payload_len); + data = gst_rtp_buffer_get_payload (&rtp); + + for (i = 0; i < payload_len; i++) + fail_unless_equals_int (data[i], payload[i]); + + gst_rtp_buffer_unmap (&rtp); + + gst_buffer_unref (buffer); +} + +/** + * +--------------+ + * | 9 | 10 | x | l1 + * | 12 | 13 | x | l2 + * | x | x | x | + * +--------------+ + * x x x + * + * Missing values: + * 11: 0xc5 + * 14: 0xb8 + */ +GST_START_TEST (test_row) +{ + guint8 payload; + GstHarness *h = + gst_harness_new_with_padnames ("rtpst2022-1-fecdec", NULL, "src"); + GstHarness *h0 = gst_harness_new_with_element (h->element, "sink", NULL); + GstHarness *h_fec_1 = + gst_harness_new_with_element (h->element, "fec_1", NULL); + + gst_harness_set_src_caps_str (h0, "application/x-rtp"); + gst_harness_set_src_caps_str (h_fec_1, "application/x-rtp"); + + payload = 0x37; + gst_harness_push (h0, make_media_sample (9, 0, &payload, 1)); + payload = 0x28; + gst_harness_push (h0, make_media_sample (10, 0, &payload, 1)); + payload = 0xff; + gst_harness_push (h0, make_media_sample (12, 0, &payload, 1)); + + /* We receive 9, 10 and 12 */ + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 3); + while (gst_harness_buffers_in_queue (h)) { + gst_buffer_unref (gst_harness_pull (h)); + } + + payload = 0xda; + gst_harness_push (h_fec_1, make_fec_sample (0, 0, 9, TRUE, 1, 3, 0, &payload, + 1, 1)); + + /* After pushing l1, we should have enough info to reconstruct 11 */ + payload = 0xc5; + pull_and_check (h, 11, 0, &payload, 1, 1); + + /* Now we try to push l2 before 13, to verify that 14 is eventually + * reconstructed once 13 is pushed */ + payload = 0x02; + gst_harness_push (h_fec_1, make_fec_sample (1, 0, 12, TRUE, 1, 3, 0, &payload, + 1, 1)); + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 0); + payload = 0x45; + gst_harness_push (h0, make_media_sample (13, 0, &payload, 1)); + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2); + payload = 0xb8; + pull_and_check (h, 14, 0, &payload, 1, 2); + payload = 0x45; + pull_and_check (h, 13, 0, &payload, 1, 1); + + gst_harness_teardown (h); + gst_harness_teardown (h0); + gst_harness_teardown (h_fec_1); +} + +GST_END_TEST; + +/** + * +--------------+ + * | 7 | 8 | x | x + * | 10 | 11 | x | x + * | x | x | x | + * +--------------+ + * d1 d2 x + * + * Missing values: + * 13: 0xc5 + * 14: 0x51 + */ +GST_START_TEST (test_column) +{ + guint8 payload; + GstHarness *h = + gst_harness_new_with_padnames ("rtpst2022-1-fecdec", NULL, "src"); + GstHarness *h0 = gst_harness_new_with_element (h->element, "sink", NULL); + GstHarness *h_fec_0 = + gst_harness_new_with_element (h->element, "fec_0", NULL); + + gst_harness_set_src_caps_str (h0, "application/x-rtp"); + gst_harness_set_src_caps_str (h_fec_0, "application/x-rtp"); + + payload = 0x37; + gst_harness_push (h0, make_media_sample (7, 0, &payload, 1)); + payload = 0x28; + gst_harness_push (h0, make_media_sample (10, 0, &payload, 1)); + + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2); + while (gst_harness_buffers_in_queue (h)) + gst_buffer_unref (gst_harness_pull (h)); + + payload = 0xda; + gst_harness_push (h_fec_0, make_fec_sample (0, 0, 7, FALSE, 3, 3, 0, &payload, + 1, 1)); + + /* After pushing d1, we should have enough info to reconstruct 13 */ + payload = 0xc5; + pull_and_check (h, 13, 0, &payload, 1, 1); + + /* Now we try to push d2 before 8 and 11, to verify that 14 is eventually + * reconstructed once 11 is pushed */ + payload = 0x04; + gst_harness_push (h_fec_0, make_fec_sample (1, 0, 8, FALSE, 3, 3, 0, &payload, + 1, 1)); + payload = 0x21; + gst_harness_push (h0, make_media_sample (8, 0, &payload, 1)); + + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1); + while (gst_harness_buffers_in_queue (h)) + gst_buffer_unref (gst_harness_pull (h)); + + payload = 0x74; + gst_harness_push (h0, make_media_sample (11, 0, &payload, 1)); + payload = 0x51; + pull_and_check (h, 14, 0, &payload, 1, 2); + payload = 0x74; + pull_and_check (h, 11, 0, &payload, 1, 1); + + gst_harness_teardown (h); + gst_harness_teardown (h0); + gst_harness_teardown (h_fec_0); +} + +GST_END_TEST; + + +/* + * +-----------+ + * | 0 | 1 | x | x + * | 3 | 4 | x | l1 + * | 6 | x | x | l2 + * +-----------+ + * d0 d1 d2 + * + * We should be able to retrieve 2 by retrieving 5 7 and 8 first. + * + * Missing values: + * 2: 0xfc + * 5: 0x3a + * 7: 0x5f + * 8: 0x21 + */ + +GST_START_TEST (test_2d) +{ + guint8 payload; + GstHarness *h = + gst_harness_new_with_padnames ("rtpst2022-1-fecdec", NULL, "src"); + GstHarness *h0 = gst_harness_new_with_element (h->element, "sink", NULL); + GstHarness *h_fec_0 = + gst_harness_new_with_element (h->element, "fec_0", NULL); + GstHarness *h_fec_1 = + gst_harness_new_with_element (h->element, "fec_1", NULL); + + gst_harness_set_src_caps_str (h0, "application/x-rtp"); + gst_harness_set_src_caps_str (h_fec_0, "application/x-rtp"); + gst_harness_set_src_caps_str (h_fec_1, "application/x-rtp"); + + payload = 0xde; + gst_harness_push (h0, make_media_sample (0, 0, &payload, 1)); + payload = 0xad; + gst_harness_push (h0, make_media_sample (1, 0, &payload, 1)); + payload = 0xbe; + gst_harness_push (h0, make_media_sample (3, 0, &payload, 1)); + payload = 0xef; + gst_harness_push (h0, make_media_sample (4, 0, &payload, 1)); + payload = 0x42; + gst_harness_push (h0, make_media_sample (6, 0, &payload, 1)); + + /* row FEC */ + /* l1 0xbe ^ 0xef ^ 0x3a */ + payload = 0x6b; + gst_harness_push (h_fec_1, make_fec_sample (0, 0, 3, TRUE, 1, 3, 0, &payload, + 1, 1)); + /* l2 0x42 ^ 0x5f ^ 0x21 */ + payload = 0x3c; + gst_harness_push (h_fec_1, make_fec_sample (0, 0, 6, TRUE, 1, 3, 0, &payload, + 1, 1)); + + /* column FEC */ + /* d0 0xde ^ 0xbe ^ 0x42 */ + payload = 0x22; + gst_harness_push (h_fec_0, make_fec_sample (0, 0, 0, FALSE, 3, 3, 0, &payload, + 1, 1)); + /* d1 0xad ^ 0xef ^ 0x5f */ + payload = 0x1d; + gst_harness_push (h_fec_0, make_fec_sample (1, 0, 1, FALSE, 3, 3, 0, &payload, + 1, 1)); + /* d2 0xfc ^ 0x3a ^ 0x21 */ + payload = 0xe7; + gst_harness_push (h_fec_0, make_fec_sample (2, 0, 2, FALSE, 3, 3, 0, &payload, + 1, 1)); + + /* We should retrieve all 9 packets despite dropping 4! */ + payload = 0xde; + pull_and_check (h, 0, 0, &payload, 1, 9); + payload = 0xad; + pull_and_check (h, 1, 0, &payload, 1, 8); + payload = 0xbe; + pull_and_check (h, 3, 0, &payload, 1, 7); + payload = 0xef; + pull_and_check (h, 4, 0, &payload, 1, 6); + payload = 0x42; + pull_and_check (h, 6, 0, &payload, 1, 5); + payload = 0x3a; + pull_and_check (h, 5, 0, &payload, 1, 4); + payload = 0x21; + pull_and_check (h, 8, 0, &payload, 1, 3); + payload = 0x5f; + pull_and_check (h, 7, 0, &payload, 1, 2); + payload = 0xfc; + pull_and_check (h, 2, 0, &payload, 1, 1); + + gst_harness_teardown (h); + gst_harness_teardown (h0); + gst_harness_teardown (h_fec_0); + gst_harness_teardown (h_fec_1); +} + +GST_END_TEST; + +static void +_xor_mem (guint8 * restrict dst, const guint8 * restrict src, gsize length) +{ + guint i; + + for (i = 0; i < (length / sizeof (guint64)); ++i) { +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + GST_WRITE_UINT64_LE (dst, + GST_READ_UINT64_LE (dst) ^ GST_READ_UINT64_LE (src)); +#else + GST_WRITE_UINT64_BE (dst, + GST_READ_UINT64_BE (dst) ^ GST_READ_UINT64_BE (src)); +#endif + dst += sizeof (guint64); + src += sizeof (guint64); + } + for (i = 0; i < (length % sizeof (guint64)); ++i) + dst[i] ^= src[i]; +} + +/** + * +-----------------+ + * | 0-1 | 1-3 | x-4 | l1 + * +-----------------+ + * x x x + * + * Missing values: + * 2: 0xc5b74108 + */ +GST_START_TEST (test_variable_length) +{ + guint8 payload[4]; + guint8 fec_payload[4]; + GstHarness *h = + gst_harness_new_with_padnames ("rtpst2022-1-fecdec", NULL, "src"); + GstHarness *h0 = gst_harness_new_with_element (h->element, "sink", NULL); + GstHarness *h_fec_1 = + gst_harness_new_with_element (h->element, "fec_1", NULL); + + gst_harness_set_src_caps_str (h0, "application/x-rtp"); + gst_harness_set_src_caps_str (h_fec_1, "application/x-rtp"); + + memset (fec_payload, 0x00, 4); + + payload[0] = 0x37; + _xor_mem (fec_payload, payload, 1); + gst_harness_push (h0, make_media_sample (0, 0, payload, 1)); + + payload[0] = 0x28; + payload[1] = 0x39; + payload[2] = 0x56; + _xor_mem (fec_payload, payload, 3); + gst_harness_push (h0, make_media_sample (1, 0, payload, 3)); + + /* We receive 0 and 1 */ + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2); + while (gst_harness_buffers_in_queue (h)) { + gst_buffer_unref (gst_harness_pull (h)); + } + + payload[0] = 0xc5; + payload[1] = 0xb7; + payload[2] = 0x41; + payload[3] = 0x08; + + _xor_mem (fec_payload, payload, 4); + gst_harness_push (h_fec_1, make_fec_sample (0, 0, 0, TRUE, 1, 3, 0, + fec_payload, 4, 1 ^ 3 ^ 4)); + + pull_and_check (h, 2, 0, payload, 4, 1); + + gst_harness_teardown (h); + gst_harness_teardown (h0); + gst_harness_teardown (h_fec_1); +} + +GST_END_TEST; + + +static Suite * +st2022_1_dec_suite (void) +{ + Suite *s = suite_create ("rtpst2022-1-fecdec"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + + tcase_add_test (tc_chain, test_row); + tcase_add_test (tc_chain, test_column); + tcase_add_test (tc_chain, test_2d); + tcase_add_test (tc_chain, test_variable_length); + + return s; +} + +GST_CHECK_MAIN (st2022_1_dec) diff --git a/tests/check/meson.build b/tests/check/meson.build index 17429463f..6f8dd9cdb 100644 --- a/tests/check/meson.build +++ b/tests/check/meson.build @@ -88,6 +88,7 @@ good_tests = [ [ 'elements/rtpulpfec' ], [ 'elements/rtpssrcdemux' ], [ 'elements/rtp-payloading' ], + [ 'elements/rtpst2022-1-fecdec' ], [ 'elements/spectrum', false, [gstfft_dep] ], [ 'elements/shapewipe' ], [ 'elements/udpsink' ], |