tests: Add AVTP CVF depayloader tests
authorEderson de Souza <ederson.desouza@intel.com>
Tue, 26 Mar 2019 21:25:56 +0000 (14:25 -0700)
committerEderson de Souza <ederson.desouza@intel.com>
Wed, 3 Jul 2019 16:59:35 +0000 (09:59 -0700)
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
tests/check/elements/avtpcvfdepay.c [new file with mode: 0644]
tests/check/meson.build

index 5060b61..14a7a48 100644 (file)
@@ -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 (file)
index 0000000..54e049f
--- /dev/null
@@ -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 <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);
index 7027f91..91d1bc8 100644 (file)
@@ -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']],