• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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