~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/pjmedia/src/pjmedia/sound_port.c

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2015-01-07 14:51:16 UTC
  • mfrom: (4.3.5 sid)
  • Revision ID: package-import@ubuntu.com-20150107145116-yxnafinf4lrdvrmx
Tags: 1.4.1-0.1ubuntu1
* Merge with Debian, remaining changes:
 - Drop soprano, nepomuk build-dep
* Drop ubuntu patches, now upstream

Show diffs side-by-side

added added

removed removed

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