~ubuntu-branches/ubuntu/trusty/sflphone/trusty

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (4.3.4 sid)
  • Revision ID: package-import@ubuntu.com-20140128182336-jrsv0k9u6cawc068
Tags: 1.3.0-1
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: conf_switch.c 4122 2012-05-14 11:04:46Z 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/conference.h>
 
21
#include <pjmedia/alaw_ulaw.h>
 
22
#include <pjmedia/errno.h>
 
23
#include <pjmedia/port.h>
 
24
#include <pjmedia/silencedet.h>
 
25
#include <pjmedia/sound_port.h>
 
26
#include <pj/array.h>
 
27
#include <pj/assert.h>
 
28
#include <pj/log.h>
 
29
#include <pj/math.h>
 
30
#include <pj/pool.h>
 
31
#include <pj/string.h>
 
32
 
 
33
#if defined(PJMEDIA_CONF_USE_SWITCH_BOARD) && PJMEDIA_CONF_USE_SWITCH_BOARD!=0
 
34
 
 
35
/* CONF_DEBUG enables detailed operation of the conference bridge.
 
36
 * Beware that it prints large amounts of logs (several lines per frame).
 
37
 */
 
38
//#define CONF_DEBUG
 
39
#ifdef CONF_DEBUG
 
40
#   include <stdio.h>
 
41
#   define TRACE_(x)   PJ_LOG(5,x)
 
42
#else
 
43
#   define TRACE_(x)
 
44
#endif
 
45
 
 
46
#define THIS_FILE           "conf_switch.c"
 
47
 
 
48
#define SIGNATURE           PJMEDIA_CONF_SWITCH_SIGNATURE
 
49
#define SIGNATURE_PORT      PJMEDIA_PORT_SIGNATURE('S', 'W', 'T', 'P')
 
50
#define NORMAL_LEVEL        128
 
51
#define SLOT_TYPE           unsigned
 
52
#define INVALID_SLOT        ((SLOT_TYPE)-1)
 
53
#define BUFFER_SIZE         PJMEDIA_MAX_MTU
 
54
#define MAX_LEVEL           (32767)
 
55
#define MIN_LEVEL           (-32768)
 
56
 
 
57
/*
 
58
 * DON'T GET CONFUSED WITH TX/RX!!
 
59
 *
 
60
 * TX and RX directions are always viewed from the conference bridge's point
 
61
 * of view, and NOT from the port's point of view. So TX means the bridge
 
62
 * is transmitting to the port, RX means the bridge is receiving from the
 
63
 * port.
 
64
 */
 
65
 
 
66
 
 
67
/**
 
68
 * This is a port connected to conference bridge.
 
69
 */
 
70
struct conf_port
 
71
{
 
72
    SLOT_TYPE            slot;          /**< Array of listeners.            */
 
73
    pj_str_t             name;          /**< Port name.                     */
 
74
    pjmedia_port        *port;          /**< get_frame() and put_frame()    */
 
75
    pjmedia_port_op      rx_setting;    /**< Can we receive from this port  */
 
76
    pjmedia_port_op      tx_setting;    /**< Can we transmit to this port   */
 
77
    unsigned             listener_cnt;  /**< Number of listeners.           */
 
78
    SLOT_TYPE           *listener_slots;/**< Array of listeners.            */
 
79
    unsigned             transmitter_cnt;/**<Number of transmitters.        */
 
80
 
 
81
    /* Shortcut for port info. */
 
82
    pjmedia_port_info   *info;
 
83
    unsigned             samples_per_frame;
 
84
 
 
85
    /* Calculated signal levels: */
 
86
    unsigned             tx_level;      /**< Last tx level to this port.    */
 
87
    unsigned             rx_level;      /**< Last rx level from this port.  */
 
88
 
 
89
    /* The normalized signal level adjustment.
 
90
     * A value of 128 (NORMAL_LEVEL) means there's no adjustment.
 
91
     */
 
92
    unsigned             tx_adj_level;  /**< Adjustment for TX.             */
 
93
    unsigned             rx_adj_level;  /**< Adjustment for RX.             */
 
94
 
 
95
    pj_timestamp         ts_clock;
 
96
    pj_timestamp         ts_rx;
 
97
    pj_timestamp         ts_tx;
 
98
 
 
99
    /* Tx buffer is a temporary buffer to be used when there's mismatch 
 
100
     * between port's ptime with conference's ptime. This buffer is used as 
 
101
     * the source to buffer the samples until there are enough samples to 
 
102
     * fulfill a complete frame to be transmitted to the port.
 
103
     */
 
104
    pj_uint8_t           tx_buf[BUFFER_SIZE]; /**< Tx buffer.               */
 
105
};
 
106
 
 
107
 
 
108
/*
 
109
 * Conference bridge.
 
110
 */
 
111
struct pjmedia_conf
 
112
{
 
113
    unsigned              options;      /**< Bitmask options.               */
 
114
    unsigned              max_ports;    /**< Maximum ports.                 */
 
115
    unsigned              port_cnt;     /**< Current number of ports.       */
 
116
    unsigned              connect_cnt;  /**< Total number of connections    */
 
117
    pjmedia_port         *master_port;  /**< Port zero's port.              */
 
118
    char                  master_name_buf[80]; /**< Port0 name buffer.      */
 
119
    pj_mutex_t           *mutex;        /**< Conference mutex.              */
 
120
    struct conf_port    **ports;        /**< Array of ports.                */
 
121
    pj_uint8_t            buf[BUFFER_SIZE];     /**< Common buffer.         */
 
122
};
 
123
 
 
124
 
 
125
/* Prototypes */
 
126
static pj_status_t put_frame(pjmedia_port *this_port, 
 
127
                             pjmedia_frame *frame);
 
128
static pj_status_t get_frame(pjmedia_port *this_port, 
 
129
                             pjmedia_frame *frame);
 
130
static pj_status_t destroy_port(pjmedia_port *this_port);
 
131
 
 
132
 
 
133
/*
 
134
 * Create port.
 
135
 */
 
136
static pj_status_t create_conf_port( pj_pool_t *pool,
 
137
                                     pjmedia_conf *conf,
 
138
                                     pjmedia_port *port,
 
139
                                     const pj_str_t *name,
 
140
                                     struct conf_port **p_conf_port)
 
141
{
 
142
    struct conf_port *conf_port;
 
143
    pjmedia_frame *f;
 
144
 
 
145
    PJ_ASSERT_RETURN(pool && conf && port && name && p_conf_port, PJ_EINVAL);
 
146
 
 
147
    /* Create port. */
 
148
    conf_port = PJ_POOL_ZALLOC_T(pool, struct conf_port);
 
149
 
 
150
    /* Set name */
 
151
    pj_strdup_with_null(pool, &conf_port->name, name);
 
152
 
 
153
    /* Default has tx and rx enabled. */
 
154
    conf_port->rx_setting = PJMEDIA_PORT_ENABLE;
 
155
    conf_port->tx_setting = PJMEDIA_PORT_ENABLE;
 
156
 
 
157
    /* Default level adjustment is 128 (which means no adjustment) */
 
158
    conf_port->tx_adj_level = NORMAL_LEVEL;
 
159
    conf_port->rx_adj_level = NORMAL_LEVEL;
 
160
 
 
161
    /* Create transmit flag array */
 
162
    conf_port->listener_slots = (SLOT_TYPE*)
 
163
                                pj_pool_zalloc(pool, 
 
164
                                          conf->max_ports * sizeof(SLOT_TYPE));
 
165
    PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM);
 
166
 
 
167
    /* Save some port's infos, for convenience. */
 
168
    conf_port->port = port;
 
169
    conf_port->info = &port->info;
 
170
    conf_port->samples_per_frame = PJMEDIA_PIA_SPF(&port->info);
 
171
 
 
172
    /* Init pjmedia_frame structure in the TX buffer. */
 
173
    f = (pjmedia_frame*)conf_port->tx_buf;
 
174
    f->buf = conf_port->tx_buf + sizeof(pjmedia_frame);
 
175
 
 
176
    /* Done */
 
177
    *p_conf_port = conf_port;
 
178
    return PJ_SUCCESS;
 
179
}
 
180
 
 
181
/*
 
182
 * Create port zero for the sound device.
 
183
 */
 
184
static pj_status_t create_sound_port( pj_pool_t *pool,
 
185
                                      pjmedia_conf *conf )
 
186
{
 
187
    struct conf_port *conf_port;
 
188
    pj_str_t name = { "Master/sound", 12 };
 
189
    pj_status_t status;
 
190
 
 
191
    status = create_conf_port(pool, conf, conf->master_port, &name, &conf_port);
 
192
    if (status != PJ_SUCCESS)
 
193
        return status;
 
194
 
 
195
     /* Add the port to the bridge */
 
196
    conf_port->slot = 0;
 
197
    conf->ports[0] = conf_port;
 
198
    conf->port_cnt++;
 
199
 
 
200
    PJ_LOG(5,(THIS_FILE, "Sound device successfully created for port 0"));
 
201
    return PJ_SUCCESS;
 
202
}
 
203
 
 
204
/*
 
205
 * Create conference bridge.
 
206
 */
 
207
PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,
 
208
                                         unsigned max_ports,
 
209
                                         unsigned clock_rate,
 
210
                                         unsigned channel_count,
 
211
                                         unsigned samples_per_frame,
 
212
                                         unsigned bits_per_sample,
 
213
                                         unsigned options,
 
214
                                         pjmedia_conf **p_conf )
 
215
{
 
216
    pjmedia_conf *conf;
 
217
    const pj_str_t name = { "Conf", 4 };
 
218
    pj_status_t status;
 
219
 
 
220
    /* Can only accept 16bits per sample, for now.. */
 
221
    PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
 
222
 
 
223
    PJ_LOG(5,(THIS_FILE, "Creating conference bridge with %d ports",
 
224
              max_ports));
 
225
 
 
226
    /* Create and init conf structure. */
 
227
    conf = PJ_POOL_ZALLOC_T(pool, pjmedia_conf);
 
228
    PJ_ASSERT_RETURN(conf, PJ_ENOMEM);
 
229
 
 
230
    conf->ports = (struct conf_port**) 
 
231
                  pj_pool_zalloc(pool, max_ports*sizeof(void*));
 
232
    PJ_ASSERT_RETURN(conf->ports, PJ_ENOMEM);
 
233
 
 
234
    conf->options = options;
 
235
    conf->max_ports = max_ports;
 
236
    
 
237
    /* Create and initialize the master port interface. */
 
238
    conf->master_port = PJ_POOL_ZALLOC_T(pool, pjmedia_port);
 
239
    PJ_ASSERT_RETURN(conf->master_port, PJ_ENOMEM);
 
240
    
 
241
    pjmedia_port_info_init(&conf->master_port->info, &name, SIGNATURE,
 
242
                           clock_rate, channel_count, bits_per_sample,
 
243
                           samples_per_frame);
 
244
 
 
245
    conf->master_port->port_data.pdata = conf;
 
246
    conf->master_port->port_data.ldata = 0;
 
247
 
 
248
    conf->master_port->get_frame = &get_frame;
 
249
    conf->master_port->put_frame = &put_frame;
 
250
    conf->master_port->on_destroy = &destroy_port;
 
251
 
 
252
 
 
253
    /* Create port zero for sound device. */
 
254
    status = create_sound_port(pool, conf);
 
255
    if (status != PJ_SUCCESS)
 
256
        return status;
 
257
 
 
258
    /* Create mutex. */
 
259
    status = pj_mutex_create_recursive(pool, "conf", &conf->mutex);
 
260
    if (status != PJ_SUCCESS)
 
261
        return status;
 
262
 
 
263
    /* Done */
 
264
 
 
265
    *p_conf = conf;
 
266
 
 
267
    return PJ_SUCCESS;
 
268
}
 
269
 
 
270
 
 
271
/*
 
272
 * Pause sound device.
 
273
 */
 
274
static pj_status_t pause_sound( pjmedia_conf *conf )
 
275
{
 
276
    /* Do nothing. */
 
277
    PJ_UNUSED_ARG(conf);
 
278
    return PJ_SUCCESS;
 
279
}
 
280
 
 
281
/*
 
282
 * Resume sound device.
 
283
 */
 
284
static pj_status_t resume_sound( pjmedia_conf *conf )
 
285
{
 
286
    /* Do nothing. */
 
287
    PJ_UNUSED_ARG(conf);
 
288
    return PJ_SUCCESS;
 
289
}
 
290
 
 
291
 
 
292
/**
 
293
 * Destroy conference bridge.
 
294
 */
 
295
PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf )
 
296
{
 
297
    PJ_ASSERT_RETURN(conf != NULL, PJ_EINVAL);
 
298
 
 
299
    /* Destroy mutex */
 
300
    pj_mutex_destroy(conf->mutex);
 
301
 
 
302
    return PJ_SUCCESS;
 
303
}
 
304
 
 
305
 
 
306
/*
 
307
 * Destroy the master port (will destroy the conference)
 
308
 */
 
309
static pj_status_t destroy_port(pjmedia_port *this_port)
 
310
{
 
311
    pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
 
312
    return pjmedia_conf_destroy(conf);
 
313
}
 
314
 
 
315
/*
 
316
 * Get port zero interface.
 
317
 */
 
318
PJ_DEF(pjmedia_port*) pjmedia_conf_get_master_port(pjmedia_conf *conf)
 
319
{
 
320
    /* Sanity check. */
 
321
    PJ_ASSERT_RETURN(conf != NULL, NULL);
 
322
 
 
323
    /* Can only return port interface when PJMEDIA_CONF_NO_DEVICE was
 
324
     * present in the option.
 
325
     */
 
326
    PJ_ASSERT_RETURN((conf->options & PJMEDIA_CONF_NO_DEVICE) != 0, NULL);
 
327
    
 
328
    return conf->master_port;
 
329
}
 
330
 
 
331
 
 
332
/*
 
333
 * Set master port name.
 
334
 */
 
335
PJ_DEF(pj_status_t) pjmedia_conf_set_port0_name(pjmedia_conf *conf,
 
336
                                                const pj_str_t *name)
 
337
{
 
338
    unsigned len;
 
339
 
 
340
    /* Sanity check. */
 
341
    PJ_ASSERT_RETURN(conf != NULL && name != NULL, PJ_EINVAL);
 
342
 
 
343
    len = name->slen;
 
344
    if (len > sizeof(conf->master_name_buf))
 
345
        len = sizeof(conf->master_name_buf);
 
346
    
 
347
    if (len > 0) pj_memcpy(conf->master_name_buf, name->ptr, len);
 
348
 
 
349
    conf->ports[0]->name.ptr = conf->master_name_buf;
 
350
    conf->ports[0]->name.slen = len;
 
351
 
 
352
    conf->master_port->info.name = conf->ports[0]->name;
 
353
 
 
354
    return PJ_SUCCESS;
 
355
}
 
356
 
 
357
/*
 
358
 * Add stream port to the conference bridge.
 
359
 */
 
360
PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf,
 
361
                                           pj_pool_t *pool,
 
362
                                           pjmedia_port *strm_port,
 
363
                                           const pj_str_t *port_name,
 
364
                                           unsigned *p_port )
 
365
{
 
366
    struct conf_port *conf_port;
 
367
    unsigned index;
 
368
    pj_status_t status;
 
369
 
 
370
    PJ_ASSERT_RETURN(conf && pool && strm_port, PJ_EINVAL);
 
371
    /*
 
372
    PJ_ASSERT_RETURN(conf->clock_rate == strm_port->info.clock_rate, 
 
373
                     PJMEDIA_ENCCLOCKRATE);
 
374
    PJ_ASSERT_RETURN(conf->channel_count == strm_port->info.channel_count, 
 
375
                     PJMEDIA_ENCCHANNEL);
 
376
    PJ_ASSERT_RETURN(conf->bits_per_sample == strm_port->info.bits_per_sample,
 
377
                     PJMEDIA_ENCBITS);
 
378
    */
 
379
 
 
380
    /* Port's samples per frame should be equal to or multiplication of 
 
381
     * conference's samples per frame.
 
382
     */
 
383
    /*
 
384
    Not sure if this is needed!
 
385
    PJ_ASSERT_RETURN((conf->samples_per_frame %
 
386
                     strm_port->info.samples_per_frame==0) ||
 
387
                     (strm_port->info.samples_per_frame %
 
388
                     conf->samples_per_frame==0),
 
389
                     PJMEDIA_ENCSAMPLESPFRAME);
 
390
    */
 
391
 
 
392
    /* If port_name is not specified, use the port's name */
 
393
    if (!port_name)
 
394
        port_name = &strm_port->info.name;
 
395
 
 
396
    pj_mutex_lock(conf->mutex);
 
397
 
 
398
    if (conf->port_cnt >= conf->max_ports) {
 
399
        pj_assert(!"Too many ports");
 
400
        pj_mutex_unlock(conf->mutex);
 
401
        return PJ_ETOOMANY;
 
402
    }
 
403
 
 
404
    /* Find empty port in the conference bridge. */
 
405
    for (index=0; index < conf->max_ports; ++index) {
 
406
        if (conf->ports[index] == NULL)
 
407
            break;
 
408
    }
 
409
 
 
410
    pj_assert(index != conf->max_ports);
 
411
 
 
412
    /* Create conf port structure. */
 
413
    status = create_conf_port(pool, conf, strm_port, port_name, &conf_port);
 
414
    if (status != PJ_SUCCESS) {
 
415
        pj_mutex_unlock(conf->mutex);
 
416
        return status;
 
417
    }
 
418
 
 
419
    /* Put the port. */
 
420
    conf_port->slot = index;
 
421
    conf->ports[index] = conf_port;
 
422
    conf->port_cnt++;
 
423
 
 
424
    /* Done. */
 
425
    if (p_port) {
 
426
        *p_port = index;
 
427
    }
 
428
 
 
429
    pj_mutex_unlock(conf->mutex);
 
430
 
 
431
    return PJ_SUCCESS;
 
432
}
 
433
 
 
434
 
 
435
/*
 
436
 * Add passive port.
 
437
 */
 
438
PJ_DEF(pj_status_t) pjmedia_conf_add_passive_port( pjmedia_conf *conf,
 
439
                                                   pj_pool_t *pool,
 
440
                                                   const pj_str_t *name,
 
441
                                                   unsigned clock_rate,
 
442
                                                   unsigned channel_count,
 
443
                                                   unsigned samples_per_frame,
 
444
                                                   unsigned bits_per_sample,
 
445
                                                   unsigned options,
 
446
                                                   unsigned *p_slot,
 
447
                                                   pjmedia_port **p_port )
 
448
{
 
449
    PJ_UNUSED_ARG(conf);
 
450
    PJ_UNUSED_ARG(pool);
 
451
    PJ_UNUSED_ARG(name);
 
452
    PJ_UNUSED_ARG(clock_rate);
 
453
    PJ_UNUSED_ARG(channel_count);
 
454
    PJ_UNUSED_ARG(samples_per_frame);
 
455
    PJ_UNUSED_ARG(bits_per_sample);
 
456
    PJ_UNUSED_ARG(options);
 
457
    PJ_UNUSED_ARG(p_slot);
 
458
    PJ_UNUSED_ARG(p_port);
 
459
 
 
460
    return PJ_ENOTSUP;
 
461
}
 
462
 
 
463
 
 
464
 
 
465
/*
 
466
 * Change TX and RX settings for the port.
 
467
 */
 
468
PJ_DEF(pj_status_t) pjmedia_conf_configure_port( pjmedia_conf *conf,
 
469
                                                  unsigned slot,
 
470
                                                  pjmedia_port_op tx,
 
471
                                                  pjmedia_port_op rx)
 
472
{
 
473
    struct conf_port *conf_port;
 
474
 
 
475
    /* Check arguments */
 
476
    PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
 
477
 
 
478
    pj_mutex_lock(conf->mutex);
 
479
 
 
480
    /* Port must be valid. */
 
481
    conf_port = conf->ports[slot];
 
482
    if (conf_port == NULL) {
 
483
        pj_mutex_unlock(conf->mutex);
 
484
        return PJ_EINVAL;
 
485
    }
 
486
 
 
487
    if (tx != PJMEDIA_PORT_NO_CHANGE)
 
488
        conf_port->tx_setting = tx;
 
489
 
 
490
    if (rx != PJMEDIA_PORT_NO_CHANGE)
 
491
        conf_port->rx_setting = rx;
 
492
 
 
493
    pj_mutex_unlock(conf->mutex);
 
494
 
 
495
    return PJ_SUCCESS;
 
496
}
 
497
 
 
498
 
 
499
/*
 
500
 * Connect port.
 
501
 */
 
502
PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf,
 
503
                                               unsigned src_slot,
 
504
                                               unsigned sink_slot,
 
505
                                               int level )
 
506
{
 
507
    struct conf_port *src_port, *dst_port;
 
508
    pj_bool_t start_sound = PJ_FALSE;
 
509
    pjmedia_audio_format_detail *src_afd, *dst_afd;
 
510
    unsigned i;
 
511
 
 
512
    /* Check arguments */
 
513
    PJ_ASSERT_RETURN(conf && src_slot<conf->max_ports && 
 
514
                     sink_slot<conf->max_ports, PJ_EINVAL);
 
515
 
 
516
    /* For now, level MUST be zero. */
 
517
    PJ_ASSERT_RETURN(level == 0, PJ_EINVAL);
 
518
 
 
519
    pj_mutex_lock(conf->mutex);
 
520
 
 
521
    /* Ports must be valid. */
 
522
    src_port = conf->ports[src_slot];
 
523
    dst_port = conf->ports[sink_slot];
 
524
    if (!src_port || !dst_port) {
 
525
        pj_mutex_unlock(conf->mutex);
 
526
        return PJ_EINVAL;
 
527
    }
 
528
 
 
529
    src_afd = pjmedia_format_get_audio_format_detail(&src_port->info->fmt, 1);
 
530
    dst_afd = pjmedia_format_get_audio_format_detail(&dst_port->info->fmt, 1);
 
531
 
 
532
    /* Format must match. */
 
533
    if (src_port->info->fmt.id != dst_port->info->fmt.id ||
 
534
        src_afd->avg_bps != dst_afd->avg_bps)
 
535
    {
 
536
        pj_mutex_unlock(conf->mutex);
 
537
        return PJMEDIA_ENOTCOMPATIBLE;
 
538
    }
 
539
 
 
540
    /* Clock rate must match. */
 
541
    if (src_afd->clock_rate != dst_afd->clock_rate) {
 
542
        pj_mutex_unlock(conf->mutex);
 
543
        return PJMEDIA_ENCCLOCKRATE;
 
544
    }
 
545
 
 
546
    /* Channel count must match. */
 
547
    if (src_afd->channel_count != dst_afd->channel_count) {
 
548
        pj_mutex_unlock(conf->mutex);
 
549
        return PJMEDIA_ENCCHANNEL;
 
550
    }
 
551
 
 
552
    /* Source and sink ptime must be equal or a multiplication factor. */
 
553
    if ((src_afd->frame_time_usec % dst_afd->frame_time_usec != 0) &&
 
554
        (dst_afd->frame_time_usec % src_afd->frame_time_usec != 0))
 
555
    {
 
556
        pj_mutex_unlock(conf->mutex);
 
557
        return PJMEDIA_ENCSAMPLESPFRAME;
 
558
    }
 
559
    
 
560
    /* If sink is currently listening to other ports, it needs to be released
 
561
     * first before the new connection made.
 
562
     */ 
 
563
    if (dst_port->transmitter_cnt > 0) {
 
564
        unsigned j;
 
565
        pj_bool_t transmitter_found = PJ_FALSE;
 
566
 
 
567
        pj_assert(dst_port->transmitter_cnt == 1);
 
568
        for (j=0; j<conf->max_ports && !transmitter_found; ++j) {
 
569
            if (conf->ports[j]) {
 
570
                unsigned k;
 
571
 
 
572
                for (k=0; k < conf->ports[j]->listener_cnt; ++k) {
 
573
                    if (conf->ports[j]->listener_slots[k] == sink_slot) {
 
574
                        PJ_LOG(2,(THIS_FILE, "Connection [%d->%d] is "
 
575
                                  "disconnected for new connection [%d->%d]",
 
576
                                  j, sink_slot, src_slot, sink_slot));
 
577
                        pjmedia_conf_disconnect_port(conf, j, sink_slot);
 
578
                        transmitter_found = PJ_TRUE;
 
579
                        break;
 
580
                    }
 
581
                }
 
582
            }
 
583
        }
 
584
        pj_assert(dst_port->transmitter_cnt == 0);
 
585
    }
 
586
 
 
587
    /* Check if connection has been made */
 
588
    for (i=0; i<src_port->listener_cnt; ++i) {
 
589
        if (src_port->listener_slots[i] == sink_slot)
 
590
            break;
 
591
    }
 
592
 
 
593
    /* Update master port info shortcut, note that application may update
 
594
     * the master port info when the audio device needs to be reopened with
 
595
     * a new format to match to ports connection format.
 
596
     */
 
597
    conf->ports[0]->samples_per_frame = PJMEDIA_PIA_SPF(conf->ports[0]->info);
 
598
 
 
599
    if (i == src_port->listener_cnt) {
 
600
        src_port->listener_slots[src_port->listener_cnt] = sink_slot;
 
601
        ++conf->connect_cnt;
 
602
        ++src_port->listener_cnt;
 
603
        ++dst_port->transmitter_cnt;
 
604
 
 
605
        if (conf->connect_cnt == 1)
 
606
            start_sound = 1;
 
607
 
 
608
        PJ_LOG(4,(THIS_FILE,"Port %d (%.*s) transmitting to port %d (%.*s)",
 
609
                  src_slot,
 
610
                  (int)src_port->name.slen,
 
611
                  src_port->name.ptr,
 
612
                  sink_slot,
 
613
                  (int)dst_port->name.slen,
 
614
                  dst_port->name.ptr));
 
615
    }
 
616
 
 
617
    pj_mutex_unlock(conf->mutex);
 
618
 
 
619
    /* Sound device must be started without mutex, otherwise the
 
620
     * sound thread will deadlock (?)
 
621
     */
 
622
    if (start_sound)
 
623
        resume_sound(conf);
 
624
 
 
625
    return PJ_SUCCESS;
 
626
}
 
627
 
 
628
 
 
629
/*
 
630
 * Disconnect port
 
631
 */
 
632
PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf,
 
633
                                                  unsigned src_slot,
 
634
                                                  unsigned sink_slot )
 
635
{
 
636
    struct conf_port *src_port, *dst_port;
 
637
    unsigned i;
 
638
 
 
639
    /* Check arguments */
 
640
    PJ_ASSERT_RETURN(conf && src_slot<conf->max_ports && 
 
641
                     sink_slot<conf->max_ports, PJ_EINVAL);
 
642
 
 
643
    pj_mutex_lock(conf->mutex);
 
644
 
 
645
    /* Ports must be valid. */
 
646
    src_port = conf->ports[src_slot];
 
647
    dst_port = conf->ports[sink_slot];
 
648
    if (!src_port || !dst_port) {
 
649
        pj_mutex_unlock(conf->mutex);
 
650
        return PJ_EINVAL;
 
651
    }
 
652
 
 
653
    /* Check if connection has been made */
 
654
    for (i=0; i<src_port->listener_cnt; ++i) {
 
655
        if (src_port->listener_slots[i] == sink_slot)
 
656
            break;
 
657
    }
 
658
 
 
659
    if (i != src_port->listener_cnt) {
 
660
        pjmedia_frame_ext *f;
 
661
 
 
662
        pj_assert(src_port->listener_cnt > 0 && 
 
663
                  src_port->listener_cnt < conf->max_ports);
 
664
        pj_assert(dst_port->transmitter_cnt > 0 && 
 
665
                  dst_port->transmitter_cnt < conf->max_ports);
 
666
        pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE), 
 
667
                       src_port->listener_cnt, i);
 
668
        --conf->connect_cnt;
 
669
        --src_port->listener_cnt;
 
670
        --dst_port->transmitter_cnt;
 
671
        
 
672
        /* Cleanup listener TX buffer. */
 
673
        f = (pjmedia_frame_ext*)dst_port->tx_buf;
 
674
        f->base.type = PJMEDIA_FRAME_TYPE_NONE;
 
675
        f->base.size = 0;
 
676
        f->samples_cnt = 0;
 
677
        f->subframe_cnt = 0;
 
678
 
 
679
        PJ_LOG(4,(THIS_FILE,
 
680
                  "Port %d (%.*s) stop transmitting to port %d (%.*s)",
 
681
                  src_slot,
 
682
                  (int)src_port->name.slen,
 
683
                  src_port->name.ptr,
 
684
                  sink_slot,
 
685
                  (int)dst_port->name.slen,
 
686
                  dst_port->name.ptr));
 
687
    }
 
688
 
 
689
    pj_mutex_unlock(conf->mutex);
 
690
 
 
691
    if (conf->connect_cnt == 0) {
 
692
        pause_sound(conf);
 
693
    }
 
694
 
 
695
    return PJ_SUCCESS;
 
696
}
 
697
 
 
698
/*
 
699
 * Get number of ports currently registered to the conference bridge.
 
700
 */
 
701
PJ_DEF(unsigned) pjmedia_conf_get_port_count(pjmedia_conf *conf)
 
702
{
 
703
    return conf->port_cnt;
 
704
}
 
705
 
 
706
/*
 
707
 * Get total number of ports connections currently set up in the bridge.
 
708
 */
 
709
PJ_DEF(unsigned) pjmedia_conf_get_connect_count(pjmedia_conf *conf)
 
710
{
 
711
    return conf->connect_cnt;
 
712
}
 
713
 
 
714
 
 
715
/*
 
716
 * Remove the specified port.
 
717
 */
 
718
PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,
 
719
                                              unsigned port )
 
720
{
 
721
    struct conf_port *conf_port;
 
722
    unsigned i;
 
723
 
 
724
    /* Check arguments */
 
725
    PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL);
 
726
 
 
727
    /* Suspend the sound devices.
 
728
     * Don't want to remove port while port is being accessed by sound
 
729
     * device's threads!
 
730
     */
 
731
 
 
732
    pj_mutex_lock(conf->mutex);
 
733
 
 
734
    /* Port must be valid. */
 
735
    conf_port = conf->ports[port];
 
736
    if (conf_port == NULL) {
 
737
        pj_mutex_unlock(conf->mutex);
 
738
        return PJ_EINVAL;
 
739
    }
 
740
 
 
741
    conf_port->tx_setting = PJMEDIA_PORT_DISABLE;
 
742
    conf_port->rx_setting = PJMEDIA_PORT_DISABLE;
 
743
 
 
744
    /* Remove this port from transmit array of other ports. */
 
745
    for (i=0; i<conf->max_ports; ++i) {
 
746
        unsigned j;
 
747
        struct conf_port *src_port;
 
748
 
 
749
        src_port = conf->ports[i];
 
750
 
 
751
        if (!src_port)
 
752
            continue;
 
753
 
 
754
        if (src_port->listener_cnt == 0)
 
755
            continue;
 
756
 
 
757
        for (j=0; j<src_port->listener_cnt; ++j) {
 
758
            if (src_port->listener_slots[j] == port) {
 
759
                pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE),
 
760
                               src_port->listener_cnt, j);
 
761
                pj_assert(conf->connect_cnt > 0);
 
762
                --conf->connect_cnt;
 
763
                --src_port->listener_cnt;
 
764
                break;
 
765
            }
 
766
        }
 
767
    }
 
768
 
 
769
    /* Update transmitter_cnt of ports we're transmitting to */
 
770
    while (conf_port->listener_cnt) {
 
771
        unsigned dst_slot;
 
772
        struct conf_port *dst_port;
 
773
        pjmedia_frame_ext *f;
 
774
 
 
775
        dst_slot = conf_port->listener_slots[conf_port->listener_cnt-1];
 
776
        dst_port = conf->ports[dst_slot];
 
777
        --dst_port->transmitter_cnt;
 
778
        --conf_port->listener_cnt;
 
779
        pj_assert(conf->connect_cnt > 0);
 
780
        --conf->connect_cnt;
 
781
 
 
782
        /* Cleanup & reinit listener TX buffer. */
 
783
        f = (pjmedia_frame_ext*)dst_port->tx_buf;
 
784
        f->base.type = PJMEDIA_FRAME_TYPE_NONE;
 
785
        f->base.size = 0;
 
786
        f->samples_cnt = 0;
 
787
        f->subframe_cnt = 0;
 
788
    }
 
789
 
 
790
    /* Remove the port. */
 
791
    conf->ports[port] = NULL;
 
792
    --conf->port_cnt;
 
793
 
 
794
    pj_mutex_unlock(conf->mutex);
 
795
 
 
796
 
 
797
    /* Stop sound if there's no connection. */
 
798
    if (conf->connect_cnt == 0) {
 
799
        pause_sound(conf);
 
800
    }
 
801
 
 
802
    return PJ_SUCCESS;
 
803
}
 
804
 
 
805
 
 
806
/*
 
807
 * Enum ports.
 
808
 */
 
809
PJ_DEF(pj_status_t) pjmedia_conf_enum_ports( pjmedia_conf *conf,
 
810
                                             unsigned ports[],
 
811
                                             unsigned *p_count )
 
812
{
 
813
    unsigned i, count=0;
 
814
 
 
815
    PJ_ASSERT_RETURN(conf && p_count && ports, PJ_EINVAL);
 
816
 
 
817
    /* Lock mutex */
 
818
    pj_mutex_lock(conf->mutex);
 
819
 
 
820
    for (i=0; i<conf->max_ports && count<*p_count; ++i) {
 
821
        if (!conf->ports[i])
 
822
            continue;
 
823
 
 
824
        ports[count++] = i;
 
825
    }
 
826
 
 
827
    /* Unlock mutex */
 
828
    pj_mutex_unlock(conf->mutex);
 
829
 
 
830
    *p_count = count;
 
831
    return PJ_SUCCESS;
 
832
}
 
833
 
 
834
/*
 
835
 * Get port info
 
836
 */
 
837
PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf,
 
838
                                                unsigned slot,
 
839
                                                pjmedia_conf_port_info *info)
 
840
{
 
841
    struct conf_port *conf_port;
 
842
    const pjmedia_audio_format_detail *afd;
 
843
 
 
844
    /* Check arguments */
 
845
    PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
 
846
 
 
847
    /* Lock mutex */
 
848
    pj_mutex_lock(conf->mutex);
 
849
 
 
850
    /* Port must be valid. */
 
851
    conf_port = conf->ports[slot];
 
852
    if (conf_port == NULL) {
 
853
        pj_mutex_unlock(conf->mutex);
 
854
        return PJ_EINVAL;
 
855
    }
 
856
 
 
857
    afd = pjmedia_format_get_audio_format_detail(&conf_port->info->fmt, 1);
 
858
 
 
859
    pj_bzero(info, sizeof(pjmedia_conf_port_info));
 
860
 
 
861
    info->slot = slot;
 
862
    info->name = conf_port->name;
 
863
    info->tx_setting = conf_port->tx_setting;
 
864
    info->rx_setting = conf_port->rx_setting;
 
865
    info->listener_cnt = conf_port->listener_cnt;
 
866
    info->listener_slots = conf_port->listener_slots;
 
867
    info->transmitter_cnt = conf_port->transmitter_cnt;
 
868
    info->clock_rate = afd->clock_rate;
 
869
    info->channel_count = afd->channel_count;
 
870
    info->samples_per_frame = conf_port->samples_per_frame;
 
871
    info->bits_per_sample = afd->bits_per_sample;
 
872
    info->format = conf_port->port->info.fmt;
 
873
    info->tx_adj_level = conf_port->tx_adj_level - NORMAL_LEVEL;
 
874
    info->rx_adj_level = conf_port->rx_adj_level - NORMAL_LEVEL;
 
875
 
 
876
    /* Unlock mutex */
 
877
    pj_mutex_unlock(conf->mutex);
 
878
 
 
879
    return PJ_SUCCESS;
 
880
}
 
881
 
 
882
 
 
883
PJ_DEF(pj_status_t) pjmedia_conf_get_ports_info(pjmedia_conf *conf,
 
884
                                                unsigned *size,
 
885
                                                pjmedia_conf_port_info info[])
 
886
{
 
887
    unsigned i, count=0;
 
888
 
 
889
    PJ_ASSERT_RETURN(conf && size && info, PJ_EINVAL);
 
890
 
 
891
    /* Lock mutex */
 
892
    pj_mutex_lock(conf->mutex);
 
893
 
 
894
    for (i=0; i<conf->max_ports && count<*size; ++i) {
 
895
        if (!conf->ports[i])
 
896
            continue;
 
897
 
 
898
        pjmedia_conf_get_port_info(conf, i, &info[count]);
 
899
        ++count;
 
900
    }
 
901
 
 
902
    /* Unlock mutex */
 
903
    pj_mutex_unlock(conf->mutex);
 
904
 
 
905
    *size = count;
 
906
    return PJ_SUCCESS;
 
907
}
 
908
 
 
909
 
 
910
/*
 
911
 * Get signal level.
 
912
 */
 
913
PJ_DEF(pj_status_t) pjmedia_conf_get_signal_level( pjmedia_conf *conf,
 
914
                                                   unsigned slot,
 
915
                                                   unsigned *tx_level,
 
916
                                                   unsigned *rx_level)
 
917
{
 
918
    struct conf_port *conf_port;
 
919
 
 
920
    /* Check arguments */
 
921
    PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
 
922
 
 
923
    /* Lock mutex */
 
924
    pj_mutex_lock(conf->mutex);
 
925
 
 
926
    /* Port must be valid. */
 
927
    conf_port = conf->ports[slot];
 
928
    if (conf_port == NULL) {
 
929
        pj_mutex_unlock(conf->mutex);
 
930
        return PJ_EINVAL;
 
931
    }
 
932
 
 
933
    if (tx_level != NULL) {
 
934
        *tx_level = conf_port->tx_level;
 
935
    }
 
936
 
 
937
    if (rx_level != NULL) 
 
938
        *rx_level = conf_port->rx_level;
 
939
 
 
940
    /* Unlock mutex */
 
941
    pj_mutex_unlock(conf->mutex);
 
942
 
 
943
    return PJ_SUCCESS;
 
944
}
 
945
 
 
946
 
 
947
/*
 
948
 * Adjust RX level of individual port.
 
949
 */
 
950
PJ_DEF(pj_status_t) pjmedia_conf_adjust_rx_level( pjmedia_conf *conf,
 
951
                                                  unsigned slot,
 
952
                                                  int adj_level )
 
953
{
 
954
    struct conf_port *conf_port;
 
955
 
 
956
    /* Check arguments */
 
957
    PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
 
958
 
 
959
    /* Value must be from -128 to +127 */
 
960
    /* Disabled, you can put more than +127, at your own risk: 
 
961
     PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
 
962
     */
 
963
    PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
 
964
 
 
965
    /* Lock mutex */
 
966
    pj_mutex_lock(conf->mutex);
 
967
 
 
968
    /* Port must be valid. */
 
969
    conf_port = conf->ports[slot];
 
970
    if (conf_port == NULL) {
 
971
        pj_mutex_unlock(conf->mutex);
 
972
        return PJ_EINVAL;
 
973
    }
 
974
 
 
975
    /* Level adjustment is applicable only for ports that work with raw PCM. */
 
976
    PJ_ASSERT_RETURN(conf_port->info->fmt.id == PJMEDIA_FORMAT_L16,
 
977
                     PJ_EIGNORED);
 
978
 
 
979
    /* Set normalized adjustment level. */
 
980
    conf_port->rx_adj_level = adj_level + NORMAL_LEVEL;
 
981
 
 
982
    /* Unlock mutex */
 
983
    pj_mutex_unlock(conf->mutex);
 
984
 
 
985
    return PJ_SUCCESS;
 
986
}
 
987
 
 
988
 
 
989
/*
 
990
 * Adjust TX level of individual port.
 
991
 */
 
992
PJ_DEF(pj_status_t) pjmedia_conf_adjust_tx_level( pjmedia_conf *conf,
 
993
                                                  unsigned slot,
 
994
                                                  int adj_level )
 
995
{
 
996
    struct conf_port *conf_port;
 
997
 
 
998
    /* Check arguments */
 
999
    PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
 
1000
 
 
1001
    /* Value must be from -128 to +127 */
 
1002
    /* Disabled, you can put more than +127,, at your own risk:
 
1003
     PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
 
1004
     */
 
1005
    PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
 
1006
 
 
1007
    /* Lock mutex */
 
1008
    pj_mutex_lock(conf->mutex);
 
1009
 
 
1010
    /* Port must be valid. */
 
1011
    conf_port = conf->ports[slot];
 
1012
    if (conf_port == NULL) {
 
1013
        pj_mutex_unlock(conf->mutex);
 
1014
        return PJ_EINVAL;
 
1015
    }
 
1016
 
 
1017
    /* Level adjustment is applicable only for ports that work with raw PCM. */
 
1018
    PJ_ASSERT_RETURN(conf_port->info->fmt.id == PJMEDIA_FORMAT_L16,
 
1019
                     PJ_EIGNORED);
 
1020
 
 
1021
    /* Set normalized adjustment level. */
 
1022
    conf_port->tx_adj_level = adj_level + NORMAL_LEVEL;
 
1023
 
 
1024
    /* Unlock mutex */
 
1025
    pj_mutex_unlock(conf->mutex);
 
1026
 
 
1027
    return PJ_SUCCESS;
 
1028
}
 
1029
 
 
1030
/* Deliver frm_src to a listener port, eventually call  port's put_frame() 
 
1031
 * when samples count in the frm_dst are equal to port's samples_per_frame.
 
1032
 */
 
1033
static pj_status_t write_frame(struct conf_port *cport_dst,
 
1034
                               const pjmedia_frame *frm_src)
 
1035
{
 
1036
    pjmedia_frame *frm_dst = (pjmedia_frame*)cport_dst->tx_buf;
 
1037
    
 
1038
    PJ_TODO(MAKE_SURE_DEST_FRAME_HAS_ENOUGH_SPACE);
 
1039
 
 
1040
    frm_dst->type = frm_src->type;
 
1041
    frm_dst->timestamp = cport_dst->ts_tx;
 
1042
 
 
1043
    if (frm_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
 
1044
 
 
1045
        pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frm_src;
 
1046
        pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
 
1047
        unsigned i;
 
1048
 
 
1049
        for (i = 0; i < f_src->subframe_cnt; ++i) {
 
1050
            pjmedia_frame_ext_subframe *sf;
 
1051
            
 
1052
            /* Copy frame to listener's TX buffer. */
 
1053
            sf = pjmedia_frame_ext_get_subframe(f_src, i);
 
1054
            pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen, 
 
1055
                                              f_src->samples_cnt / 
 
1056
                                              f_src->subframe_cnt);
 
1057
 
 
1058
            /* Check if it's time to deliver the TX buffer to listener, 
 
1059
             * i.e: samples count in TX buffer equal to listener's
 
1060
             * samples per frame.
 
1061
             */
 
1062
            if (f_dst->samples_cnt >= cport_dst->samples_per_frame)
 
1063
            {
 
1064
                if (cport_dst->slot) {
 
1065
                    pjmedia_port_put_frame(cport_dst->port, 
 
1066
                                           (pjmedia_frame*)f_dst);
 
1067
 
 
1068
                    /* Reset TX buffer. */
 
1069
                    f_dst->subframe_cnt = 0;
 
1070
                    f_dst->samples_cnt = 0;
 
1071
                }
 
1072
 
 
1073
                /* Update TX timestamp. */
 
1074
                pj_add_timestamp32(&cport_dst->ts_tx,
 
1075
                                   cport_dst->samples_per_frame);
 
1076
            }
 
1077
        }
 
1078
 
 
1079
    } else if (frm_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
 
1080
 
 
1081
        pj_int16_t *f_start, *f_end;
 
1082
 
 
1083
        f_start = (pj_int16_t*)frm_src->buf;
 
1084
        f_end   = f_start + (frm_src->size >> 1);
 
1085
 
 
1086
        while (f_start < f_end) {
 
1087
            unsigned nsamples_to_copy, nsamples_req;
 
1088
 
 
1089
            /* Copy frame to listener's TX buffer.
 
1090
             * Note that if the destination is port 0, just copy the whole
 
1091
             * available samples.
 
1092
             */
 
1093
            nsamples_to_copy = f_end - f_start;
 
1094
            nsamples_req = cport_dst->samples_per_frame -
 
1095
                          (frm_dst->size>>1);
 
1096
            if (cport_dst->slot && nsamples_to_copy > nsamples_req)
 
1097
                nsamples_to_copy = nsamples_req;
 
1098
 
 
1099
            /* Adjust TX level. */
 
1100
            if (cport_dst->tx_adj_level != NORMAL_LEVEL) {
 
1101
                pj_int16_t *p, *p_end;
 
1102
 
 
1103
                p = f_start;
 
1104
                p_end = p + nsamples_to_copy;
 
1105
                while (p < p_end) {
 
1106
                    pj_int32_t itemp = *p;
 
1107
 
 
1108
                    /* Adjust the level */
 
1109
                    itemp = (itemp * cport_dst->tx_adj_level) >> 7;
 
1110
 
 
1111
                    /* Clip the signal if it's too loud */
 
1112
                    if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
 
1113
                    else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
 
1114
 
 
1115
                    /* Put back in the buffer. */
 
1116
                    *p = (pj_int16_t)itemp;
 
1117
                    ++p;
 
1118
                }
 
1119
            }
 
1120
 
 
1121
            pjmedia_copy_samples((pj_int16_t*)frm_dst->buf + (frm_dst->size>>1),
 
1122
                                 f_start, 
 
1123
                                 nsamples_to_copy);
 
1124
            frm_dst->size += nsamples_to_copy << 1;
 
1125
            f_start += nsamples_to_copy;
 
1126
 
 
1127
            /* Check if it's time to deliver the TX buffer to listener, 
 
1128
             * i.e: samples count in TX buffer equal to listener's
 
1129
             * samples per frame. Note that for destination port 0 this
 
1130
             * function will just populate all samples in the TX buffer.
 
1131
             */
 
1132
            if (cport_dst->slot == 0) {
 
1133
                /* Update TX timestamp. */
 
1134
                pj_add_timestamp32(&cport_dst->ts_tx, nsamples_to_copy);
 
1135
            } else if ((frm_dst->size >> 1) == 
 
1136
                       cport_dst->samples_per_frame)
 
1137
            {
 
1138
                pjmedia_port_put_frame(cport_dst->port, frm_dst);
 
1139
 
 
1140
                /* Reset TX buffer. */
 
1141
                frm_dst->size = 0;
 
1142
 
 
1143
                /* Update TX timestamp. */
 
1144
                pj_add_timestamp32(&cport_dst->ts_tx, 
 
1145
                                   cport_dst->samples_per_frame);
 
1146
            }
 
1147
        }
 
1148
 
 
1149
    } else if (frm_src->type == PJMEDIA_FRAME_TYPE_NONE) {
 
1150
 
 
1151
        /* Check port format. */
 
1152
        if (cport_dst->port &&
 
1153
            cport_dst->port->info.fmt.id == PJMEDIA_FORMAT_L16)
 
1154
        {
 
1155
            /* When there is already some samples in listener's TX buffer, 
 
1156
             * pad the buffer with "zero samples".
 
1157
             */
 
1158
            if (frm_dst->size != 0) {
 
1159
                pjmedia_zero_samples((pj_int16_t*)frm_dst->buf,
 
1160
                                     cport_dst->samples_per_frame -
 
1161
                                     (frm_dst->size>>1));
 
1162
 
 
1163
                frm_dst->type = PJMEDIA_FRAME_TYPE_AUDIO;
 
1164
                frm_dst->size = cport_dst->samples_per_frame << 1;
 
1165
                if (cport_dst->slot) {
 
1166
                    pjmedia_port_put_frame(cport_dst->port, frm_dst);
 
1167
 
 
1168
                    /* Reset TX buffer. */
 
1169
                    frm_dst->size = 0;
 
1170
                }
 
1171
 
 
1172
                /* Update TX timestamp. */
 
1173
                pj_add_timestamp32(&cport_dst->ts_tx, 
 
1174
                                   cport_dst->samples_per_frame);
 
1175
            }
 
1176
        } else {
 
1177
            pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
 
1178
 
 
1179
            if (f_dst->samples_cnt != 0) {
 
1180
                frm_dst->type = PJMEDIA_FRAME_TYPE_EXTENDED;
 
1181
                pjmedia_frame_ext_append_subframe(f_dst, NULL, 0, (pj_uint16_t)
 
1182
                    (cport_dst->samples_per_frame - f_dst->samples_cnt));
 
1183
                if (cport_dst->slot) {
 
1184
                    pjmedia_port_put_frame(cport_dst->port, frm_dst);
 
1185
 
 
1186
                    /* Reset TX buffer. */
 
1187
                    f_dst->subframe_cnt = 0;
 
1188
                    f_dst->samples_cnt = 0;
 
1189
                }
 
1190
 
 
1191
                /* Update TX timestamp. */
 
1192
                pj_add_timestamp32(&cport_dst->ts_tx, 
 
1193
                                   cport_dst->samples_per_frame);
 
1194
            }
 
1195
        }
 
1196
 
 
1197
        /* Synchronize clock. */
 
1198
        while (pj_cmp_timestamp(&cport_dst->ts_clock, 
 
1199
                                &cport_dst->ts_tx) > 0)
 
1200
        {
 
1201
            frm_dst->type = PJMEDIA_FRAME_TYPE_NONE;
 
1202
            frm_dst->timestamp = cport_dst->ts_tx;
 
1203
            if (cport_dst->slot)
 
1204
                pjmedia_port_put_frame(cport_dst->port, frm_dst);
 
1205
 
 
1206
            /* Update TX timestamp. */
 
1207
            pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->samples_per_frame);
 
1208
        }
 
1209
    }
 
1210
 
 
1211
    return PJ_SUCCESS;
 
1212
}
 
1213
 
 
1214
/*
 
1215
 * Player callback.
 
1216
 */
 
1217
static pj_status_t get_frame(pjmedia_port *this_port, 
 
1218
                             pjmedia_frame *frame)
 
1219
{
 
1220
    pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
 
1221
    unsigned ci, i;
 
1222
    
 
1223
    /* Lock mutex */
 
1224
    pj_mutex_lock(conf->mutex);
 
1225
 
 
1226
    /* Call get_frame() from all ports (except port 0) that has 
 
1227
     * receiver and distribute the frame (put the frame to the destination 
 
1228
     * port's buffer to accommodate different ptime, and ultimately call 
 
1229
     * put_frame() of that port) to ports that are receiving from this port.
 
1230
     */
 
1231
    for (i=1, ci=1; i<conf->max_ports && ci<conf->port_cnt; ++i) {
 
1232
        struct conf_port *cport = conf->ports[i];
 
1233
        unsigned master_samples_per_frame;
 
1234
 
 
1235
        /* Skip empty port. */
 
1236
        if (!cport)
 
1237
            continue;
 
1238
 
 
1239
        /* Var "ci" is to count how many ports have been visited so far. */
 
1240
        ++ci;
 
1241
 
 
1242
        master_samples_per_frame = PJMEDIA_PIA_SPF(&conf->master_port->info);
 
1243
 
 
1244
        /* Update clock of the port. */
 
1245
        pj_add_timestamp32(&cport->ts_clock, master_samples_per_frame);
 
1246
 
 
1247
        /* Skip if we're not allowed to receive from this port or 
 
1248
         * the port doesn't have listeners.
 
1249
         */
 
1250
        if (cport->rx_setting == PJMEDIA_PORT_DISABLE || 
 
1251
            cport->listener_cnt == 0)
 
1252
        {
 
1253
            cport->rx_level = 0;
 
1254
            pj_add_timestamp32(&cport->ts_rx, master_samples_per_frame);
 
1255
            continue;
 
1256
        }
 
1257
 
 
1258
        /* Get frame from each port, put it to the listener TX buffer,
 
1259
         * and eventually call put_frame() of the listener. This loop 
 
1260
         * will also make sure the ptime between conf & port synchronized.
 
1261
         */
 
1262
        while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_rx) > 0) {
 
1263
            pjmedia_frame *f = (pjmedia_frame*)conf->buf;
 
1264
            pj_status_t status;
 
1265
            unsigned j;
 
1266
            pj_int32_t level = 0;
 
1267
 
 
1268
            pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
 
1269
            
 
1270
            f->buf = &conf->buf[sizeof(pjmedia_frame)];
 
1271
            f->size = cport->samples_per_frame<<1;
 
1272
 
 
1273
            /* Get frame from port. */
 
1274
            status = pjmedia_port_get_frame(cport->port, f);
 
1275
            if (status != PJ_SUCCESS)
 
1276
                continue;
 
1277
 
 
1278
            /* Calculate & adjust RX level. */
 
1279
            if (f->type == PJMEDIA_FRAME_TYPE_AUDIO) {
 
1280
                if (cport->rx_adj_level != NORMAL_LEVEL) {
 
1281
                    pj_int16_t *p = (pj_int16_t*)f->buf;
 
1282
                    pj_int16_t *end;
 
1283
 
 
1284
                    end = p + (f->size >> 1);
 
1285
                    while (p < end) {
 
1286
                        pj_int32_t itemp = *p;
 
1287
 
 
1288
                        /* Adjust the level */
 
1289
                        itemp = (itemp * cport->rx_adj_level) >> 7;
 
1290
 
 
1291
                        /* Clip the signal if it's too loud */
 
1292
                        if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
 
1293
                        else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
 
1294
 
 
1295
                        level += PJ_ABS(itemp);
 
1296
 
 
1297
                        /* Put back in the buffer. */
 
1298
                        *p = (pj_int16_t)itemp;
 
1299
                        ++p;
 
1300
                    }
 
1301
                    level /= (f->size >> 1);
 
1302
                } else {
 
1303
                    level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
 
1304
                                                    f->size >> 1);
 
1305
                }
 
1306
            } else if (f->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
 
1307
                /* For extended frame, level is unknown, so we just set 
 
1308
                 * it to NORMAL_LEVEL. 
 
1309
                 */
 
1310
                level = NORMAL_LEVEL;
 
1311
            }
 
1312
 
 
1313
            cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff;
 
1314
 
 
1315
            /* Put the frame to all listeners. */
 
1316
            for (j=0; j < cport->listener_cnt; ++j) 
 
1317
            {
 
1318
                struct conf_port *listener;
 
1319
 
 
1320
                listener = conf->ports[cport->listener_slots[j]];
 
1321
 
 
1322
                /* Skip if this listener doesn't want to receive audio */
 
1323
                if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
 
1324
                    pj_add_timestamp32(&listener->ts_tx, 
 
1325
                                       listener->samples_per_frame);
 
1326
                    listener->tx_level = 0;
 
1327
                    continue;
 
1328
                }
 
1329
            
 
1330
                status = write_frame(listener, f);
 
1331
                if (status != PJ_SUCCESS) {
 
1332
                    listener->tx_level = 0;
 
1333
                    continue;
 
1334
                }
 
1335
 
 
1336
                /* Set listener TX level based on transmitter RX level & 
 
1337
                 * listener TX level.
 
1338
                 */
 
1339
                listener->tx_level = (cport->rx_level * listener->tx_adj_level)
 
1340
                                     >> 8;
 
1341
            }
 
1342
        }
 
1343
    }
 
1344
 
 
1345
    /* Keep alive. Update TX timestamp and send frame type NONE to all 
 
1346
     * underflow ports at their own clock.
 
1347
     */
 
1348
    for (i=1, ci=1; i<conf->max_ports && ci<conf->port_cnt; ++i) {
 
1349
        struct conf_port *cport = conf->ports[i];
 
1350
 
 
1351
        /* Skip empty port. */
 
1352
        if (!cport)
 
1353
            continue;
 
1354
 
 
1355
        /* Var "ci" is to count how many ports have been visited so far. */
 
1356
        ++ci;
 
1357
 
 
1358
        if (cport->tx_setting==PJMEDIA_PORT_MUTE || cport->transmitter_cnt==0)
 
1359
        {
 
1360
            pjmedia_frame_ext *f;
 
1361
            
 
1362
            /* Clear left-over samples in tx_buffer, if any, so that it won't
 
1363
             * be transmitted next time we have audio signal.
 
1364
             */
 
1365
            f = (pjmedia_frame_ext*)cport->tx_buf;
 
1366
            f->base.type = PJMEDIA_FRAME_TYPE_NONE;
 
1367
            f->base.size = 0;
 
1368
            f->samples_cnt = 0;
 
1369
            f->subframe_cnt = 0;
 
1370
            
 
1371
            cport->tx_level = 0;
 
1372
 
 
1373
            while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_tx) > 0)
 
1374
            {
 
1375
                if (cport->tx_setting == PJMEDIA_PORT_ENABLE) {
 
1376
                    pjmedia_frame tmp_f;
 
1377
 
 
1378
                    tmp_f.timestamp = cport->ts_tx;
 
1379
                    tmp_f.type = PJMEDIA_FRAME_TYPE_NONE;
 
1380
                    tmp_f.buf = NULL;
 
1381
                    tmp_f.size = 0;
 
1382
 
 
1383
                    pjmedia_port_put_frame(cport->port, &tmp_f);
 
1384
                    pj_add_timestamp32(&cport->ts_tx, cport->samples_per_frame);
 
1385
                }
 
1386
            }
 
1387
        }
 
1388
    }
 
1389
 
 
1390
    /* Return sound playback frame. */
 
1391
    do {
 
1392
        struct conf_port *this_cport = conf->ports[this_port->port_data.ldata];
 
1393
        pjmedia_frame *f_src = (pjmedia_frame*) this_cport->tx_buf;
 
1394
 
 
1395
        frame->type = f_src->type;
 
1396
 
 
1397
        if (f_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
 
1398
            pjmedia_frame_ext *f_src_ = (pjmedia_frame_ext*)f_src;
 
1399
            pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frame;
 
1400
            pjmedia_frame_ext_subframe *sf;
 
1401
            unsigned samples_per_subframe;
 
1402
            
 
1403
            if (f_src_->samples_cnt < this_cport->samples_per_frame) {
 
1404
                f_dst->base.type = PJMEDIA_FRAME_TYPE_NONE;
 
1405
                f_dst->samples_cnt = 0;
 
1406
                f_dst->subframe_cnt = 0;
 
1407
                break;
 
1408
            }
 
1409
 
 
1410
            f_dst->samples_cnt = 0;
 
1411
            f_dst->subframe_cnt = 0;
 
1412
            i = 0;
 
1413
            samples_per_subframe = f_src_->samples_cnt / f_src_->subframe_cnt;
 
1414
 
 
1415
 
 
1416
            while (f_dst->samples_cnt < this_cport->samples_per_frame) {
 
1417
                sf = pjmedia_frame_ext_get_subframe(f_src_, i++);
 
1418
                pj_assert(sf);
 
1419
                pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
 
1420
                                                  samples_per_subframe);
 
1421
            }
 
1422
 
 
1423
            /* Shift left TX buffer. */
 
1424
            pjmedia_frame_ext_pop_subframes(f_src_, i);
 
1425
 
 
1426
        } else if (f_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
 
1427
            if ((f_src->size>>1) < this_cport->samples_per_frame) {
 
1428
                frame->type = PJMEDIA_FRAME_TYPE_NONE;
 
1429
                frame->size = 0;
 
1430
                break;
 
1431
            }
 
1432
 
 
1433
            pjmedia_copy_samples((pj_int16_t*)frame->buf, 
 
1434
                                 (pj_int16_t*)f_src->buf, 
 
1435
                                 this_cport->samples_per_frame);
 
1436
            frame->size = this_cport->samples_per_frame << 1;
 
1437
 
 
1438
            /* Shift left TX buffer. */
 
1439
            f_src->size -= frame->size;
 
1440
            if (f_src->size)
 
1441
                pjmedia_move_samples((pj_int16_t*)f_src->buf,
 
1442
                                     (pj_int16_t*)f_src->buf + 
 
1443
                                     this_cport->samples_per_frame,
 
1444
                                     f_src->size >> 1);
 
1445
        } else { /* PJMEDIA_FRAME_TYPE_NONE */
 
1446
            pjmedia_frame_ext *f_src_ = (pjmedia_frame_ext*)f_src;
 
1447
 
 
1448
            /* Reset source/TX buffer */
 
1449
            f_src_->base.size = 0;
 
1450
            f_src_->samples_cnt = 0;
 
1451
            f_src_->subframe_cnt = 0;
 
1452
        }
 
1453
    } while (0);
 
1454
 
 
1455
    /* Unlock mutex */
 
1456
    pj_mutex_unlock(conf->mutex);
 
1457
 
 
1458
    return PJ_SUCCESS;
 
1459
}
 
1460
 
 
1461
/*
 
1462
 * Recorder callback.
 
1463
 */
 
1464
static pj_status_t put_frame(pjmedia_port *this_port, 
 
1465
                             pjmedia_frame *f)
 
1466
{
 
1467
    pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
 
1468
    struct conf_port *cport;
 
1469
    unsigned j;
 
1470
    pj_int32_t level = 0;
 
1471
 
 
1472
    /* Lock mutex */
 
1473
    pj_mutex_lock(conf->mutex);
 
1474
 
 
1475
    /* Get conf port of this port */
 
1476
    cport = conf->ports[this_port->port_data.ldata];
 
1477
    if (cport == NULL) {
 
1478
        /* Unlock mutex */
 
1479
        pj_mutex_unlock(conf->mutex);
 
1480
        return PJ_SUCCESS;
 
1481
    }
 
1482
 
 
1483
    pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
 
1484
    
 
1485
    /* Skip if this port is muted/disabled. */
 
1486
    if (cport->rx_setting == PJMEDIA_PORT_DISABLE) {
 
1487
        cport->rx_level = 0;
 
1488
        /* Unlock mutex */
 
1489
        pj_mutex_unlock(conf->mutex);
 
1490
        return PJ_SUCCESS;
 
1491
    }
 
1492
 
 
1493
    /* Skip if no port is listening to the microphone */
 
1494
    if (cport->listener_cnt == 0) {
 
1495
        cport->rx_level = 0;
 
1496
        /* Unlock mutex */
 
1497
        pj_mutex_unlock(conf->mutex);
 
1498
        return PJ_SUCCESS;
 
1499
    }
 
1500
 
 
1501
    /* Calculate & adjust RX level. */
 
1502
    if (f->type == PJMEDIA_FRAME_TYPE_AUDIO) {
 
1503
        if (cport->rx_adj_level != NORMAL_LEVEL) {
 
1504
            pj_int16_t *p = (pj_int16_t*)f->buf;
 
1505
            pj_int16_t *end;
 
1506
 
 
1507
            end = p + (f->size >> 1);
 
1508
            while (p < end) {
 
1509
                pj_int32_t itemp = *p;
 
1510
 
 
1511
                /* Adjust the level */
 
1512
                itemp = (itemp * cport->rx_adj_level) >> 7;
 
1513
 
 
1514
                /* Clip the signal if it's too loud */
 
1515
                if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
 
1516
                else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
 
1517
 
 
1518
                level += PJ_ABS(itemp);
 
1519
 
 
1520
                /* Put back in the buffer. */
 
1521
                *p = (pj_int16_t)itemp;
 
1522
                ++p;
 
1523
            }
 
1524
            level /= (f->size >> 1);
 
1525
        } else {
 
1526
            level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
 
1527
                                            f->size >> 1);
 
1528
        }
 
1529
    } else if (f->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
 
1530
        /* For extended frame, level is unknown, so we just set 
 
1531
         * it to NORMAL_LEVEL. 
 
1532
         */
 
1533
        level = NORMAL_LEVEL;
 
1534
    }
 
1535
 
 
1536
    cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff;
 
1537
 
 
1538
    /* Put the frame to all listeners. */
 
1539
    for (j=0; j < cport->listener_cnt; ++j) 
 
1540
    {
 
1541
        struct conf_port *listener;
 
1542
        pj_status_t status;
 
1543
 
 
1544
        listener = conf->ports[cport->listener_slots[j]];
 
1545
 
 
1546
        /* Skip if this listener doesn't want to receive audio */
 
1547
        if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
 
1548
            pj_add_timestamp32(&listener->ts_tx, 
 
1549
                               listener->samples_per_frame);
 
1550
            listener->tx_level = 0;
 
1551
            continue;
 
1552
        }
 
1553
 
 
1554
        /* Skip loopback for now. */
 
1555
        if (listener == cport) {
 
1556
            pj_add_timestamp32(&listener->ts_tx, 
 
1557
                               listener->samples_per_frame);
 
1558
            listener->tx_level = 0;
 
1559
            continue;
 
1560
        }
 
1561
            
 
1562
        status = write_frame(listener, f);
 
1563
        if (status != PJ_SUCCESS) {
 
1564
            listener->tx_level = 0;
 
1565
            continue;
 
1566
        }
 
1567
 
 
1568
        /* Set listener TX level based on transmitter RX level & listener
 
1569
         * TX level.
 
1570
         */
 
1571
        listener->tx_level = (cport->rx_level * listener->tx_adj_level) >> 8;
 
1572
    }
 
1573
 
 
1574
    /* Unlock mutex */
 
1575
    pj_mutex_unlock(conf->mutex);
 
1576
 
 
1577
    return PJ_SUCCESS;
 
1578
}
 
1579
 
 
1580
#endif