avtp: Introduce AAF depayloader element
authorAndre Guedes <andre.guedes@intel.com>
Thu, 24 Jan 2019 00:20:27 +0000 (16:20 -0800)
committerEderson de Souza <ederson.desouza@intel.com>
Wed, 3 Jul 2019 16:59:35 +0000 (09:59 -0700)
This patch introduces the AAF depayloader element, the counterpart from
the AAF payloader. As expected, this element inputs AVTPDUs and outputs
audio raw data and supports AAF PCM encapsulation only.

The AAF depayloader srcpad produces a fixed format that is encoded
within the AVTPDU. Once the first AVTPDU is received by the element, the
audio features e.g. sample format, rate, number of channels, are decoded
and the srcpad caps are set accordingly. Also, at this point, the
element pushes a SEGMENT event downstream defining the segment according
to the AVTP presentation time.

All AVTP depayloaders will share some common code. For that reason, this
patch introduces the GstAvtpBaseDepayload abstract class that implements
common depayloader functionalities. AAF-specific functionalities are
implemented in the derived class GstAvtpAafDepay.

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

index 242a72c..46151ee 100644 (file)
@@ -2,7 +2,9 @@ plugin_LTLIBRARIES = libgstavtp.la
 
 libgstavtp_la_SOURCES = \
        gstavtp.c \
+       gstavtpaafdepay.c \
        gstavtpaafpay.c \
+       gstavtpbasedepayload.c \
        gstavtpbasepayload.c
 
 libgstavtp_la_CFLAGS = \
@@ -20,5 +22,7 @@ libgstavtp_la_LIBADD = \
 libgstavtp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 
 noinst_HEADERS = \
+       gstavtpaafdepay.h \
        gstavtpaafpay.h \
+       gstavtpbasedepayload.h \
        gstavtpbasepayload.h
index 91998da..d64be61 100644 (file)
@@ -50,6 +50,7 @@
 
 #include <gst/gst.h>
 
+#include "gstavtpaafdepay.h"
 #include "gstavtpaafpay.h"
 
 static gboolean
@@ -57,6 +58,8 @@ plugin_init (GstPlugin * plugin)
 {
   if (!gst_avtp_aaf_pay_plugin_init (plugin))
     return FALSE;
+  if (!gst_avtp_aaf_depay_plugin_init (plugin))
+    return FALSE;
 
   return TRUE;
 }
diff --git a/ext/avtp/gstavtpaafdepay.c b/ext/avtp/gstavtpaafdepay.c
new file mode 100644 (file)
index 0000000..4534bba
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * 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-avtpaafdepay
+ * @see_also: avtpaafpay
+ *
+ * Extract raw audio from 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 avtpsrc ! avtpaafdepay ! autoaudiosink
+ * ]| This example pipeline will depayload AVTPDUs. Refer to the avtpaafpay
+ * example to create the AVTP stream.
+ * </refsect2>
+ */
+
+#include <avtp.h>
+#include <avtp_aaf.h>
+#include <gst/audio/audio-format.h>
+
+#include "gstavtpaafdepay.h"
+
+GST_DEBUG_CATEGORY_STATIC (avtpaafdepay_debug);
+#define GST_CAT_DEFAULT (avtpaafdepay_debug)
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw, "
+        "format = (string) { S16BE, S24BE, S32BE, F32BE }, "
+        "rate = (int) { 8000, 16000, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000 }, "
+        "channels = " GST_AUDIO_CHANNELS_RANGE ", "
+        "layout = (string) interleaved")
+    );
+
+G_DEFINE_TYPE (GstAvtpAafDepay, gst_avtp_aaf_depay,
+    GST_TYPE_AVTP_BASE_DEPAYLOAD);
+
+static GstFlowReturn gst_avtp_aaf_depay_chain (GstPad * pad, GstObject * parent,
+    GstBuffer * buffer);
+
+static void
+gst_avtp_aaf_depay_class_init (GstAvtpAafDepayClass * klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+  GstAvtpBaseDepayloadClass *avtpbasedepayload_class =
+      GST_AVTP_BASE_DEPAYLOAD_CLASS (klass);
+
+  gst_element_class_add_static_pad_template (element_class, &src_template);
+
+  gst_element_class_set_static_metadata (element_class,
+      "AVTP Audio Format (AAF) depayloader",
+      "Codec/Depayloader/Network/AVTP",
+      "Extracts raw audio from AAF AVTPDUs",
+      "Andre Guedes <andre.guedes@intel.com>");
+
+  avtpbasedepayload_class->chain = GST_DEBUG_FUNCPTR (gst_avtp_aaf_depay_chain);
+
+  GST_DEBUG_CATEGORY_INIT (avtpaafdepay_debug, "avtpaafdepay", 0,
+      "AAF AVTP Depayloader");
+}
+
+static void
+gst_avtp_aaf_depay_init (GstAvtpAafDepay * avtpaafdepay)
+{
+  avtpaafdepay->channels = 0;
+  avtpaafdepay->depth = 0;
+  avtpaafdepay->rate = 0;
+  avtpaafdepay->format = 0;
+}
+
+static const gchar *
+avtp_to_gst_format (int avtp_format)
+{
+  GstAudioFormat gst_format;
+
+  switch (avtp_format) {
+    case AVTP_AAF_FORMAT_INT_16BIT:
+      gst_format = GST_AUDIO_FORMAT_S16BE;
+      break;
+    case AVTP_AAF_FORMAT_INT_24BIT:
+      gst_format = GST_AUDIO_FORMAT_S24BE;
+      break;
+    case AVTP_AAF_FORMAT_INT_32BIT:
+      gst_format = GST_AUDIO_FORMAT_S32BE;
+      break;
+    case AVTP_AAF_FORMAT_FLOAT_32BIT:
+      gst_format = GST_AUDIO_FORMAT_F32BE;
+      break;
+    default:
+      gst_format = GST_AUDIO_FORMAT_UNKNOWN;
+      break;
+  }
+
+  return gst_audio_format_to_string (gst_format);
+}
+
+static gint
+avtp_to_gst_rate (int rate)
+{
+  switch (rate) {
+    case AVTP_AAF_PCM_NSR_8KHZ:
+      return 8000;
+    case AVTP_AAF_PCM_NSR_16KHZ:
+      return 16000;
+    case AVTP_AAF_PCM_NSR_24KHZ:
+      return 24000;
+    case AVTP_AAF_PCM_NSR_32KHZ:
+      return 32000;
+    case AVTP_AAF_PCM_NSR_44_1KHZ:
+      return 44100;
+    case AVTP_AAF_PCM_NSR_48KHZ:
+      return 48000;
+    case AVTP_AAF_PCM_NSR_88_2KHZ:
+      return 88200;
+    case AVTP_AAF_PCM_NSR_96KHZ:
+      return 96000;
+    case AVTP_AAF_PCM_NSR_176_4KHZ:
+      return 176400;
+    case AVTP_AAF_PCM_NSR_192KHZ:
+      return 192000;
+    default:
+      return 0;
+  }
+}
+
+static gboolean
+gst_avtp_aaf_depay_push_caps_event (GstAvtpAafDepay * avtpaafdepay,
+    guint64 rate, guint64 depth, guint64 format, guint64 channels)
+{
+  GstCaps *caps;
+  GstEvent *event;
+  GstAvtpBaseDepayload *avtpbasedepayload =
+      GST_AVTP_BASE_DEPAYLOAD (avtpaafdepay);
+
+  caps = gst_caps_new_simple ("audio/x-raw",
+      "format", G_TYPE_STRING, avtp_to_gst_format (format),
+      "rate", G_TYPE_INT, avtp_to_gst_rate (rate),
+      "channels", G_TYPE_INT, channels,
+      "layout", G_TYPE_STRING, "interleaved", NULL);
+
+  event = gst_event_new_caps (caps);
+
+  if (!gst_pad_push_event (avtpbasedepayload->srcpad, event)) {
+    GST_ERROR_OBJECT (avtpaafdepay, "Failed to push CAPS event");
+    gst_caps_unref (caps);
+    return FALSE;
+  }
+
+  GST_DEBUG_OBJECT (avtpaafdepay, "CAPS event pushed %" GST_PTR_FORMAT, caps);
+
+  avtpaafdepay->rate = rate;
+  avtpaafdepay->depth = depth;
+  avtpaafdepay->format = format;
+  avtpaafdepay->channels = channels;
+  gst_caps_unref (caps);
+  return TRUE;
+}
+
+static gboolean
+gst_avtp_aaf_depay_are_audio_features_valid (GstAvtpAafDepay * avtpaafdepay,
+    guint64 rate, guint64 depth, guint64 format, guint64 channels)
+{
+  if (G_UNLIKELY (rate != avtpaafdepay->rate)) {
+    GST_INFO_OBJECT (avtpaafdepay, "Rate doesn't match, disarding buffer");
+    return FALSE;
+  }
+  if (G_UNLIKELY (depth != avtpaafdepay->depth)) {
+    GST_INFO_OBJECT (avtpaafdepay, "Bit depth doesn't match, disarding buffer");
+    return FALSE;
+  }
+  if (G_UNLIKELY (format != avtpaafdepay->format)) {
+    GST_INFO_OBJECT (avtpaafdepay,
+        "Sample format doesn't match, disarding buffer");
+    return FALSE;
+  }
+  if (G_UNLIKELY (channels != avtpaafdepay->channels)) {
+    GST_INFO_OBJECT (avtpaafdepay,
+        "Number of channels doesn't match, disarding buffer");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_avtp_aaf_depay_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
+{
+  int res;
+  GstMapInfo info;
+  guint32 subtype, version;
+  GstClockTime ptime;
+  GstBuffer *subbuffer;
+  struct avtp_stream_pdu *pdu;
+  guint64 channels, depth, rate, format, tstamp, seqnum, streamid,
+      streamid_valid, data_len;
+  GstAvtpBaseDepayload *avtpbasedepayload = GST_AVTP_BASE_DEPAYLOAD (parent);
+  GstAvtpAafDepay *avtpaafdepay = GST_AVTP_AAF_DEPAY (avtpbasedepayload);
+
+  if (!gst_buffer_map (buffer, &info, GST_MAP_READ)) {
+    GST_ELEMENT_ERROR (avtpaafdepay, RESOURCE, READ, ("Failed to map memory"),
+        (NULL));
+    gst_buffer_unref (buffer);
+    return GST_FLOW_ERROR;
+  }
+
+  if (info.size < sizeof (struct avtp_stream_pdu)) {
+    GST_DEBUG_OBJECT (avtpaafdepay, "Malformed AVTPDU, discarding it");
+    gst_buffer_unmap (buffer, &info);
+    goto discard;
+  }
+
+  pdu = (struct avtp_stream_pdu *) info.data;
+  res = avtp_aaf_pdu_get (pdu, AVTP_AAF_FIELD_NSR, &rate);
+  g_assert (res == 0);
+  res = avtp_aaf_pdu_get (pdu, AVTP_AAF_FIELD_FORMAT, &format);
+  g_assert (res == 0);
+  res = avtp_aaf_pdu_get (pdu, AVTP_AAF_FIELD_SEQ_NUM, &seqnum);
+  g_assert (res == 0);
+  res = avtp_aaf_pdu_get (pdu, AVTP_AAF_FIELD_BIT_DEPTH, &depth);
+  g_assert (res == 0);
+  res = avtp_aaf_pdu_get (pdu, AVTP_AAF_FIELD_TIMESTAMP, &tstamp);
+  g_assert (res == 0);
+  res = avtp_aaf_pdu_get (pdu, AVTP_AAF_FIELD_SV, &streamid_valid);
+  g_assert (res == 0);
+  res = avtp_aaf_pdu_get (pdu, AVTP_AAF_FIELD_STREAM_ID, &streamid);
+  g_assert (res == 0);
+  res = avtp_aaf_pdu_get (pdu, AVTP_AAF_FIELD_CHAN_PER_FRAME, &channels);
+  g_assert (res == 0);
+  res = avtp_aaf_pdu_get (pdu, AVTP_AAF_FIELD_STREAM_DATA_LEN, &data_len);
+  g_assert (res == 0);
+  res = avtp_pdu_get ((struct avtp_common_pdu *) pdu, AVTP_FIELD_SUBTYPE,
+      &subtype);
+  g_assert (res == 0);
+  res = avtp_pdu_get ((struct avtp_common_pdu *) pdu, AVTP_FIELD_VERSION,
+      &version);
+  g_assert (res == 0);
+
+  gst_buffer_unmap (buffer, &info);
+
+  if (subtype != AVTP_SUBTYPE_AAF) {
+    GST_DEBUG_OBJECT (avtpaafdepay, "Subtype doesn't match, discarding buffer");
+    goto discard;
+  }
+  if (version != 0) {
+    GST_DEBUG_OBJECT (avtpaafdepay, "Version doesn't match, discarding buffer");
+    goto discard;
+  }
+  if (streamid_valid != 1 || streamid != avtpbasedepayload->streamid) {
+    GST_DEBUG_OBJECT (avtpaafdepay, "Invalid StreamID, discarding buffer");
+    goto discard;
+  }
+  if (gst_buffer_get_size (buffer) < sizeof (*pdu) + data_len) {
+    GST_DEBUG_OBJECT (avtpaafdepay, "Incomplete AVTPDU, discarding buffer");
+    goto discard;
+  }
+
+  if (G_UNLIKELY (!gst_pad_has_current_caps (avtpbasedepayload->srcpad))) {
+    if (!gst_avtp_aaf_depay_push_caps_event (avtpaafdepay, rate, depth, format,
+            channels)) {
+      gst_buffer_unref (buffer);
+      return GST_FLOW_NOT_NEGOTIATED;
+    }
+    if (!gst_avtp_base_depayload_push_segment_event (avtpbasedepayload, tstamp)) {
+      gst_buffer_unref (buffer);
+      return GST_FLOW_ERROR;
+    }
+
+    avtpbasedepayload->seqnum = seqnum;
+  }
+
+  if (G_UNLIKELY (!gst_avtp_aaf_depay_are_audio_features_valid (avtpaafdepay,
+              rate, depth, format, channels)))
+    goto discard;
+
+  if (seqnum != avtpbasedepayload->seqnum) {
+    GST_INFO_OBJECT (avtpaafdepay, "Sequence number mismatch: expected %"
+        G_GUINT16_FORMAT " received %" G_GUINT64_FORMAT,
+        avtpbasedepayload->seqnum, seqnum);
+    avtpbasedepayload->seqnum = seqnum;
+  }
+  avtpbasedepayload->seqnum++;
+
+  ptime = gst_avtp_base_depayload_tstamp_to_ptime (avtpbasedepayload, tstamp,
+      avtpbasedepayload->prev_ptime);
+
+  subbuffer = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL,
+      sizeof (struct avtp_stream_pdu), data_len);
+  GST_BUFFER_PTS (subbuffer) = ptime;
+  GST_BUFFER_DTS (subbuffer) = ptime;
+
+  avtpbasedepayload->prev_ptime = ptime;
+  gst_buffer_unref (buffer);
+  return gst_pad_push (avtpbasedepayload->srcpad, subbuffer);
+
+discard:
+  gst_buffer_unref (buffer);
+  return GST_FLOW_OK;
+}
+
+gboolean
+gst_avtp_aaf_depay_plugin_init (GstPlugin * plugin)
+{
+  return gst_element_register (plugin, "avtpaafdepay", GST_RANK_NONE,
+      GST_TYPE_AVTP_AAF_DEPAY);
+}
diff --git a/ext/avtp/gstavtpaafdepay.h b/ext/avtp/gstavtpaafdepay.h
new file mode 100644 (file)
index 0000000..ef639b7
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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_AAF_DEPAY_H__
+#define __GST_AVTP_AAF_DEPAY_H__
+
+#include <gst/gst.h>
+
+#include "gstavtpbasedepayload.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_AVTP_AAF_DEPAY (gst_avtp_aaf_depay_get_type())
+#define GST_AVTP_AAF_DEPAY(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVTP_AAF_DEPAY,GstAvtpAafDepay))
+#define GST_AVTP_AAF_DEPAY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVTP_AAF_DEPAY,GstAvtpAafDepayClass))
+#define GST_IS_AVTP_AAF_DEPAY(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVTP_AAF_DEPAY))
+#define GST_IS_AVTP_AAF_DEPAY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVTP_AAF_DEPAY))
+
+typedef struct _GstAvtpAafDepay GstAvtpAafDepay;
+typedef struct _GstAvtpAafDepayClass GstAvtpAafDepayClass;
+
+struct _GstAvtpAafDepay
+{
+  GstAvtpBaseDepayload depayload;
+
+  gint channels;
+  gint depth;
+  gint rate;
+  gint format;
+};
+
+struct _GstAvtpAafDepayClass
+{
+  GstAvtpBaseDepayloadClass parent_class;
+};
+
+GType gst_avtp_aaf_depay_get_type (void);
+
+gboolean gst_avtp_aaf_depay_plugin_init (GstPlugin * plugin);
+
+G_END_DECLS
+
+#endif /* __GST_AVTP_AAF_DEPAY_H__ */
diff --git a/ext/avtp/gstavtpbasedepayload.c b/ext/avtp/gstavtpbasedepayload.c
new file mode 100644 (file)
index 0000000..420911e
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * 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 "gstavtpbasedepayload.h"
+
+GST_DEBUG_CATEGORY_STATIC (avtpbasedepayload_debug);
+#define GST_CAT_DEFAULT (avtpbasedepayload_debug)
+
+#define DEFAULT_STREAMID 0xAABBCCDDEEFF0000
+
+enum
+{
+  PROP_0,
+  PROP_STREAMID,
+};
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("application/x-avtp")
+    );
+
+static void gst_avtp_base_depayload_class_init (GstAvtpBaseDepayloadClass *
+    klass);
+static void gst_avtp_base_depayload_init (GstAvtpBaseDepayload *
+    avtpbasedepayload, gpointer g_class);
+
+static void gst_avtp_base_depayload_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_avtp_base_depayload_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
+
+static gboolean gst_avtp_base_depayload_sink_event (GstPad * pad,
+    GstObject * parent, GstEvent * event);
+
+GType
+gst_avtp_base_depayload_get_type (void)
+{
+  static GType avtpbasedepayload_type = 0;
+
+  if (g_once_init_enter ((gsize *) & avtpbasedepayload_type)) {
+    static const GTypeInfo avtpbasedepayload_info = {
+      sizeof (GstAvtpBaseDepayloadClass),
+      NULL,
+      NULL,
+      (GClassInitFunc) gst_avtp_base_depayload_class_init,
+      NULL,
+      NULL,
+      sizeof (GstAvtpBaseDepayload),
+      0,
+      (GInstanceInitFunc) gst_avtp_base_depayload_init,
+    };
+    GType _type;
+
+    _type = g_type_register_static (GST_TYPE_ELEMENT, "GstAvtpBaseDepayload",
+        &avtpbasedepayload_info, G_TYPE_FLAG_ABSTRACT);
+
+    g_once_init_leave ((gsize *) & avtpbasedepayload_type, _type);
+  }
+  return avtpbasedepayload_type;
+}
+
+static void
+gst_avtp_base_depayload_class_init (GstAvtpBaseDepayloadClass * klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->set_property = gst_avtp_base_depayload_set_property;
+  object_class->get_property = gst_avtp_base_depayload_get_property;
+
+  g_object_class_install_property (object_class, PROP_STREAMID,
+      g_param_spec_uint64 ("streamid", "Stream ID",
+          "Stream ID associated with the AVTPDU", 0, G_MAXUINT64,
+          DEFAULT_STREAMID, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+          GST_PARAM_MUTABLE_PAUSED));
+
+  klass->chain = NULL;
+  klass->sink_event = GST_DEBUG_FUNCPTR (gst_avtp_base_depayload_sink_event);
+
+  GST_DEBUG_CATEGORY_INIT (avtpbasedepayload_debug, "avtpbasedepayload", 0,
+      "Base class for AVTP depayloaders");
+}
+
+static void
+gst_avtp_base_depayload_init (GstAvtpBaseDepayload * avtpbasedepayload,
+    gpointer g_class)
+{
+  GstPadTemplate *templ;
+  GstElement *element = GST_ELEMENT (avtpbasedepayload);
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+  GstAvtpBaseDepayloadClass *avtpbasedepayload_class =
+      GST_AVTP_BASE_DEPAYLOAD_CLASS (g_class);
+
+  g_assert (avtpbasedepayload_class->chain != NULL);
+
+  templ = gst_element_class_get_pad_template (element_class, "src");
+  g_assert (templ != NULL);
+  avtpbasedepayload->srcpad = gst_pad_new_from_template (templ, "src");
+  gst_pad_use_fixed_caps (avtpbasedepayload->srcpad);
+  gst_element_add_pad (element, avtpbasedepayload->srcpad);
+
+  avtpbasedepayload->sinkpad =
+      gst_pad_new_from_static_template (&sink_template, "sink");
+  gst_pad_set_chain_function (avtpbasedepayload->sinkpad,
+      avtpbasedepayload_class->chain);
+  gst_pad_set_event_function (avtpbasedepayload->sinkpad,
+      avtpbasedepayload_class->sink_event);
+  gst_element_add_pad (element, avtpbasedepayload->sinkpad);
+
+  avtpbasedepayload->streamid = DEFAULT_STREAMID;
+
+  avtpbasedepayload->prev_ptime = 0;
+  avtpbasedepayload->seqnum = 0;
+}
+
+static void
+gst_avtp_base_depayload_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstAvtpBaseDepayload *avtpbasedepayload = GST_AVTP_BASE_DEPAYLOAD (object);
+
+  GST_DEBUG_OBJECT (avtpbasedepayload, "prop_id %u", prop_id);
+
+  switch (prop_id) {
+    case PROP_STREAMID:
+      avtpbasedepayload->streamid = g_value_get_uint64 (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_avtp_base_depayload_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstAvtpBaseDepayload *avtpbasedepayload = GST_AVTP_BASE_DEPAYLOAD (object);
+
+  GST_DEBUG_OBJECT (avtpbasedepayload, "prop_id %u", prop_id);
+
+  switch (prop_id) {
+    case PROP_STREAMID:
+      g_value_set_uint64 (value, avtpbasedepayload->streamid);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static gboolean
+gst_avtp_base_depayload_sink_event (GstPad * pad, GstObject * parent,
+    GstEvent * event)
+{
+  GstAvtpBaseDepayload *avtpbasedepayload = GST_AVTP_BASE_DEPAYLOAD (parent);
+
+  GST_DEBUG_OBJECT (avtpbasedepayload, "event %s", GST_EVENT_TYPE_NAME (event));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_SEGMENT:
+      /* Once the first AVTPDU is received, proper CAPS and SEGMENT events are
+       * pushed downstream. These events are expected to be pushed in that
+       * order by GStreamer. Since the default handling implemented by
+       * gst_pad_event_default() pushes the SEGMENT event downstream right
+       * away, it doesn't work for us and we have to handle it ourselves.
+       *
+       * Our handling is very straightforward: we discard this event and send
+       * a proper segment event once the first AVTPDU is received. See
+       * gst_avtp_base_depayload_push_segment_event() for more information.
+       */
+      gst_event_unref (event);
+      return TRUE;
+    default:
+      return gst_pad_event_default (pad, parent, event);
+  }
+}
+
+/* Helper function to convert AVTP timestamp to AVTP presentation time. Since
+ * AVTP timestamp represents the lower 32-bit part from AVTP presentation time,
+ * the helper requires a reference time ('ref' argument) to convert it properly.
+ * The reference time must be in gstreamer clock-time coordinate.
+ */
+GstClockTime
+gst_avtp_base_depayload_tstamp_to_ptime (GstAvtpBaseDepayload *
+    avtpbasedepayload, guint32 tstamp, GstClockTime ref)
+{
+  GstClockTime ptime;
+
+  ptime = (ref & 0xFFFFFFFF00000000ULL) | tstamp;
+
+  /* If 'ptime' is less than the our reference time, it means the higher part
+   * from 'ptime' needs to be incremented by 1 in order reflect the correct
+   * presentation time.
+   */
+  if (ptime < ref)
+    ptime += (1ULL << 32);
+
+  GST_LOG_OBJECT (avtpbasedepayload, "AVTP presentation time %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (ptime));
+  return ptime;
+}
+
+gboolean
+gst_avtp_base_depayload_push_segment_event (GstAvtpBaseDepayload *
+    avtpbasedepayload, guint32 avtp_tstamp)
+{
+  GstClock *clock;
+  GstEvent *event;
+  GstSegment segment;
+  GstClockTime now, base_time, avtp_ptime;
+
+  clock = GST_ELEMENT_CLOCK (avtpbasedepayload);
+
+  now = gst_clock_get_time (clock);
+  avtp_ptime =
+      gst_avtp_base_depayload_tstamp_to_ptime (avtpbasedepayload, avtp_tstamp,
+      now);
+  base_time = gst_element_get_base_time (GST_ELEMENT (avtpbasedepayload));
+
+  gst_segment_init (&segment, GST_FORMAT_TIME);
+  segment.base = avtp_ptime - base_time;
+  segment.start = avtp_ptime;
+  segment.stop = -1;
+
+  event = gst_event_new_segment (&segment);
+  if (!event) {
+    GST_ERROR_OBJECT (avtpbasedepayload, "Failed to create SEGMENT event");
+    return FALSE;
+  }
+
+  if (!gst_pad_push_event (avtpbasedepayload->srcpad, event)) {
+    GST_ERROR_OBJECT (avtpbasedepayload, "Failed to push SEGMENT event");
+    return FALSE;
+  }
+
+  GST_DEBUG_OBJECT (avtpbasedepayload, "SEGMENT event pushed: %"
+      GST_SEGMENT_FORMAT, &segment);
+
+  avtpbasedepayload->prev_ptime = avtp_ptime;
+  return TRUE;
+}
diff --git a/ext/avtp/gstavtpbasedepayload.h b/ext/avtp/gstavtpbasedepayload.h
new file mode 100644 (file)
index 0000000..3db040c
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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_BASE_DEPAYLOAD_H__
+#define __GST_AVTP_BASE_DEPAYLOAD_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_AVTP_BASE_DEPAYLOAD (gst_avtp_base_depayload_get_type())
+#define GST_AVTP_BASE_DEPAYLOAD(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVTP_BASE_DEPAYLOAD,GstAvtpBaseDepayload))
+#define GST_AVTP_BASE_DEPAYLOAD_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVTP_BASE_DEPAYLOAD,GstAvtpBaseDepayloadClass))
+#define GST_IS_AVTP_BASE_DEPAYLOAD(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVTP_BASE_DEPAYLOAD))
+#define GST_IS_AVTP_BASE_DEPAYLOAD_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVTP_BASE_DEPAYLOAD))
+
+typedef struct _GstAvtpBaseDepayload GstAvtpBaseDepayload;
+typedef struct _GstAvtpBaseDepayloadClass GstAvtpBaseDepayloadClass;
+
+struct _GstAvtpBaseDepayload
+{
+  GstElement element;
+
+  GstPad *sinkpad;
+  GstPad *srcpad;
+
+  guint64 streamid;
+
+  GstClockTime prev_ptime;
+  guint8 seqnum;
+
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstAvtpBaseDepayloadClass
+{
+  GstElementClass parent_class;
+
+  /* Pure virtual function. */
+  GstPadChainFunction chain;
+
+  GstPadEventFunction sink_event;
+
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_avtp_base_depayload_get_type (void);
+
+GstClockTime gst_avtp_base_depayload_tstamp_to_ptime (GstAvtpBaseDepayload *
+    avtpbasedepayload, guint32 tstamp, GstClockTime ref);
+
+gboolean gst_avtp_base_depayload_push_segment_event (GstAvtpBaseDepayload *
+    avtpbasedepayload, guint32 avtp_tstamp);
+
+G_END_DECLS
+
+#endif /* __GST_AVTP_BASE_DEPAYLOAD_H__ */
index 250fe9e..ce65f9a 100644 (file)
@@ -1,6 +1,8 @@
 avtp_sources = [
   'gstavtp.c',
+  'gstavtpaafdepay.c',
   'gstavtpaafpay.c',
+  'gstavtpbasedepayload.c',
   'gstavtpbasepayload.c',
 ]