diff --git a/ext/avtp/Makefile.am b/ext/avtp/Makefile.am index 242a72c12a..46151eeeae 100644 --- a/ext/avtp/Makefile.am +++ b/ext/avtp/Makefile.am @@ -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 diff --git a/ext/avtp/gstavtp.c b/ext/avtp/gstavtp.c index 91998da895..d64be61fd6 100644 --- a/ext/avtp/gstavtp.c +++ b/ext/avtp/gstavtp.c @@ -50,6 +50,7 @@ #include +#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 index 0000000000..4534bba64d --- /dev/null +++ b/ext/avtp/gstavtpaafdepay.c @@ -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. + * + * + * Example pipeline + * |[ + * gst-launch-1.0 avtpsrc ! avtpaafdepay ! autoaudiosink + * ]| This example pipeline will depayload AVTPDUs. Refer to the avtpaafpay + * example to create the AVTP stream. + * + */ + +#include +#include +#include + +#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 "); + + 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 index 0000000000..ef639b70ec --- /dev/null +++ b/ext/avtp/gstavtpaafdepay.h @@ -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 + +#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 index 0000000000..420911e99f --- /dev/null +++ b/ext/avtp/gstavtpbasedepayload.c @@ -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 index 0000000000..3db040c559 --- /dev/null +++ b/ext/avtp/gstavtpbasedepayload.h @@ -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 + +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__ */ diff --git a/ext/avtp/meson.build b/ext/avtp/meson.build index 250fe9ef1f..ce65f9a1e7 100644 --- a/ext/avtp/meson.build +++ b/ext/avtp/meson.build @@ -1,6 +1,8 @@ avtp_sources = [ 'gstavtp.c', + 'gstavtpaafdepay.c', 'gstavtpaafpay.c', + 'gstavtpbasedepayload.c', 'gstavtpbasepayload.c', ]