~ubuntu-branches/ubuntu/raring/sflphone/raring

« back to all changes in this revision

Viewing changes to sflphone-common/libs/pjproject/pjmedia/src/pjmedia/conf_switch.c

  • Committer: Package Import Robot
  • Author(s): Francois Marier
  • Date: 2011-11-25 13:24:12 UTC
  • mfrom: (4.1.10 sid)
  • Revision ID: package-import@ubuntu.com-20111125132412-dc4qvhyosk74cd42
Tags: 1.0.1-4
Don't assume that arch:all packages will get built (closes: #649726)

Show diffs side-by-side

added added

removed removed

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