gstreamer/ext/avtp/gstavtpaafdepay.c
Andre Guedes 6477884a56 avtp: Introduce AAF depayloader element
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.
2019-07-03 09:59:35 -07:00

328 lines
10 KiB
C

/*
* 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);
}