libgstavtp_la_SOURCES = \
gstavtp.c \
+ gstavtpaafdepay.c \
gstavtpaafpay.c \
+ gstavtpbasedepayload.c \
gstavtpbasepayload.c
libgstavtp_la_CFLAGS = \
libgstavtp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
noinst_HEADERS = \
+ gstavtpaafdepay.h \
gstavtpaafpay.h \
+ gstavtpbasedepayload.h \
gstavtpbasepayload.h
#include <gst/gst.h>
+#include "gstavtpaafdepay.h"
#include "gstavtpaafpay.h"
static gboolean
{
if (!gst_avtp_aaf_pay_plugin_init (plugin))
return FALSE;
+ if (!gst_avtp_aaf_depay_plugin_init (plugin))
+ return FALSE;
return TRUE;
}
--- /dev/null
+/*
+ * GStreamer AVTP Plugin
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+/**
+ * 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);
+}
--- /dev/null
+/*
+ * GStreamer AVTP Plugin
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#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__ */
--- /dev/null
+/*
+ * GStreamer AVTP Plugin
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#include "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;
+}
--- /dev/null
+/*
+ * GStreamer AVTP Plugin
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#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__ */
avtp_sources = [
'gstavtp.c',
+ 'gstavtpaafdepay.c',
'gstavtpaafpay.c',
+ 'gstavtpbasedepayload.c',
'gstavtpbasepayload.c',
]