From 989bbe29d8bb87073f80feaa01a0e0ba166bb816 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 23 Apr 2020 10:17:31 +0200 Subject: [PATCH] opusdec: add 'stats' property Allow users to retrieve the number of samples, and their duration, generated using PLC. Part-of: --- ext/opus/gstopusdec.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++-- ext/opus/gstopusdec.h | 8 ++- 2 files changed, 178 insertions(+), 5 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 7ff9bee..987c8f0 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -90,7 +90,8 @@ enum PROP_0, PROP_USE_INBAND_FEC, PROP_APPLY_GAIN, - PROP_PHASE_INVERSION + PROP_PHASE_INVERSION, + PROP_STATS, }; @@ -156,6 +157,29 @@ gst_opus_dec_class_init (GstOpusDecClass * klass) #endif + /** + * GstOpusDec:stats: + * + * Various decoder statistics. This property returns a GstStructure + * with name application/x-opusdec-stats with the following fields: + * + * * #guint64 `num-pushed`: the number of packets pushed out. + * * #guint64 `num-gap`: the number of gap packets received. + * * #guint64 `plc-num-samples`: the number of samples generated using PLC + * * #guint64 `plc-duration`: the total duration, in ns, of samples generated using PLC + * * #guint32 `bandwidth`: decoder last bandpass, in kHz, or 0 if unknown + * * #guint32 `sample-rate`: decoder sampling rate, or 0 if unknown + * * #guint32 `gain`: decoder gain adjustement, in Q8 dB units, or 0 if unknown + * * #guint32 `last-packet-duration`: duration, in samples, of the last packet successfully decoded or concealed, or 0 if unknown + * * #guint `channels`: the number of channels + * + * Since: 1.18 + */ + g_object_class_install_property (gobject_class, PROP_STATS, + g_param_spec_boxed ("stats", "Statistics", + "Various statistics", GST_TYPE_STRUCTURE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + GST_DEBUG_CATEGORY_INIT (opusdec_debug, "opusdec", 0, "opus decoding element"); } @@ -215,6 +239,13 @@ gst_opus_dec_start (GstAudioDecoder * dec) gst_audio_decoder_set_latency (dec, 120 * GST_MSECOND, 120 * GST_MSECOND); } + GST_OBJECT_LOCK (dec); + odec->num_pushed = 0; + odec->num_gap = 0; + odec->plc_num_samples = 0; + odec->plc_duration = 0; + GST_OBJECT_UNLOCK (dec); + return TRUE; } @@ -338,7 +369,7 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) if (!gst_codec_utils_opus_parse_header (buf, &dec->sample_rate, - &dec->n_channels, + (guint8 *) & dec->n_channels, &dec->channel_mapping_family, &dec->n_streams, &dec->n_stereo_streams, @@ -586,6 +617,10 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) " plus leftover %" GST_TIME_FORMAT, GST_TIME_ARGS (missing_duration), GST_TIME_ARGS (dec->leftover_plc_duration)); + GST_OBJECT_LOCK (dec); + dec->num_gap++; + GST_OBJECT_UNLOCK (dec); + /* add the leftover PLC duration to that of the buffer */ missing_duration += dec->leftover_plc_duration; @@ -618,6 +653,11 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) " num frame samples: %d new leftover: %" GST_TIME_FORMAT, GST_TIME_ARGS (aligned_missing_duration), samples, GST_TIME_ARGS (dec->leftover_plc_duration)); + + GST_OBJECT_LOCK (dec); + dec->plc_num_samples += samples; + dec->plc_duration += aligned_missing_duration; + GST_OBJECT_UNLOCK (dec); } else { /* use maximum size (120 ms) as the number of returned samples is not constant over the stream. */ @@ -766,6 +806,10 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) gst_buffer_replace (&dec->last_buffer, buffer); } + GST_OBJECT_LOCK (dec); + dec->num_pushed++; + GST_OBJECT_UNLOCK (dec); + res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1); if (res != GST_FLOW_OK) @@ -841,8 +885,8 @@ gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) const GstAudioChannelPosition *posn = NULL; if (!gst_codec_utils_opus_parse_caps (caps, &dec->sample_rate, - &dec->n_channels, &dec->channel_mapping_family, &dec->n_streams, - &dec->n_stereo_streams, dec->channel_mapping)) { + (guint8 *) & dec->n_channels, &dec->channel_mapping_family, + &dec->n_streams, &dec->n_stereo_streams, dec->channel_mapping)) { ret = FALSE; goto done; } @@ -943,6 +987,126 @@ gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf) return res; } +/* Called with object lock hold */ +static guint32 +get_bandwidth (GstOpusDec * self) +{ + gint err; + gint32 bw; + + if (!self->state) + return 0; + + err = opus_multistream_decoder_ctl (self->state, OPUS_GET_BANDWIDTH (&bw)); + if (err != OPUS_OK) { + GST_WARNING_OBJECT (self, "Could not retrieve bandwith: %s", + opus_strerror (err)); + return 0; + } + + switch (bw) { + case OPUS_BANDWIDTH_NARROWBAND: + return 4; + case OPUS_BANDWIDTH_MEDIUMBAND: + return 6; + case OPUS_BANDWIDTH_WIDEBAND: + return 8; + case OPUS_BANDWIDTH_SUPERWIDEBAND: + return 12; + case OPUS_BANDWIDTH_FULLBAND: + return 20; + default: + GST_WARNING_OBJECT (self, "Unknown bandwith enum: %d", bw); + return 0; + } +} + +/* Called with object lock hold */ +static guint32 +get_sample_rate (GstOpusDec * self) +{ + gint err; + gint32 rate; + + if (!self->state) + return 0; + + err = + opus_multistream_decoder_ctl (self->state, OPUS_GET_SAMPLE_RATE (&rate)); + if (err != OPUS_OK) { + GST_WARNING_OBJECT (self, "Could not retrieve sample rate: %s", + opus_strerror (err)); + return 0; + } + + return rate; +} + +/* Called with object lock hold */ +static guint32 +get_gain (GstOpusDec * self) +{ + gint err; + gint32 gain; + + if (!self->state) + return 0; + + err = opus_multistream_decoder_ctl (self->state, OPUS_GET_GAIN (&gain)); + if (err != OPUS_OK) { + GST_WARNING_OBJECT (self, "Could not retrieve gain: %s", + opus_strerror (err)); + return 0; + } + + return gain; +} + +/* Called with object lock hold */ +static guint32 +get_last_packet_duration (GstOpusDec * self) +{ + gint err; + gint32 duration; + + if (!self->state) + return 0; + + err = + opus_multistream_decoder_ctl (self->state, + OPUS_GET_LAST_PACKET_DURATION (&duration)); + if (err != OPUS_OK) { + GST_WARNING_OBJECT (self, "Could not retrieve last packet duration: %s", + opus_strerror (err)); + return 0; + } + + return duration; +} + +static GstStructure * +gst_opus_dec_create_stats (GstOpusDec * self) +{ + GstStructure *s; + + GST_OBJECT_LOCK (self); + + s = gst_structure_new ("application/x-opusdec-stats", + "num-pushed", G_TYPE_UINT64, self->num_pushed, + "num-gap", G_TYPE_UINT64, self->num_gap, + "plc-num-samples", G_TYPE_UINT64, self->plc_num_samples, + "plc-duration", G_TYPE_UINT64, self->plc_duration, + "bandwidth", G_TYPE_UINT, get_bandwidth (self), + "sample-rate", G_TYPE_UINT, get_sample_rate (self), + "gain", G_TYPE_UINT, get_gain (self), + "last-packet-duration", G_TYPE_UINT, get_last_packet_duration (self), + "channels", G_TYPE_UINT, self->n_channels, NULL); + + GST_OBJECT_UNLOCK (self); + + return s; +} + static void gst_opus_dec_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) @@ -959,6 +1123,9 @@ gst_opus_dec_get_property (GObject * object, guint prop_id, GValue * value, case PROP_PHASE_INVERSION: g_value_set_boolean (value, dec->phase_inversion); break; + case PROP_STATS: + g_value_take_boxed (value, gst_opus_dec_create_stats (dec)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index 1673a4d..107bfdb 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -53,7 +53,7 @@ struct _GstOpusDec { GstBuffer *vorbiscomment; guint32 sample_rate; - guint8 n_channels; + guint n_channels; guint16 pre_skip; gint16 r128_gain; @@ -77,6 +77,12 @@ struct _GstOpusDec { GstClockTime last_known_buffer_duration; gboolean phase_inversion; + + /* Used to generate the 'stats' property. Protected by object lock */ + guint64 num_pushed; + guint64 num_gap; + guint64 plc_num_samples; + guint64 plc_duration; }; struct _GstOpusDecClass { -- 2.7.4