From: Guillaume Desmottes Date: Wed, 31 Mar 2021 09:18:30 +0000 (+0200) Subject: rtpopuspay: add DTX support X-Git-Tag: 1.19.3~509^2~137 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=41ba8c1b00f5ad5f27c37b97ddee196d9d6e6d6a;p=platform%2Fupstream%2Fgstreamer.git rtpopuspay: add DTX support If enabled, the payloader won't transmit empty frames. Can be tested using: opusenc dtx=true bitrate-type=vbr ! rtpopuspay dtx=true Part-of: --- diff --git a/docs/gst_plugins_cache.json b/docs/gst_plugins_cache.json index 85f25cc..eb2cf5d 100644 --- a/docs/gst_plugins_cache.json +++ b/docs/gst_plugins_cache.json @@ -14854,7 +14854,20 @@ "presence": "always" } }, - "properties": {}, + "properties": { + "dtx": { + "blurb": "If enabled, the payloader will not transmit empty packets", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "false", + "mutable": "playing", + "readable": true, + "type": "gboolean", + "writable": true + } + }, "rank": "primary" }, "rtppcmadepay": { diff --git a/gst/rtp/gstrtpopuspay.c b/gst/rtp/gstrtpopuspay.c index bee7bb5..bb3bf6d 100644 --- a/gst/rtp/gstrtpopuspay.c +++ b/gst/rtp/gstrtpopuspay.c @@ -58,6 +58,13 @@ GST_DEBUG_CATEGORY_STATIC (rtpopuspay_debug); #define GST_CAT_DEFAULT (rtpopuspay_debug) +enum +{ + PROP_0, + PROP_DTX, +}; + +#define DEFAULT_DTX FALSE static GstStaticPadTemplate gst_rtp_opus_pay_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", @@ -90,24 +97,77 @@ G_DEFINE_TYPE (GstRtpOPUSPay, gst_rtp_opus_pay, GST_TYPE_RTP_BASE_PAYLOAD); GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (rtpopuspay, "rtpopuspay", GST_RANK_PRIMARY, GST_TYPE_RTP_OPUS_PAY, rtp_element_init (plugin)); +#define GST_RTP_OPUS_PAY_CAST(obj) ((GstRtpOPUSPay *)(obj)) + +static void +gst_rtp_opus_pay_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstRtpOPUSPay *self = GST_RTP_OPUS_PAY (object); + + switch (prop_id) { + case PROP_DTX: + self->dtx = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_opus_pay_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstRtpOPUSPay *self = GST_RTP_OPUS_PAY (object); + + switch (prop_id) { + case PROP_DTX: + g_value_set_boolean (value, self->dtx); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static void gst_rtp_opus_pay_class_init (GstRtpOPUSPayClass * klass) { GstRTPBasePayloadClass *gstbasertppayload_class; GstElementClass *element_class; + GObjectClass *gobject_class; gstbasertppayload_class = (GstRTPBasePayloadClass *) klass; element_class = GST_ELEMENT_CLASS (klass); + gobject_class = (GObjectClass *) klass; gstbasertppayload_class->set_caps = gst_rtp_opus_pay_setcaps; gstbasertppayload_class->get_caps = gst_rtp_opus_pay_getcaps; gstbasertppayload_class->handle_buffer = gst_rtp_opus_pay_handle_buffer; + gobject_class->set_property = gst_rtp_opus_pay_set_property; + gobject_class->get_property = gst_rtp_opus_pay_get_property; + gst_element_class_add_static_pad_template (element_class, &gst_rtp_opus_pay_src_template); gst_element_class_add_static_pad_template (element_class, &gst_rtp_opus_pay_sink_template); + /** + * GstRtpOPUSPay:dtx: + * + * If enabled, the payloader will not transmit empty packets. + * + * Since: 1.20 + */ + g_object_class_install_property (gobject_class, PROP_DTX, + g_param_spec_boolean ("dtx", "Discontinuous Transmission", + "If enabled, the payloader will not transmit empty packets", + DEFAULT_DTX, + G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING | + G_PARAM_STATIC_STRINGS)); + gst_element_class_set_static_metadata (element_class, "RTP Opus payloader", "Codec/Payloader/Network/RTP", @@ -121,6 +181,7 @@ gst_rtp_opus_pay_class_init (GstRtpOPUSPayClass * klass) static void gst_rtp_opus_pay_init (GstRtpOPUSPay * rtpopuspay) { + rtpopuspay->dtx = DEFAULT_DTX; } static gboolean @@ -236,9 +297,18 @@ static GstFlowReturn gst_rtp_opus_pay_handle_buffer (GstRTPBasePayload * basepayload, GstBuffer * buffer) { + GstRtpOPUSPay *self = GST_RTP_OPUS_PAY_CAST (basepayload); GstBuffer *outbuf; GstClockTime pts, dts, duration; + /* DTX packets are zero-length frames, with a 1 or 2-bytes header */ + if (self->dtx && gst_buffer_get_size (buffer) <= 2) { + GST_LOG_OBJECT (self, + "discard empty buffer as DTX is enabled: %" GST_PTR_FORMAT, buffer); + gst_buffer_unref (buffer); + return GST_FLOW_OK; + } + pts = GST_BUFFER_PTS (buffer); dts = GST_BUFFER_DTS (buffer); duration = GST_BUFFER_DURATION (buffer); diff --git a/gst/rtp/gstrtpopuspay.h b/gst/rtp/gstrtpopuspay.h index e21bbe3..c7dbab1 100644 --- a/gst/rtp/gstrtpopuspay.h +++ b/gst/rtp/gstrtpopuspay.h @@ -44,6 +44,8 @@ typedef struct _GstRtpOPUSPayClass GstRtpOPUSPayClass; struct _GstRtpOPUSPay { GstRTPBasePayload payload; + + gboolean dtx; }; struct _GstRtpOPUSPayClass diff --git a/tests/check/elements/rtp-payloading.c b/tests/check/elements/rtp-payloading.c index 49db40f..038fac4 100644 --- a/tests/check/elements/rtp-payloading.c +++ b/tests/check/elements/rtp-payloading.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #define RELEASE_ELEMENT(x) if(x) {gst_object_unref(x); x = NULL;} @@ -1670,6 +1671,93 @@ GST_START_TEST (rtp_vorbis_renegotiate) GST_END_TEST; +static guint16 +pull_rtp_buffer (GstHarness * h) +{ + gint16 seq; + GstBuffer *buf; + GstRTPBuffer rtp = GST_RTP_BUFFER_INIT; + + buf = gst_harness_try_pull (h); + fail_unless (buf); + + fail_unless (gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp)); + seq = gst_rtp_buffer_get_seq (&rtp); + gst_rtp_buffer_unmap (&rtp); + + gst_buffer_unref (buf); + return seq; +} + +static void +test_rtp_opus_dtx (gboolean dtx) +{ + GstHarness *h; + GstBuffer *buf; + /* generated with a muted mic using: + * gst-launch-1.0 pulsesrc ! opusenc dtx=true bitrate-type=vbr ! fakesink silent=false dump=true -v + */ + static const guint8 opus_empty[] = { 0xf8 }; + static const guint8 opus_frame[] = { 0xf8, 0xff, 0xfe }; + guint16 seq, expected_seq; + + h = gst_harness_new_parse ("rtpopuspay"); + fail_unless (h); + + gst_harness_set (h, "rtpopuspay", "dtx", dtx, NULL); + + gst_harness_set_caps_str (h, + "audio/x-opus, rate=48000, channels=1, channel-mapping-family=0", + "application/x-rtp, media=audio, clock-rate=48000, encoding-name=OPUS, sprop-stereo=(string)0, encoding-params=(string)2, sprop-maxcapturerate=(string)48000, payload=96"); + + /* push first opus frame */ + buf = + gst_buffer_new_wrapped (g_memdup (opus_frame, sizeof (opus_frame)), + sizeof (opus_frame)); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + seq = pull_rtp_buffer (h); + expected_seq = seq + 1; + + /* push empty frame */ + buf = + gst_buffer_new_wrapped (g_memdup (opus_empty, sizeof (opus_empty)), + sizeof (opus_empty)); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + if (dtx) { + /* buffer is not transmitted if dtx is enabled */ + buf = gst_harness_try_pull (h); + fail_if (buf); + } else { + seq = pull_rtp_buffer (h); + fail_unless_equals_int (seq, expected_seq); + expected_seq++; + } + + /* push second opus frame */ + buf = + gst_buffer_new_wrapped (g_memdup (opus_frame, sizeof (opus_frame)), + sizeof (opus_frame)); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + seq = pull_rtp_buffer (h); + fail_unless_equals_int (seq, expected_seq); + + gst_harness_teardown (h); +} + +GST_START_TEST (rtp_opus_dtx_disabled) +{ + test_rtp_opus_dtx (FALSE); +} + +GST_END_TEST; + +GST_START_TEST (rtp_opus_dtx_enabled) +{ + test_rtp_opus_dtx (TRUE); +} + +GST_END_TEST; + /* * Creates the test suite. * @@ -1734,6 +1822,8 @@ rtp_payloading_suite (void) tcase_add_test (tc_chain, rtp_g729); tcase_add_test (tc_chain, rtp_gst_custom_event); tcase_add_test (tc_chain, rtp_vorbis_renegotiate); + tcase_add_test (tc_chain, rtp_opus_dtx_disabled); + tcase_add_test (tc_chain, rtp_opus_dtx_enabled); return s; }