1 /*
2 * WebRTC Audio Processing Elements
3 *
4 * Copyright 2016 Collabora Ltd
5 * @author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 */
22
23 /**
24 * SECTION:element-webrtcdsp
25 * @short_description: Audio Filter using WebRTC Audio Processing library
26 *
27 * A voice enhancement filter based on WebRTC Audio Processing library. This
28 * library provides a whide variety of enhancement algorithms. This element
29 * tries to enable as much as possible. The currently enabled enhancements are
30 * High Pass Filter, Echo Canceller, Noise Suppression, Automatic Gain Control,
31 * and some extended filters.
32 *
33 * While webrtcdsp element can be used alone, there is an exception for the
34 * echo canceller. The audio canceller need to be aware of the far end streams
35 * that are played to loud speakers. For this, you must place a webrtcechoprobe
36 * element at that far end. Note that the sample rate must match between
37 * webrtcdsp and the webrtechoprobe. Though, the number of channels can differ.
38 * The probe is found by the DSP element using it's object name. By default,
39 * webrtcdsp looks for webrtcechoprobe0, which means it just work if you have
40 * a single probe and DSP.
41 *
42 * The probe can only be used within the same top level GstPipeline.
43 * Additionally, to simplify the code, the probe element must be created
44 * before the DSP sink pad is activated. It does not need to be in any
45 * particular state and does not even need to be added to the pipeline yet.
46 *
47 * # Example launch line
48 *
49 * As a convenience, the echo canceller can be tested using an echo loop. In
50 * this configuration, one would expect a single echo to be heard.
51 *
52 * |[
53 * gst-launch-1.0 pulsesrc ! webrtcdsp ! webrtcechoprobe ! pulsesink
54 * ]|
55 *
56 * In real environment, you'll place the probe before the playback, but only
57 * process the far end streams. The DSP should be placed as close as possible
58 * to the audio capture. The following pipeline is astracted and does not
59 * represent a real pipeline.
60 *
61 * |[
62 * gst-launch-1.0 far-end-src ! audio/x-raw,rate=48000 ! webrtcechoprobe ! pulsesink \
63 * pulsesrc ! audio/x-raw,rate=48000 ! webrtcdsp ! far-end-sink
64 * ]|
65 */
66
67 #ifdef HAVE_CONFIG_H
68 #include "config.h"
69 #endif
70
71 #include "gstwebrtcdsp.h"
72 #include "gstwebrtcechoprobe.h"
73
74 #include <webrtc/modules/audio_processing/include/audio_processing.h>
75 #include <webrtc/modules/interface/module_common_types.h>
76 #include <webrtc/system_wrappers/include/trace.h>
77
78 GST_DEBUG_CATEGORY (webrtc_dsp_debug);
79 #define GST_CAT_DEFAULT (webrtc_dsp_debug)
80
81 #define DEFAULT_TARGET_LEVEL_DBFS 3
82 #define DEFAULT_COMPRESSION_GAIN_DB 9
83 #define DEFAULT_STARTUP_MIN_VOLUME 12
84 #define DEFAULT_LIMITER TRUE
85 #define DEFAULT_GAIN_CONTROL_MODE webrtc::GainControl::kAdaptiveDigital
86 #define DEFAULT_VOICE_DETECTION FALSE
87 #define DEFAULT_VOICE_DETECTION_FRAME_SIZE_MS 10
88 #define DEFAULT_VOICE_DETECTION_LIKELIHOOD webrtc::VoiceDetection::kLowLikelihood
89
90 static GstStaticPadTemplate gst_webrtc_dsp_sink_template =
91 GST_STATIC_PAD_TEMPLATE ("sink",
92 GST_PAD_SINK,
93 GST_PAD_ALWAYS,
94 GST_STATIC_CAPS ("audio/x-raw, "
95 "format = (string) " GST_AUDIO_NE (S16) ", "
96 "layout = (string) interleaved, "
97 "rate = (int) { 48000, 32000, 16000, 8000 }, "
98 "channels = (int) [1, MAX];"
99 "audio/x-raw, "
100 "format = (string) " GST_AUDIO_NE (F32) ", "
101 "layout = (string) non-interleaved, "
102 "rate = (int) { 48000, 32000, 16000, 8000 }, "
103 "channels = (int) [1, MAX]")
104 );
105
106 static GstStaticPadTemplate gst_webrtc_dsp_src_template =
107 GST_STATIC_PAD_TEMPLATE ("src",
108 GST_PAD_SRC,
109 GST_PAD_ALWAYS,
110 GST_STATIC_CAPS ("audio/x-raw, "
111 "format = (string) " GST_AUDIO_NE (S16) ", "
112 "layout = (string) interleaved, "
113 "rate = (int) { 48000, 32000, 16000, 8000 }, "
114 "channels = (int) [1, MAX];"
115 "audio/x-raw, "
116 "format = (string) " GST_AUDIO_NE (F32) ", "
117 "layout = (string) non-interleaved, "
118 "rate = (int) { 48000, 32000, 16000, 8000 }, "
119 "channels = (int) [1, MAX]")
120 );
121
122 typedef webrtc::EchoCancellation::SuppressionLevel GstWebrtcEchoSuppressionLevel;
123 #define GST_TYPE_WEBRTC_ECHO_SUPPRESSION_LEVEL \
124 (gst_webrtc_echo_suppression_level_get_type ())
125 static GType
gst_webrtc_echo_suppression_level_get_type(void)126 gst_webrtc_echo_suppression_level_get_type (void)
127 {
128 static GType suppression_level_type = 0;
129 static const GEnumValue level_types[] = {
130 {webrtc::EchoCancellation::kLowSuppression, "Low Suppression", "low"},
131 {webrtc::EchoCancellation::kModerateSuppression,
132 "Moderate Suppression", "moderate"},
133 {webrtc::EchoCancellation::kHighSuppression, "high Suppression", "high"},
134 {0, NULL, NULL}
135 };
136
137 if (!suppression_level_type) {
138 suppression_level_type =
139 g_enum_register_static ("GstWebrtcEchoSuppressionLevel", level_types);
140 }
141 return suppression_level_type;
142 }
143
144 typedef webrtc::NoiseSuppression::Level GstWebrtcNoiseSuppressionLevel;
145 #define GST_TYPE_WEBRTC_NOISE_SUPPRESSION_LEVEL \
146 (gst_webrtc_noise_suppression_level_get_type ())
147 static GType
gst_webrtc_noise_suppression_level_get_type(void)148 gst_webrtc_noise_suppression_level_get_type (void)
149 {
150 static GType suppression_level_type = 0;
151 static const GEnumValue level_types[] = {
152 {webrtc::NoiseSuppression::kLow, "Low Suppression", "low"},
153 {webrtc::NoiseSuppression::kModerate, "Moderate Suppression", "moderate"},
154 {webrtc::NoiseSuppression::kHigh, "High Suppression", "high"},
155 {webrtc::NoiseSuppression::kVeryHigh, "Very High Suppression",
156 "very-high"},
157 {0, NULL, NULL}
158 };
159
160 if (!suppression_level_type) {
161 suppression_level_type =
162 g_enum_register_static ("GstWebrtcNoiseSuppressionLevel", level_types);
163 }
164 return suppression_level_type;
165 }
166
167 typedef webrtc::GainControl::Mode GstWebrtcGainControlMode;
168 #define GST_TYPE_WEBRTC_GAIN_CONTROL_MODE \
169 (gst_webrtc_gain_control_mode_get_type ())
170 static GType
gst_webrtc_gain_control_mode_get_type(void)171 gst_webrtc_gain_control_mode_get_type (void)
172 {
173 static GType gain_control_mode_type = 0;
174 static const GEnumValue mode_types[] = {
175 {webrtc::GainControl::kAdaptiveDigital, "Adaptive Digital", "adaptive-digital"},
176 {webrtc::GainControl::kFixedDigital, "Fixed Digital", "fixed-digital"},
177 {0, NULL, NULL}
178 };
179
180 if (!gain_control_mode_type) {
181 gain_control_mode_type =
182 g_enum_register_static ("GstWebrtcGainControlMode", mode_types);
183 }
184 return gain_control_mode_type;
185 }
186
187 typedef webrtc::VoiceDetection::Likelihood GstWebrtcVoiceDetectionLikelihood;
188 #define GST_TYPE_WEBRTC_VOICE_DETECTION_LIKELIHOOD \
189 (gst_webrtc_voice_detection_likelihood_get_type ())
190 static GType
gst_webrtc_voice_detection_likelihood_get_type(void)191 gst_webrtc_voice_detection_likelihood_get_type (void)
192 {
193 static GType likelihood_type = 0;
194 static const GEnumValue likelihood_types[] = {
195 {webrtc::VoiceDetection::kVeryLowLikelihood, "Very Low Likelihood", "very-low"},
196 {webrtc::VoiceDetection::kLowLikelihood, "Low Likelihood", "low"},
197 {webrtc::VoiceDetection::kModerateLikelihood, "Moderate Likelihood", "moderate"},
198 {webrtc::VoiceDetection::kHighLikelihood, "High Likelihood", "high"},
199 {0, NULL, NULL}
200 };
201
202 if (!likelihood_type) {
203 likelihood_type =
204 g_enum_register_static ("GstWebrtcVoiceDetectionLikelihood", likelihood_types);
205 }
206 return likelihood_type;
207 }
208
209 enum
210 {
211 PROP_0,
212 PROP_PROBE,
213 PROP_HIGH_PASS_FILTER,
214 PROP_ECHO_CANCEL,
215 PROP_ECHO_SUPPRESSION_LEVEL,
216 PROP_NOISE_SUPPRESSION,
217 PROP_NOISE_SUPPRESSION_LEVEL,
218 PROP_GAIN_CONTROL,
219 PROP_EXPERIMENTAL_AGC,
220 PROP_EXTENDED_FILTER,
221 PROP_DELAY_AGNOSTIC,
222 PROP_TARGET_LEVEL_DBFS,
223 PROP_COMPRESSION_GAIN_DB,
224 PROP_STARTUP_MIN_VOLUME,
225 PROP_LIMITER,
226 PROP_GAIN_CONTROL_MODE,
227 PROP_VOICE_DETECTION,
228 PROP_VOICE_DETECTION_FRAME_SIZE_MS,
229 PROP_VOICE_DETECTION_LIKELIHOOD,
230 };
231
232 /**
233 * GstWebrtcDSP:
234 *
235 * The adder object structure.
236 */
237 struct _GstWebrtcDsp
238 {
239 GstAudioFilter element;
240
241 /* Protected by the object lock */
242 GstAudioInfo info;
243 gboolean interleaved;
244 guint period_size;
245 guint period_samples;
246 gboolean stream_has_voice;
247
248 /* Protected by the stream lock */
249 GstAdapter *adapter;
250 GstPlanarAudioAdapter *padapter;
251 webrtc::AudioProcessing * apm;
252
253 /* Protected by the object lock */
254 gchar *probe_name;
255 GstWebrtcEchoProbe *probe;
256
257 /* Properties */
258 gboolean high_pass_filter;
259 gboolean echo_cancel;
260 webrtc::EchoCancellation::SuppressionLevel echo_suppression_level;
261 gboolean noise_suppression;
262 webrtc::NoiseSuppression::Level noise_suppression_level;
263 gboolean gain_control;
264 gboolean experimental_agc;
265 gboolean extended_filter;
266 gboolean delay_agnostic;
267 gint target_level_dbfs;
268 gint compression_gain_db;
269 gint startup_min_volume;
270 gboolean limiter;
271 webrtc::GainControl::Mode gain_control_mode;
272 gboolean voice_detection;
273 gint voice_detection_frame_size_ms;
274 webrtc::VoiceDetection::Likelihood voice_detection_likelihood;
275 };
276
277 G_DEFINE_TYPE_WITH_CODE (GstWebrtcDsp, gst_webrtc_dsp, GST_TYPE_AUDIO_FILTER,
278 GST_DEBUG_CATEGORY_INIT (webrtc_dsp_debug, "webrtcdsp", 0,
279 "libwebrtcdsp wrapping elements"););
280 GST_ELEMENT_REGISTER_DEFINE (webrtcdsp, "webrtcdsp", GST_RANK_NONE,
281 GST_TYPE_WEBRTC_DSP);
282
283 static const gchar *
webrtc_error_to_string(gint err)284 webrtc_error_to_string (gint err)
285 {
286 const gchar *str = "unknown error";
287
288 switch (err) {
289 case webrtc::AudioProcessing::kNoError:
290 str = "success";
291 break;
292 case webrtc::AudioProcessing::kUnspecifiedError:
293 str = "unspecified error";
294 break;
295 case webrtc::AudioProcessing::kCreationFailedError:
296 str = "creating failed";
297 break;
298 case webrtc::AudioProcessing::kUnsupportedComponentError:
299 str = "unsupported component";
300 break;
301 case webrtc::AudioProcessing::kUnsupportedFunctionError:
302 str = "unsupported function";
303 break;
304 case webrtc::AudioProcessing::kNullPointerError:
305 str = "null pointer";
306 break;
307 case webrtc::AudioProcessing::kBadParameterError:
308 str = "bad parameter";
309 break;
310 case webrtc::AudioProcessing::kBadSampleRateError:
311 str = "bad sample rate";
312 break;
313 case webrtc::AudioProcessing::kBadDataLengthError:
314 str = "bad data length";
315 break;
316 case webrtc::AudioProcessing::kBadNumberChannelsError:
317 str = "bad number of channels";
318 break;
319 case webrtc::AudioProcessing::kFileError:
320 str = "file IO error";
321 break;
322 case webrtc::AudioProcessing::kStreamParameterNotSetError:
323 str = "stream parameter not set";
324 break;
325 case webrtc::AudioProcessing::kNotEnabledError:
326 str = "not enabled";
327 break;
328 default:
329 break;
330 }
331
332 return str;
333 }
334
335 static GstBuffer *
gst_webrtc_dsp_take_buffer(GstWebrtcDsp * self)336 gst_webrtc_dsp_take_buffer (GstWebrtcDsp * self)
337 {
338 GstBuffer *buffer;
339 GstClockTime timestamp;
340 guint64 distance;
341 gboolean at_discont;
342
343 if (self->interleaved) {
344 timestamp = gst_adapter_prev_pts (self->adapter, &distance);
345 distance /= self->info.bpf;
346 } else {
347 timestamp = gst_planar_audio_adapter_prev_pts (self->padapter, &distance);
348 }
349
350 timestamp += gst_util_uint64_scale_int (distance, GST_SECOND, self->info.rate);
351
352 if (self->interleaved) {
353 buffer = gst_adapter_take_buffer (self->adapter, self->period_size);
354 at_discont = (gst_adapter_pts_at_discont (self->adapter) == timestamp);
355 } else {
356 buffer = gst_planar_audio_adapter_take_buffer (self->padapter,
357 self->period_samples, GST_MAP_READWRITE);
358 at_discont =
359 (gst_planar_audio_adapter_pts_at_discont (self->padapter) == timestamp);
360 }
361
362 GST_BUFFER_PTS (buffer) = timestamp;
363 GST_BUFFER_DURATION (buffer) = 10 * GST_MSECOND;
364
365 if (at_discont && distance == 0) {
366 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
367 } else {
368 GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DISCONT);
369 }
370
371 return buffer;
372 }
373
374 static GstFlowReturn
gst_webrtc_dsp_analyze_reverse_stream(GstWebrtcDsp * self,GstClockTime rec_time)375 gst_webrtc_dsp_analyze_reverse_stream (GstWebrtcDsp * self,
376 GstClockTime rec_time)
377 {
378 GstWebrtcEchoProbe *probe = NULL;
379 webrtc::AudioProcessing * apm;
380 webrtc::AudioFrame frame;
381 GstBuffer *buf = NULL;
382 GstFlowReturn ret = GST_FLOW_OK;
383 gint err, delay;
384
385 GST_OBJECT_LOCK (self);
386 if (self->echo_cancel)
387 probe = GST_WEBRTC_ECHO_PROBE (g_object_ref (self->probe));
388 GST_OBJECT_UNLOCK (self);
389
390 /* If echo cancellation is disabled */
391 if (!probe)
392 return GST_FLOW_OK;
393
394 apm = self->apm;
395
396 if (self->delay_agnostic)
397 rec_time = GST_CLOCK_TIME_NONE;
398
399 again:
400 delay = gst_webrtc_echo_probe_read (probe, rec_time, (gpointer) &frame, &buf);
401 apm->set_stream_delay_ms (delay);
402
403 if (delay < 0)
404 goto done;
405
406 if (frame.sample_rate_hz_ != self->info.rate) {
407 GST_ELEMENT_ERROR (self, STREAM, FORMAT,
408 ("Echo Probe has rate %i , while the DSP is running at rate %i,"
409 " use a caps filter to ensure those are the same.",
410 frame.sample_rate_hz_, self->info.rate), (NULL));
411 ret = GST_FLOW_ERROR;
412 goto done;
413 }
414
415 if (buf) {
416 webrtc::StreamConfig config (frame.sample_rate_hz_, frame.num_channels_,
417 false);
418 GstAudioBuffer abuf;
419 float * const * data;
420
421 gst_audio_buffer_map (&abuf, &self->info, buf, GST_MAP_READWRITE);
422 data = (float * const *) abuf.planes;
423 if ((err = apm->ProcessReverseStream (data, config, config, data)) < 0)
424 GST_WARNING_OBJECT (self, "Reverse stream analyses failed: %s.",
425 webrtc_error_to_string (err));
426 gst_audio_buffer_unmap (&abuf);
427 gst_buffer_replace (&buf, NULL);
428 } else {
429 if ((err = apm->AnalyzeReverseStream (&frame)) < 0)
430 GST_WARNING_OBJECT (self, "Reverse stream analyses failed: %s.",
431 webrtc_error_to_string (err));
432 }
433
434 if (self->delay_agnostic)
435 goto again;
436
437 done:
438 gst_object_unref (probe);
439 gst_buffer_replace (&buf, NULL);
440
441 return ret;
442 }
443
444 static void
gst_webrtc_vad_post_activity(GstWebrtcDsp * self,GstBuffer * buffer,gboolean stream_has_voice)445 gst_webrtc_vad_post_activity (GstWebrtcDsp *self, GstBuffer *buffer,
446 gboolean stream_has_voice)
447 {
448 GstClockTime timestamp = GST_BUFFER_PTS (buffer);
449 GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (self);
450 GstStructure *s;
451 GstClockTime stream_time;
452 GstAudioLevelMeta *meta;
453 guint8 level;
454
455 level = self->apm->level_estimator ()->RMS ();
456 meta = gst_buffer_get_audio_level_meta (buffer);
457 if (meta) {
458 meta->voice_activity = stream_has_voice;
459 meta->level = level;
460 } else {
461 gst_buffer_add_audio_level_meta (buffer, level, stream_has_voice);
462 }
463
464 stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
465 timestamp);
466
467 s = gst_structure_new ("voice-activity",
468 "stream-time", G_TYPE_UINT64, stream_time,
469 "stream-has-voice", G_TYPE_BOOLEAN, stream_has_voice, NULL);
470
471 GST_LOG_OBJECT (self, "Posting voice activity message, stream %s voice",
472 stream_has_voice ? "now has" : "no longer has");
473
474 gst_element_post_message (GST_ELEMENT (self),
475 gst_message_new_element (GST_OBJECT (self), s));
476 }
477
478 static GstFlowReturn
gst_webrtc_dsp_process_stream(GstWebrtcDsp * self,GstBuffer * buffer)479 gst_webrtc_dsp_process_stream (GstWebrtcDsp * self,
480 GstBuffer * buffer)
481 {
482 GstAudioBuffer abuf;
483 webrtc::AudioProcessing * apm = self->apm;
484 gint err;
485
486 if (!gst_audio_buffer_map (&abuf, &self->info, buffer,
487 (GstMapFlags) GST_MAP_READWRITE)) {
488 gst_buffer_unref (buffer);
489 return GST_FLOW_ERROR;
490 }
491
492 if (self->interleaved) {
493 webrtc::AudioFrame frame;
494 frame.num_channels_ = self->info.channels;
495 frame.sample_rate_hz_ = self->info.rate;
496 frame.samples_per_channel_ = self->period_samples;
497
498 memcpy (frame.data_, abuf.planes[0], self->period_size);
499 err = apm->ProcessStream (&frame);
500 if (err >= 0)
501 memcpy (abuf.planes[0], frame.data_, self->period_size);
502 } else {
503 float * const * data = (float * const *) abuf.planes;
504 webrtc::StreamConfig config (self->info.rate, self->info.channels, false);
505
506 err = apm->ProcessStream (data, config, config, data);
507 }
508
509 if (err < 0) {
510 GST_WARNING_OBJECT (self, "Failed to filter the audio: %s.",
511 webrtc_error_to_string (err));
512 } else {
513 if (self->voice_detection) {
514 gboolean stream_has_voice = apm->voice_detection ()->stream_has_voice ();
515
516 if (stream_has_voice != self->stream_has_voice)
517 gst_webrtc_vad_post_activity (self, buffer, stream_has_voice);
518
519 self->stream_has_voice = stream_has_voice;
520 }
521 }
522
523 gst_audio_buffer_unmap (&abuf);
524
525 return GST_FLOW_OK;
526 }
527
528 static GstFlowReturn
gst_webrtc_dsp_submit_input_buffer(GstBaseTransform * btrans,gboolean is_discont,GstBuffer * buffer)529 gst_webrtc_dsp_submit_input_buffer (GstBaseTransform * btrans,
530 gboolean is_discont, GstBuffer * buffer)
531 {
532 GstWebrtcDsp *self = GST_WEBRTC_DSP (btrans);
533
534 buffer = gst_buffer_make_writable (buffer);
535 GST_BUFFER_PTS (buffer) = gst_segment_to_running_time (&btrans->segment,
536 GST_FORMAT_TIME, GST_BUFFER_PTS (buffer));
537
538 if (is_discont) {
539 GST_DEBUG_OBJECT (self,
540 "Received discont, clearing adapter.");
541 if (self->interleaved)
542 gst_adapter_clear (self->adapter);
543 else
544 gst_planar_audio_adapter_clear (self->padapter);
545 }
546
547 if (self->interleaved)
548 gst_adapter_push (self->adapter, buffer);
549 else
550 gst_planar_audio_adapter_push (self->padapter, buffer);
551
552 return GST_FLOW_OK;
553 }
554
555 static GstFlowReturn
gst_webrtc_dsp_generate_output(GstBaseTransform * btrans,GstBuffer ** outbuf)556 gst_webrtc_dsp_generate_output (GstBaseTransform * btrans, GstBuffer ** outbuf)
557 {
558 GstWebrtcDsp *self = GST_WEBRTC_DSP (btrans);
559 GstFlowReturn ret;
560 gboolean not_enough;
561
562 if (self->interleaved)
563 not_enough = gst_adapter_available (self->adapter) < self->period_size;
564 else
565 not_enough = gst_planar_audio_adapter_available (self->padapter) <
566 self->period_samples;
567
568 if (not_enough) {
569 *outbuf = NULL;
570 return GST_FLOW_OK;
571 }
572
573 *outbuf = gst_webrtc_dsp_take_buffer (self);
574 ret = gst_webrtc_dsp_analyze_reverse_stream (self, GST_BUFFER_PTS (*outbuf));
575
576 if (ret == GST_FLOW_OK)
577 ret = gst_webrtc_dsp_process_stream (self, *outbuf);
578
579 return ret;
580 }
581
582 static gboolean
gst_webrtc_dsp_start(GstBaseTransform * btrans)583 gst_webrtc_dsp_start (GstBaseTransform * btrans)
584 {
585 GstWebrtcDsp *self = GST_WEBRTC_DSP (btrans);
586 webrtc::Config config;
587
588 GST_OBJECT_LOCK (self);
589
590 config.Set < webrtc::ExtendedFilter >
591 (new webrtc::ExtendedFilter (self->extended_filter));
592 config.Set < webrtc::ExperimentalAgc >
593 (new webrtc::ExperimentalAgc (self->experimental_agc, self->startup_min_volume));
594 config.Set < webrtc::DelayAgnostic >
595 (new webrtc::DelayAgnostic (self->delay_agnostic));
596
597 /* TODO Intelligibility enhancer, Beamforming, etc. */
598
599 self->apm = webrtc::AudioProcessing::Create (config);
600
601 if (self->echo_cancel) {
602 self->probe = gst_webrtc_acquire_echo_probe (self->probe_name);
603
604 if (self->probe == NULL) {
605 GST_OBJECT_UNLOCK (self);
606 GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
607 ("No echo probe with name %s found.", self->probe_name), (NULL));
608 return FALSE;
609 }
610 }
611
612 GST_OBJECT_UNLOCK (self);
613
614 return TRUE;
615 }
616
617 static gboolean
gst_webrtc_dsp_setup(GstAudioFilter * filter,const GstAudioInfo * info)618 gst_webrtc_dsp_setup (GstAudioFilter * filter, const GstAudioInfo * info)
619 {
620 GstWebrtcDsp *self = GST_WEBRTC_DSP (filter);
621 webrtc::AudioProcessing * apm;
622 webrtc::ProcessingConfig pconfig;
623 GstAudioInfo probe_info = *info;
624 gint err = 0;
625
626 GST_LOG_OBJECT (self, "setting format to %s with %i Hz and %i channels",
627 info->finfo->description, info->rate, info->channels);
628
629 GST_OBJECT_LOCK (self);
630
631 gst_adapter_clear (self->adapter);
632 gst_planar_audio_adapter_clear (self->padapter);
633
634 self->info = *info;
635 self->interleaved = (info->layout == GST_AUDIO_LAYOUT_INTERLEAVED);
636 apm = self->apm;
637
638 if (!self->interleaved)
639 gst_planar_audio_adapter_configure (self->padapter, info);
640
641 /* WebRTC library works with 10ms buffers, compute once this size */
642 self->period_samples = info->rate / 100;
643 self->period_size = self->period_samples * info->bpf;
644
645 if (self->interleaved &&
646 (webrtc::AudioFrame::kMaxDataSizeSamples * 2) < self->period_size)
647 goto period_too_big;
648
649 if (self->probe) {
650 GST_WEBRTC_ECHO_PROBE_LOCK (self->probe);
651
652 if (self->probe->info.rate != 0) {
653 if (self->probe->info.rate != info->rate)
654 goto probe_has_wrong_rate;
655 probe_info = self->probe->info;
656 }
657
658 GST_WEBRTC_ECHO_PROBE_UNLOCK (self->probe);
659 }
660
661 /* input stream */
662 pconfig.streams[webrtc::ProcessingConfig::kInputStream] =
663 webrtc::StreamConfig (info->rate, info->channels, false);
664 /* output stream */
665 pconfig.streams[webrtc::ProcessingConfig::kOutputStream] =
666 webrtc::StreamConfig (info->rate, info->channels, false);
667 /* reverse input stream */
668 pconfig.streams[webrtc::ProcessingConfig::kReverseInputStream] =
669 webrtc::StreamConfig (probe_info.rate, probe_info.channels, false);
670 /* reverse output stream */
671 pconfig.streams[webrtc::ProcessingConfig::kReverseOutputStream] =
672 webrtc::StreamConfig (probe_info.rate, probe_info.channels, false);
673
674 if ((err = apm->Initialize (pconfig)) < 0)
675 goto initialize_failed;
676
677 /* Setup Filters */
678 if (self->high_pass_filter) {
679 GST_DEBUG_OBJECT (self, "Enabling High Pass filter");
680 apm->high_pass_filter ()->Enable (true);
681 }
682
683 if (self->echo_cancel) {
684 GST_DEBUG_OBJECT (self, "Enabling Echo Cancellation");
685 apm->echo_cancellation ()->enable_drift_compensation (false);
686 apm->echo_cancellation ()
687 ->set_suppression_level (self->echo_suppression_level);
688 apm->echo_cancellation ()->Enable (true);
689 }
690
691 if (self->noise_suppression) {
692 GST_DEBUG_OBJECT (self, "Enabling Noise Suppression");
693 apm->noise_suppression ()->set_level (self->noise_suppression_level);
694 apm->noise_suppression ()->Enable (true);
695 }
696
697 if (self->gain_control) {
698 GEnumClass *mode_class = (GEnumClass *)
699 g_type_class_ref (GST_TYPE_WEBRTC_GAIN_CONTROL_MODE);
700
701 GST_DEBUG_OBJECT (self, "Enabling Digital Gain Control, target level "
702 "dBFS %d, compression gain dB %d, limiter %senabled, mode: %s",
703 self->target_level_dbfs, self->compression_gain_db,
704 self->limiter ? "" : "NOT ",
705 g_enum_get_value (mode_class, self->gain_control_mode)->value_name);
706
707 g_type_class_unref (mode_class);
708
709 apm->gain_control ()->set_mode (self->gain_control_mode);
710 apm->gain_control ()->set_target_level_dbfs (self->target_level_dbfs);
711 apm->gain_control ()->set_compression_gain_db (self->compression_gain_db);
712 apm->gain_control ()->enable_limiter (self->limiter);
713 apm->gain_control ()->Enable (true);
714 }
715
716 if (self->voice_detection) {
717 GEnumClass *likelihood_class = (GEnumClass *)
718 g_type_class_ref (GST_TYPE_WEBRTC_VOICE_DETECTION_LIKELIHOOD);
719 GST_DEBUG_OBJECT (self, "Enabling Voice Activity Detection, frame size "
720 "%d milliseconds, likelihood: %s", self->voice_detection_frame_size_ms,
721 g_enum_get_value (likelihood_class,
722 self->voice_detection_likelihood)->value_name);
723 g_type_class_unref (likelihood_class);
724
725 self->stream_has_voice = FALSE;
726
727 apm->voice_detection ()->Enable (true);
728 apm->voice_detection ()->set_likelihood (self->voice_detection_likelihood);
729 apm->voice_detection ()->set_frame_size_ms (
730 self->voice_detection_frame_size_ms);
731 apm->level_estimator ()->Enable (true);
732 }
733
734 GST_OBJECT_UNLOCK (self);
735
736 return TRUE;
737
738 period_too_big:
739 GST_OBJECT_UNLOCK (self);
740 GST_WARNING_OBJECT (self, "webrtcdsp format produce too big period "
741 "(maximum is %" G_GSIZE_FORMAT " samples and we have %u samples), "
742 "reduce the number of channels or the rate.",
743 webrtc::AudioFrame::kMaxDataSizeSamples, self->period_size / 2);
744 return FALSE;
745
746 probe_has_wrong_rate:
747 GST_WEBRTC_ECHO_PROBE_UNLOCK (self->probe);
748 GST_OBJECT_UNLOCK (self);
749 GST_ELEMENT_ERROR (self, STREAM, FORMAT,
750 ("Echo Probe has rate %i , while the DSP is running at rate %i,"
751 " use a caps filter to ensure those are the same.",
752 probe_info.rate, info->rate), (NULL));
753 return FALSE;
754
755 initialize_failed:
756 GST_OBJECT_UNLOCK (self);
757 GST_ELEMENT_ERROR (self, LIBRARY, INIT,
758 ("Failed to initialize WebRTC Audio Processing library"),
759 ("webrtc::AudioProcessing::Initialize() failed: %s",
760 webrtc_error_to_string (err)));
761 return FALSE;
762 }
763
764 static gboolean
gst_webrtc_dsp_stop(GstBaseTransform * btrans)765 gst_webrtc_dsp_stop (GstBaseTransform * btrans)
766 {
767 GstWebrtcDsp *self = GST_WEBRTC_DSP (btrans);
768
769 GST_OBJECT_LOCK (self);
770
771 gst_adapter_clear (self->adapter);
772 gst_planar_audio_adapter_clear (self->padapter);
773
774 if (self->probe) {
775 gst_webrtc_release_echo_probe (self->probe);
776 self->probe = NULL;
777 }
778
779 delete self->apm;
780 self->apm = NULL;
781
782 GST_OBJECT_UNLOCK (self);
783
784 return TRUE;
785 }
786
787 static void
gst_webrtc_dsp_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)788 gst_webrtc_dsp_set_property (GObject * object,
789 guint prop_id, const GValue * value, GParamSpec * pspec)
790 {
791 GstWebrtcDsp *self = GST_WEBRTC_DSP (object);
792
793 GST_OBJECT_LOCK (self);
794 switch (prop_id) {
795 case PROP_PROBE:
796 g_free (self->probe_name);
797 self->probe_name = g_value_dup_string (value);
798 break;
799 case PROP_HIGH_PASS_FILTER:
800 self->high_pass_filter = g_value_get_boolean (value);
801 break;
802 case PROP_ECHO_CANCEL:
803 self->echo_cancel = g_value_get_boolean (value);
804 break;
805 case PROP_ECHO_SUPPRESSION_LEVEL:
806 self->echo_suppression_level =
807 (GstWebrtcEchoSuppressionLevel) g_value_get_enum (value);
808 break;
809 case PROP_NOISE_SUPPRESSION:
810 self->noise_suppression = g_value_get_boolean (value);
811 break;
812 case PROP_NOISE_SUPPRESSION_LEVEL:
813 self->noise_suppression_level =
814 (GstWebrtcNoiseSuppressionLevel) g_value_get_enum (value);
815 break;
816 case PROP_GAIN_CONTROL:
817 self->gain_control = g_value_get_boolean (value);
818 break;
819 case PROP_EXPERIMENTAL_AGC:
820 self->experimental_agc = g_value_get_boolean (value);
821 break;
822 case PROP_EXTENDED_FILTER:
823 self->extended_filter = g_value_get_boolean (value);
824 break;
825 case PROP_DELAY_AGNOSTIC:
826 self->delay_agnostic = g_value_get_boolean (value);
827 break;
828 case PROP_TARGET_LEVEL_DBFS:
829 self->target_level_dbfs = g_value_get_int (value);
830 break;
831 case PROP_COMPRESSION_GAIN_DB:
832 self->compression_gain_db = g_value_get_int (value);
833 break;
834 case PROP_STARTUP_MIN_VOLUME:
835 self->startup_min_volume = g_value_get_int (value);
836 break;
837 case PROP_LIMITER:
838 self->limiter = g_value_get_boolean (value);
839 break;
840 case PROP_GAIN_CONTROL_MODE:
841 self->gain_control_mode =
842 (GstWebrtcGainControlMode) g_value_get_enum (value);
843 break;
844 case PROP_VOICE_DETECTION:
845 self->voice_detection = g_value_get_boolean (value);
846 break;
847 case PROP_VOICE_DETECTION_FRAME_SIZE_MS:
848 self->voice_detection_frame_size_ms = g_value_get_int (value);
849 break;
850 case PROP_VOICE_DETECTION_LIKELIHOOD:
851 self->voice_detection_likelihood =
852 (GstWebrtcVoiceDetectionLikelihood) g_value_get_enum (value);
853 break;
854 default:
855 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
856 break;
857 }
858 GST_OBJECT_UNLOCK (self);
859 }
860
861 static void
gst_webrtc_dsp_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)862 gst_webrtc_dsp_get_property (GObject * object,
863 guint prop_id, GValue * value, GParamSpec * pspec)
864 {
865 GstWebrtcDsp *self = GST_WEBRTC_DSP (object);
866
867 GST_OBJECT_LOCK (self);
868 switch (prop_id) {
869 case PROP_PROBE:
870 g_value_set_string (value, self->probe_name);
871 break;
872 case PROP_HIGH_PASS_FILTER:
873 g_value_set_boolean (value, self->high_pass_filter);
874 break;
875 case PROP_ECHO_CANCEL:
876 g_value_set_boolean (value, self->echo_cancel);
877 break;
878 case PROP_ECHO_SUPPRESSION_LEVEL:
879 g_value_set_enum (value, self->echo_suppression_level);
880 break;
881 case PROP_NOISE_SUPPRESSION:
882 g_value_set_boolean (value, self->noise_suppression);
883 break;
884 case PROP_NOISE_SUPPRESSION_LEVEL:
885 g_value_set_enum (value, self->noise_suppression_level);
886 break;
887 case PROP_GAIN_CONTROL:
888 g_value_set_boolean (value, self->gain_control);
889 break;
890 case PROP_EXPERIMENTAL_AGC:
891 g_value_set_boolean (value, self->experimental_agc);
892 break;
893 case PROP_EXTENDED_FILTER:
894 g_value_set_boolean (value, self->extended_filter);
895 break;
896 case PROP_DELAY_AGNOSTIC:
897 g_value_set_boolean (value, self->delay_agnostic);
898 break;
899 case PROP_TARGET_LEVEL_DBFS:
900 g_value_set_int (value, self->target_level_dbfs);
901 break;
902 case PROP_COMPRESSION_GAIN_DB:
903 g_value_set_int (value, self->compression_gain_db);
904 break;
905 case PROP_STARTUP_MIN_VOLUME:
906 g_value_set_int (value, self->startup_min_volume);
907 break;
908 case PROP_LIMITER:
909 g_value_set_boolean (value, self->limiter);
910 break;
911 case PROP_GAIN_CONTROL_MODE:
912 g_value_set_enum (value, self->gain_control_mode);
913 break;
914 case PROP_VOICE_DETECTION:
915 g_value_set_boolean (value, self->voice_detection);
916 break;
917 case PROP_VOICE_DETECTION_FRAME_SIZE_MS:
918 g_value_set_int (value, self->voice_detection_frame_size_ms);
919 break;
920 case PROP_VOICE_DETECTION_LIKELIHOOD:
921 g_value_set_enum (value, self->voice_detection_likelihood);
922 break;
923 default:
924 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
925 break;
926 }
927 GST_OBJECT_UNLOCK (self);
928 }
929
930
931 static void
gst_webrtc_dsp_finalize(GObject * object)932 gst_webrtc_dsp_finalize (GObject * object)
933 {
934 GstWebrtcDsp *self = GST_WEBRTC_DSP (object);
935
936 gst_object_unref (self->adapter);
937 gst_object_unref (self->padapter);
938 g_free (self->probe_name);
939
940 G_OBJECT_CLASS (gst_webrtc_dsp_parent_class)->finalize (object);
941 }
942
943 static void
gst_webrtc_dsp_init(GstWebrtcDsp * self)944 gst_webrtc_dsp_init (GstWebrtcDsp * self)
945 {
946 self->adapter = gst_adapter_new ();
947 self->padapter = gst_planar_audio_adapter_new ();
948 gst_audio_info_init (&self->info);
949 }
950
951 static void
gst_webrtc_dsp_class_init(GstWebrtcDspClass * klass)952 gst_webrtc_dsp_class_init (GstWebrtcDspClass * klass)
953 {
954 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
955 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
956 GstBaseTransformClass *btrans_class = GST_BASE_TRANSFORM_CLASS (klass);
957 GstAudioFilterClass *audiofilter_class = GST_AUDIO_FILTER_CLASS (klass);
958
959 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_webrtc_dsp_finalize);
960 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_webrtc_dsp_set_property);
961 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_webrtc_dsp_get_property);
962
963 btrans_class->passthrough_on_same_caps = FALSE;
964 btrans_class->start = GST_DEBUG_FUNCPTR (gst_webrtc_dsp_start);
965 btrans_class->stop = GST_DEBUG_FUNCPTR (gst_webrtc_dsp_stop);
966 btrans_class->submit_input_buffer =
967 GST_DEBUG_FUNCPTR (gst_webrtc_dsp_submit_input_buffer);
968 btrans_class->generate_output =
969 GST_DEBUG_FUNCPTR (gst_webrtc_dsp_generate_output);
970
971 audiofilter_class->setup = GST_DEBUG_FUNCPTR (gst_webrtc_dsp_setup);
972
973 gst_element_class_add_static_pad_template (element_class,
974 &gst_webrtc_dsp_src_template);
975 gst_element_class_add_static_pad_template (element_class,
976 &gst_webrtc_dsp_sink_template);
977 gst_element_class_set_static_metadata (element_class,
978 "Voice Processor (AGC, AEC, filters, etc.)",
979 "Generic/Audio",
980 "Pre-processes voice with WebRTC Audio Processing Library",
981 "Nicolas Dufresne <nicolas.dufresne@collabora.com>");
982
983 g_object_class_install_property (gobject_class,
984 PROP_PROBE,
985 g_param_spec_string ("probe", "Echo Probe",
986 "The name of the webrtcechoprobe element that record the audio being "
987 "played through loud speakers. Must be set before PAUSED state.",
988 "webrtcechoprobe0",
989 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
990 G_PARAM_CONSTRUCT)));
991
992 g_object_class_install_property (gobject_class,
993 PROP_HIGH_PASS_FILTER,
994 g_param_spec_boolean ("high-pass-filter", "High Pass Filter",
995 "Enable or disable high pass filtering", TRUE,
996 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
997 G_PARAM_CONSTRUCT)));
998
999 g_object_class_install_property (gobject_class,
1000 PROP_ECHO_CANCEL,
1001 g_param_spec_boolean ("echo-cancel", "Echo Cancel",
1002 "Enable or disable echo canceller", TRUE,
1003 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
1004 G_PARAM_CONSTRUCT)));
1005
1006 g_object_class_install_property (gobject_class,
1007 PROP_ECHO_SUPPRESSION_LEVEL,
1008 g_param_spec_enum ("echo-suppression-level", "Echo Suppression Level",
1009 "Controls the aggressiveness of the suppressor. A higher level "
1010 "trades off double-talk performance for increased echo suppression.",
1011 GST_TYPE_WEBRTC_ECHO_SUPPRESSION_LEVEL,
1012 webrtc::EchoCancellation::kModerateSuppression,
1013 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
1014 G_PARAM_CONSTRUCT)));
1015
1016 g_object_class_install_property (gobject_class,
1017 PROP_NOISE_SUPPRESSION,
1018 g_param_spec_boolean ("noise-suppression", "Noise Suppression",
1019 "Enable or disable noise suppression", TRUE,
1020 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
1021 G_PARAM_CONSTRUCT)));
1022
1023 g_object_class_install_property (gobject_class,
1024 PROP_NOISE_SUPPRESSION_LEVEL,
1025 g_param_spec_enum ("noise-suppression-level", "Noise Suppression Level",
1026 "Controls the aggressiveness of the suppression. Increasing the "
1027 "level will reduce the noise level at the expense of a higher "
1028 "speech distortion.", GST_TYPE_WEBRTC_NOISE_SUPPRESSION_LEVEL,
1029 webrtc::EchoCancellation::kModerateSuppression,
1030 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
1031 G_PARAM_CONSTRUCT)));
1032
1033 g_object_class_install_property (gobject_class,
1034 PROP_GAIN_CONTROL,
1035 g_param_spec_boolean ("gain-control", "Gain Control",
1036 "Enable or disable automatic digital gain control",
1037 TRUE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
1038 G_PARAM_CONSTRUCT)));
1039
1040 g_object_class_install_property (gobject_class,
1041 PROP_EXPERIMENTAL_AGC,
1042 g_param_spec_boolean ("experimental-agc", "Experimental AGC",
1043 "Enable or disable experimental automatic gain control.",
1044 FALSE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
1045 G_PARAM_CONSTRUCT)));
1046
1047 g_object_class_install_property (gobject_class,
1048 PROP_EXTENDED_FILTER,
1049 g_param_spec_boolean ("extended-filter", "Extended Filter",
1050 "Enable or disable the extended filter.",
1051 TRUE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
1052 G_PARAM_CONSTRUCT)));
1053
1054 g_object_class_install_property (gobject_class,
1055 PROP_DELAY_AGNOSTIC,
1056 g_param_spec_boolean ("delay-agnostic", "Delay Agnostic",
1057 "Enable or disable the delay agnostic mode.",
1058 FALSE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
1059 G_PARAM_CONSTRUCT)));
1060
1061 g_object_class_install_property (gobject_class,
1062 PROP_TARGET_LEVEL_DBFS,
1063 g_param_spec_int ("target-level-dbfs", "Target Level dBFS",
1064 "Sets the target peak |level| (or envelope) of the gain control in "
1065 "dBFS (decibels from digital full-scale).",
1066 0, 31, DEFAULT_TARGET_LEVEL_DBFS, (GParamFlags) (G_PARAM_READWRITE |
1067 G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)));
1068
1069 g_object_class_install_property (gobject_class,
1070 PROP_COMPRESSION_GAIN_DB,
1071 g_param_spec_int ("compression-gain-db", "Compression Gain dB",
1072 "Sets the maximum |gain| the digital compression stage may apply, "
1073 "in dB.",
1074 0, 90, DEFAULT_COMPRESSION_GAIN_DB, (GParamFlags) (G_PARAM_READWRITE |
1075 G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)));
1076
1077 g_object_class_install_property (gobject_class,
1078 PROP_STARTUP_MIN_VOLUME,
1079 g_param_spec_int ("startup-min-volume", "Startup Minimum Volume",
1080 "At startup the experimental AGC moves the microphone volume up to "
1081 "|startup_min_volume| if the current microphone volume is set too "
1082 "low. No effect if experimental-agc isn't enabled.",
1083 12, 255, DEFAULT_STARTUP_MIN_VOLUME, (GParamFlags) (G_PARAM_READWRITE |
1084 G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)));
1085
1086 g_object_class_install_property (gobject_class,
1087 PROP_LIMITER,
1088 g_param_spec_boolean ("limiter", "Limiter",
1089 "When enabled, the compression stage will hard limit the signal to "
1090 "the target level. Otherwise, the signal will be compressed but not "
1091 "limited above the target level.",
1092 DEFAULT_LIMITER, (GParamFlags) (G_PARAM_READWRITE |
1093 G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)));
1094
1095 g_object_class_install_property (gobject_class,
1096 PROP_GAIN_CONTROL_MODE,
1097 g_param_spec_enum ("gain-control-mode", "Gain Control Mode",
1098 "Controls the mode of the compression stage",
1099 GST_TYPE_WEBRTC_GAIN_CONTROL_MODE,
1100 DEFAULT_GAIN_CONTROL_MODE,
1101 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
1102 G_PARAM_CONSTRUCT)));
1103
1104 g_object_class_install_property (gobject_class,
1105 PROP_VOICE_DETECTION,
1106 g_param_spec_boolean ("voice-detection", "Voice Detection",
1107 "Enable or disable the voice activity detector",
1108 DEFAULT_VOICE_DETECTION, (GParamFlags) (G_PARAM_READWRITE |
1109 G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)));
1110
1111 g_object_class_install_property (gobject_class,
1112 PROP_VOICE_DETECTION_FRAME_SIZE_MS,
1113 g_param_spec_int ("voice-detection-frame-size-ms",
1114 "Voice Detection Frame Size Milliseconds",
1115 "Sets the |size| of the frames in ms on which the VAD will operate. "
1116 "Larger frames will improve detection accuracy, but reduce the "
1117 "frequency of updates",
1118 10, 30, DEFAULT_VOICE_DETECTION_FRAME_SIZE_MS,
1119 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
1120 G_PARAM_CONSTRUCT)));
1121
1122 g_object_class_install_property (gobject_class,
1123 PROP_VOICE_DETECTION_LIKELIHOOD,
1124 g_param_spec_enum ("voice-detection-likelihood",
1125 "Voice Detection Likelihood",
1126 "Specifies the likelihood that a frame will be declared to contain "
1127 "voice.",
1128 GST_TYPE_WEBRTC_VOICE_DETECTION_LIKELIHOOD,
1129 DEFAULT_VOICE_DETECTION_LIKELIHOOD,
1130 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
1131 G_PARAM_CONSTRUCT)));
1132
1133 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_GAIN_CONTROL_MODE, (GstPluginAPIFlags) 0);
1134 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_NOISE_SUPPRESSION_LEVEL, (GstPluginAPIFlags) 0);
1135 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_ECHO_SUPPRESSION_LEVEL, (GstPluginAPIFlags) 0);
1136 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_VOICE_DETECTION_LIKELIHOOD, (GstPluginAPIFlags) 0);
1137 }
1138