mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-25 08:38:21 +00:00
level: add GstRTPAudioLevelMeta on buffers
This meta can be used by a RTP payloader to send the level information to the peer. Part of https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/issues/446 Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/630>
This commit is contained in:
parent
25f98ab134
commit
4b6c3c9a1b
4 changed files with 107 additions and 1 deletions
|
@ -8868,6 +8868,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"audio-level-meta": {
|
||||||
|
"blurb": "Set GstAudioLevelMeta on buffers",
|
||||||
|
"conditionally-available": false,
|
||||||
|
"construct": false,
|
||||||
|
"construct-only": false,
|
||||||
|
"controllable": false,
|
||||||
|
"default": "false",
|
||||||
|
"mutable": "null",
|
||||||
|
"readable": true,
|
||||||
|
"type": "gboolean",
|
||||||
|
"writable": true
|
||||||
|
},
|
||||||
"interval": {
|
"interval": {
|
||||||
"blurb": "Interval of time between message posts (in nanoseconds)",
|
"blurb": "Interval of time between message posts (in nanoseconds)",
|
||||||
"conditionally-available": false,
|
"conditionally-available": false,
|
||||||
|
|
|
@ -98,7 +98,8 @@ enum
|
||||||
PROP_MESSAGE,
|
PROP_MESSAGE,
|
||||||
PROP_INTERVAL,
|
PROP_INTERVAL,
|
||||||
PROP_PEAK_TTL,
|
PROP_PEAK_TTL,
|
||||||
PROP_PEAK_FALLOFF
|
PROP_PEAK_FALLOFF,
|
||||||
|
PROP_AUDIO_LEVEL_META,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define gst_level_parent_class parent_class
|
#define gst_level_parent_class parent_class
|
||||||
|
@ -171,6 +172,17 @@ gst_level_class_init (GstLevelClass * klass)
|
||||||
g_param_spec_double ("peak-falloff", "Peak Falloff",
|
g_param_spec_double ("peak-falloff", "Peak Falloff",
|
||||||
"Decay rate of decay peak after TTL (in dB/sec)",
|
"Decay rate of decay peak after TTL (in dB/sec)",
|
||||||
0.0, G_MAXDOUBLE, 10.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
0.0, G_MAXDOUBLE, 10.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
/**
|
||||||
|
* GstLevel:audio-level-meta:
|
||||||
|
*
|
||||||
|
* If %TRUE, generate or update GstAudioLevelMeta on output buffers.
|
||||||
|
*
|
||||||
|
* Since: 1.20
|
||||||
|
*/
|
||||||
|
g_object_class_install_property (gobject_class, PROP_AUDIO_LEVEL_META,
|
||||||
|
g_param_spec_boolean ("audio-level-meta", "Audio Level Meta",
|
||||||
|
"Set GstAudioLevelMeta on buffers", FALSE,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_INIT (level_debug, "level", 0, "Level calculation");
|
GST_DEBUG_CATEGORY_INIT (level_debug, "level", 0, "Level calculation");
|
||||||
|
|
||||||
|
@ -262,6 +274,9 @@ gst_level_set_property (GObject * object, guint prop_id,
|
||||||
case PROP_PEAK_FALLOFF:
|
case PROP_PEAK_FALLOFF:
|
||||||
filter->decay_peak_falloff = g_value_get_double (value);
|
filter->decay_peak_falloff = g_value_get_double (value);
|
||||||
break;
|
break;
|
||||||
|
case PROP_AUDIO_LEVEL_META:
|
||||||
|
filter->audio_level_meta = g_value_get_boolean (value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -289,6 +304,9 @@ gst_level_get_property (GObject * object, guint prop_id,
|
||||||
case PROP_PEAK_FALLOFF:
|
case PROP_PEAK_FALLOFF:
|
||||||
g_value_set_double (value, filter->decay_peak_falloff);
|
g_value_set_double (value, filter->decay_peak_falloff);
|
||||||
break;
|
break;
|
||||||
|
case PROP_AUDIO_LEVEL_META:
|
||||||
|
g_value_set_boolean (value, filter->audio_level_meta);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -546,6 +564,24 @@ gst_level_message_append_channel (GstMessage * m, gdouble rms, gdouble peak,
|
||||||
g_value_unset (&v);
|
g_value_unset (&v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_level_rtp_audio_level_meta (GstLevel * self, GstBuffer * buffer,
|
||||||
|
guint8 level)
|
||||||
|
{
|
||||||
|
GstAudioLevelMeta *meta;
|
||||||
|
|
||||||
|
/* Update the existing meta, if any, so we can have an upstream element
|
||||||
|
* filling the voice activity part of the meta. */
|
||||||
|
meta = gst_buffer_get_audio_level_meta (buffer);
|
||||||
|
if (meta) {
|
||||||
|
meta->level = level;
|
||||||
|
} else {
|
||||||
|
/* Assume audio does not contain voice, it can be detected by another
|
||||||
|
* downstream element. */
|
||||||
|
gst_buffer_add_audio_level_meta (buffer, level, FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in)
|
gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in)
|
||||||
{
|
{
|
||||||
|
@ -562,6 +598,7 @@ gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in)
|
||||||
* intervals */
|
* intervals */
|
||||||
GstClockTimeDiff falloff_time;
|
GstClockTimeDiff falloff_time;
|
||||||
gint channels, rate, bps;
|
gint channels, rate, bps;
|
||||||
|
gdouble CS_tot = 0; /* Total Cumulative Square on all samples */
|
||||||
|
|
||||||
filter = GST_LEVEL (trans);
|
filter = GST_LEVEL (trans);
|
||||||
|
|
||||||
|
@ -598,6 +635,7 @@ gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in)
|
||||||
if (!GST_BUFFER_FLAG_IS_SET (in, GST_BUFFER_FLAG_GAP)) {
|
if (!GST_BUFFER_FLAG_IS_SET (in, GST_BUFFER_FLAG_GAP)) {
|
||||||
filter->process (in_data + (bps * i), block_int_size, channels, &CS,
|
filter->process (in_data + (bps * i), block_int_size, channels, &CS,
|
||||||
&filter->peak[i]);
|
&filter->peak[i]);
|
||||||
|
CS_tot += CS;
|
||||||
GST_LOG_OBJECT (filter,
|
GST_LOG_OBJECT (filter,
|
||||||
"[%d]: cumulative squares %lf, over %d samples/%d channels",
|
"[%d]: cumulative squares %lf, over %d samples/%d channels",
|
||||||
i, CS, block_int_size, channels);
|
i, CS, block_int_size, channels);
|
||||||
|
@ -664,6 +702,13 @@ gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in)
|
||||||
|
|
||||||
gst_buffer_unmap (in, &map);
|
gst_buffer_unmap (in, &map);
|
||||||
|
|
||||||
|
if (filter->audio_level_meta) {
|
||||||
|
gdouble RMS = sqrt (CS_tot / num_int_samples);
|
||||||
|
gdouble RMSdB = 20 * log10 (RMS + EPSILON);
|
||||||
|
|
||||||
|
gst_level_rtp_audio_level_meta (filter, in, -RMSdB);
|
||||||
|
}
|
||||||
|
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,7 @@ struct _GstLevel {
|
||||||
* since last emit */
|
* since last emit */
|
||||||
gint interval_frames; /* after how many frame to sent a message */
|
gint interval_frames; /* after how many frame to sent a message */
|
||||||
GstClockTime message_ts; /* starttime for next message */
|
GstClockTime message_ts; /* starttime for next message */
|
||||||
|
gboolean audio_level_meta; /* whether or not generate GstAudioLevelMeta */
|
||||||
|
|
||||||
/* per-channel arrays for intermediate values */
|
/* per-channel arrays for intermediate values */
|
||||||
gdouble *CS; /* normalized Cumulative Square */
|
gdouble *CS; /* normalized Cumulative Square */
|
||||||
|
|
|
@ -588,6 +588,53 @@ GST_START_TEST (test_message_timestamps)
|
||||||
|
|
||||||
GST_END_TEST;
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (test_rtp_audio_level_meta)
|
||||||
|
{
|
||||||
|
GstElement *level;
|
||||||
|
GstBuffer *inbuffer, *outbuffer;
|
||||||
|
GstAudioLevelMeta *meta;
|
||||||
|
|
||||||
|
level = setup_level (LEVEL_S16_CAPS_STRING);
|
||||||
|
g_object_set (level, "post-messages", FALSE, "audio-level-meta", TRUE, NULL);
|
||||||
|
gst_element_set_state (level, GST_STATE_PLAYING);
|
||||||
|
|
||||||
|
/* create a fake 0.1 sec buffer with a half-amplitude block signal */
|
||||||
|
inbuffer = create_s16_buffer (16536, 16536);
|
||||||
|
|
||||||
|
fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
|
||||||
|
fail_unless_equals_int (g_list_length (buffers), 1);
|
||||||
|
fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
|
||||||
|
fail_unless (inbuffer == outbuffer);
|
||||||
|
|
||||||
|
/* level added the meta */
|
||||||
|
meta = gst_buffer_get_audio_level_meta (outbuffer);
|
||||||
|
fail_unless (meta);
|
||||||
|
fail_unless_equals_int (meta->voice_activity, 0);
|
||||||
|
fail_unless_equals_int (meta->level, 5);
|
||||||
|
|
||||||
|
/* same but with input buffer already having the meta so level will update it */
|
||||||
|
inbuffer = create_s16_buffer (16536, 16536);
|
||||||
|
gst_buffer_add_audio_level_meta (inbuffer, 0, TRUE);
|
||||||
|
|
||||||
|
fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
|
||||||
|
fail_unless_equals_int (g_list_length (buffers), 2);
|
||||||
|
fail_if ((outbuffer = (GstBuffer *) buffers->next->data) == NULL);
|
||||||
|
fail_unless (inbuffer == outbuffer);
|
||||||
|
|
||||||
|
/* level updated the meta */
|
||||||
|
meta = gst_buffer_get_audio_level_meta (outbuffer);
|
||||||
|
fail_unless (meta);
|
||||||
|
fail_unless_equals_int (meta->voice_activity, 1);
|
||||||
|
fail_unless_equals_int (meta->level, 5);
|
||||||
|
|
||||||
|
/* clean up */
|
||||||
|
/* flush current messages,and future state change messages */
|
||||||
|
gst_element_set_state (level, GST_STATE_NULL);
|
||||||
|
cleanup_level (level);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
static Suite *
|
static Suite *
|
||||||
level_suite (void)
|
level_suite (void)
|
||||||
{
|
{
|
||||||
|
@ -603,6 +650,7 @@ level_suite (void)
|
||||||
tcase_add_test (tc_chain, test_message_on_eos);
|
tcase_add_test (tc_chain, test_message_on_eos);
|
||||||
tcase_add_test (tc_chain, test_message_count);
|
tcase_add_test (tc_chain, test_message_count);
|
||||||
tcase_add_test (tc_chain, test_message_timestamps);
|
tcase_add_test (tc_chain, test_message_timestamps);
|
||||||
|
tcase_add_test (tc_chain, test_rtp_audio_level_meta);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue