tests: Add AVTP CVF payloader tests
authorEderson de Souza <ederson.desouza@intel.com>
Tue, 26 Mar 2019 00:23:49 +0000 (17:23 -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
payloader, simulating some scenarios and checking what comes out from
it.

tests/check/Makefile.am
tests/check/elements/avtpcvfpay.c [new file with mode: 0644]
tests/check/meson.build

index 8df7237..5060b61 100644 (file)
@@ -40,7 +40,7 @@ check_assrender =
 endif
 
 if USE_AVTP
-check_avtp = elements/avtpaafpay elements/avtpaafdepay elements/avtpsink elements/avtpsrc
+check_avtp = elements/avtpaafpay elements/avtpaafdepay elements/avtpcvfpay elements/avtpsink elements/avtpsrc
 else
 check_avtp =
 endif
@@ -287,7 +287,6 @@ check_PROGRAMS = \
        elements/h263parse \
        elements/h264parse \
        elements/mpegtsmux \
-       elements/mpegvideoparse \
        elements/mpeg4videoparse \
        elements/mxfdemux \
        elements/mxfmux \
@@ -510,6 +509,9 @@ elements_avtpaafpay_LDADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(LDADD) $
 elements_avtpaafdepay_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(AM_CFLAGS) $(AVTP_CFLAGS)
 elements_avtpaafdepay_LDADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(LDADD) $(AVTP_LIBS)
 
+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_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/avtpcvfpay.c b/tests/check/elements/avtpcvfpay.c
new file mode 100644 (file)
index 0000000..9f92ba8
--- /dev/null
@@ -0,0 +1,648 @@
+/*
+ * 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 0xAABBCCDDEEFF0001
+
+/* Simple codec data, with only the NAL size len, no SPS/PPS. */
+static GstCaps *
+generate_caps (guint8 nal_size_len)
+{
+  GstBuffer *codec_data;
+  GstMapInfo map;
+  GstCaps *caps;
+
+  /* 7 is the minimal codec_data size, when no SPS/PPS is sent */
+  codec_data = gst_buffer_new_allocate (NULL, 7, NULL);
+  gst_buffer_map (codec_data, &map, GST_MAP_READWRITE);
+
+  memset (map.data, 0, map.size);
+  map.data[0] = 1;              /* version */
+  map.data[4] = (nal_size_len - 1) | 0xfc;      /* Other 6 bits are 1 */
+  map.data[5] = 0xe0;           /* first 3 bits are 1 */
+
+  gst_buffer_unmap (codec_data, &map);
+
+  caps = gst_caps_new_simple ("video/x-h264",
+      "stream-format", G_TYPE_STRING, "avc",
+      "alignment", G_TYPE_STRING, "au",
+      "codec_data", GST_TYPE_BUFFER, codec_data, NULL);
+  gst_buffer_unref (codec_data);
+
+  return caps;
+}
+
+static void
+fill (guint8 * buf, gsize size)
+{
+  guint8 i = 0;
+
+  while (size--)
+    *buf++ = i++;
+}
+
+static gboolean
+check_nal_filling (GstBuffer * buffer, guint8 first)
+{
+  GstMapInfo map;
+  gint i;
+  gsize offset = AVTP_CVF_H264_HEADER_SIZE + 1;
+  gboolean result = TRUE;
+
+  gst_buffer_map (buffer, &map, GST_MAP_READ);
+  if ((map.data[AVTP_CVF_H264_HEADER_SIZE] & 0x1f) == 28)
+    offset++;                   /* Fragmented NALs have 2 bytes header */
+
+  for (i = offset; i < map.size; i++) {
+    if (map.data[i] != first++) {
+      result = FALSE;
+      break;
+    }
+  }
+
+  return result;
+}
+
+static void
+add_nal (GstBuffer * buffer, gsize size, guint type, gsize offset)
+{
+  GstMapInfo map;
+
+  gst_buffer_map (buffer, &map, GST_MAP_READWRITE);
+
+  map.data[offset] = map.data[offset + 1] = 0;
+  map.data[offset + 2] = size >> 8;
+  map.data[offset + 3] = size & 0xff;
+  map.data[offset + 4] = type & 0x1f;
+  fill (&map.data[offset + 5], size - 1);
+
+  gst_buffer_unmap (buffer, &map);
+}
+
+/* This function assumes that NAL size len is 2 */
+static void
+add_nal_2 (GstBuffer * buffer, gsize size, guint type, gsize offset)
+{
+  GstMapInfo map;
+
+  gst_buffer_map (buffer, &map, GST_MAP_READWRITE);
+
+  map.data[offset] = size >> 8;
+  map.data[offset + 1] = size & 0xff;
+  map.data[offset + 2] = type & 0x1f;
+  fill (&map.data[offset + 3], size - 1);
+
+  gst_buffer_unmap (buffer, &map);
+}
+
+static gboolean
+compare_h264_avtpdu (struct avtp_stream_pdu *pdu, GstBuffer * buffer)
+{
+  GstMapInfo map;
+  gboolean result;
+
+  gst_buffer_map (buffer, &map, GST_MAP_READ);
+  /* buffer must have at least the header size */
+  if (map.size < AVTP_CVF_H264_HEADER_SIZE)
+    return FALSE;
+
+  result = memcmp (map.data, pdu, AVTP_CVF_H264_HEADER_SIZE) == 0;
+
+  gst_buffer_unmap (buffer, &map);
+
+  return result;
+}
+
+GST_START_TEST (test_payloader_no_codec_data)
+{
+  GstHarness *h;
+  GstCaps *caps;
+  GstBuffer *in;
+
+  /* Caps without codec_data */
+  caps = gst_caps_new_simple ("video/x-h264",
+      "stream-format", G_TYPE_STRING, "avc",
+      "alignment", G_TYPE_STRING, "au", NULL);
+
+  /* Create the harness for the avtpcvfpay */
+  h = gst_harness_new_parse
+      ("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 processing-deadline=0");
+  gst_harness_set_src_caps (h, caps);
+
+  /* No buffer should come out when we send input */
+  in = gst_harness_create_buffer (h, 8);
+  add_nal (in, 4, 1, 0);
+  GST_BUFFER_DTS (in) = 1000000;
+  GST_BUFFER_PTS (in) = 2000000;
+
+  gst_harness_push (h, in);
+  fail_unless_equals_int (gst_harness_buffers_received (h), 0);
+
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_payloader_invalid_caps)
+{
+  GstBuffer *codec_data;
+  GstElement *element;
+  GstPad *sinkpad;
+  GstMapInfo map;
+  GstCaps *caps;
+  GstHarness *h;
+
+  /* 7 is the minimal codec_data size, when no SPS/PPS is sent */
+  codec_data = gst_buffer_new_allocate (NULL, 7, NULL);
+  gst_buffer_map (codec_data, &map, GST_MAP_READWRITE);
+
+  memset (map.data, 0, map.size);
+  map.data[0] = 0;              /* version */
+  map.data[4] = 0x03 | 0xfc;    /* Other 6 bits are 1 */
+  map.data[5] = 0xe0;           /* first 3 bits are 1 */
+
+  gst_buffer_unmap (codec_data, &map);
+
+  caps = gst_caps_new_simple ("video/x-h264",
+      "stream-format", G_TYPE_STRING, "avc",
+      "alignment", G_TYPE_STRING, "au",
+      "codec_data", GST_TYPE_BUFFER, codec_data, NULL);
+  gst_buffer_unref (codec_data);
+
+  /* Create the harness for the avtpcvfpay */
+  h = gst_harness_new_parse
+      ("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000");
+  element = gst_harness_find_element (h, "avtpcvfpay");
+  sinkpad = gst_element_get_static_pad (element, "sink");
+
+  /* 'codec_data' caps has invalid version */
+  gst_harness_push_event (h, gst_event_new_caps (caps));
+  fail_unless (gst_pad_get_current_caps (sinkpad) == NULL);
+  gst_caps_unref (caps);
+
+  /* Send a 'codec_data' too small */
+  codec_data = gst_buffer_new_allocate (NULL, 6, NULL);
+  caps = gst_caps_new_simple ("video/x-h264",
+      "stream-format", G_TYPE_STRING, "avc",
+      "alignment", G_TYPE_STRING, "au",
+      "codec_data", GST_TYPE_BUFFER, codec_data, NULL);
+  gst_buffer_unref (codec_data);
+
+  gst_harness_push_event (h, gst_event_new_caps (caps));
+  fail_unless (gst_pad_get_current_caps (sinkpad) == NULL);
+  gst_caps_unref (caps);
+
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_payloader_incomplete_nal)
+{
+  GstHarness *h;
+  GstBuffer *in, *out;
+  GstMapInfo map;
+  struct avtp_stream_pdu *pdu = alloca (AVTP_CVF_H264_HEADER_SIZE);
+  const gint DATA_LEN = sizeof (guint32) + 3;
+
+  /* Create the 'expected' header */
+  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, 3000000);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 4000000);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN);
+
+  /* Create the harness for the avtpcvfpay */
+  h = gst_harness_new_parse
+      ("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 processing-deadline=0");
+  gst_harness_set_src_caps (h, generate_caps (4));
+
+  /* Buffer must have the nal len (4 bytes) and the nal (3 bytes) */
+  in = gst_harness_create_buffer (h, 7);
+  GST_BUFFER_DTS (in) = 1000000;
+  GST_BUFFER_PTS (in) = 2000000;
+
+  gst_buffer_map (in, &map, GST_MAP_READWRITE);
+  map.data[0] = map.data[1] = map.data[2] = 0;
+  map.data[3] = 8;              /* Lie that NAL size is 8, when buffer is only 7 (so NAL is 3) */
+  map.data[4] = 1;              /* Some dummy vcl NAL type */
+  map.data[5] = 0x0;
+  map.data[6] = 0x1;
+
+  out = gst_harness_push_and_pull (h, in);
+
+  /* avtpcvfpay will happily payload the three byte nal. Now, we check it */
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  fail_unless (check_nal_filling (out, 0) == TRUE);
+
+  gst_buffer_unref (out);
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_payloader_properties)
+{
+  GstHarness *h;
+  GstElement *element;
+  guint mtu, mtt, tu;
+  guint64 streamid, processing_deadline;
+
+  /* Create the harness for the avtpcvfpay */
+  h = gst_harness_new_parse
+      ("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=2000000 mtu=100 processing-deadline=5000");
+
+  /* Check if all properties were properly set up */
+  element = gst_harness_find_element (h, "avtpcvfpay");
+  g_object_get (G_OBJECT (element), "mtt", &mtt, NULL);
+  fail_unless_equals_uint64 (mtt, 1000000);
+
+  g_object_get (G_OBJECT (element), "mtu", &mtu, NULL);
+  fail_unless_equals_uint64 (mtu, 100);
+
+  g_object_get (G_OBJECT (element), "tu", &tu, NULL);
+  fail_unless_equals_uint64 (tu, 2000000);
+
+  g_object_get (G_OBJECT (element), "streamid", &streamid, NULL);
+  fail_unless_equals_uint64 (streamid, 0xAABBCCDDEEFF0001);
+
+  g_object_get (G_OBJECT (element), "processing-deadline", &processing_deadline,
+      NULL);
+  fail_unless_equals_uint64 (processing_deadline, 5000);
+
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+GST_START_TEST (test_payloader_single_and_fragment_edge)
+{
+  GstHarness *h;
+  GstBuffer *in, *out;
+  struct avtp_stream_pdu *pdu = alloca (AVTP_CVF_H264_HEADER_SIZE);
+  const gint DATA_LEN_1 = sizeof (guint32) + 100;
+  const gint DATA_LEN_2 = sizeof (guint32) + 100;
+  const gint DATA_LEN_3 = sizeof (guint32) + 4;
+
+  /* Create the 'expected' header */
+  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_TIMESTAMP, 3000000);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 4000000);
+
+  /* Create the harness for the avtpcvfpay. Setting mtu=128 ensures that
+   * NAL units will be broken roughly at 100 bytes. More details below. */
+  h = gst_harness_new_parse
+      ("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 mtu=128 processing-deadline=0");
+  gst_harness_set_src_caps (h, generate_caps (4));
+
+  /* Create a buffer to contain the multiple NAL units. This buffer
+   * will hold two NAL units, with 100 and 101 bytes, each preceded
+   * by a 4 bytes header */
+  in = gst_harness_create_buffer (h, 100 + 101 + 2 * 4);
+  add_nal (in, 100, 7, 0);
+  add_nal (in, 101, 1, 104);
+  GST_BUFFER_DTS (in) = 1000000;
+  GST_BUFFER_PTS (in) = 2000000;
+
+  /* We now push the buffer, and check if we get three from the avtpcvfpay */
+  gst_harness_push (h, in);
+  fail_unless (gst_harness_buffers_received (h) == 3);
+
+  out = gst_harness_pull (h);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_1);
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  gst_buffer_unref (out);
+
+  out = gst_harness_pull (h);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_2);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 0);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 0);
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  gst_buffer_unref (out);
+
+  /* DATA_LEN_3 is 4 because only 98 bytes from the original NAL unit are
+   * sent on the first buffer (due 2 bytes header), and the two remaining
+   * bytes are preceded by the 2 bytes header. Note that the first byte of
+   * the NAL is stripped before the fragmentation (see comment on
+   * test_payloader_single_and_fragment below for more details). */
+  out = gst_harness_pull (h);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_3);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 2);
+  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, 3000000);
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  gst_buffer_unref (out);
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_payloader_single_and_fragment)
+{
+  GstHarness *h;
+  GstBuffer *in, *out;
+  struct avtp_stream_pdu *pdu = alloca (AVTP_CVF_H264_HEADER_SIZE);
+  const gint DATA_LEN_1 = sizeof (guint32) + 4;
+  const gint DATA_LEN_2 = sizeof (guint32) + 100;
+  const gint DATA_LEN_3 = sizeof (guint32) + 100;
+  const gint DATA_LEN_4 = sizeof (guint32) + 55;
+
+  /* Create the 'expected' header */
+  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_TIMESTAMP, 4000000);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 5000000);
+
+  /* Create the harness for the avtpcvfpay. Setting mtu=128 ensures that
+   * NAL units will be broken roughly at 100 bytes. More details below. */
+  h = gst_harness_new_parse
+      ("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 processing-deadline=1000000 mtu=128");
+  gst_harness_set_src_caps (h, generate_caps (4));
+
+  /* Create a buffer to contain the multiple NAL units. This buffer
+   * will hold two NAL units, with 4 and 250 bytes, each preceded
+   * by a 4 bytes header */
+  in = gst_harness_create_buffer (h, 4 + 250 + 2 * 4);
+  add_nal (in, 4, 7, 0);
+  add_nal (in, 250, 1, 8);
+  GST_BUFFER_DTS (in) = 1000000;
+  GST_BUFFER_PTS (in) = 2000000;
+
+  /* We now push the buffer, and check if we get four from the avtpcvfpay */
+  gst_harness_push (h, in);
+  fail_unless (gst_harness_buffers_received (h) == 4);
+
+  out = gst_harness_pull (h);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_1);
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  fail_unless (check_nal_filling (out, 0) == TRUE);
+  gst_buffer_unref (out);
+
+  out = gst_harness_pull (h);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_2);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 0);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 0);
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  fail_unless (check_nal_filling (out, 0) == TRUE);
+  gst_buffer_unref (out);
+
+  out = gst_harness_pull (h);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_3);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 2);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 0);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, 0);
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  fail_unless (check_nal_filling (out, 98) == TRUE);
+  gst_buffer_unref (out);
+
+  /* For those wondering why DATA_LEN_4 is 55 and not 50 - or why
+   * comment above states that NAL units are broken "rougly" at 100 bytes:
+   * With mtu=128, there are only 100 bytes left for NAL units, so anything
+   * bigger will be broken. But AVTP NAL units fragments have a header with
+   * two bytes, so NAL units will use only 98 bytes. This leaves the last
+   * fragment with 54 bytes. However, instead of being 56 (54 bytes plus
+   * 2 bytes header), it is 55 (53 bytes plus 2 bytes header) due to the
+   * fact that the first byte of the NAL unit (the NAL unit header) is
+   * in fact stripped from the NAL unit before the fragmentation. */
+  out = gst_harness_pull (h);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_4);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 3);
+  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, 4000000);
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  fail_unless (check_nal_filling (out, 196) == TRUE);
+  gst_buffer_unref (out);
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_payloader_multiple_single_2)
+{
+  GstHarness *h;
+  GstBuffer *in, *out;
+  struct avtp_stream_pdu *pdu = alloca (AVTP_CVF_H264_HEADER_SIZE);
+  const gint DATA_LEN_1 = sizeof (guint32) + 32;
+  const gint DATA_LEN_2 = sizeof (guint32) + 16;
+  const gint DATA_LEN_3 = sizeof (guint32) + 8;
+
+  /* Create the 'expected' header */
+  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_TIMESTAMP, 3000000);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 4000000);
+
+  /* Create the harness for the avtpcvfpay */
+  h = gst_harness_new_parse
+      ("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 processing-deadline=0");
+  gst_harness_set_src_caps (h, generate_caps (2));
+
+  /* Create a buffer to contain the multiple NAL units. This buffer
+   * will hold three NAL units, with 32, 16 and 8 bytes, each preceded
+   * by a 2 bytes header */
+  in = gst_harness_create_buffer (h, 32 + 16 + 8 + 4 * 2);
+  add_nal_2 (in, 32, 7, 0);
+  add_nal_2 (in, 16, 7, 34);
+  add_nal_2 (in, 8, 1, 52);
+  GST_BUFFER_DTS (in) = 1000000;
+  GST_BUFFER_PTS (in) = 2000000;
+
+  /* We now push the buffer, and check if we get three from the avtpcvfpay */
+  gst_harness_push (h, in);
+  fail_unless (gst_harness_buffers_received (h) == 3);
+
+  out = gst_harness_pull (h);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_1);
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  fail_unless (check_nal_filling (out, 0) == TRUE);
+  gst_buffer_unref (out);
+
+  out = gst_harness_pull (h);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_2);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1);
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  fail_unless (check_nal_filling (out, 0) == TRUE);
+  gst_buffer_unref (out);
+
+  out = gst_harness_pull (h);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_3);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 2);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1);
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  fail_unless (check_nal_filling (out, 0) == TRUE);
+  gst_buffer_unref (out);
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_payloader_multiple_single)
+{
+  GstHarness *h;
+  GstBuffer *in, *out;
+  struct avtp_stream_pdu *pdu = alloca (AVTP_CVF_H264_HEADER_SIZE);
+  const gint DATA_LEN_1 = sizeof (guint32) + 32;
+  const gint DATA_LEN_2 = sizeof (guint32) + 16;
+  const gint DATA_LEN_3 = sizeof (guint32) + 8;
+
+  /* Create the 'expected' header */
+  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_TIMESTAMP, 3000000);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 4000000);
+
+  /* Create the harness for the avtpcvfpay */
+  h = gst_harness_new_parse
+      ("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 processing-deadline=0");
+  gst_harness_set_src_caps (h, generate_caps (4));
+
+  /* Create a buffer to contain the multiple NAL units. This buffer
+   * will hold three NAL units, with 32, 16 and 8 bytes, each preceded
+   * by a 4 bytes header */
+  in = gst_harness_create_buffer (h, 32 + 16 + 8 + 4 * 4);
+  add_nal (in, 32, 7, 0);
+  add_nal (in, 16, 7, 36);
+  add_nal (in, 8, 1, 56);
+  GST_BUFFER_DTS (in) = 1000000;
+  GST_BUFFER_PTS (in) = 2000000;
+
+  /* We now push the buffer, and check if we get three from the avtpcvfpay */
+  gst_harness_push (h, in);
+  fail_unless (gst_harness_buffers_received (h) == 3);
+
+  out = gst_harness_pull (h);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_1);
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  fail_unless (check_nal_filling (out, 0) == TRUE);
+  gst_buffer_unref (out);
+
+  out = gst_harness_pull (h);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_2);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1);
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  fail_unless (check_nal_filling (out, 0) == TRUE);
+  gst_buffer_unref (out);
+
+  out = gst_harness_pull (h);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN_3);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 2);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 1);
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  fail_unless (check_nal_filling (out, 0) == TRUE);
+  gst_buffer_unref (out);
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_payloader_single)
+{
+  GstHarness *h;
+  GstBuffer *in, *out;
+  struct avtp_stream_pdu *pdu = alloca (AVTP_CVF_H264_HEADER_SIZE);
+  const gint DATA_LEN = sizeof (guint32) + 4;
+
+  /* Create the 'expected' header */
+  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, 3000000);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, 4000000);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN, DATA_LEN);
+
+  /* Create the harness for the avtpcvfpay */
+  h = gst_harness_new_parse
+      ("avtpcvfpay streamid=0xAABBCCDDEEFF0001 mtt=1000000 tu=1000000 processing-deadline=0");
+  gst_harness_set_src_caps (h, generate_caps (4));
+
+  /* Buffer must have the nal len (4 bytes) and the nal (4 bytes) */
+  in = gst_harness_create_buffer (h, 8);
+  add_nal (in, 4, 1, 0);
+  GST_BUFFER_DTS (in) = 1000000;
+  GST_BUFFER_PTS (in) = 2000000;
+
+  out = gst_harness_push_and_pull (h, in);
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  gst_buffer_unref (out);
+
+  /* Now test if, when nal_type is not vcl (not between 1 and 5), M is not set.
+   * Also, as we're using the same element, seqnum should increase by one */
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M, 0);
+  avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM, 1);
+
+  in = gst_harness_create_buffer (h, 8);
+  add_nal (in, 4, 6, 0);
+  GST_BUFFER_DTS (in) = 1000000;
+  GST_BUFFER_PTS (in) = 2000000;
+
+  out = gst_harness_push_and_pull (h, in);
+  fail_unless (compare_h264_avtpdu (pdu, out) == TRUE);
+  fail_unless (check_nal_filling (out, 0) == TRUE);
+  gst_buffer_unref (out);
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+static Suite *
+avtpcvfpay_suite (void)
+{
+  Suite *s = suite_create ("avtpcvfpay");
+  TCase *tc_chain = tcase_create ("general");
+
+  suite_add_tcase (s, tc_chain);
+  tcase_add_test (tc_chain, test_payloader_single);
+  tcase_add_test (tc_chain, test_payloader_multiple_single);
+  tcase_add_test (tc_chain, test_payloader_multiple_single_2);
+  tcase_add_test (tc_chain, test_payloader_single_and_fragment);
+  tcase_add_test (tc_chain, test_payloader_single_and_fragment_edge);
+  tcase_add_test (tc_chain, test_payloader_incomplete_nal);
+  tcase_add_test (tc_chain, test_payloader_invalid_caps);
+  tcase_add_test (tc_chain, test_payloader_properties);
+  tcase_add_test (tc_chain, test_payloader_no_codec_data);
+
+  return s;
+}
+
+GST_CHECK_MAIN (avtpcvfpay);
index 60a64eb..7027f91 100644 (file)
@@ -72,6 +72,7 @@ if host_machine.system() != 'windows'
     [['elements/assrender.c'], not ass_dep.found(), [ass_dep]],
     [['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/avtpsink.c'], not avtp_dep.found(), [avtp_dep]],
     [['elements/avtpsrc.c'], not avtp_dep.found(), [avtp_dep]],
     [['elements/ccconverter.c']],