From 8c3ec644734b2be57ae44fa89fb94a0d03bc3b96 Mon Sep 17 00:00:00 2001 From: Sanchayan Maity Date: Mon, 14 Sep 2020 13:12:50 +0530 Subject: [PATCH] rtp: ldacpay: Add LDAC RTP payloader Part-of: --- docs/gst_plugins_cache.json | 27 ++++++ gst/rtp/gstrtp.c | 4 + gst/rtp/gstrtpldacpay.c | 175 ++++++++++++++++++++++++++++++++++++ gst/rtp/gstrtpldacpay.h | 55 ++++++++++++ gst/rtp/meson.build | 1 + 5 files changed, 262 insertions(+) create mode 100644 gst/rtp/gstrtpldacpay.c create mode 100644 gst/rtp/gstrtpldacpay.h diff --git a/docs/gst_plugins_cache.json b/docs/gst_plugins_cache.json index 00ea2de190..e88b45e97c 100644 --- a/docs/gst_plugins_cache.json +++ b/docs/gst_plugins_cache.json @@ -14315,6 +14315,33 @@ "properties": {}, "rank": "secondary" }, + "rtpldacpay": { + "author": "Sanchayan Maity ", + "description": "Payload LDAC audio as RTP packets", + "hierarchy": [ + "GstRtpLdacPay", + "GstRTPBasePayload", + "GstElement", + "GstObject", + "GInitiallyUnowned", + "GObject" + ], + "klass": "Codec/Payloader/Network", + "long-name": "RTP packet payloader", + "pad-templates": { + "sink": { + "caps": "audio/x-ldac:\n channels: [ 1, 2 ]\n rate: { (int)44100, (int)48000, (int)88200, (int)96000 }\n", + "direction": "sink", + "presence": "always" + }, + "src": { + "caps": "application/x-rtp:\n media: audio\n payload: [ 96, 127 ]\n clock-rate: { (int)44100, (int)48000, (int)88200, (int)96000 }\n encoding-name: X-GST-LDAC\n", + "direction": "src", + "presence": "always" + } + }, + "rank": "none" + }, "rtpmp1sdepay": { "author": "Wim Taymans ", "description": "Extracts MPEG1 System Streams from RTP packets (RFC 3555)", diff --git a/gst/rtp/gstrtp.c b/gst/rtp/gstrtp.c index 7de78ed2b3..83549017ec 100644 --- a/gst/rtp/gstrtp.c +++ b/gst/rtp/gstrtp.c @@ -84,6 +84,7 @@ #include "gstrtpL16pay.h" #include "gstrtpL24depay.h" #include "gstrtpL24pay.h" +#include "gstrtpldacpay.h" #include "gstasteriskh263.h" #include "gstrtpmp1sdepay.h" #include "gstrtpmp2tdepay.h" @@ -302,6 +303,9 @@ plugin_init (GstPlugin * plugin) if (!gst_rtp_L24_depay_plugin_init (plugin)) return FALSE; + if (!gst_rtp_ldac_pay_plugin_init (plugin)) + return FALSE; + if (!gst_asteriskh263_plugin_init (plugin)) return FALSE; diff --git a/gst/rtp/gstrtpldacpay.c b/gst/rtp/gstrtpldacpay.c new file mode 100644 index 0000000000..0e200ac0cf --- /dev/null +++ b/gst/rtp/gstrtpldacpay.c @@ -0,0 +1,175 @@ +/* GStreamer RTP LDAC payloader + * Copyright (C) 2020 Asymptotic + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rtpldacpay + * @title: rtpldacpay + * + * Payload LDAC encoded audio into RTP packets. + * + * LDAC does not have a public specification and concerns itself only with + * bluetooth transmission. Due to the unavailability of a specification, we + * consider the encoding-name as X-GST-LDAC. + * + * The best reference is [libldac](https://android.googlesource.com/platform/external/libldac/) + * and the A2DP LDAC implementation in Android's bluetooth stack [Flouride] + * (https://android.googlesource.com/platform/system/bt/+/refs/heads/master/stack/a2dp/a2dp_vendor_ldac_encoder.cc). + * + * ## Example pipeline + * |[ + * gst-launch-1.0 -v audiotestsrc ! ldacenc ! rtpldacpay mtu=679 ! avdtpsink + * ]| This example pipeline will payload LDAC encoded audio. + * + * Since: 1.20 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "gstrtpldacpay.h" +#include "gstrtputils.h" + +#define GST_RTP_HEADER_LENGTH 12 +/* MTU size required for LDAC A2DP streaming */ +#define GST_LDAC_MTU_REQUIRED 679 + +GST_DEBUG_CATEGORY_STATIC (gst_rtp_ldac_pay_debug); +#define GST_CAT_DEFAULT gst_rtp_ldac_pay_debug + +#define parent_class gst_rtp_ldac_pay_parent_class +G_DEFINE_TYPE (GstRtpLdacPay, gst_rtp_ldac_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static GstStaticPadTemplate gst_rtp_ldac_pay_sink_factory = +GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-ldac, " + "channels = (int) [ 1, 2 ], " + "rate = (int) { 44100, 48000, 88200, 96000 }") + ); + +static GstStaticPadTemplate gst_rtp_ldac_pay_src_factory = +GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) audio," + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) { 44100, 48000, 88200, 96000 }," + "encoding-name = (string) \"X-GST-LDAC\"") + ); + +static gboolean gst_rtp_ldac_pay_set_caps (GstRTPBasePayload * payload, + GstCaps * caps); +static GstFlowReturn gst_rtp_ldac_pay_handle_buffer (GstRTPBasePayload * + payload, GstBuffer * buffer); + +static void +gst_rtp_ldac_pay_class_init (GstRtpLdacPayClass * klass) +{ + GstRTPBasePayloadClass *payload_class = GST_RTP_BASE_PAYLOAD_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + payload_class->set_caps = GST_DEBUG_FUNCPTR (gst_rtp_ldac_pay_set_caps); + payload_class->handle_buffer = + GST_DEBUG_FUNCPTR (gst_rtp_ldac_pay_handle_buffer); + + gst_element_class_add_static_pad_template (element_class, + &gst_rtp_ldac_pay_sink_factory); + gst_element_class_add_static_pad_template (element_class, + &gst_rtp_ldac_pay_src_factory); + + gst_element_class_set_static_metadata (element_class, "RTP packet payloader", + "Codec/Payloader/Network", "Payload LDAC audio as RTP packets", + "Sanchayan Maity "); + + GST_DEBUG_CATEGORY_INIT (gst_rtp_ldac_pay_debug, "rtpldacpay", 0, + "RTP LDAC payloader"); +} + +static void +gst_rtp_ldac_pay_init (GstRtpLdacPay * self) +{ + +} + +static gboolean +gst_rtp_ldac_pay_set_caps (GstRTPBasePayload * payload, GstCaps * caps) +{ + GstRtpLdacPay *ldacpay = GST_RTP_LDAC_PAY (payload); + GstStructure *structure; + gint rate; + + if (GST_RTP_BASE_PAYLOAD_MTU (ldacpay) < GST_LDAC_MTU_REQUIRED) { + GST_ERROR_OBJECT (ldacpay, "Invalid MTU %d, should be >= %d", + GST_RTP_BASE_PAYLOAD_MTU (ldacpay), GST_LDAC_MTU_REQUIRED); + return FALSE; + } + + structure = gst_caps_get_structure (caps, 0); + if (!gst_structure_get_int (structure, "rate", &rate)) { + GST_ERROR_OBJECT (ldacpay, "Failed to get audio rate from caps"); + return FALSE; + } + + gst_rtp_base_payload_set_options (payload, "audio", TRUE, "X-GST-LDAC", rate); + + return gst_rtp_base_payload_set_outcaps (payload, NULL); +} + +/* + * LDAC encoder does not handle split frames. Currently, the encoder will + * always emit 660 bytes worth of payload encapsulating multiple LDAC frames. + * This is as per eqmid and GST_LDAC_MTU_REQUIRED passed for configuring the + * encoder upstream. Since the encoder always emit full frames and we do not + * need to handle frame splitting, we do not use an adapter and also push out + * the buffer as it is received. + */ +static GstFlowReturn +gst_rtp_ldac_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) +{ + GstRtpLdacPay *ldacpay = GST_RTP_LDAC_PAY (payload); + GstBuffer *outbuf; + GstClockTime outbuf_frame_duration, outbuf_pts; + gsize buf_sz; + + outbuf = + gst_rtp_base_payload_allocate_output_buffer (GST_RTP_BASE_PAYLOAD + (ldacpay), GST_RTP_HEADER_LENGTH, 0, 0); + + outbuf_pts = GST_BUFFER_PTS (buffer); + outbuf_frame_duration = GST_BUFFER_DURATION (buffer); + buf_sz = gst_buffer_get_size (buffer); + + gst_rtp_copy_audio_meta (ldacpay, outbuf, buffer); + outbuf = gst_buffer_append (outbuf, buffer); + + GST_BUFFER_PTS (outbuf) = outbuf_pts; + GST_BUFFER_DURATION (outbuf) = outbuf_frame_duration; + GST_DEBUG_OBJECT (ldacpay, + "Pushing %" G_GSIZE_FORMAT " bytes: %" GST_TIME_FORMAT, buf_sz, + GST_TIME_ARGS (GST_BUFFER_PTS (outbuf))); + + return gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (ldacpay), outbuf); +} + +gboolean +gst_rtp_ldac_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpldacpay", GST_RANK_NONE, + GST_TYPE_RTP_LDAC_PAY); +} diff --git a/gst/rtp/gstrtpldacpay.h b/gst/rtp/gstrtpldacpay.h new file mode 100644 index 0000000000..0865ce7ade --- /dev/null +++ b/gst/rtp/gstrtpldacpay.h @@ -0,0 +1,55 @@ +/* GStreamer RTP LDAC payloader + * Copyright (C) 2020 Asymptotic + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_LDAC_PAY \ + (gst_rtp_ldac_pay_get_type()) +#define GST_RTP_LDAC_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_LDAC_PAY,\ + GstRtpLdacPay)) +#define GST_RTP_LDAC_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_LDAC_PAY,\ + GstRtpLdacPayClass)) +#define GST_IS_RTP_LDAC_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_LDAC_PAY)) +#define GST_IS_RTP_LDAC_PAY_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_LDAC_PAY)) + +typedef struct _GstRtpLdacPay GstRtpLdacPay; +typedef struct _GstRtpLdacPayClass GstRtpLdacPayClass; + +struct _GstRtpLdacPay { + GstRTPBasePayload base; +}; + +struct _GstRtpLdacPayClass { + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_ldac_pay_get_type(void); + +gboolean gst_rtp_ldac_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS diff --git a/gst/rtp/meson.build b/gst/rtp/meson.build index 38392184d9..1d8c4b9d5e 100644 --- a/gst/rtp/meson.build +++ b/gst/rtp/meson.build @@ -61,6 +61,7 @@ rtp_sources = [ 'gstrtpL16pay.c', 'gstrtpL24depay.c', 'gstrtpL24pay.c', + 'gstrtpldacpay.c', 'gstasteriskh263.c', 'gstrtpmp1sdepay.c', 'gstrtpmp2tdepay.c',