--- /dev/null
+/*
+ * 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 <gst/check/gstcheck.h>
+#include <gst/check/gstharness.h>
+
+#include <avtp.h>
+#include <avtp_cvf.h>
+
+#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);