avtp: Introduce AVTP CVF payloader element
authorEderson de Souza <ederson.desouza@intel.com>
Thu, 28 Feb 2019 23:49:02 +0000 (15:49 -0800)
committerEderson de Souza <ederson.desouza@intel.com>
Wed, 3 Jul 2019 16:59:35 +0000 (09:59 -0700)
This patch introduces the AVTP Compressed Video Format (CVF) payloader
specified in IEEE 1722-2016 section 8. Currently, this payload only
supports H.264 encapsulation described in section 8.5.

Is also worth noting that only single NAL units are encapsulated: no
aggregation or fragmentation is performed by the payloader.

An interesting characteristic of CVF H.264 spec is that it defines an
H264_TIMESTAMP, in addition to the AVTP timestamp. The later is
translated to the GST_BUFFER_DTS while the former is translated to the
GST_BUFFER_PTS. From AVTP CVF H.264 spec, it is clear that the AVTP
timestamp is related to the decoding order, while the H264_TIMESTAMP is
an ancillary information to the H.264 decoder.

Upon receiving a buffer containing a group of NAL units, the avtpcvfpay
element will extract each NAL unit and payload them into individual AVTP
packets. The last AVTP packet generated for a group of NAL units will
have the M bit set, so the depayloader is able to properly regroup them.

The exact format of the buffer of NAL units is described on the
'codec_data' capability, which is parsed by the avtpcvfpay, in the same
way done in rtph264pay.

This patch reuses the infra provided by gstavtpbasepayload.c.

ext/avtp/Makefile.am
ext/avtp/gstavtp.c
ext/avtp/gstavtpcvfpay.c [new file with mode: 0644]
ext/avtp/gstavtpcvfpay.h [new file with mode: 0644]
ext/avtp/meson.build

index db6fe48..1d3eb5b 100644 (file)
@@ -6,6 +6,7 @@ libgstavtp_la_SOURCES = \
        gstavtpaafpay.c \
        gstavtpbasedepayload.c \
        gstavtpbasepayload.c \
+       gstavtpcvfpay.c \
        gstavtpsink.c \
        gstavtpsrc.c
 
@@ -28,5 +29,6 @@ noinst_HEADERS = \
        gstavtpaafpay.h \
        gstavtpbasedepayload.h \
        gstavtpbasepayload.h \
+       gstavtpcvfpay.h \
        gstavtpsink.h \
        gstavtpsrc.h
index c0d15bf..c077eb4 100644 (file)
@@ -52,6 +52,7 @@
 
 #include "gstavtpaafdepay.h"
 #include "gstavtpaafpay.h"
+#include "gstavtpcvfpay.h"
 #include "gstavtpsink.h"
 #include "gstavtpsrc.h"
 
@@ -66,6 +67,8 @@ plugin_init (GstPlugin * plugin)
     return FALSE;
   if (!gst_avtp_src_plugin_init (plugin))
     return FALSE;
+  if (!gst_avtp_cvf_pay_plugin_init (plugin))
+    return FALSE;
 
   return TRUE;
 }
diff --git a/ext/avtp/gstavtpcvfpay.c b/ext/avtp/gstavtpcvfpay.c
new file mode 100644 (file)
index 0000000..b131d6a
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * 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
+ */
+
+/**
+ * SECTION:element-avtpcvfpay
+ * @see_also: avtpcvfdepay
+ *
+ * Payload compressed video (currently, only H.264) into AVTPDUs according
+ * to IEEE 1722-2016. For detailed information see
+ * https://standards.ieee.org/standard/1722-2016.html.
+ *
+ * <refsect2>
+ * <title>Example pipeline</title>
+ * |[
+ * gst-launch-1.0 videotestsrc ! x264enc ! avtpcvfpay ! avtpsink
+ * ]| This example pipeline will payload H.264 video. Refer to the avtpcvfdepay
+ * example to depayload and play the AVTP stream.
+ * </refsect2>
+ */
+
+#include <avtp.h>
+#include <avtp_cvf.h>
+
+#include "gstavtpcvfpay.h"
+
+GST_DEBUG_CATEGORY_STATIC (avtpcvfpay_debug);
+#define GST_CAT_DEFAULT avtpcvfpay_debug
+
+/* prototypes */
+
+static GstFlowReturn gst_avtp_cvf_pay_chain (GstPad * pad, GstObject * parent,
+    GstBuffer * buffer);
+static gboolean gst_avtp_cvf_pay_sink_event (GstPad * pad, GstObject * parent,
+    GstEvent * event);
+
+static void gst_avtp_cvf_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_avtp_cvf_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static GstStateChangeReturn gst_avtp_cvf_change_state (GstElement *
+    element, GstStateChange transition);
+
+enum
+{
+  PROP_0,
+  PROP_MTU
+};
+
+#define DEFAULT_MTU 1500
+
+#define AVTP_CVF_H264_HEADER_SIZE (sizeof(struct avtp_stream_pdu) + sizeof(guint32))
+
+#define NAL_TYPE_MASK       0x1f
+#define FIRST_NAL_VCL_TYPE  0x01
+#define LAST_NAL_VCL_TYPE   0x05
+#define NAL_LEN_SIZE_MASK   0x03
+
+/* pad templates */
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/x-h264, "
+        "stream-format = (string) avc, alignment = (string) au")
+    );
+
+/* class initialization */
+
+#define gst_avtp_cvf_pay_parent_class parent_class
+G_DEFINE_TYPE (GstAvtpCvfPay, gst_avtp_cvf_pay, GST_TYPE_AVTP_BASE_PAYLOAD);
+
+static void
+gst_avtp_cvf_pay_class_init (GstAvtpCvfPayClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+  GstAvtpBasePayloadClass *avtpbasepayload_class =
+      GST_AVTP_BASE_PAYLOAD_CLASS (klass);
+
+  gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
+
+  gst_element_class_set_static_metadata (gstelement_class,
+      "AVTP Compressed Video Format (CVF) payloader",
+      "Codec/Payloader/Network/AVTP",
+      "Payload-encode compressed video into CVF AVTPDU (IEEE 1722)",
+      "Ederson de Souza <ederson.desouza@intel.com>");
+
+  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_avtp_cvf_set_property);
+  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_avtp_cvf_get_property);
+
+  gstelement_class->change_state =
+      GST_DEBUG_FUNCPTR (gst_avtp_cvf_change_state);
+
+  avtpbasepayload_class->chain = GST_DEBUG_FUNCPTR (gst_avtp_cvf_pay_chain);
+  avtpbasepayload_class->sink_event =
+      GST_DEBUG_FUNCPTR (gst_avtp_cvf_pay_sink_event);
+
+  g_object_class_install_property (gobject_class, PROP_MTU,
+      g_param_spec_uint ("mtu", "Maximum Transit Unit",
+          "Maximum Transit Unit (MTU) of underlying network in bytes", 0,
+          G_MAXUINT, DEFAULT_MTU, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  GST_DEBUG_CATEGORY_INIT (avtpcvfpay_debug, "avtpcvfpay",
+      0, "debug category for avtpcvfpay element");
+}
+
+static void
+gst_avtp_cvf_pay_init (GstAvtpCvfPay * avtpcvfpay)
+{
+  avtpcvfpay->mtu = DEFAULT_MTU;
+  avtpcvfpay->header = NULL;
+  avtpcvfpay->nal_length_size = 0;
+}
+
+static void
+gst_avtp_cvf_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstAvtpCvfPay *avtpcvfpay = GST_AVTP_CVF_PAY (object);
+
+  GST_DEBUG_OBJECT (avtpcvfpay, "prop_id: %u", prop_id);
+
+  if (prop_id == PROP_MTU) {
+    avtpcvfpay->mtu = g_value_get_uint (value);
+  } else {
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+gst_avtp_cvf_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstAvtpCvfPay *avtpcvfpay = GST_AVTP_CVF_PAY (object);
+
+  GST_DEBUG_OBJECT (avtpcvfpay, "prop_id: %u", prop_id);
+
+  if (prop_id == PROP_MTU) {
+    g_value_set_uint (value, avtpcvfpay->mtu);
+  } else {
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static GstStateChangeReturn
+gst_avtp_cvf_change_state (GstElement * element, GstStateChange transition)
+{
+  GstAvtpCvfPay *avtpcvfpay = GST_AVTP_CVF_PAY (element);
+  GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (avtpcvfpay);
+  GstStateChangeReturn ret;
+
+  if (transition == GST_STATE_CHANGE_NULL_TO_READY) {
+    GstMapInfo map;
+    struct avtp_stream_pdu *pdu;
+    int res;
+
+    avtpcvfpay->header = gst_buffer_new_allocate (NULL,
+        AVTP_CVF_H264_HEADER_SIZE, NULL);
+    if (avtpcvfpay->header == NULL) {
+      GST_ERROR_OBJECT (avtpcvfpay, "Could not allocate buffer");
+      return GST_STATE_CHANGE_FAILURE;
+    }
+
+    gst_buffer_map (avtpcvfpay->header, &map, GST_MAP_WRITE);
+    pdu = (struct avtp_stream_pdu *) map.data;
+
+    res = avtp_cvf_pdu_init (pdu, AVTP_CVF_FORMAT_SUBTYPE_H264);
+    g_assert (res == 0);
+    res = avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TV, 1);
+    g_assert (res == 0);
+    res =
+        avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_ID,
+        avtpbasepayload->streamid);
+    g_assert (res == 0);
+
+    gst_buffer_unmap (avtpcvfpay->header, &map);
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+  if (ret == GST_STATE_CHANGE_FAILURE) {
+    return ret;
+  }
+
+  if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
+    gst_buffer_unref (avtpcvfpay->header);
+  }
+
+  return ret;
+}
+
+static void
+gst_avtp_cvf_pay_extract_nals (GstAvtpCvfPay * avtpcvfpay,
+    GstBuffer * buffer, GPtrArray * nals)
+{
+  /* The buffer may have more than one NAL. They are grouped together, and before
+   * each NAL there are some bytes that indicate how big is the NAL */
+
+  gsize size, offset = 0;
+  GstMapInfo map;
+  guint8 *data;
+  gboolean res;
+
+  if (G_UNLIKELY (avtpcvfpay->nal_length_size == 0)) {
+    GST_ERROR_OBJECT (avtpcvfpay,
+        "Can't extract NAL units without nal length size. Missing codec_data caps?");
+    goto end;
+  }
+
+  res = gst_buffer_map (buffer, &map, GST_MAP_READ);
+  if (!res) {
+    GST_ERROR_OBJECT (avtpcvfpay, "Could map buffer");
+    goto end;
+  }
+
+  size = map.size;
+  data = map.data;
+
+  while (size > avtpcvfpay->nal_length_size) {
+    gint i;
+    guint nal_len = 0;
+    GstBuffer *nal;
+
+    /* Gets NAL length */
+    for (i = 0; i < avtpcvfpay->nal_length_size; i++) {
+      nal_len = (nal_len << 8) + data[i];
+    }
+
+    offset += avtpcvfpay->nal_length_size;
+    data += avtpcvfpay->nal_length_size;
+    size -= avtpcvfpay->nal_length_size;
+
+    if (G_UNLIKELY (size < nal_len)) {
+      GST_WARNING_OBJECT (avtpcvfpay,
+          "Got incomplete NAL: NAL len %u, buffer len %zu", nal_len, size);
+      nal_len = size;
+    }
+
+    nal = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, offset, nal_len);
+    GST_BUFFER_PTS (nal) = GST_BUFFER_PTS (buffer);
+    GST_BUFFER_DTS (nal) = GST_BUFFER_DTS (buffer);
+    g_ptr_array_add (nals, nal);
+
+    offset += nal_len;
+    data += nal_len;
+    size -= nal_len;
+  }
+
+  gst_buffer_unmap (buffer, &map);
+
+end:
+  /* This function consumes the buffer, and all references to it are in the
+   * extracted nals, so we can release the reference to the buffer itself */
+  gst_buffer_unref (buffer);
+
+  GST_LOG_OBJECT (avtpcvfpay, "Extracted %u NALu's from buffer", nals->len);
+}
+
+static gboolean
+gst_avtp_cvf_pay_is_nal_vcl (GstAvtpCvfPay * avtpcvfpay, GstBuffer * nal)
+{
+  guint8 nal_header, nal_type;
+
+  gst_buffer_extract (nal, 0, &nal_header, 1);
+  nal_type = nal_header & NAL_TYPE_MASK;
+
+  return nal_type >= FIRST_NAL_VCL_TYPE && nal_type <= LAST_NAL_VCL_TYPE;
+}
+
+static gboolean
+gst_avtp_cvf_pay_prepare_avtp_packets (GstAvtpCvfPay * avtpcvfpay,
+    GPtrArray * nals, GPtrArray * avtp_packets)
+{
+  GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (avtpcvfpay);
+  GstBuffer *header, *packet, *nal;
+  GstMapInfo map;
+  gint i;
+
+  for (i = 0; i < nals->len; i++) {
+    int res;
+    struct avtp_stream_pdu *pdu;
+    guint64 avtp_time, h264_time;
+
+    /* Copy saved header to reuse common fields and change what is needed */
+    header = gst_buffer_copy (avtpcvfpay->header);
+    gst_buffer_map (header, &map, GST_MAP_WRITE);
+    pdu = (struct avtp_stream_pdu *) map.data;
+
+    nal = g_ptr_array_index (nals, i);
+    GST_LOG_OBJECT (avtpcvfpay,
+        "Preparing AVTP packet for NAL whose size is %lu",
+        gst_buffer_get_size (nal));
+
+    /* Calculate timestamps. Note that we do it twice, one using DTS as base,
+     * the other using PTS - using code inherited from avtpbasepayload.
+     * Also worth noting: `avtpbasepayload->latency` is updated after
+     * first call to gst_avtp_base_payload_calc_ptime, so we MUST call
+     * it before using the latency value */
+    h264_time = gst_avtp_base_payload_calc_ptime (avtpbasepayload, nal);
+
+    avtp_time =
+        gst_element_get_base_time (GST_ELEMENT (avtpcvfpay)) +
+        gst_segment_to_running_time (&avtpbasepayload->segment, GST_FORMAT_TIME,
+        GST_BUFFER_DTS_OR_PTS (nal)) + avtpbasepayload->mtt +
+        avtpbasepayload->tu + avtpbasepayload->processing_deadline +
+        avtpbasepayload->latency;
+
+    res = avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_TIMESTAMP, avtp_time);
+    g_assert (res == 0);
+    res =
+        avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_SEQ_NUM,
+        avtpbasepayload->seqnum++);
+    g_assert (res == 0);
+
+    /* Set M only if last NAL and it is a VCL NAL */
+    res = avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_M,
+        i == nals->len - 1 && gst_avtp_cvf_pay_is_nal_vcl (avtpcvfpay, nal));
+    g_assert (res == 0);
+
+    /* Stream data len includes AVTP H264 header len as this is part of
+     * the payload too. It's just the uint32_t with the h264 timestamp*/
+    res =
+        avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_STREAM_DATA_LEN,
+        gst_buffer_get_size (nal) + sizeof (uint32_t));
+    g_assert (res == 0);
+
+    res = avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_TIMESTAMP, h264_time);
+    g_assert (res == 0);
+
+    res = avtp_cvf_pdu_set (pdu, AVTP_CVF_FIELD_H264_PTV, 1);
+    g_assert (res == 0);
+    /* TODO check if NALs can be grouped, or need to be
+     * fragmented */
+
+    gst_buffer_unmap (header, &map);
+    packet = gst_buffer_append (header, nal);
+
+    g_ptr_array_add (avtp_packets, packet);
+  }
+
+  GST_LOG_OBJECT (avtpcvfpay, "Prepared %u AVTP packets", avtp_packets->len);
+
+  return TRUE;
+}
+
+static gboolean
+gst_avtp_cvf_pay_push_packets (GstAvtpCvfPay * avtpcvfpay,
+    GPtrArray * avtp_packets)
+{
+  int i;
+  GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (avtpcvfpay);
+
+  for (i = 0; i < avtp_packets->len; i++) {
+    GstBuffer *packet;
+
+    packet = g_ptr_array_index (avtp_packets, i);
+    if (gst_pad_push (avtpbasepayload->srcpad, packet) != GST_FLOW_OK)
+      return FALSE;
+  }
+
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_avtp_cvf_pay_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
+{
+  GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (parent);
+  GstAvtpCvfPay *avtpcvfpay = GST_AVTP_CVF_PAY (avtpbasepayload);
+  GPtrArray *nals, *avtp_packets;
+  GstFlowReturn ret = GST_FLOW_OK;
+
+  GST_LOG_OBJECT (avtpcvfpay,
+      "Incoming buffer size: %lu PTS: %" GST_TIME_FORMAT " DTS: %"
+      GST_TIME_FORMAT, gst_buffer_get_size (buffer),
+      GST_TIME_ARGS (GST_BUFFER_PTS (buffer)),
+      GST_TIME_ARGS (GST_BUFFER_DTS (buffer)));
+
+  /* Get all NALs inside buffer */
+  nals = g_ptr_array_new ();
+  gst_avtp_cvf_pay_extract_nals (avtpcvfpay, buffer, nals);
+
+  /* Prepare a list of avtp_packets to send */
+  avtp_packets = g_ptr_array_new ();
+  gst_avtp_cvf_pay_prepare_avtp_packets (avtpcvfpay, nals, avtp_packets);
+
+  if (!gst_avtp_cvf_pay_push_packets (avtpcvfpay, avtp_packets))
+    ret = GST_FLOW_ERROR;
+
+  /* Contents of both ptr_arrays should be unref'd or transferred
+   * to rightful owner by this point, no need to unref them again */
+  g_ptr_array_free (nals, TRUE);
+  g_ptr_array_free (avtp_packets, TRUE);
+
+  return ret;
+}
+
+static gboolean
+gst_avtp_cvf_pay_new_caps (GstAvtpCvfPay * avtpcvfpay, GstCaps * caps)
+{
+  const GValue *value;
+  GstStructure *str;
+  GstBuffer *buffer;
+  GstMapInfo map;
+
+  str = gst_caps_get_structure (caps, 0);
+
+  if ((value = gst_structure_get_value (str, "codec_data"))) {
+    guint8 *data;
+    gsize size;
+
+    buffer = gst_value_get_buffer (value);
+    gst_buffer_map (buffer, &map, GST_MAP_READ);
+    data = map.data;
+    size = map.size;
+
+    if (G_UNLIKELY (size < 7)) {
+      GST_ERROR_OBJECT (avtpcvfpay, "avcC size %" G_GSIZE_FORMAT " < 7", size);
+      goto error;
+    }
+    if (G_UNLIKELY (data[0] != 1)) {
+      GST_ERROR_OBJECT (avtpcvfpay, "avcC version %u != 1", data[0]);
+      goto error;
+    }
+
+    /* Number of bytes in front of NAL units marking their size */
+    avtpcvfpay->nal_length_size = (data[4] & NAL_LEN_SIZE_MASK) + 1;
+    GST_DEBUG_OBJECT (avtpcvfpay, "Got NAL length from caps: %u",
+        avtpcvfpay->nal_length_size);
+
+    gst_buffer_unmap (buffer, &map);
+  }
+
+  return TRUE;
+
+error:
+  gst_buffer_unmap (buffer, &map);
+  return FALSE;
+}
+
+static gboolean
+gst_avtp_cvf_pay_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+  GstCaps *caps;
+  GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (parent);
+  GstAvtpCvfPay *avtpcvfpay = GST_AVTP_CVF_PAY (avtpbasepayload);
+
+  GST_DEBUG_OBJECT (avtpcvfpay, "Sink event %s", GST_EVENT_TYPE_NAME (event));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_CAPS:
+      gst_event_parse_caps (event, &caps);
+      return gst_avtp_cvf_pay_new_caps (avtpcvfpay, caps);
+    default:
+      break;
+  }
+
+  return GST_AVTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (pad, parent,
+      event);
+}
+
+gboolean
+gst_avtp_cvf_pay_plugin_init (GstPlugin * plugin)
+{
+  return gst_element_register (plugin, "avtpcvfpay", GST_RANK_NONE,
+      GST_TYPE_AVTP_CVF_PAY);
+}
diff --git a/ext/avtp/gstavtpcvfpay.h b/ext/avtp/gstavtpcvfpay.h
new file mode 100644 (file)
index 0000000..cd9d987
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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
+ */
+
+#ifndef __GST_AVTP_CVF_PAY_H__
+#define __GST_AVTP_CVF_PAY_H__
+
+#include <gst/gst.h>
+
+#include "gstavtpbasepayload.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_AVTP_CVF_PAY (gst_avtp_cvf_pay_get_type())
+#define GST_AVTP_CVF_PAY(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVTP_CVF_PAY,GstAvtpCvfPay))
+#define GST_AVTP_CVF_PAY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVTP_CVF_PAY,GstAvtpCvfPayClass))
+#define GST_IS_AVTP_CVF_PAY(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVTP_CVF_PAY))
+#define GST_IS_AVTP_CVF_PAY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVTP_CVF_PAY))
+
+typedef struct _GstAvtpCvfPay GstAvtpCvfPay;
+typedef struct _GstAvtpCvfPayClass GstAvtpCvfPayClass;
+
+struct _GstAvtpCvfPay
+{
+  GstAvtpBasePayload payload;
+
+  GstBuffer *header;
+  guint mtu;
+
+  /* H.264 specific information */
+  guint8 nal_length_size;
+};
+
+struct _GstAvtpCvfPayClass
+{
+  GstAvtpBasePayloadClass parent_class;
+};
+
+GType gst_avtp_cvf_pay_get_type (void);
+
+gboolean gst_avtp_cvf_pay_plugin_init (GstPlugin * plugin);
+
+G_END_DECLS
+
+#endif /* __GST_AVTP_CVF_PAY_H__ */
index ba843a1..2eb3347 100644 (file)
@@ -2,6 +2,7 @@ avtp_sources = [
   'gstavtp.c',
   'gstavtpaafdepay.c',
   'gstavtpaafpay.c',
+  'gstavtpcvfpay.c',
   'gstavtpbasedepayload.c',
   'gstavtpbasepayload.c',
   'gstavtpsink.c',
@@ -15,7 +16,7 @@ if avtp_dep.found()
     avtp_sources,
     c_args : gst_plugins_bad_args,
     include_directories : [configinc],
-    dependencies : [gstaudio_dep, avtp_dep],
+    dependencies : [gstaudio_dep, gstvideo_dep, avtp_dep],
     install : true,
     install_dir : plugins_install_dir,
   )