From a6fc6558ebac9fa06bc60460cc736039c040da57 Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Tue, 26 Mar 2019 14:25:56 -0700 Subject: [PATCH] tests: Add AVTP CVF depayloader tests In these tests, some specially crafted buffers are sent to the depayloader, simulating some scenarios and checking what comes out from it. --- tests/check/Makefile.am | 11 +- tests/check/elements/avtpcvfdepay.c | 1205 +++++++++++++++++++++++++++++++++++ tests/check/meson.build | 1 + 3 files changed, 1216 insertions(+), 1 deletion(-) create mode 100644 tests/check/elements/avtpcvfdepay.c diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 5060b61..14a7a48 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -40,7 +40,13 @@ check_assrender = endif if USE_AVTP -check_avtp = elements/avtpaafpay elements/avtpaafdepay elements/avtpcvfpay elements/avtpsink elements/avtpsrc +check_avtp = \ + elements/avtpaafpay \ + elements/avtpaafdepay \ + elements/avtpcvfpay \ + elements/avtpcvfdepay \ + elements/avtpsink \ + elements/avtpsrc else check_avtp = endif @@ -512,6 +518,9 @@ elements_avtpaafdepay_LDADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(LDADD) elements_avtpcvfpay_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(AM_CFLAGS) $(AVTP_CFLAGS) elements_avtpcvfpay_LDADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(LDADD) $(AVTP_LIBS) +elements_avtpcvfdepay_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(AM_CFLAGS) $(AVTP_CFLAGS) +elements_avtpcvfdepay_LDADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(LDADD) $(AVTP_LIBS) + elements_avtpsink_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(AM_CFLAGS) elements_avtpsink_LDADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(LDADD) diff --git a/tests/check/elements/avtpcvfdepay.c b/tests/check/elements/avtpcvfdepay.c new file mode 100644 index 0000000..54e049f --- /dev/null +++ b/tests/check/elements/avtpcvfdepay.c @@ -0,0 +1,1205 @@ +/* + * GStreamer AVTP Plugin + * Copyright (C) 2019 Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include +#include + +#include +#include + +#define AVTP_CVF_H264_HEADER_SIZE (sizeof(struct avtp_stream_pdu) + sizeof(guint32)) +#define STREAM_ID 0xAABBCCDDEEFF0000 + +static gboolean +check_nal_filling (GstBuffer * buffer, guint8 first) +{ + GstMapInfo map; + gboolean result = TRUE; + gsize offset = 5; /* 4 bytes for the nal size and one with nal type */ + int i; + + gst_buffer_map (buffer, &map, GST_MAP_READ); + + for (i = offset; i < map.size; i++) { + if (map.data[i] != first++) { + result = FALSE; + break; + } + } + + return result; +} + +static void +fill_nal (guint8 * buf, gsize size, guint8 first) +{ + while (size--) { + *buf++ = first++; + } +} + +static gsize +nal_size (GstBuffer * buffer) +{ + guint8 nal_size[4]; + + gst_buffer_extract (buffer, 0, nal_size, 4); + return (nal_size[0] << 24) | (nal_size[1] << 16) | (nal_size[2] << 8) | + nal_size[3]; +} + +static gsize +nal_type (GstBuffer * buffer) +{ + guint8 nal_type; + + gst_buffer_extract (buffer, 4, &nal_type, 1); + return nal_type & 0x1f; +} + +static GstBuffer * +fetch_nal (GstBuffer * buffer, gsize * offset) +{ + gsize nal_size; + GstBuffer *ret; + guint8 buf[4]; + + if (*offset >= (gst_buffer_get_size (buffer) - 4)) + return NULL; + + gst_buffer_extract (buffer, *offset, buf, 4); + nal_size = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + + ret = + gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY, *offset, + nal_size + 4); + *offset += nal_size + 4; + + return ret; +} + +GST_START_TEST (test_depayloader_invalid_avtpdu) +{ + GstHarness *h; + GstBuffer *in, *small; + const gint DATA_LEN = sizeof (guint32) + 4; + struct avtp_stream_pdu *pdu; + GstMapInfo map; + + /* Create the harness for the avtpcvfpay */ + h = gst_harness_new ("avtpcvfdepay"); + gst_harness_set_src_caps_str (h, "application/x-avtp"); + + /* Create the input AVTPDU header */ + in = gst_harness_create_buffer (h, AVTP_CVF_H264_HEADER_SIZE + 4); + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + + avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_MJPEG); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 1000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 2000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 0x1; /* Add NAL type */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 1], 3, 0); + gst_buffer_unmap (in, &map); + + /* Invalid CVF subtype */ + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 0); + + /* Invalid subtype */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_FORMAT_SUBTYPE, + AVTP_CVF_FORMAT_SUBTYPE_H264); + avtp_pdu_set ((struct avtp_common_pdu *) pdu, AVTP_FIELD_SUBTYPE, + AVTP_SUBTYPE_CRF); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 0); + + /* Invalid CVF type */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_pdu_set ((struct avtp_common_pdu *) pdu, AVTP_FIELD_SUBTYPE, + AVTP_SUBTYPE_CVF); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_FORMAT, 0); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 0); + + /* Invalid AVTP version */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_FORMAT, AVTP_CVF_FORMAT_RFC); + avtp_pdu_set ((struct avtp_common_pdu *) pdu, AVTP_FIELD_VERSION, 3); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 0); + + /* Invalid SV */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_pdu_set ((struct avtp_common_pdu *) pdu, AVTP_FIELD_VERSION, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SV, 0); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 0); + + /* Invalid stream id */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, 0xAABBCCDDEEFF0001); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 0); + + /* Invalid stream data len */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, 100); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 0); + + /* Invalid NAL type (STAP-A) */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 24; + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 0); + + /* Invalid NAL type (STAP-B) */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 25; + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 0); + + /* Invalid NAL type (MTAP16) */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 26; + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 0); + + /* Invalid NAL type (MTAP24) */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 27; + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 0); + + /* Invalid NAL type (FU-B) */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 29; + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 0); + + /* Invalid NAL type (STAP-A) */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 24; + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 0); + + /* Invalid buffer size (too small to fit an AVTP header) */ + small = gst_harness_create_buffer (h, AVTP_CVF_H264_HEADER_SIZE / 2); + gst_harness_push (h, small); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 0); + + /* Invalid buffer size (too small to fit a fragment header) */ + small = gst_harness_create_buffer (h, AVTP_CVF_H264_HEADER_SIZE + 1); + gst_buffer_map (small, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 2000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, sizeof (guint32) + 1); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 28; + + gst_harness_push (h, small); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 0); + + gst_buffer_unref (in); + gst_harness_teardown (h); +} + +GST_END_TEST; + +/* + * This test will send some invalid fragments, but with valid seqnum + * (misbehaving payloader).*/ +GST_START_TEST (test_depayloader_lost_fragments) +{ + GstHarness *h; + GstBuffer *in, *out, *nal; + const gint DATA_LEN = sizeof (guint32) + 10; + struct avtp_stream_pdu *pdu; + GstMapInfo map; + gsize offset; + + /* Create the harness for the avtpcvfpay */ + h = gst_harness_new ("avtpcvfdepay"); + gst_harness_set_src_caps_str (h, "application/x-avtp"); + + /* Create the input AVTPDU */ + in = gst_harness_create_buffer (h, AVTP_CVF_H264_HEADER_SIZE + 10); + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + + /* First fragment doesn't have start bit set, so it should be ignored */ + avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 2000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 3 << 5 | 28; /* NAL type FU-A, NRI 3 */ + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = 4; /* S = 0, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 8, 0); + gst_buffer_unmap (in, &map); + + /* We push a copy so that we can change only what is necessary on our buffer */ + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Send second AVTPDU - but this should be also ignored as it doesn't have the + * start bit set */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1); + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = 4; /* type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 8, 8); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Send third AVTPDU, with end bit set, but it should be discarded as there + * was no start fragment */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 1000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 2); + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = (1 << 6) | 4; /* E = 1, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 8, 16); + gst_buffer_unmap (in, &map); + + /* Ensure no buffer came out */ + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Now, let's send an invalid one, with both start and end bits set */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 3); + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = (3 << 6) | 4; /* S = E = 1, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 8, 24); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Send a fragment with proper start */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 4); + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = (1 << 7) | 4; /* S = 1, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 8, 32); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* But send start again. Previous one should be dropped */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 5); + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = (1 << 7) | 4; /* S = 1, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 8, 40); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Finally, send ending fragment. It should come out a buffer + * whose content starts on 40 (starting of start fragment) */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 6); + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = (1 << 6) | 4; /* E = 1, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 8, 48); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + + out = gst_harness_pull (h); + fail_unless_equals_uint64 (GST_BUFFER_DTS (out), 1000000); + fail_unless_equals_uint64 (GST_BUFFER_PTS (out), 2000000); + + /* NAL is composed of 8 bytes fragment + reconstructed NAL header, so 17 bytes */ + offset = 0; + nal = fetch_nal (out, &offset); + fail_unless_equals_uint64 (nal_size (nal), 17); + fail_unless (check_nal_filling (nal, 40) == TRUE); + fail_unless_equals_uint64 (nal_type (nal), 4); + + /* Ensure no other NAL units are present */ + nal = fetch_nal (out, &offset); + fail_unless (nal == NULL); + + gst_buffer_unref (out); + gst_buffer_unref (in); + gst_harness_teardown (h); +} + +GST_END_TEST; + +/* This test jumps one seq_num, thus simulating a lost packet */ +GST_START_TEST (test_depayloader_lost_packet) +{ + GstHarness *h; + GstBuffer *in, *out, *nal; + const gint DATA_LEN = sizeof (guint32) + 4; + struct avtp_stream_pdu *pdu; + GstMapInfo map; + gsize offset; + + /* Create the harness for the avtpcvfpay */ + h = gst_harness_new ("avtpcvfdepay"); + gst_harness_set_src_caps_str (h, "application/x-avtp"); + + /* Create the input AVTPDU header */ + in = gst_harness_create_buffer (h, AVTP_CVF_H264_HEADER_SIZE + 4); + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + + avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 1000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 2000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 0x7; /* Add NAL type */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 1], 3, 0); + gst_buffer_unmap (in, &map); + + /* We push a copy so that we can change only what is necessary on our buffer */ + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Send second AVTPDU */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1); + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 1], 3, 0); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Send third and last AVTPDU, but jumping one SEQ_NUM. + * This should make the first two NAL units to be flushed, + * despite M not being set on this third packet. + * Also, this NAL is not filled from 0, so if it somehow + * leaks - it's not supposed to go outside of the avtpcvdepay + * as it doesn't have M bit set - we can catch on checks below */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 3); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 0x1; /* Add NAL type */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 1], 3, 5); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_int (gst_harness_buffers_received (h), 1); + + out = gst_harness_pull (h); + fail_unless_equals_uint64 (GST_BUFFER_DTS (out), 1000000); + fail_unless_equals_uint64 (GST_BUFFER_PTS (out), 2000000); + + /* Validate each NAL unit size and content */ + offset = 0; + nal = fetch_nal (out, &offset); + fail_unless_equals_uint64 (nal_size (nal), 4); + fail_unless (check_nal_filling (nal, 0) == TRUE); + fail_unless_equals_uint64 (nal_type (nal), 7); + nal = fetch_nal (out, &offset); + fail_unless_equals_uint64 (nal_size (nal), 4); + fail_unless (check_nal_filling (nal, 0) == TRUE); + fail_unless_equals_uint64 (nal_type (nal), 7); + + /* Ensure no other NAL units are present */ + nal = fetch_nal (out, &offset); + fail_unless (nal == NULL); + + gst_buffer_unref (out); + gst_buffer_unref (in); + gst_harness_teardown (h); +} + +GST_END_TEST; + +/* This test simulates a scenario in which one single NAL unit is sent, + * followed by a fragment without start bit set, so fragment is discarded + * and previous single NAL is sent to the pipeline, as avtpcvfdepay is not + * sure about the sanity of the data anymore - but hopes h264decoder knows + * what to do. This scenario emerges from misbehaving payloaders. */ +GST_START_TEST (test_depayloader_single_and_messed_fragments) +{ + GstHarness *h; + GstBuffer *in, *out, *nal; + const gint DATA_LEN = sizeof (guint32) + 4; + struct avtp_stream_pdu *pdu; + GstMapInfo map; + gsize offset; + + /* Create the harness for the avtpcvfpay */ + h = gst_harness_new ("avtpcvfdepay"); + gst_harness_set_src_caps_str (h, "application/x-avtp"); + + /* Create the input AVTPDU header */ + in = gst_harness_create_buffer (h, AVTP_CVF_H264_HEADER_SIZE + 4); + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + + avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 1000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 2000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 0x1; /* Add NAL type */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 1], 3, 0); + gst_buffer_unmap (in, &map); + + /* First, we send a single NAL with M = 0, so nothing should come out */ + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Then, we send invalid fragment */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 3 << 5 | 28; /* NAL type FU-A, NRI 3 */ + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = 4; /* S = 0, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 2, 0); + gst_buffer_unmap (in, &map); + + /* When we push it, it should be discarded, but previous single NAL + * should come out */ + out = gst_harness_push_and_pull (h, gst_buffer_copy (in)); + + /* Check that we got the right one */ + fail_unless_equals_uint64 (GST_BUFFER_DTS (out), 1000000); + fail_unless_equals_uint64 (GST_BUFFER_PTS (out), 2000000); + + offset = 0; + nal = fetch_nal (out, &offset); + fail_unless_equals_uint64 (nal_size (nal), 4); + fail_unless_equals_uint64 (nal_type (nal), 1); + fail_unless (check_nal_filling (nal, 0) == TRUE); + + /* Ensure no other NAL units are present */ + nal = fetch_nal (out, &offset); + fail_unless (nal == NULL); + + gst_buffer_unref (out); + gst_buffer_unref (in); + gst_harness_teardown (h); +} + +GST_END_TEST; + +/* This test explores the case in which a fragment is followed by + * a single NAL - and not by an ending fragment. Fragments stored + * so far are dropped, and things shall flow normally for the single NAL. + * This can be created by a misbehaving payloader */ +GST_START_TEST (test_depayloader_single_and_messed_fragments_2) +{ + GstHarness *h; + GstBuffer *in, *out, *nal; + const gint DATA_LEN = sizeof (guint32) + 4; + struct avtp_stream_pdu *pdu; + GstMapInfo map; + gsize offset; + + /* Create the harness for the avtpcvfpay */ + h = gst_harness_new ("avtpcvfdepay"); + gst_harness_set_src_caps_str (h, "application/x-avtp"); + + /* Create the input AVTPDU header */ + in = gst_harness_create_buffer (h, AVTP_CVF_H264_HEADER_SIZE + 4); + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + + avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 1000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 2000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 3 << 5 | 28; /* NAL type FU-A, NRI 3 */ + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = (1 << 7) | 4; /* S = 1, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 2, 0); + gst_buffer_unmap (in, &map); + + /* Send a perfectly valid start fragment */ + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Then, we send a single NAL. Previous fragment should be dropped */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 0x2; /* Add NAL type */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 1], 3, 5); + gst_buffer_unmap (in, &map); + + /* When we push it, it should come out as it has M = 1 */ + out = gst_harness_push_and_pull (h, gst_buffer_copy (in)); + + /* Check that we got the right one - its NAL filling should start with 5 */ + fail_unless_equals_uint64 (GST_BUFFER_DTS (out), 1000000); + fail_unless_equals_uint64 (GST_BUFFER_PTS (out), 2000000); + + offset = 0; + nal = fetch_nal (out, &offset); + fail_unless_equals_uint64 (nal_size (nal), 4); + fail_unless_equals_uint64 (nal_type (nal), 2); + fail_unless (check_nal_filling (nal, 5) == TRUE); + + /* Ensure no other NAL units are present */ + nal = fetch_nal (out, &offset); + fail_unless (nal == NULL); + + /* To be really sure, send an ending fragment. It should be dropped, + * as there should not be any previous fragment on the wait */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 2); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 3 << 5 | 28; /* NAL type FU-A, NRI 3 */ + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = (1 << 6) | 4; /* E = 1, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 2, 2); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + gst_buffer_unref (out); + gst_buffer_unref (in); + gst_harness_teardown (h); +} + +GST_END_TEST; + +/* This test ensures that, if a fragment is dropped due arrival of a single + * NAL (and fragment was never completed), any previous single NAL waiting + * for M set NAL are flushed to the pipeline. avtpcvfdepay never sents known + * incomplete NAL units to the pipeline, but should not hold forever NALs + * waiting for an M set NAL - specially after something wrong already happened */ +GST_START_TEST (test_depayloader_single_and_messed_fragments_3) +{ + GstHarness *h; + GstBuffer *in, *out, *nal; + const gint DATA_LEN = sizeof (guint32) + 4; + struct avtp_stream_pdu *pdu; + GstMapInfo map; + gsize offset; + + /* Create the harness for the avtpcvfpay */ + h = gst_harness_new ("avtpcvfdepay"); + gst_harness_set_src_caps_str (h, "application/x-avtp"); + + /* Create the input AVTPDU header */ + in = gst_harness_create_buffer (h, AVTP_CVF_H264_HEADER_SIZE + 4); + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + + avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 1000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 2000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 0x2; /* Add NAL type */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 1], 3, 0); + gst_buffer_unmap (in, &map); + + /* Send a single NAL with M = 0, so nothing will come out */ + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Send a valid start fragment */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 0); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 3 << 5 | 28; /* NAL type FU-A, NRI 3 */ + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = (1 << 7) | 4; /* S = 1, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 2, 0); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Send a single NAL without ending fragment. So, both first NAL and second should + * come out, on two different buffers. Fragment should be gone. */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 2); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 0x3; /* Add NAL type */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 1], 3, 7); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_uint64 (gst_harness_buffers_received (h), 2); + + /* Check that we got the right ones. First has nal_type 2, and second 3. + * Second also has its nal filling starting from 7 */ + out = gst_harness_pull (h); + fail_unless_equals_uint64 (GST_BUFFER_DTS (out), 1000000); + fail_unless_equals_uint64 (GST_BUFFER_PTS (out), 2000000); + + offset = 0; + nal = fetch_nal (out, &offset); + fail_unless_equals_uint64 (nal_size (nal), 4); + fail_unless_equals_uint64 (nal_type (nal), 2); + fail_unless (check_nal_filling (nal, 0) == TRUE); + + /* Ensure no other NAL units are present */ + nal = fetch_nal (out, &offset); + fail_unless (nal == NULL); + gst_buffer_unref (out); + + out = gst_harness_pull (h); + fail_unless_equals_uint64 (GST_BUFFER_DTS (out), 1000000); + fail_unless_equals_uint64 (GST_BUFFER_PTS (out), 2000000); + + offset = 0; + nal = fetch_nal (out, &offset); + fail_unless_equals_uint64 (nal_size (nal), 4); + fail_unless_equals_uint64 (nal_type (nal), 3); + fail_unless (check_nal_filling (nal, 7) == TRUE); + + /* Ensure no other NAL units are present */ + nal = fetch_nal (out, &offset); + fail_unless (nal == NULL); + gst_buffer_unref (out); + + /* To be really sure, send an ending fragment. It should be dropped, + * as there should not be any previous fragment on the wait */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 3); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 3 << 5 | 28; /* NAL type FU-A, NRI 3 */ + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = (1 << 6) | 4; /* E = 1, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 2, 2); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + gst_buffer_unref (in); + gst_harness_teardown (h); +} + +GST_END_TEST; + +GST_START_TEST (test_depayloader_property) +{ + GstHarness *h; + GstElement *element; + guint64 streamid; + + /* Create the harness for the avtpcvfpay */ + h = gst_harness_new_parse ("avtpcvfdepay streamid=0xAABBCCDDEEFF0001"); + + /* Check if property was properly set up */ + element = gst_harness_find_element (h, "avtpcvfdepay"); + g_object_get (G_OBJECT (element), "streamid", &streamid, NULL); + fail_unless_equals_uint64 (streamid, 0xAABBCCDDEEFF0001); + + gst_harness_teardown (h); +} + +GST_END_TEST; + +/* Tests if everything goes right when a single NAL unit without M bit is + * followed by fragments that, when merged, have the M bit set */ +GST_START_TEST (test_depayloader_single_and_fragmented) +{ + GstHarness *h; + GstBuffer *in, *out, *nal; + const gint DATA_LEN = sizeof (guint32) + 4; + struct avtp_stream_pdu *pdu; + GstMapInfo map; + gsize offset; + + /* Create the harness for the avtpcvfpay */ + h = gst_harness_new ("avtpcvfdepay"); + gst_harness_set_src_caps_str (h, "application/x-avtp"); + + /* Create the input AVTPDU header */ + in = gst_harness_create_buffer (h, AVTP_CVF_H264_HEADER_SIZE + 4); + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + + avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 1000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 2000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 0x1; /* Add NAL type */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 1], 3, 0); + gst_buffer_unmap (in, &map); + + /* First, we send a single NAL with M = 0, so nothing should come out */ + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Then, we send first fragment */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 3 << 5 | 28; /* NAL type FU-A, NRI 3 */ + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = (1 << 7) | 4; /* S = 1, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 2, 0); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* And last */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 2); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1); + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = (1 << 6) | 4; /* E = 1, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 2, 2); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_int (gst_harness_buffers_received (h), 1); + + out = gst_harness_pull (h); + fail_unless_equals_uint64 (GST_BUFFER_DTS (out), 1000000); + fail_unless_equals_uint64 (GST_BUFFER_PTS (out), 2000000); + + /* Validate each NAL unit size and content */ + offset = 0; + nal = fetch_nal (out, &offset); + fail_unless_equals_uint64 (nal_size (nal), 4); + fail_unless (check_nal_filling (nal, 0) == TRUE); + fail_unless_equals_uint64 (nal_type (nal), 1); + nal = fetch_nal (out, &offset); + fail_unless_equals_uint64 (nal_size (nal), 5); + fail_unless (check_nal_filling (nal, 0) == TRUE); + fail_unless_equals_uint64 (nal_type (nal), 4); + + /* Ensure no other NAL units are present */ + nal = fetch_nal (out, &offset); + fail_unless (nal == NULL); + + gst_buffer_unref (out); + gst_buffer_unref (in); + gst_harness_teardown (h); +} + +GST_END_TEST; + +/* Tests a simple fragmented NAL scenario */ +GST_START_TEST (test_depayloader_fragmented) +{ + GstHarness *h; + GstBuffer *in, *out, *nal; + const gint DATA_LEN = sizeof (guint32) + 10; + struct avtp_stream_pdu *pdu; + GstMapInfo map; + gsize offset; + + /* Create the harness for the avtpcvfpay */ + h = gst_harness_new ("avtpcvfdepay"); + gst_harness_set_src_caps_str (h, "application/x-avtp"); + + /* Create the input AVTPDU */ + in = gst_harness_create_buffer (h, AVTP_CVF_H264_HEADER_SIZE + 10); + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + + avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 2000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 3 << 5 | 28; /* NAL type FU-A, NRI 3 */ + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = (1 << 7) | 4; /* S = 1, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 8, 0); + gst_buffer_unmap (in, &map); + + /* We push a copy so that we can change only what is necessary on our buffer */ + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Send second AVTPDU */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1); + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = 4; /* type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 8, 8); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Send third and last AVTPDU */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 1000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 2); + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = (1 << 6) | 4; /* E = 1, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], 8, 16); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_int (gst_harness_buffers_received (h), 1); + + out = gst_harness_pull (h); + fail_unless_equals_uint64 (GST_BUFFER_DTS (out), 1000000); + fail_unless_equals_uint64 (GST_BUFFER_PTS (out), 2000000); + + offset = 0; + nal = fetch_nal (out, &offset); + fail_unless_equals_uint64 (nal_size (nal), 25); + fail_unless (check_nal_filling (nal, 0) == TRUE); + fail_unless_equals_uint64 (nal_type (nal), 4); + + gst_buffer_unref (out); + gst_buffer_unref (in); + gst_harness_teardown (h); +} + +GST_END_TEST; + +/* Tests a big fragmented NAL scenario */ +GST_START_TEST (test_depayloader_fragmented_big) +{ + GstHarness *h; + GstBuffer *in, *out, *nal; + const gint DATA_LEN = 1470; + struct avtp_stream_pdu *pdu; + /* 12000 * 1468 > 2^24 - so we can check if nal size is retrieved correctly */ + const gint nal_count = 12000; + guint8 seq_num = 0; + GstMapInfo map; + gsize offset; + gint i; + + /* Create the harness for the avtpcvfpay */ + h = gst_harness_new ("avtpcvfdepay"); + gst_harness_set_src_caps_str (h, "application/x-avtp"); + + /* Create the input AVTPDU */ + in = gst_harness_create_buffer (h, AVTP_CVF_H264_HEADER_SIZE + DATA_LEN); + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + + avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 2000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, + DATA_LEN + sizeof (guint32)); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 3 << 5 | 28; /* NAL type FU-A, NRI 3 */ + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = (1 << 7) | 4; /* S = 1, type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], DATA_LEN - 2, 0); + gst_buffer_unmap (in, &map); + + /* We push a copy so that we can change only what is necessary on our buffer */ + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Loop sending fragments. The idea is to create a NAL unit big enough + * to use the 4 bytes of nal_length_size */ + for (i = 0; i < nal_count - 1; i++) { + + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, ++seq_num); + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = 4; /* type 4 */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 2], DATA_LEN - 2, + (guint8) ((DATA_LEN - 2) * seq_num)); + + /* Last one is special - need to set M and TV, etc */ + if (i == nal_count - 2) { + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 1000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1); + map.data[AVTP_CVF_H264_HEADER_SIZE + 1] = (1 << 6) | 4; /* E = 1, type 4 */ + } + + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + if (i < nal_count - 2) + fail_unless (gst_harness_try_pull (h) == NULL); + } + + /* After last one was sent, we check everything */ + fail_unless_equals_int (gst_harness_buffers_received (h), 1); + + out = gst_harness_pull (h); + fail_unless_equals_uint64 (GST_BUFFER_DTS (out), 1000000); + fail_unless_equals_uint64 (GST_BUFFER_PTS (out), 2000000); + + offset = 0; + nal = fetch_nal (out, &offset); + fail_unless_equals_uint64 (nal_size (nal), (DATA_LEN - 2) * nal_count + 1); + fail_unless (check_nal_filling (nal, 0) == TRUE); + fail_unless_equals_uint64 (nal_type (nal), 4); + + gst_buffer_unref (out); + gst_buffer_unref (in); + gst_harness_teardown (h); +} + +GST_END_TEST; + +/* Tests several single NAL units. They should be grouped and delivered + * to the pipeline only when one NAL unit with M bit set arrives */ +GST_START_TEST (test_depayloader_multiple_single) +{ + GstHarness *h; + GstBuffer *in, *out, *nal; + const gint DATA_LEN = sizeof (guint32) + 4; + struct avtp_stream_pdu *pdu; + GstMapInfo map; + gsize offset; + + /* Create the harness for the avtpcvfpay */ + h = gst_harness_new ("avtpcvfdepay"); + gst_harness_set_src_caps_str (h, "application/x-avtp"); + + /* Create the input AVTPDU header */ + in = gst_harness_create_buffer (h, AVTP_CVF_H264_HEADER_SIZE + 4); + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + + avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 0); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 1000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 2000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 0x7; /* Add NAL type */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 1], 3, 0); + gst_buffer_unmap (in, &map); + + /* We push a copy so that we can change only what is necessary on our buffer */ + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Send second AVTPDU */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1); + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 1], 3, 0); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless (gst_harness_try_pull (h) == NULL); + + /* Send third and last AVTPDU */ + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 2); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 0x1; /* Add NAL type */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 1], 3, 0); + gst_buffer_unmap (in, &map); + + gst_harness_push (h, gst_buffer_copy (in)); + fail_unless_equals_int (gst_harness_buffers_received (h), 1); + + out = gst_harness_pull (h); + fail_unless_equals_uint64 (GST_BUFFER_DTS (out), 1000000); + fail_unless_equals_uint64 (GST_BUFFER_PTS (out), 2000000); + + /* Validate each NAL unit size and content */ + offset = 0; + nal = fetch_nal (out, &offset); + fail_unless_equals_uint64 (nal_size (nal), 4); + fail_unless (check_nal_filling (nal, 0) == TRUE); + fail_unless_equals_uint64 (nal_type (nal), 7); + nal = fetch_nal (out, &offset); + fail_unless_equals_uint64 (nal_size (nal), 4); + fail_unless (check_nal_filling (nal, 0) == TRUE); + fail_unless_equals_uint64 (nal_type (nal), 7); + nal = fetch_nal (out, &offset); + fail_unless_equals_uint64 (nal_size (nal), 4); + fail_unless (check_nal_filling (nal, 0) == TRUE); + fail_unless_equals_uint64 (nal_type (nal), 1); + + /* Ensure no other NAL units are present */ + nal = fetch_nal (out, &offset); + fail_unless (nal == NULL); + + gst_buffer_unref (out); + gst_buffer_unref (in); + gst_harness_teardown (h); +} + +GST_END_TEST; + +GST_START_TEST (test_depayloader_single) +{ + GstHarness *h; + GstBuffer *in, *out; + const gint DATA_LEN = sizeof (guint32) + 4; + struct avtp_stream_pdu *pdu; + GstMapInfo map; + + /* Create the harness for the avtpcvfpay */ + h = gst_harness_new ("avtpcvfdepay"); + gst_harness_set_src_caps_str (h, "application/x-avtp"); + + /* Create the input AVTPDU header */ + in = gst_harness_create_buffer (h, AVTP_CVF_H264_HEADER_SIZE + 4); + gst_buffer_map (in, &map, GST_MAP_READWRITE); + pdu = (struct avtp_stream_pdu *) map.data; + + avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID, STREAM_ID); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 1000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 2000000); + avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN); + map.data[AVTP_CVF_H264_HEADER_SIZE] = 0x1; /* Add NAL type */ + fill_nal (&map.data[AVTP_CVF_H264_HEADER_SIZE + 1], 3, 0); + gst_buffer_unmap (in, &map); + + out = gst_harness_push_and_pull (h, in); + + fail_unless_equals_uint64 (GST_BUFFER_DTS (out), 1000000); + fail_unless_equals_uint64 (GST_BUFFER_PTS (out), 2000000); + fail_unless_equals_uint64 (nal_size (out), 4); + fail_unless_equals_uint64 (nal_type (out), 1); + fail_unless (check_nal_filling (out, 0) == TRUE); + gst_buffer_unref (out); + gst_harness_teardown (h); +} + +GST_END_TEST; + +static Suite * +avtpcvfdepay_suite (void) +{ + Suite *s = suite_create ("avtpcvfdepay"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + /* 'fragmented_big' may take some time to run, so give it a bit more time */ + tcase_set_timeout (tc_chain, 10); + tcase_add_test (tc_chain, test_depayloader_single); + tcase_add_test (tc_chain, test_depayloader_multiple_single); + tcase_add_test (tc_chain, test_depayloader_fragmented); + tcase_add_test (tc_chain, test_depayloader_fragmented_big); + tcase_add_test (tc_chain, test_depayloader_single_and_fragmented); + tcase_add_test (tc_chain, test_depayloader_property); + tcase_add_test (tc_chain, test_depayloader_lost_packet); + tcase_add_test (tc_chain, test_depayloader_lost_fragments); + tcase_add_test (tc_chain, test_depayloader_single_and_messed_fragments); + tcase_add_test (tc_chain, test_depayloader_single_and_messed_fragments_2); + tcase_add_test (tc_chain, test_depayloader_single_and_messed_fragments_3); + tcase_add_test (tc_chain, test_depayloader_invalid_avtpdu); + + return s; +} + +GST_CHECK_MAIN (avtpcvfdepay); diff --git a/tests/check/meson.build b/tests/check/meson.build index 7027f91..91d1bc8 100644 --- a/tests/check/meson.build +++ b/tests/check/meson.build @@ -73,6 +73,7 @@ if host_machine.system() != 'windows' [['elements/avtpaafpay.c'], not avtp_dep.found(), [avtp_dep]], [['elements/avtpaafdepay.c'], not avtp_dep.found(), [avtp_dep]], [['elements/avtpcvfpay.c'], not avtp_dep.found(), [avtp_dep]], + [['elements/avtpcvfdepay.c'], not avtp_dep.found(), [avtp_dep]], [['elements/avtpsink.c'], not avtp_dep.found(), [avtp_dep]], [['elements/avtpsrc.c'], not avtp_dep.found(), [avtp_dep]], [['elements/ccconverter.c']], -- 2.7.4