diff --git a/ext/avtp/Makefile.am b/ext/avtp/Makefile.am index 709bdfa671..242a72c12a 100644 --- a/ext/avtp/Makefile.am +++ b/ext/avtp/Makefile.am @@ -1,6 +1,9 @@ plugin_LTLIBRARIES = libgstavtp.la -libgstavtp_la_SOURCES = gstavtp.c +libgstavtp_la_SOURCES = \ + gstavtp.c \ + gstavtpaafpay.c \ + gstavtpbasepayload.c libgstavtp_la_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ @@ -9,9 +12,13 @@ libgstavtp_la_CFLAGS = \ $(AVTP_CFLAGS) libgstavtp_la_LIBADD = \ - $(GST_PLUGINS_BASE_LIBS) \ + $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_API_VERSION) \ $(GST_BASE_LIBS) \ $(GST_LIBS) \ $(AVTP_LIBS) libgstavtp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +noinst_HEADERS = \ + gstavtpaafpay.h \ + gstavtpbasepayload.h diff --git a/ext/avtp/gstavtp.c b/ext/avtp/gstavtp.c index af0adf7121..91998da895 100644 --- a/ext/avtp/gstavtp.c +++ b/ext/avtp/gstavtp.c @@ -36,6 +36,13 @@ * * If libavtp isn't detected by configure, the plugin isn't built. * + * ### The application/x-avtp mime type + * + * For valid AVTPDUs encapsulated in GstBuffers, we use the caps with mime type + * application/x-avtp. + * + * AVTP mime type is pretty simple and has no fields. + * */ #ifdef HAVE_CONFIG_H #include @@ -43,9 +50,14 @@ #include +#include "gstavtpaafpay.h" + static gboolean plugin_init (GstPlugin * plugin) { + if (!gst_avtp_aaf_pay_plugin_init (plugin)) + return FALSE; + return TRUE; } diff --git a/ext/avtp/gstavtpaafpay.c b/ext/avtp/gstavtpaafpay.c new file mode 100644 index 0000000000..1174f8e017 --- /dev/null +++ b/ext/avtp/gstavtpaafpay.c @@ -0,0 +1,387 @@ +/* + * 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-avtpaafpay + * @see_also: avtpaafdepay + * + * Payload raw audio into AVTPDUs according to IEEE 1722-2016. For detailed + * information see https://standards.ieee.org/standard/1722-2016.html. + * + * + * Example pipeline + * |[ + * gst-launch-1.0 audiotestsrc ! audioconvert ! avtpaafpay ! avtpsink + * ]| This example pipeline will payload raw audio. Refer to the avtpaafdepay + * example to depayload and play the AVTP stream. + * + */ + +#include +#include +#include + +#include "gstavtpaafpay.h" + +GST_DEBUG_CATEGORY_STATIC (avtpaafpay_debug); +#define GST_CAT_DEFAULT (avtpaafpay_debug) + +#define DEFAULT_TSTAMP_MODE GST_AVTP_AAF_TSTAMP_MODE_NORMAL + +enum +{ + PROP_0, + PROP_TSTAMP_MODE, +}; + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + 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") + ); + +#define GST_TYPE_AVTP_AAF_TSTAMP_MODE (gst_avtp_aaf_tstamp_mode_get_type()) +static GType +gst_avtp_aaf_tstamp_mode_get_type (void) +{ + static GType tstamp_mode_type = 0; + static const GEnumValue tstamp_mode_types[] = { + {GST_AVTP_AAF_TSTAMP_MODE_NORMAL, "Normal timestamping mode", "normal"}, + {GST_AVTP_AAF_TSTAMP_MODE_SPARSE, "Sparse timestamping mode", "sparse"}, + {0, NULL, NULL}, + }; + + tstamp_mode_type = + g_enum_register_static ("GstAvtpAafTstampMode", tstamp_mode_types); + + return tstamp_mode_type; +} + +#define gst_avtp_aaf_pay_parent_class parent_class +G_DEFINE_TYPE (GstAvtpAafPay, gst_avtp_aaf_pay, GST_TYPE_AVTP_BASE_PAYLOAD); + +static void gst_avtp_aaf_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_avtp_aaf_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static GstStateChangeReturn gst_avtp_aaf_pay_change_state (GstElement * element, + GstStateChange transition); + +static GstFlowReturn gst_avtp_aaf_pay_chain (GstPad * pad, GstObject * parent, + GstBuffer * buffer); +static gboolean gst_avtp_aaf_pay_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event); + +static void +gst_avtp_aaf_pay_class_init (GstAvtpAafPayClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstAvtpBasePayloadClass *avtpbasepayload_class = + GST_AVTP_BASE_PAYLOAD_CLASS (klass); + + object_class->set_property = gst_avtp_aaf_pay_set_property; + object_class->get_property = gst_avtp_aaf_pay_get_property; + + g_object_class_install_property (object_class, PROP_TSTAMP_MODE, + g_param_spec_enum ("tstamp-mode", "Timestamping Mode", + "AAF timestamping mode", GST_TYPE_AVTP_AAF_TSTAMP_MODE, + DEFAULT_TSTAMP_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PAUSED)); + + element_class->change_state = + GST_DEBUG_FUNCPTR (gst_avtp_aaf_pay_change_state); + + gst_element_class_add_static_pad_template (element_class, &sink_template); + + gst_element_class_set_static_metadata (element_class, + "AVTP Audio Format (AAF) payloader", + "Codec/Payloader/Network/AVTP", + "Payload-encode Raw audio into AAF AVTPDU (IEEE 1722)", + "Andre Guedes "); + + avtpbasepayload_class->chain = GST_DEBUG_FUNCPTR (gst_avtp_aaf_pay_chain); + avtpbasepayload_class->sink_event = + GST_DEBUG_FUNCPTR (gst_avtp_aaf_pay_sink_event); + + GST_DEBUG_CATEGORY_INIT (avtpaafpay_debug, "avtpaafpay", 0, + "AAF AVTP Payloader"); +} + +static void +gst_avtp_aaf_pay_init (GstAvtpAafPay * avtpaafpay) +{ + avtpaafpay->tstamp_mode = DEFAULT_TSTAMP_MODE; + + avtpaafpay->header = NULL; + avtpaafpay->channels = 0; + avtpaafpay->depth = 0; + avtpaafpay->rate = 0; + avtpaafpay->format = 0; +} + +static void +gst_avtp_aaf_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAvtpAafPay *avtpaafpay = GST_AVTP_AAF_PAY (object); + + GST_DEBUG_OBJECT (avtpaafpay, "prop_id %u", prop_id); + + switch (prop_id) { + case PROP_TSTAMP_MODE: + avtpaafpay->tstamp_mode = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_avtp_aaf_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstAvtpAafPay *avtpaafpay = GST_AVTP_AAF_PAY (object); + + GST_DEBUG_OBJECT (avtpaafpay, "prop_id %u", prop_id); + + switch (prop_id) { + case PROP_TSTAMP_MODE: + g_value_set_enum (value, avtpaafpay->tstamp_mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstStateChangeReturn +gst_avtp_aaf_pay_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstAvtpAafPay *avtpaafpay = GST_AVTP_AAF_PAY (element); + + GST_DEBUG_OBJECT (avtpaafpay, "transition %d", transition); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY:{ + GstMemory *mem; + + mem = gst_allocator_alloc (NULL, sizeof (struct avtp_stream_pdu), NULL); + if (!mem) { + GST_ERROR_OBJECT (avtpaafpay, "Failed to allocate GstMemory"); + return GST_STATE_CHANGE_FAILURE; + } + avtpaafpay->header = gst_memory_ref (mem); + break; + } + case GST_STATE_CHANGE_READY_TO_PAUSED:{ + int res; + GstMapInfo info; + struct avtp_stream_pdu *pdu; + GstMemory *mem = avtpaafpay->header; + GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (element); + + if (!gst_memory_map (mem, &info, GST_MAP_WRITE)) { + GST_ERROR_OBJECT (avtpaafpay, "Failed to map GstMemory"); + return GST_STATE_CHANGE_FAILURE; + } + pdu = (struct avtp_stream_pdu *) info.data; + res = avtp_aaf_pdu_init (pdu); + g_assert (res == 0); + res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_MR, 0); + g_assert (res == 0); + res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_TV, 1); + g_assert (res == 0); + res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_TU, 0); + g_assert (res == 0); + res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_STREAM_ID, + avtpbasepayload->streamid); + g_assert (res == 0); + res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_SP, avtpaafpay->tstamp_mode); + g_assert (res == 0); + gst_memory_unmap (mem, &info); + break; + } + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) { + GST_ERROR_OBJECT (avtpaafpay, "Parent failed to handle state transition"); + return ret; + } + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + gst_memory_unref (avtpaafpay->header); + break; + default: + break; + } + + return ret; +} + +static GstFlowReturn +gst_avtp_aaf_pay_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) +{ + int res; + GstMemory *mem; + GstMapInfo info; + gsize data_len; + GstClockTime ptime; + struct avtp_stream_pdu *pdu; + GstAvtpAafPay *avtpaafpay = GST_AVTP_AAF_PAY (parent); + GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (parent); + + ptime = gst_avtp_base_payload_calc_ptime (avtpbasepayload, buffer); + data_len = gst_buffer_get_size (buffer); + + mem = gst_memory_copy (avtpaafpay->header, 0, -1); + if (!gst_memory_map (mem, &info, GST_MAP_WRITE)) { + GST_ELEMENT_ERROR (avtpaafpay, RESOURCE, WRITE, ("Failed to map memory"), + (NULL)); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } + pdu = (struct avtp_stream_pdu *) info.data; + res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_TIMESTAMP, ptime); + g_assert (res == 0); + res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_NSR, avtpaafpay->rate); + g_assert (res == 0); + res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_FORMAT, avtpaafpay->format); + g_assert (res == 0); + res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_BIT_DEPTH, avtpaafpay->depth); + g_assert (res == 0); + res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_STREAM_DATA_LEN, data_len); + g_assert (res == 0); + res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_CHAN_PER_FRAME, + avtpaafpay->channels); + g_assert (res == 0); + res = avtp_aaf_pdu_set (pdu, AVTP_AAF_FIELD_SEQ_NUM, + avtpbasepayload->seqnum++); + g_assert (res == 0); + gst_memory_unmap (mem, &info); + + gst_buffer_prepend_memory (buffer, mem); + return gst_pad_push (avtpbasepayload->srcpad, buffer); +} + +static int +gst_to_avtp_rate (gint rate) +{ + switch (rate) { + case 8000: + return AVTP_AAF_PCM_NSR_8KHZ; + case 16000: + return AVTP_AAF_PCM_NSR_16KHZ; + case 24000: + return AVTP_AAF_PCM_NSR_24KHZ; + case 32000: + return AVTP_AAF_PCM_NSR_32KHZ; + case 44100: + return AVTP_AAF_PCM_NSR_44_1KHZ; + case 48000: + return AVTP_AAF_PCM_NSR_48KHZ; + case 88200: + return AVTP_AAF_PCM_NSR_88_2KHZ; + case 96000: + return AVTP_AAF_PCM_NSR_96KHZ; + case 176400: + return AVTP_AAF_PCM_NSR_176_4KHZ; + case 192000: + return AVTP_AAF_PCM_NSR_192KHZ; + default: + return AVTP_AAF_PCM_NSR_USER; + } +} + +static int +gst_to_avtp_format (GstAudioFormat format) +{ + switch (format) { + case GST_AUDIO_FORMAT_S16BE: + return AVTP_AAF_FORMAT_INT_16BIT; + case GST_AUDIO_FORMAT_S24BE: + return AVTP_AAF_FORMAT_INT_24BIT; + case GST_AUDIO_FORMAT_S32BE: + return AVTP_AAF_FORMAT_INT_32BIT; + case GST_AUDIO_FORMAT_F32BE: + return AVTP_AAF_FORMAT_FLOAT_32BIT; + default: + return AVTP_AAF_FORMAT_USER; + } +} + +static gboolean +gst_avtp_aaf_pay_new_caps (GstAvtpAafPay * avtpaafpay, GstCaps * caps) +{ + GstAudioInfo info; + + gst_audio_info_init (&info); + if (!gst_audio_info_from_caps (&info, caps)) { + GST_ERROR_OBJECT (avtpaafpay, "Failed to get info from caps"); + return FALSE; + } + + avtpaafpay->channels = info.channels; + avtpaafpay->depth = info.finfo->depth; + avtpaafpay->rate = gst_to_avtp_rate (info.rate); + avtpaafpay->format = gst_to_avtp_format (info.finfo->format); + + GST_DEBUG_OBJECT (avtpaafpay, "channels %d, depth %d, rate %d, format %s", + info.channels, info.finfo->depth, info.rate, + gst_audio_format_to_string (info.finfo->format)); + return TRUE; +} + +static gboolean +gst_avtp_aaf_pay_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + GstCaps *caps; + GstAvtpAafPay *avtpaafpay = GST_AVTP_AAF_PAY (parent); + + GST_DEBUG_OBJECT (avtpaafpay, "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_aaf_pay_new_caps (avtpaafpay, caps); + default: + return GST_AVTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (pad, + parent, event); + } +} + +gboolean +gst_avtp_aaf_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "avtpaafpay", GST_RANK_NONE, + GST_TYPE_AVTP_AAF_PAY); +} diff --git a/ext/avtp/gstavtpaafpay.h b/ext/avtp/gstavtpaafpay.h new file mode 100644 index 0000000000..548573d651 --- /dev/null +++ b/ext/avtp/gstavtpaafpay.h @@ -0,0 +1,74 @@ +/* + * 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_PAY_H__ +#define __GST_AVTP_AAF_PAY_H__ + +#include + +#include "gstavtpbasepayload.h" + +G_BEGIN_DECLS + +#define GST_TYPE_AVTP_AAF_PAY (gst_avtp_aaf_pay_get_type()) +#define GST_AVTP_AAF_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVTP_AAF_PAY,GstAvtpAafPay)) +#define GST_AVTP_AAF_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVTP_AAF_PAY,GstAvtpAafPayClass)) +#define GST_IS_AVTP_AAF_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVTP_AAF_PAY)) +#define GST_IS_AVTP_AAF_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVTP_AAF_PAY)) + +typedef struct _GstAvtpAafPay GstAvtpAafPay; +typedef struct _GstAvtpAafPayClass GstAvtpAafPayClass; +typedef enum _GstAvtpAafTstampMode GstAvtpAafTstampMode; + +enum _GstAvtpAafTstampMode { + GST_AVTP_AAF_TSTAMP_MODE_NORMAL, + GST_AVTP_AAF_TSTAMP_MODE_SPARSE, +}; + +struct _GstAvtpAafPay +{ + GstAvtpBasePayload payload; + + GstAvtpAafTstampMode tstamp_mode; + + GstMemory *header; + gint channels; + gint depth; + gint rate; + gint format; +}; + +struct _GstAvtpAafPayClass +{ + GstAvtpBasePayloadClass parent_class; +}; + +GType gst_avtp_aaf_pay_get_type (void); + +gboolean gst_avtp_aaf_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_AVTP_AAF_PAY_H__ */ diff --git a/ext/avtp/gstavtpbasepayload.c b/ext/avtp/gstavtpbasepayload.c new file mode 100644 index 0000000000..c75ea6579d --- /dev/null +++ b/ext/avtp/gstavtpbasepayload.c @@ -0,0 +1,255 @@ +/* + * 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 "gstavtpbasepayload.h" + +GST_DEBUG_CATEGORY_STATIC (avtpbasepayload_debug); +#define GST_CAT_DEFAULT (avtpbasepayload_debug) + +#define DEFAULT_STREAMID 0xAABBCCDDEEFF0000 +#define DEFAULT_MTT 50000000 +#define DEFAULT_TU 1000000 +#define DEFAULT_PROCESSING_DEADLINE (20 * GST_MSECOND) + +enum +{ + PROP_0, + PROP_STREAMID, + PROP_MTT, + PROP_TU, + PROP_PROCESSING_DEADLINE, +}; + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-avtp") + ); + +static void gst_avtp_base_payload_class_init (GstAvtpBasePayloadClass * klass); +static void gst_avtp_base_payload_init (GstAvtpBasePayload * avtpbasepayload, + gpointer g_class); + +static void gst_avtp_base_payload_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_avtp_base_payload_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_avtp_base_payload_sink_event (GstPad * pad, + GstObject * parent, GstEvent * event); + +GType +gst_avtp_base_payload_get_type (void) +{ + static GType avtpbasepayload_type = 0; + + if (g_once_init_enter ((gsize *) & avtpbasepayload_type)) { + static const GTypeInfo avtpbasepayload_info = { + sizeof (GstAvtpBasePayloadClass), + NULL, + NULL, + (GClassInitFunc) gst_avtp_base_payload_class_init, + NULL, + NULL, + sizeof (GstAvtpBasePayload), + 0, + (GInstanceInitFunc) gst_avtp_base_payload_init, + }; + GType _type; + + _type = g_type_register_static (GST_TYPE_ELEMENT, "GstAvtpBasePayload", + &avtpbasepayload_info, G_TYPE_FLAG_ABSTRACT); + + g_once_init_leave ((gsize *) & avtpbasepayload_type, _type); + } + return avtpbasepayload_type; +} + +static void +gst_avtp_base_payload_class_init (GstAvtpBasePayloadClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gst_avtp_base_payload_set_property; + object_class->get_property = gst_avtp_base_payload_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_READY)); + g_object_class_install_property (object_class, PROP_MTT, + g_param_spec_uint ("mtt", "Maximum Transit Time", + "Maximum Transit Time (MTT) in nanoseconds", 0, + G_MAXUINT, DEFAULT_MTT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_TU, + g_param_spec_uint ("tu", "Timing Uncertainty", + "Timing Uncertainty (TU) in nanoseconds", 0, + G_MAXUINT, DEFAULT_TU, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_PROCESSING_DEADLINE, + g_param_spec_uint64 ("processing-deadline", "Processing deadline", + "Maximum amount of time (in ns) the pipeline can take for processing the buffer", + 0, G_MAXUINT64, DEFAULT_PROCESSING_DEADLINE, G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + klass->chain = NULL; + klass->sink_event = GST_DEBUG_FUNCPTR (gst_avtp_base_payload_sink_event); + + GST_DEBUG_CATEGORY_INIT (avtpbasepayload_debug, "avtpbasepayload", 0, + "Base class for AVTP payloaders"); +} + +static void +gst_avtp_base_payload_init (GstAvtpBasePayload * avtpbasepayload, + gpointer g_class) +{ + GstPadTemplate *templ; + GstElement *element = GST_ELEMENT (avtpbasepayload); + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GstAvtpBasePayloadClass *avtpbasepayload_class = + GST_AVTP_BASE_PAYLOAD_CLASS (g_class); + + g_assert (avtpbasepayload_class->chain != NULL); + + avtpbasepayload->srcpad = gst_pad_new_from_static_template (&src_template, + "src"); + gst_element_add_pad (element, avtpbasepayload->srcpad); + + templ = gst_element_class_get_pad_template (element_class, "sink"); + g_assert (templ != NULL); + avtpbasepayload->sinkpad = gst_pad_new_from_template (templ, "sink"); + gst_pad_set_chain_function (avtpbasepayload->sinkpad, + avtpbasepayload_class->chain); + gst_pad_set_event_function (avtpbasepayload->sinkpad, + avtpbasepayload_class->sink_event); + gst_element_add_pad (element, avtpbasepayload->sinkpad); + + avtpbasepayload->streamid = DEFAULT_STREAMID; + avtpbasepayload->mtt = DEFAULT_MTT; + avtpbasepayload->tu = DEFAULT_TU; + avtpbasepayload->processing_deadline = DEFAULT_PROCESSING_DEADLINE; + + avtpbasepayload->latency = GST_CLOCK_TIME_NONE; + avtpbasepayload->seqnum = 0; + gst_segment_init (&avtpbasepayload->segment, GST_FORMAT_UNDEFINED); +} + +static void +gst_avtp_base_payload_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (object); + + GST_DEBUG_OBJECT (avtpbasepayload, "prop_id %u", prop_id); + + switch (prop_id) { + case PROP_STREAMID: + avtpbasepayload->streamid = g_value_get_uint64 (value); + break; + case PROP_MTT: + avtpbasepayload->mtt = g_value_get_uint (value); + break; + case PROP_TU: + avtpbasepayload->tu = g_value_get_uint (value); + break; + case PROP_PROCESSING_DEADLINE: + avtpbasepayload->processing_deadline = g_value_get_uint64 (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_avtp_base_payload_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (object); + + GST_DEBUG_OBJECT (avtpbasepayload, "prop_id %u", prop_id); + + switch (prop_id) { + case PROP_STREAMID: + g_value_set_uint64 (value, avtpbasepayload->streamid); + break; + case PROP_MTT: + g_value_set_uint (value, avtpbasepayload->mtt); + break; + case PROP_TU: + g_value_set_uint (value, avtpbasepayload->tu); + break; + case PROP_PROCESSING_DEADLINE: + g_value_set_uint64 (value, avtpbasepayload->processing_deadline); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_avtp_base_payload_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event) +{ + GstAvtpBasePayload *avtpbasepayload = GST_AVTP_BASE_PAYLOAD (parent); + + GST_DEBUG_OBJECT (avtpbasepayload, "event %s", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEGMENT: + gst_event_copy_segment (event, &avtpbasepayload->segment); + /* Fall through */ + default: + return gst_pad_event_default (pad, parent, event); + } +} + +GstClockTime +gst_avtp_base_payload_calc_ptime (GstAvtpBasePayload * avtpbasepayload, + GstBuffer * buffer) +{ + GstClockTime base_time, running_time; + + g_assert (GST_BUFFER_PTS (buffer) != GST_CLOCK_TIME_NONE); + + if (G_UNLIKELY (avtpbasepayload->latency == GST_CLOCK_TIME_NONE)) { + GstQuery *query; + + query = gst_query_new_latency (); + if (!gst_pad_peer_query (avtpbasepayload->sinkpad, query)) + return GST_CLOCK_TIME_NONE; + gst_query_parse_latency (query, NULL, &avtpbasepayload->latency, NULL); + gst_query_unref (query); + + GST_DEBUG_OBJECT (avtpbasepayload, "latency %" GST_TIME_FORMAT, + GST_TIME_ARGS (avtpbasepayload->latency)); + } + + base_time = gst_element_get_base_time (GST_ELEMENT (avtpbasepayload)); + + running_time = gst_segment_to_running_time (&avtpbasepayload->segment, + avtpbasepayload->segment.format, GST_BUFFER_PTS (buffer)); + + return base_time + running_time + avtpbasepayload->latency + + avtpbasepayload->processing_deadline + avtpbasepayload->mtt + + avtpbasepayload->tu; +} diff --git a/ext/avtp/gstavtpbasepayload.h b/ext/avtp/gstavtpbasepayload.h new file mode 100644 index 0000000000..f7154258c8 --- /dev/null +++ b/ext/avtp/gstavtpbasepayload.h @@ -0,0 +1,80 @@ +/* + * 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_PAYLOAD_H__ +#define __GST_AVTP_BASE_PAYLOAD_H__ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_AVTP_BASE_PAYLOAD (gst_avtp_base_payload_get_type()) +#define GST_AVTP_BASE_PAYLOAD(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVTP_BASE_PAYLOAD,GstAvtpBasePayload)) +#define GST_AVTP_BASE_PAYLOAD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVTP_BASE_PAYLOAD,GstAvtpBasePayloadClass)) +#define GST_IS_AVTP_BASE_PAYLOAD(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVTP_BASE_PAYLOAD)) +#define GST_IS_AVTP_BASE_PAYLOAD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVTP_BASE_PAYLOAD)) + +typedef struct _GstAvtpBasePayload GstAvtpBasePayload; +typedef struct _GstAvtpBasePayloadClass GstAvtpBasePayloadClass; + +struct _GstAvtpBasePayload +{ + GstElement element; + + GstPad *sinkpad; + GstPad *srcpad; + + guint64 streamid; + guint mtt; + guint tu; + guint64 processing_deadline; + + GstClockTime latency; + GstSegment segment; + guint8 seqnum; + + gpointer _gst_reserved[GST_PADDING]; +}; + +struct _GstAvtpBasePayloadClass +{ + GstElementClass parent_class; + + /* Pure virtual function. */ + GstPadChainFunction chain; + + GstPadEventFunction sink_event; + + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_avtp_base_payload_get_type (void); + +GstClockTime gst_avtp_base_payload_calc_ptime (GstAvtpBasePayload * + avtpbasepayload, GstBuffer * buffer); + +G_END_DECLS + +#endif /* __GST_AVTP_BASE_PAYLOAD_H__ */ diff --git a/ext/avtp/meson.build b/ext/avtp/meson.build index 17706d9271..250fe9ef1f 100644 --- a/ext/avtp/meson.build +++ b/ext/avtp/meson.build @@ -1,5 +1,7 @@ avtp_sources = [ 'gstavtp.c', + 'gstavtpaafpay.c', + 'gstavtpbasepayload.c', ] avtp_dep = dependency('avtp', required: get_option('avtp')) @@ -9,7 +11,7 @@ if avtp_dep.found() avtp_sources, c_args : gst_plugins_bad_args, include_directories : [configinc], - dependencies : libavtp_dep, + dependencies : [gstaudio_dep, avtp_dep], install : true, install_dir : plugins_install_dir, )