1 /*
2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2012  Belledonne Communications, Grenoble, France
4 
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "mediastreamer2/msfilter.h"
25 #include "mediastreamer2/msticker.h"
26 #ifdef BUILD_AEC
27 #include "echo_cancellation.h"
28 #include "aec_splitting_filter.h"
29 #endif
30 #ifdef BUILD_AECM
31 #include "echo_control_mobile.h"
32 #endif
33 #include "ortp/b64.h"
34 
35 #ifdef _WIN32
36 #include <malloc.h> /* for alloca */
37 #endif
38 
39 //#define EC_DUMP 1
40 #ifdef ANDROID
41 #define EC_DUMP_PREFIX "/sdcard"
42 #else
43 #define EC_DUMP_PREFIX "/dynamic/tests"
44 #endif
45 
46 #include "mediastreamer2/flowcontrol.h"
47 
48 static const float smooth_factor = 0.05f;
49 static const int framesize = 80;
50 
51 
52 typedef enum _WebRTCAECType {
53 	WebRTCAECTypeNormal,
54 	WebRTCAECTypeMobile
55 } WebRTCAECType;
56 
57 typedef struct WebRTCAECState {
58 	void *aecInst;
59 	MSBufferizer delayed_ref;
60 	MSFlowControlledBufferizer ref;
61 	MSBufferizer echo;
62 	int framesize;
63 	int samplerate;
64 	int delay_ms;
65 	int nominal_ref_samples;
66 	char *state_str;
67 #ifdef EC_DUMP
68 	FILE *echofile;
69 	FILE *reffile;
70 	FILE *cleanfile;
71 #endif
72 	bool_t echostarted;
73 	bool_t bypass_mode;
74 	bool_t using_zeroes;
75 	WebRTCAECType aec_type;
76 #ifdef BUILD_AEC
77 	MSWebRtcAecSplittingFilter *splitting_filter;
78 #endif
79 } WebRTCAECState;
80 
webrtc_aecgeneric_init(MSFilter * f,WebRTCAECType aec_type)81 static void webrtc_aecgeneric_init(MSFilter *f, WebRTCAECType aec_type) {
82 	WebRTCAECState *s = (WebRTCAECState *) ms_new0(WebRTCAECState, 1);
83 
84 	s->samplerate = 8000;
85 	ms_bufferizer_init(&s->delayed_ref);
86 	ms_bufferizer_init(&s->echo);
87 	ms_flow_controlled_bufferizer_init(&s->ref, f, s->samplerate, 1);
88 	s->delay_ms = 0;
89 	s->aecInst = NULL;
90 	s->framesize = framesize;
91 	s->state_str = NULL;
92 	s->using_zeroes = FALSE;
93 	s->echostarted = FALSE;
94 	s->bypass_mode = FALSE;
95 	s->aec_type = aec_type;
96 
97 #ifdef EC_DUMP
98 	{
99 		char *fname = ms_strdup_printf("%s/mswebrtcaec-%p-echo.raw", EC_DUMP_PREFIX, f);
100 		s->echofile = fopen(fname, "w");
101 		ms_free(fname);
102 		fname = ms_strdup_printf("%s/mswebrtcaec-%p-ref.raw", EC_DUMP_PREFIX, f);
103 		s->reffile = fopen(fname, "w");
104 		ms_free(fname);
105 		fname = ms_strdup_printf("%s/mswebrtcaec-%p-clean.raw", EC_DUMP_PREFIX, f);
106 		s->cleanfile = fopen(fname, "w");
107 		ms_free(fname);
108 	}
109 #endif
110 
111 	f->data = s;
112 }
113 
114 #ifdef BUILD_AEC
webrtc_aec_init(MSFilter * f)115 static void webrtc_aec_init(MSFilter *f) {
116 	webrtc_aecgeneric_init(f, WebRTCAECTypeNormal);
117 }
118 #endif
119 
120 #ifdef BUILD_AECM
webrtc_aecm_init(MSFilter * f)121 static void webrtc_aecm_init(MSFilter *f) {
122 	webrtc_aecgeneric_init(f, WebRTCAECTypeMobile);
123 }
124 #endif
125 
webrtc_aec_uninit(MSFilter * f)126 static void webrtc_aec_uninit(MSFilter *f) {
127 	WebRTCAECState *s = (WebRTCAECState *) f->data;
128 	if (s->state_str) ms_free(s->state_str);
129 	ms_bufferizer_uninit(&s->delayed_ref);
130 #ifdef EC_DUMP
131 	if (s->echofile)
132 		fclose(s->echofile);
133 	if (s->reffile)
134 		fclose(s->reffile);
135 #endif
136 	ms_free(s);
137 }
138 
configure_flow_controlled_bufferizer(WebRTCAECState * s)139 static void configure_flow_controlled_bufferizer(WebRTCAECState *s) {
140 	ms_flow_controlled_bufferizer_set_samplerate(&s->ref, s->samplerate);
141 	ms_flow_controlled_bufferizer_set_max_size_ms(&s->ref, s->delay_ms);
142 	ms_flow_controlled_bufferizer_set_granularity_ms(&s->ref, (s->framesize * 1000) / s->samplerate);
143 }
144 
webrtc_aec_preprocess(MSFilter * f)145 static void webrtc_aec_preprocess(MSFilter *f) {
146 	WebRTCAECState *s = (WebRTCAECState *) f->data;
147 #ifdef BUILD_AEC
148 	AecConfig aec_config;
149 #endif
150 #ifdef BUILD_AECM
151 	AecmConfig aecm_config;
152 	int error_code;
153 #endif
154 	int delay_samples = 0;
155 	mblk_t *m;
156 
157 	s->echostarted = FALSE;
158 	delay_samples = s->delay_ms * s->samplerate / 1000;
159 	s->framesize=(framesize*s->samplerate)/8000;
160 	ms_message("Initializing WebRTC echo canceler with framesize=%i, delay_ms=%i, delay_samples=%i", s->framesize, s->delay_ms, delay_samples);
161 	configure_flow_controlled_bufferizer(s);
162 
163 #ifdef BUILD_AEC
164 	if (s->aec_type == WebRTCAECTypeNormal) {
165 		if ((s->aecInst = WebRtcAec_Create()) == NULL) {
166 			s->bypass_mode = TRUE;
167 			ms_error("WebRtcAec_Create(): error, entering bypass mode");
168 			return;
169 		}
170 		if ((WebRtcAec_Init(s->aecInst, MIN(48000, s->samplerate), s->samplerate)) < 0) {
171 			ms_error("WebRtcAec_Init(): WebRTC echo canceller does not support %d samplerate", s->samplerate);
172 			s->bypass_mode = TRUE;
173 			ms_error("Entering bypass mode");
174 			return;
175 		}
176 		aec_config.nlpMode = kAecNlpAggressive;
177 		aec_config.skewMode = kAecFalse;
178 		aec_config.metricsMode = kAecFalse;
179 		aec_config.delay_logging = kAecFalse;
180 		if (WebRtcAec_set_config(s->aecInst, aec_config) != 0) {
181 			ms_error("WebRtcAec_set_config(): failed.");
182 		}
183 	}
184 #endif
185 #ifdef BUILD_AECM
186 	if (s->aec_type == WebRTCAECTypeMobile) {
187 		if ((s->aecInst = WebRtcAecm_Create()) == NULL) {
188 			s->bypass_mode = TRUE;
189 			ms_error("WebRtcAecm_Create(): error, entering bypass mode");
190 			return;
191 		}
192 		if ((error_code = WebRtcAecm_Init(s->aecInst, s->samplerate)) < 0) {
193 			if (error_code == AECM_BAD_PARAMETER_ERROR) {
194 				ms_error("WebRtcAecm_Init(): WebRTC echo canceller does not support %d samplerate", s->samplerate);
195 			}
196 			s->bypass_mode = TRUE;
197 			ms_error("Entering bypass mode");
198 			return;
199 		}
200 		aecm_config.cngMode = TRUE;
201 		aecm_config.echoMode = 3;
202 		if (WebRtcAecm_set_config(s->aecInst, aecm_config)!=0){
203 			ms_error("WebRtcAecm_set_config(): failed.");
204 		}
205 	}
206 #endif
207 
208 	/* fill with zeroes for the time of the delay*/
209 	m = allocb(delay_samples * 2, 0);
210 	m->b_wptr += delay_samples * 2;
211 	ms_bufferizer_put(&s->delayed_ref, m);
212 	s->nominal_ref_samples = delay_samples;
213 }
214 
215 /*	inputs[0]= reference signal from far end (sent to soundcard)
216  *	inputs[1]= near speech & echo signal (read from soundcard)
217  *	outputs[0]=  is a copy of inputs[0] to be sent to soundcard
218  *	outputs[1]=  near end speech, echo removed - towards far end
219 */
webrtc_aec_process(MSFilter * f)220 static void webrtc_aec_process(MSFilter *f) {
221 	WebRTCAECState *s = (WebRTCAECState *) f->data;
222 	int nbytes = s->framesize * sizeof(int16_t);
223 	mblk_t *refm;
224 	int16_t *ref, *echo;
225 	int nbands = 1;
226 	int bandsize = s->framesize;
227 
228 	if (s->bypass_mode) {
229 		while ((refm = ms_queue_get(f->inputs[0])) != NULL) {
230 			ms_queue_put(f->outputs[0], refm);
231 		}
232 		while ((refm = ms_queue_get(f->inputs[1])) != NULL) {
233 			ms_queue_put(f->outputs[1], refm);
234 		}
235 		return;
236 	}
237 
238 	if (f->inputs[0] != NULL) {
239 		if (s->echostarted) {
240 			while ((refm = ms_queue_get(f->inputs[0])) != NULL) {
241 				mblk_t *cp=dupmsg(refm);
242 				ms_bufferizer_put(&s->delayed_ref,cp);
243 				ms_flow_controlled_bufferizer_put(&s->ref,refm);
244 			}
245 		} else {
246 			ms_warning("Getting reference signal but no echo to synchronize on.");
247 			ms_queue_flush(f->inputs[0]);
248 		}
249 	}
250 
251 	ms_bufferizer_put_from_queue(&s->echo, f->inputs[1]);
252 
253 	ref = (int16_t *) alloca(nbytes);
254 	echo = (int16_t *) alloca(nbytes);
255 #ifdef BUILD_AEC
256 	if (s->aec_type == WebRTCAECTypeNormal) {
257 		if (s->samplerate > 16000) {
258 			nbands = s->samplerate / 16000;
259 			bandsize = 160;
260 		}
261 		if (!s->splitting_filter) {
262 			s->splitting_filter = mswebrtc_aec_splitting_filter_create(nbands, bandsize);
263 		}
264 	}
265 #endif
266 	while (ms_bufferizer_read(&s->echo, (uint8_t *)echo, (size_t)nbytes) >= (size_t)nbytes) {
267 		mblk_t *oecho = allocb(nbytes, 0);
268 		int avail;
269 		int avail_samples;
270 
271 		if (!s->echostarted) s->echostarted = TRUE;
272 		if ((avail = ms_bufferizer_get_avail(&s->delayed_ref)) < ((s->nominal_ref_samples * 2) + nbytes)) {
273 			/*we don't have enough to read in a reference signal buffer, inject silence instead*/
274 			refm = allocb(nbytes, 0);
275 			memset(refm->b_wptr, 0, nbytes);
276 			refm->b_wptr += nbytes;
277 			ms_bufferizer_put(&s->delayed_ref, refm);
278 			ms_queue_put(f->outputs[0], dupmsg(refm));
279 			if (!s->using_zeroes) {
280 				ms_warning("Not enough ref samples, using zeroes");
281 				s->using_zeroes = TRUE;
282 			}
283 		} else {
284 			if (s->using_zeroes) {
285 				ms_message("Samples are back.");
286 				s->using_zeroes = FALSE;
287 			}
288 			/* read from our no-delay buffer and output */
289 			refm = allocb(nbytes, 0);
290 			if (ms_flow_controlled_bufferizer_read(&s->ref, refm->b_wptr, nbytes) == 0) {
291 				ms_fatal("Should never happen");
292 			}
293 			refm->b_wptr += nbytes;
294 			ms_queue_put(f->outputs[0], refm);
295 		}
296 
297 		/*now read a valid buffer of delayed ref samples*/
298 		if (ms_bufferizer_read(&s->delayed_ref, (uint8_t *)ref, nbytes) == 0) {
299 			ms_fatal("Should never happen");
300 		}
301 		avail -= nbytes;
302 		avail_samples = avail / 2;
303 
304 #ifdef EC_DUMP
305 		if (s->reffile)
306 			fwrite(ref, nbytes, 1, s->reffile);
307 		if (s->echofile)
308 			fwrite(echo, nbytes, 1, s->echofile);
309 #endif
310 #ifdef BUILD_AEC
311 		if (s->aec_type == WebRTCAECTypeNormal) {
312 			mswebrtc_aec_splitting_filter_analysis(s->splitting_filter, ref, echo);
313 			if (WebRtcAec_BufferFarend(s->aecInst,
314 					mswebrtc_aec_splitting_filter_get_ref(s->splitting_filter),
315 					(size_t)mswebrtc_aec_splitting_filter_get_bandsize(s->splitting_filter)) != 0)
316 				ms_error("WebRtcAec_BufferFarend() failed.");
317 			if (WebRtcAec_Process(s->aecInst,
318 					mswebrtc_aec_splitting_filter_get_echo_bands(s->splitting_filter),
319 					mswebrtc_aec_splitting_filter_get_number_of_bands(s->splitting_filter),
320 					mswebrtc_aec_splitting_filter_get_output_bands(s->splitting_filter),
321 					(size_t)mswebrtc_aec_splitting_filter_get_bandsize(s->splitting_filter), 0, 0) != 0)
322 				ms_error("WebRtcAec_Process() failed.");
323 			mswebrtc_aec_splitting_filter_synthesis(s->splitting_filter, (int16_t *)oecho->b_wptr);
324 		}
325 #endif
326 #ifdef BUILD_AECM
327 		if (s->aec_type == WebRTCAECTypeMobile) {
328 			if (WebRtcAecm_BufferFarend(s->aecInst, ref, (size_t)s->framesize) != 0)
329 				ms_error("WebRtcAecm_BufferFarend() failed.");
330 			if (WebRtcAecm_Process(s->aecInst, echo, NULL, (int16_t *)oecho->b_wptr, (size_t)s->framesize, 0) != 0)
331 				ms_error("WebRtcAecm_Process() failed.");
332 		}
333 #endif
334 #ifdef EC_DUMP
335 		if (s->cleanfile)
336 			fwrite(oecho->b_wptr, nbytes, 1, s->cleanfile);
337 #endif
338 		oecho->b_wptr += nbytes;
339 		ms_queue_put(f->outputs[1], oecho);
340 	}
341 }
342 
webrtc_aec_postprocess(MSFilter * f)343 static void webrtc_aec_postprocess(MSFilter *f) {
344 	WebRTCAECState *s = (WebRTCAECState *) f->data;
345 
346 	ms_bufferizer_flush(&s->delayed_ref);
347 	ms_bufferizer_flush(&s->echo);
348 	ms_flow_controlled_bufferizer_flush(&s->ref);
349 #ifdef BUILD_AEC
350 	if (s->splitting_filter) {
351 		mswebrtc_aec_splitting_filter_destroy(s->splitting_filter);
352 		s->splitting_filter = NULL;
353 	}
354 #endif
355 	if (s->aecInst != NULL) {
356 #ifdef BUILD_AEC
357 		if (s->aec_type == WebRTCAECTypeNormal) {
358 			WebRtcAec_Free(s->aecInst);
359 		}
360 #endif
361 #ifdef BUILD_AECM
362 		if (s->aec_type == WebRTCAECTypeMobile) {
363 			WebRtcAecm_Free(s->aecInst);
364 		}
365 #endif
366 		s->aecInst = NULL;
367 	}
368 }
369 
webrtc_aec_set_sr(MSFilter * f,void * arg)370 static int webrtc_aec_set_sr(MSFilter *f, void *arg) {
371 	WebRTCAECState *s = (WebRTCAECState *) f->data;
372 	int requested_sr = *(int *) arg;
373 	int sr = requested_sr;
374 
375 	if ((requested_sr != 8000) && (requested_sr != 16000) && (requested_sr != 32000) && (requested_sr != 48000)) {
376 		if ((s->aec_type == WebRTCAECTypeNormal) && (requested_sr > 48000)) {
377 			sr = 48000;
378 		} else if ((s->aec_type == WebRTCAECTypeNormal) && (requested_sr > 32000)) {
379 			sr = 32000;
380 		} else if (requested_sr > 16000) {
381 			sr = 16000;
382 		} else {
383 			sr = 8000;
384 		}
385 		ms_message("Webrtc aec does not support sampling rate %i, using %i instead", requested_sr, sr);
386 	}
387 	s->samplerate = sr;
388 	configure_flow_controlled_bufferizer(s);
389 	return 0;
390 }
391 
webrtc_aec_get_sr(MSFilter * f,void * arg)392 static int webrtc_aec_get_sr(MSFilter *f, void *arg) {
393 	WebRTCAECState *s = (WebRTCAECState *) f->data;
394 	*(int *) arg=s->samplerate;
395 	return 0;
396 }
397 
webrtc_aec_set_framesize(MSFilter * f,void * arg)398 static int webrtc_aec_set_framesize(MSFilter *f, void *arg) {
399 	/* Do nothing because the WebRTC echo canceller only accept specific values: 80 and 160. We use 80 at 8khz, and 160 at 16khz */
400 	return 0;
401 }
402 
webrtc_aec_set_delay(MSFilter * f,void * arg)403 static int webrtc_aec_set_delay(MSFilter *f, void *arg) {
404 	WebRTCAECState *s = (WebRTCAECState *) f->data;
405 	s->delay_ms = *(int *) arg;
406 	configure_flow_controlled_bufferizer(s);
407 	return 0;
408 }
409 
webrtc_aec_set_tail_length(MSFilter * f,void * arg)410 static int webrtc_aec_set_tail_length(MSFilter *f, void *arg) {
411 	/* Do nothing because this is not needed by the WebRTC echo canceller. */
412 	return 0;
413 }
webrtc_aec_set_bypass_mode(MSFilter * f,void * arg)414 static int webrtc_aec_set_bypass_mode(MSFilter *f, void *arg) {
415 	WebRTCAECState *s = (WebRTCAECState *) f->data;
416 	s->bypass_mode = *(bool_t *) arg;
417 	ms_message("set EC bypass mode to [%i]", s->bypass_mode);
418 	return 0;
419 }
webrtc_aec_get_bypass_mode(MSFilter * f,void * arg)420 static int webrtc_aec_get_bypass_mode(MSFilter *f, void *arg) {
421 	WebRTCAECState *s = (WebRTCAECState *) f->data;
422 	*(bool_t *) arg = s->bypass_mode;
423 	return 0;
424 }
425 
webrtc_aec_set_state(MSFilter * f,void * arg)426 static int webrtc_aec_set_state(MSFilter *f, void *arg) {
427 	WebRTCAECState *s = (WebRTCAECState *) f->data;
428 	s->state_str = ms_strdup((const char *) arg);
429 	return 0;
430 }
431 
webrtc_aec_get_state(MSFilter * f,void * arg)432 static int webrtc_aec_get_state(MSFilter *f, void *arg) {
433 	WebRTCAECState *s = (WebRTCAECState *) f->data;
434 	*(char **) arg = s->state_str;
435 	return 0;
436 }
437 
438 static MSFilterMethod webrtc_aec_methods[] = {
439 	{	MS_FILTER_SET_SAMPLE_RATE		,	webrtc_aec_set_sr 		},
440 	{	MS_FILTER_GET_SAMPLE_RATE		,	webrtc_aec_get_sr 		},
441 	{	MS_ECHO_CANCELLER_SET_TAIL_LENGTH	,	webrtc_aec_set_tail_length	},
442 	{	MS_ECHO_CANCELLER_SET_DELAY		,	webrtc_aec_set_delay		},
443 	{	MS_ECHO_CANCELLER_SET_FRAMESIZE		,	webrtc_aec_set_framesize	},
444 	{	MS_ECHO_CANCELLER_SET_BYPASS_MODE	,	webrtc_aec_set_bypass_mode	},
445 	{	MS_ECHO_CANCELLER_GET_BYPASS_MODE	,	webrtc_aec_get_bypass_mode	},
446 	{	MS_ECHO_CANCELLER_GET_STATE_STRING	,	webrtc_aec_get_state		},
447 	{	MS_ECHO_CANCELLER_SET_STATE_STRING	,	webrtc_aec_set_state		}
448 };
449 
450 
451 #ifdef BUILD_AEC
452 
453 #define MS_WEBRTC_AEC_NAME        "MSWebRTCAEC"
454 #define MS_WEBRTC_AEC_DESCRIPTION "Echo canceller using WebRTC library."
455 #define MS_WEBRTC_AEC_CATEGORY    MS_FILTER_OTHER
456 #define MS_WEBRTC_AEC_ENC_FMT     NULL
457 #define MS_WEBRTC_AEC_NINPUTS     2
458 #define MS_WEBRTC_AEC_NOUTPUTS    2
459 #define MS_WEBRTC_AEC_FLAGS       0
460 
461 #ifdef _MSC_VER
462 
463 MSFilterDesc ms_webrtc_aec_desc = {
464 	MS_FILTER_PLUGIN_ID,
465 	MS_WEBRTC_AEC_NAME,
466 	MS_WEBRTC_AEC_DESCRIPTION,
467 	MS_WEBRTC_AEC_CATEGORY,
468 	MS_WEBRTC_AEC_ENC_FMT,
469 	MS_WEBRTC_AEC_NINPUTS,
470 	MS_WEBRTC_AEC_NOUTPUTS,
471 	webrtc_aec_init,
472 	webrtc_aec_preprocess,
473 	webrtc_aec_process,
474 	webrtc_aec_postprocess,
475 	webrtc_aec_uninit,
476 	webrtc_aec_methods,
477 	MS_WEBRTC_AEC_FLAGS
478 };
479 
480 #else
481 
482 MSFilterDesc ms_webrtc_aec_desc = {
483 	.id = MS_FILTER_PLUGIN_ID,
484 	.name = MS_WEBRTC_AEC_NAME,
485 	.text = MS_WEBRTC_AEC_DESCRIPTION,
486 	.category = MS_WEBRTC_AEC_CATEGORY,
487 	.enc_fmt = MS_WEBRTC_AEC_ENC_FMT,
488 	.ninputs = MS_WEBRTC_AEC_NINPUTS,
489 	.noutputs = MS_WEBRTC_AEC_NOUTPUTS,
490 	.init = webrtc_aec_init,
491 	.preprocess = webrtc_aec_preprocess,
492 	.process = webrtc_aec_process,
493 	.postprocess = webrtc_aec_postprocess,
494 	.uninit = webrtc_aec_uninit,
495 	.methods = webrtc_aec_methods,
496 	.flags = MS_WEBRTC_AEC_FLAGS
497 };
498 
499 #endif
500 
501 MS_FILTER_DESC_EXPORT(ms_webrtc_aec_desc)
502 
503 #endif /* BUILD_AEC */
504 
505 #ifdef BUILD_AECM
506 
507 #define MS_WEBRTC_AECM_NAME        "MSWebRTCAECM"
508 #define MS_WEBRTC_AECM_DESCRIPTION "Echo canceller for mobile using WebRTC library."
509 #define MS_WEBRTC_AECM_CATEGORY    MS_FILTER_OTHER
510 #define MS_WEBRTC_AECM_ENC_FMT     NULL
511 #define MS_WEBRTC_AECM_NINPUTS     2
512 #define MS_WEBRTC_AECM_NOUTPUTS    2
513 #define MS_WEBRTC_AECM_FLAGS       0
514 
515 #ifdef _MSC_VER
516 
517 MSFilterDesc ms_webrtc_aecm_desc = {
518 	MS_FILTER_PLUGIN_ID,
519 	MS_WEBRTC_AECM_NAME,
520 	MS_WEBRTC_AECM_DESCRIPTION,
521 	MS_WEBRTC_AECM_CATEGORY,
522 	MS_WEBRTC_AECM_ENC_FMT,
523 	MS_WEBRTC_AECM_NINPUTS,
524 	MS_WEBRTC_AECM_NOUTPUTS,
525 	webrtc_aecm_init,
526 	webrtc_aec_preprocess,
527 	webrtc_aec_process,
528 	webrtc_aec_postprocess,
529 	webrtc_aec_uninit,
530 	webrtc_aec_methods,
531 	MS_WEBRTC_AECM_FLAGS
532 };
533 
534 #else
535 
536 MSFilterDesc ms_webrtc_aecm_desc = {
537 	.id = MS_FILTER_PLUGIN_ID,
538 	.name = MS_WEBRTC_AECM_NAME,
539 	.text = MS_WEBRTC_AECM_DESCRIPTION,
540 	.category = MS_WEBRTC_AECM_CATEGORY,
541 	.enc_fmt = MS_WEBRTC_AECM_ENC_FMT,
542 	.ninputs = MS_WEBRTC_AECM_NINPUTS,
543 	.noutputs = MS_WEBRTC_AECM_NOUTPUTS,
544 	.init = webrtc_aecm_init,
545 	.preprocess = webrtc_aec_preprocess,
546 	.process = webrtc_aec_process,
547 	.postprocess = webrtc_aec_postprocess,
548 	.uninit = webrtc_aec_uninit,
549 	.methods = webrtc_aec_methods,
550 	.flags = MS_WEBRTC_AECM_FLAGS
551 };
552 
553 #endif
554 
555 MS_FILTER_DESC_EXPORT(ms_webrtc_aecm_desc)
556 
557 #endif /* BUILD_AECM */
558