mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-25 11:11:08 +00:00
6477884a56
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.
327 lines
10 KiB
C
327 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);
|
|
}
|