1 /* $Id$ */
2 /*
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <pjmedia/sound_port.h>
21 #include <pjmedia/alaw_ulaw.h>
22 #include <pjmedia/delaybuf.h>
23 #include <pjmedia/echo.h>
24 #include <pjmedia/errno.h>
25 #include <pj/assert.h>
26 #include <pj/log.h>
27 #include <pj/rand.h>
28 #include <pj/string.h>	    /* pj_memset() */
29 
30 #define AEC_TAIL	    128	    /* default AEC length in ms */
31 #define AEC_SUSPEND_LIMIT   5	    /* seconds of no activity	*/
32 
33 #define THIS_FILE	    "sound_port.c"
34 
35 //#define TEST_OVERFLOW_UNDERFLOW
36 
37 struct pjmedia_snd_port
38 {
39     int			 rec_id;
40     int			 play_id;
41     pj_uint32_t		 aud_caps;
42     pjmedia_aud_param	 aud_param;
43     pjmedia_aud_stream	*aud_stream;
44     pjmedia_dir		 dir;
45     pjmedia_port	*port;
46 
47     pjmedia_clock_src    cap_clocksrc,
48                          play_clocksrc;
49 
50     unsigned		 clock_rate;
51     unsigned		 channel_count;
52     unsigned		 samples_per_frame;
53     unsigned		 bits_per_sample;
54     unsigned		 options;
55     unsigned		 prm_ec_options;
56 
57     /* software ec */
58     pjmedia_echo_state	*ec_state;
59     unsigned		 ec_options;
60     unsigned		 ec_tail_len;
61     pj_bool_t		 ec_suspended;
62     unsigned		 ec_suspend_count;
63     unsigned		 ec_suspend_limit;
64 
65     /* audio frame preview callbacks */
66     void		*user_data;
67     pjmedia_aud_play_cb  on_play_frame;
68     pjmedia_aud_rec_cb   on_rec_frame;
69 };
70 
71 /*
72  * The callback called by sound player when it needs more samples to be
73  * played.
74  */
play_cb(void * user_data,pjmedia_frame * frame)75 static pj_status_t play_cb(void *user_data, pjmedia_frame *frame)
76 {
77     pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
78     pjmedia_port *port;
79     const unsigned required_size = (unsigned)frame->size;
80     pj_status_t status;
81 
82     pjmedia_clock_src_update(&snd_port->play_clocksrc, &frame->timestamp);
83 
84     port = snd_port->port;
85     if (port == NULL)
86 	goto no_frame;
87 
88     status = pjmedia_port_get_frame(port, frame);
89     if (status != PJ_SUCCESS)
90 	goto no_frame;
91 
92     if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO)
93 	goto no_frame;
94 
95     /* Must supply the required samples */
96     pj_assert(frame->size == required_size);
97 
98     if (snd_port->ec_state) {
99 	if (snd_port->ec_suspended) {
100 	    snd_port->ec_suspended = PJ_FALSE;
101 	    //pjmedia_echo_state_reset(snd_port->ec_state);
102 	    PJ_LOG(4,(THIS_FILE, "EC activated"));
103 	}
104 	snd_port->ec_suspend_count = 0;
105 	pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf);
106     }
107 
108     /* Invoke preview callback */
109     if (snd_port->on_play_frame)
110 	(*snd_port->on_play_frame)(snd_port->user_data, frame);
111 
112     return PJ_SUCCESS;
113 
114 no_frame:
115     frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
116     frame->size = required_size;
117     pj_bzero(frame->buf, frame->size);
118 
119     if (snd_port->ec_state && !snd_port->ec_suspended) {
120 	++snd_port->ec_suspend_count;
121 	if (snd_port->ec_suspend_count > snd_port->ec_suspend_limit) {
122 	    snd_port->ec_suspended = PJ_TRUE;
123 	    PJ_LOG(4,(THIS_FILE, "EC suspended because of inactivity"));
124 	}
125 	if (snd_port->ec_state) {
126 	    /* To maintain correct delay in EC */
127 	    pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf);
128 	}
129     }
130 
131     /* Invoke preview callback */
132     if (snd_port->on_play_frame)
133 	(*snd_port->on_play_frame)(snd_port->user_data, frame);
134 
135     return PJ_SUCCESS;
136 }
137 
138 
139 /*
140  * The callback called by sound recorder when it has finished capturing a
141  * frame.
142  */
rec_cb(void * user_data,pjmedia_frame * frame)143 static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame)
144 {
145     pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
146     pjmedia_port *port;
147 
148     pjmedia_clock_src_update(&snd_port->cap_clocksrc, &frame->timestamp);
149 
150     /* Invoke preview callback */
151     if (snd_port->on_rec_frame)
152 	(*snd_port->on_rec_frame)(snd_port->user_data, frame);
153 
154     port = snd_port->port;
155     if (port == NULL)
156 	return PJ_SUCCESS;
157 
158     /* Cancel echo */
159     if (snd_port->ec_state && !snd_port->ec_suspended) {
160 	pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) frame->buf, 0);
161     }
162 
163     pjmedia_port_put_frame(port, frame);
164 
165 
166     return PJ_SUCCESS;
167 }
168 
169 /*
170  * The callback called by sound player when it needs more samples to be
171  * played. This version is for non-PCM data.
172  */
play_cb_ext(void * user_data,pjmedia_frame * frame)173 static pj_status_t play_cb_ext(void *user_data, pjmedia_frame *frame)
174 {
175     pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
176     pjmedia_port *port = snd_port->port;
177 
178     if (port == NULL) {
179 	frame->type = PJMEDIA_FRAME_TYPE_NONE;
180 	return PJ_SUCCESS;
181     }
182 
183     pjmedia_port_get_frame(port, frame);
184 
185     /* Invoke preview callback */
186     if (snd_port->on_play_frame)
187 	(*snd_port->on_play_frame)(snd_port->user_data, frame);
188 
189     return PJ_SUCCESS;
190 }
191 
192 
193 /*
194  * The callback called by sound recorder when it has finished capturing a
195  * frame. This version is for non-PCM data.
196  */
rec_cb_ext(void * user_data,pjmedia_frame * frame)197 static pj_status_t rec_cb_ext(void *user_data, pjmedia_frame *frame)
198 {
199     pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
200     pjmedia_port *port;
201 
202     /* Invoke preview callback */
203     if (snd_port->on_rec_frame)
204 	(*snd_port->on_rec_frame)(snd_port->user_data, frame);
205 
206     port = snd_port->port;
207     if (port == NULL)
208 	return PJ_SUCCESS;
209 
210     pjmedia_port_put_frame(port, frame);
211 
212     return PJ_SUCCESS;
213 }
214 
215 /* Initialize with default values (zero) */
pjmedia_snd_port_param_default(pjmedia_snd_port_param * prm)216 PJ_DEF(void) pjmedia_snd_port_param_default(pjmedia_snd_port_param *prm)
217 {
218     pj_bzero(prm, sizeof(*prm));
219 }
220 
221 /*
222  * Start the sound stream.
223  * This may be called even when the sound stream has already been started.
224  */
start_sound_device(pj_pool_t * pool,pjmedia_snd_port * snd_port)225 static pj_status_t start_sound_device( pj_pool_t *pool,
226 				       pjmedia_snd_port *snd_port )
227 {
228     pjmedia_aud_rec_cb snd_rec_cb;
229     pjmedia_aud_play_cb snd_play_cb;
230     pjmedia_aud_param param_copy;
231     pj_status_t status;
232 
233     /* Check if sound has been started. */
234     if (snd_port->aud_stream != NULL)
235 	return PJ_SUCCESS;
236 
237     PJ_ASSERT_RETURN(snd_port->dir == PJMEDIA_DIR_CAPTURE ||
238 		     snd_port->dir == PJMEDIA_DIR_PLAYBACK ||
239 		     snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK,
240 		     PJ_EBUG);
241 
242     /* Get device caps */
243     if ((snd_port->aud_param.dir & PJMEDIA_DIR_CAPTURE) ||
244         (snd_port->aud_param.dir & PJMEDIA_DIR_PLAYBACK))
245     {
246 	pjmedia_aud_dev_info dev_info;
247         pjmedia_aud_dev_index dev_id =
248             (snd_port->aud_param.dir & PJMEDIA_DIR_CAPTURE) ?
249             snd_port->aud_param.rec_id :
250             snd_port->aud_param.play_id;
251 
252 	status = pjmedia_aud_dev_get_info(dev_id, &dev_info);
253 	if (status != PJ_SUCCESS)
254 	    return status;
255 
256 	snd_port->aud_caps = dev_info.caps;
257     }
258 
259     /* Process EC settings */
260     pj_memcpy(&param_copy, &snd_port->aud_param, sizeof(param_copy));
261     if (param_copy.flags & PJMEDIA_AUD_DEV_CAP_EC) {
262 	/* EC is wanted */
263 	if ((snd_port->prm_ec_options & PJMEDIA_ECHO_USE_SW_ECHO) == 0 &&
264             (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC))
265         {
266 	    /* Device supports EC */
267 	    /* Nothing to do */
268 	} else {
269 	    /* Application wants to use software EC or device
270              * doesn't support EC, remove EC settings from
271 	     * device parameters
272 	     */
273 	    param_copy.flags &= ~(PJMEDIA_AUD_DEV_CAP_EC |
274 				  PJMEDIA_AUD_DEV_CAP_EC_TAIL);
275 	}
276     }
277 
278     /* Use different callback if format is not PCM */
279     if (snd_port->aud_param.ext_fmt.id == PJMEDIA_FORMAT_L16) {
280 	snd_rec_cb = &rec_cb;
281 	snd_play_cb = &play_cb;
282     } else {
283 	snd_rec_cb = &rec_cb_ext;
284 	snd_play_cb = &play_cb_ext;
285     }
286 
287     /* Open the device */
288     status = pjmedia_aud_stream_create(&param_copy,
289 				       snd_rec_cb,
290 				       snd_play_cb,
291 				       snd_port,
292 				       &snd_port->aud_stream);
293 
294     if (status != PJ_SUCCESS)
295 	return status;
296 
297     /* Inactivity limit before EC is suspended. */
298     snd_port->ec_suspend_limit = AEC_SUSPEND_LIMIT *
299 				 (snd_port->clock_rate /
300 				  snd_port->samples_per_frame);
301 
302     /* Create software EC if parameter specifies EC and
303      * (app specifically requests software EC or device
304      * doesn't support EC). Only do this if the format is PCM!
305      */
306     if ((snd_port->aud_param.flags & PJMEDIA_AUD_DEV_CAP_EC) &&
307 	((snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC)==0 ||
308          (snd_port->prm_ec_options & PJMEDIA_ECHO_USE_SW_ECHO) != 0) &&
309 	param_copy.ext_fmt.id == PJMEDIA_FORMAT_PCM)
310     {
311 	if ((snd_port->aud_param.flags & PJMEDIA_AUD_DEV_CAP_EC_TAIL)==0) {
312 	    snd_port->aud_param.flags |= PJMEDIA_AUD_DEV_CAP_EC_TAIL;
313 	    snd_port->aud_param.ec_tail_ms = AEC_TAIL;
314 	    PJ_LOG(4,(THIS_FILE, "AEC tail is set to default %u ms",
315 				 snd_port->aud_param.ec_tail_ms));
316 	}
317 
318 	status = pjmedia_snd_port_set_ec(snd_port, pool,
319 					 snd_port->aud_param.ec_tail_ms,
320 					 snd_port->prm_ec_options);
321 	if (status != PJ_SUCCESS) {
322 	    pjmedia_aud_stream_destroy(snd_port->aud_stream);
323 	    snd_port->aud_stream = NULL;
324 	    return status;
325 	}
326     }
327 
328     /* Start sound stream. */
329     if (!(snd_port->options & PJMEDIA_SND_PORT_NO_AUTO_START)) {
330 	status = pjmedia_aud_stream_start(snd_port->aud_stream);
331     }
332     if (status != PJ_SUCCESS) {
333 	pjmedia_aud_stream_destroy(snd_port->aud_stream);
334 	snd_port->aud_stream = NULL;
335 	return status;
336     }
337 
338     return PJ_SUCCESS;
339 }
340 
341 
342 /*
343  * Stop the sound device.
344  * This may be called even when there's no sound device in the port.
345  */
stop_sound_device(pjmedia_snd_port * snd_port)346 static pj_status_t stop_sound_device( pjmedia_snd_port *snd_port )
347 {
348     /* Check if we have sound stream device. */
349     if (snd_port->aud_stream) {
350 	pjmedia_aud_stream_stop(snd_port->aud_stream);
351 	pjmedia_aud_stream_destroy(snd_port->aud_stream);
352 	snd_port->aud_stream = NULL;
353     }
354 
355     /* Destroy AEC */
356     if (snd_port->ec_state) {
357 	pjmedia_echo_destroy(snd_port->ec_state);
358 	snd_port->ec_state = NULL;
359     }
360 
361     return PJ_SUCCESS;
362 }
363 
364 
365 /*
366  * Create bidirectional port.
367  */
pjmedia_snd_port_create(pj_pool_t * pool,int rec_id,int play_id,unsigned clock_rate,unsigned channel_count,unsigned samples_per_frame,unsigned bits_per_sample,unsigned options,pjmedia_snd_port ** p_port)368 PJ_DEF(pj_status_t) pjmedia_snd_port_create( pj_pool_t *pool,
369 					     int rec_id,
370 					     int play_id,
371 					     unsigned clock_rate,
372 					     unsigned channel_count,
373 					     unsigned samples_per_frame,
374 					     unsigned bits_per_sample,
375 					     unsigned options,
376 					     pjmedia_snd_port **p_port)
377 {
378     pjmedia_snd_port_param param;
379     pj_status_t status;
380 
381     pjmedia_snd_port_param_default(&param);
382 
383     /* Normalize rec_id & play_id */
384     if (rec_id < 0)
385 	rec_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
386     if (play_id < 0)
387 	play_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
388 
389     status = pjmedia_aud_dev_default_param(rec_id, &param.base);
390     if (status != PJ_SUCCESS)
391 	return status;
392 
393     param.base.dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
394     param.base.rec_id = rec_id;
395     param.base.play_id = play_id;
396     param.base.clock_rate = clock_rate;
397     param.base.channel_count = channel_count;
398     param.base.samples_per_frame = samples_per_frame;
399     param.base.bits_per_sample = bits_per_sample;
400     param.options = options;
401     param.ec_options = 0;
402 
403     return pjmedia_snd_port_create2(pool, &param, p_port);
404 }
405 
406 /*
407  * Create sound recorder AEC.
408  */
pjmedia_snd_port_create_rec(pj_pool_t * pool,int dev_id,unsigned clock_rate,unsigned channel_count,unsigned samples_per_frame,unsigned bits_per_sample,unsigned options,pjmedia_snd_port ** p_port)409 PJ_DEF(pj_status_t) pjmedia_snd_port_create_rec( pj_pool_t *pool,
410 						 int dev_id,
411 						 unsigned clock_rate,
412 						 unsigned channel_count,
413 						 unsigned samples_per_frame,
414 						 unsigned bits_per_sample,
415 						 unsigned options,
416 						 pjmedia_snd_port **p_port)
417 {
418     pjmedia_snd_port_param param;
419     pj_status_t status;
420 
421     pjmedia_snd_port_param_default(&param);
422 
423     /* Normalize dev_id */
424     if (dev_id < 0)
425 	dev_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
426 
427     status = pjmedia_aud_dev_default_param(dev_id, &param.base);
428     if (status != PJ_SUCCESS)
429 	return status;
430 
431     param.base.dir = PJMEDIA_DIR_CAPTURE;
432     param.base.rec_id = dev_id;
433     param.base.clock_rate = clock_rate;
434     param.base.channel_count = channel_count;
435     param.base.samples_per_frame = samples_per_frame;
436     param.base.bits_per_sample = bits_per_sample;
437     param.options = options;
438     param.ec_options = 0;
439 
440     return pjmedia_snd_port_create2(pool, &param, p_port);
441 }
442 
443 
444 /*
445  * Create sound player port.
446  */
pjmedia_snd_port_create_player(pj_pool_t * pool,int dev_id,unsigned clock_rate,unsigned channel_count,unsigned samples_per_frame,unsigned bits_per_sample,unsigned options,pjmedia_snd_port ** p_port)447 PJ_DEF(pj_status_t) pjmedia_snd_port_create_player( pj_pool_t *pool,
448 						    int dev_id,
449 						    unsigned clock_rate,
450 						    unsigned channel_count,
451 						    unsigned samples_per_frame,
452 						    unsigned bits_per_sample,
453 						    unsigned options,
454 						    pjmedia_snd_port **p_port)
455 {
456     pjmedia_snd_port_param param;
457     pj_status_t status;
458 
459     pjmedia_snd_port_param_default(&param);
460 
461     /* Normalize dev_id */
462     if (dev_id < 0)
463 	dev_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
464 
465     status = pjmedia_aud_dev_default_param(dev_id, &param.base);
466     if (status != PJ_SUCCESS)
467 	return status;
468 
469     param.base.dir = PJMEDIA_DIR_PLAYBACK;
470     param.base.play_id = dev_id;
471     param.base.clock_rate = clock_rate;
472     param.base.channel_count = channel_count;
473     param.base.samples_per_frame = samples_per_frame;
474     param.base.bits_per_sample = bits_per_sample;
475     param.options = options;
476     param.ec_options = 0;
477 
478     return pjmedia_snd_port_create2(pool, &param, p_port);
479 }
480 
481 
482 /*
483  * Create sound port.
484  */
pjmedia_snd_port_create2(pj_pool_t * pool,const pjmedia_snd_port_param * prm,pjmedia_snd_port ** p_port)485 PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool,
486 					     const pjmedia_snd_port_param *prm,
487 					     pjmedia_snd_port **p_port)
488 {
489     pjmedia_snd_port *snd_port;
490     pj_status_t status;
491     unsigned ptime_usec;
492 
493     PJ_ASSERT_RETURN(pool && prm && p_port, PJ_EINVAL);
494 
495     snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
496     PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM);
497 
498     snd_port->dir = prm->base.dir;
499     snd_port->rec_id = prm->base.rec_id;
500     snd_port->play_id = prm->base.play_id;
501     snd_port->clock_rate = prm->base.clock_rate;
502     snd_port->channel_count = prm->base.channel_count;
503     snd_port->samples_per_frame = prm->base.samples_per_frame;
504     snd_port->bits_per_sample = prm->base.bits_per_sample;
505     pj_memcpy(&snd_port->aud_param, &prm->base, sizeof(snd_port->aud_param));
506     snd_port->options = prm->options;
507     snd_port->prm_ec_options = prm->ec_options;
508     snd_port->user_data = prm->user_data;
509     snd_port->on_play_frame = prm->on_play_frame;
510     snd_port->on_rec_frame = prm->on_rec_frame;
511 
512     ptime_usec = prm->base.samples_per_frame * 1000 / prm->base.channel_count /
513                  prm->base.clock_rate * 1000;
514     pjmedia_clock_src_init(&snd_port->cap_clocksrc, PJMEDIA_TYPE_AUDIO,
515                            snd_port->clock_rate, ptime_usec);
516     pjmedia_clock_src_init(&snd_port->play_clocksrc, PJMEDIA_TYPE_AUDIO,
517                            snd_port->clock_rate, ptime_usec);
518 
519     /* Start sound device immediately.
520      * If there's no port connected, the sound callback will return
521      * empty signal.
522      */
523     status = start_sound_device( pool, snd_port );
524     if (status != PJ_SUCCESS) {
525 	pjmedia_snd_port_destroy(snd_port);
526 	return status;
527     }
528 
529     *p_port = snd_port;
530     return PJ_SUCCESS;
531 }
532 
533 
534 /*
535  * Destroy port (also destroys the sound device).
536  */
pjmedia_snd_port_destroy(pjmedia_snd_port * snd_port)537 PJ_DEF(pj_status_t) pjmedia_snd_port_destroy(pjmedia_snd_port *snd_port)
538 {
539     PJ_ASSERT_RETURN(snd_port, PJ_EINVAL);
540 
541     return stop_sound_device(snd_port);
542 }
543 
544 
545 /*
546  * Retrieve the sound stream associated by this sound device port.
547  */
pjmedia_snd_port_get_snd_stream(pjmedia_snd_port * snd_port)548 PJ_DEF(pjmedia_aud_stream*) pjmedia_snd_port_get_snd_stream(
549 						pjmedia_snd_port *snd_port)
550 {
551     PJ_ASSERT_RETURN(snd_port, NULL);
552     return snd_port->aud_stream;
553 }
554 
555 
556 /*
557  * Change EC settings.
558  */
pjmedia_snd_port_set_ec(pjmedia_snd_port * snd_port,pj_pool_t * pool,unsigned tail_ms,unsigned options)559 PJ_DEF(pj_status_t) pjmedia_snd_port_set_ec( pjmedia_snd_port *snd_port,
560 					     pj_pool_t *pool,
561 					     unsigned tail_ms,
562 					     unsigned options)
563 {
564     pjmedia_aud_param prm;
565     pj_status_t status;
566 
567     PJ_ASSERT_RETURN(snd_port &&
568                      ((snd_port->dir & PJMEDIA_DIR_CAPTURE)||
569                       (snd_port->dir & PJMEDIA_DIR_PLAYBACK)), PJ_EINVALIDOP);
570 
571     /* Determine whether we use device or software EC */
572     if ((snd_port->prm_ec_options & PJMEDIA_ECHO_USE_SW_ECHO) == 0 &&
573         (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC))
574     {
575 	/* We use device EC */
576 	pj_bool_t ec_enabled;
577 
578 	/* Query EC status */
579 	status = pjmedia_aud_stream_get_cap(snd_port->aud_stream,
580 					    PJMEDIA_AUD_DEV_CAP_EC,
581 					    &ec_enabled);
582 	if (status != PJ_SUCCESS)
583 	    return status;
584 
585 	if (tail_ms != 0) {
586 	    /* Change EC setting */
587 
588 	    if (!ec_enabled) {
589 		/* Enable EC first */
590 		pj_bool_t value = PJ_TRUE;
591 		status = pjmedia_aud_stream_set_cap(snd_port->aud_stream,
592 						    PJMEDIA_AUD_DEV_CAP_EC,
593 						    &value);
594 		if (status != PJ_SUCCESS)
595 		    return status;
596 	    }
597 
598 	    if ((snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC_TAIL)==0) {
599 		/* Device does not support setting EC tail */
600 		return PJMEDIA_EAUD_INVCAP;
601 	    }
602 
603 	    return pjmedia_aud_stream_set_cap(snd_port->aud_stream,
604 					      PJMEDIA_AUD_DEV_CAP_EC_TAIL,
605 					      &tail_ms);
606 
607 	} else if (ec_enabled) {
608 	    /* Disable EC */
609 	    pj_bool_t value = PJ_FALSE;
610 	    return pjmedia_aud_stream_set_cap(snd_port->aud_stream,
611 					      PJMEDIA_AUD_DEV_CAP_EC,
612 					      &value);
613 	} else {
614 	    /* Request to disable EC but EC has been disabled */
615 	    /* Do nothing */
616 	    return PJ_SUCCESS;
617 	}
618 
619     } else {
620 	/* We use software EC */
621 
622 	/* Check if there is change in parameters */
623 	if (tail_ms==snd_port->ec_tail_len && options==snd_port->ec_options) {
624 	    PJ_LOG(5,(THIS_FILE, "pjmedia_snd_port_set_ec() ignored, no "
625 				 "change in settings"));
626 	    return PJ_SUCCESS;
627 	}
628 
629 	status = pjmedia_aud_stream_get_param(snd_port->aud_stream, &prm);
630 	if (status != PJ_SUCCESS)
631 	    return status;
632 
633 	/* Audio stream must be in PCM format */
634 	PJ_ASSERT_RETURN(prm.ext_fmt.id == PJMEDIA_FORMAT_PCM,
635 			 PJ_EINVALIDOP);
636 
637 	/* Destroy AEC */
638 	if (snd_port->ec_state) {
639 	    pjmedia_echo_destroy(snd_port->ec_state);
640 	    snd_port->ec_state = NULL;
641 	}
642 
643 	if (tail_ms != 0) {
644 	    unsigned delay_ms;
645 
646 	    //No need to add input latency in the latency calculation,
647 	    //since actual input latency should be zero.
648 	    //delay_ms = (si.rec_latency + si.play_latency) * 1000 /
649 	    //	   snd_port->clock_rate;
650 	    /* Set EC latency to 3/4 of output latency to reduce the
651 	     * possibility of missing/late reference frame.
652 	     */
653 	    delay_ms = prm.output_latency_ms * 3/4;
654 	    status = pjmedia_echo_create2(pool, snd_port->clock_rate,
655 					  snd_port->channel_count,
656 					  snd_port->samples_per_frame,
657 					  tail_ms, delay_ms,
658 					  options, &snd_port->ec_state);
659 	    if (status != PJ_SUCCESS)
660 		snd_port->ec_state = NULL;
661 	    else
662 		snd_port->ec_suspended = PJ_FALSE;
663 	} else {
664 	    PJ_LOG(4,(THIS_FILE, "Echo canceller is now disabled in the "
665 				 "sound port"));
666 	    status = PJ_SUCCESS;
667 	}
668 
669 	snd_port->ec_options = options;
670 	snd_port->ec_tail_len = tail_ms;
671     }
672 
673     return status;
674 }
675 
676 
677 /* Get AEC tail length */
pjmedia_snd_port_get_ec_tail(pjmedia_snd_port * snd_port,unsigned * p_length)678 PJ_DEF(pj_status_t) pjmedia_snd_port_get_ec_tail( pjmedia_snd_port *snd_port,
679 						  unsigned *p_length)
680 {
681     PJ_ASSERT_RETURN(snd_port && p_length, PJ_EINVAL);
682 
683     /* Determine whether we use device or software EC */
684     if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC) {
685 	/* We use device EC */
686 	pj_bool_t ec_enabled;
687 	pj_status_t status;
688 
689 	/* Query EC status */
690 	status = pjmedia_aud_stream_get_cap(snd_port->aud_stream,
691 					    PJMEDIA_AUD_DEV_CAP_EC,
692 					    &ec_enabled);
693 	if (status != PJ_SUCCESS)
694 	    return status;
695 
696 	if (!ec_enabled) {
697 	    *p_length = 0;
698 	} else if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC_TAIL) {
699 	    /* Get device EC tail */
700 	    status = pjmedia_aud_stream_get_cap(snd_port->aud_stream,
701 						PJMEDIA_AUD_DEV_CAP_EC_TAIL,
702 						p_length);
703 	    if (status != PJ_SUCCESS)
704 		return status;
705 	} else {
706 	    /* Just use default */
707 	    *p_length = AEC_TAIL;
708 	}
709 
710     } else {
711 	/* We use software EC */
712 	*p_length =  snd_port->ec_state ? snd_port->ec_tail_len : 0;
713     }
714     return PJ_SUCCESS;
715 }
716 
717 
718 /*
719  * Get echo canceller statistics.
720  */
pjmedia_snd_port_get_ec_stat(pjmedia_snd_port * snd_port,pjmedia_echo_stat * p_stat)721 PJ_DEF(pj_status_t) pjmedia_snd_port_get_ec_stat( pjmedia_snd_port *snd_port,
722 						  pjmedia_echo_stat *p_stat)
723 {
724     PJ_ASSERT_RETURN(snd_port && p_stat, PJ_EINVAL);
725 
726     if (snd_port->ec_state) {
727     	return pjmedia_echo_get_stat(snd_port->ec_state, p_stat);
728     } else {
729     	return PJ_ENOTFOUND;
730     }
731 }
732 
733 
734 /*
735  * Get clock source.
736  */
737 PJ_DEF(pjmedia_clock_src *)
pjmedia_snd_port_get_clock_src(pjmedia_snd_port * snd_port,pjmedia_dir dir)738 pjmedia_snd_port_get_clock_src( pjmedia_snd_port *snd_port,
739                                 pjmedia_dir dir )
740 {
741     return (dir == PJMEDIA_DIR_CAPTURE? &snd_port->cap_clocksrc:
742             &snd_port->play_clocksrc);
743 }
744 
745 
746 /*
747  * Connect a port.
748  */
pjmedia_snd_port_connect(pjmedia_snd_port * snd_port,pjmedia_port * port)749 PJ_DEF(pj_status_t) pjmedia_snd_port_connect( pjmedia_snd_port *snd_port,
750 					      pjmedia_port *port)
751 {
752     pjmedia_audio_format_detail *afd;
753 
754     PJ_ASSERT_RETURN(snd_port && port, PJ_EINVAL);
755 
756     afd = pjmedia_format_get_audio_format_detail(&port->info.fmt, PJ_TRUE);
757 
758     /* Check that port has the same configuration as the sound device
759      * port.
760      */
761     if (afd->clock_rate != snd_port->clock_rate)
762 	return PJMEDIA_ENCCLOCKRATE;
763 
764     if (PJMEDIA_AFD_SPF(afd) != snd_port->samples_per_frame)
765 	return PJMEDIA_ENCSAMPLESPFRAME;
766 
767     if (afd->channel_count != snd_port->channel_count)
768 	return PJMEDIA_ENCCHANNEL;
769 
770     if (afd->bits_per_sample != snd_port->bits_per_sample)
771 	return PJMEDIA_ENCBITS;
772 
773     /* Port is okay. */
774     snd_port->port = port;
775     return PJ_SUCCESS;
776 }
777 
778 
779 /*
780  * Get the connected port.
781  */
pjmedia_snd_port_get_port(pjmedia_snd_port * snd_port)782 PJ_DEF(pjmedia_port*) pjmedia_snd_port_get_port(pjmedia_snd_port *snd_port)
783 {
784     PJ_ASSERT_RETURN(snd_port, NULL);
785     return snd_port->port;
786 }
787 
788 
789 /*
790  * Disconnect port.
791  */
pjmedia_snd_port_disconnect(pjmedia_snd_port * snd_port)792 PJ_DEF(pj_status_t) pjmedia_snd_port_disconnect(pjmedia_snd_port *snd_port)
793 {
794     PJ_ASSERT_RETURN(snd_port, PJ_EINVAL);
795 
796     snd_port->port = NULL;
797 
798     return PJ_SUCCESS;
799 }
800 
801 
802