1
/* $Id: conf_switch.c 2725 2009-05-30 09:23:03Z nanang $ */
3
* Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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.
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.
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
20
* Additional permission under GNU GPL version 3 section 7:
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.
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>
39
#include <pj/assert.h>
42
#include <pj/string.h>
44
#if defined(PJMEDIA_CONF_USE_SWITCH_BOARD) && PJMEDIA_CONF_USE_SWITCH_BOARD!=0
46
/* CONF_DEBUG enables detailed operation of the conference bridge.
47
* Beware that it prints large amounts of logs (several lines per frame).
52
# define TRACE_(x) PJ_LOG(5,x)
57
#define THIS_FILE "conf_switch.c"
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)
69
* DON'T GET CONFUSED WITH TX/RX!!
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
79
* This is a port connected to conference bridge.
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. */
92
/* Shortcut for port info. */
93
pjmedia_port_info *info;
95
/* Calculated signal levels: */
96
unsigned tx_level; /**< Last tx level to this port. */
97
unsigned rx_level; /**< Last rx level from this port. */
99
/* The normalized signal level adjustment.
100
* A value of 128 (NORMAL_LEVEL) means there's no adjustment.
102
unsigned tx_adj_level; /**< Adjustment for TX. */
103
unsigned rx_adj_level; /**< Adjustment for RX. */
105
pj_timestamp ts_clock;
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.
114
pj_uint8_t tx_buf[BUFFER_SIZE]; /**< Tx buffer. */
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. */
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);
146
static pj_status_t create_conf_port( pj_pool_t *pool,
149
const pj_str_t *name,
150
struct conf_port **p_conf_port)
152
struct conf_port *conf_port;
155
PJ_ASSERT_RETURN(pool && conf && port && name && p_conf_port, PJ_EINVAL);
158
conf_port = PJ_POOL_ZALLOC_T(pool, struct conf_port);
161
pj_strdup_with_null(pool, &conf_port->name, name);
163
/* Default has tx and rx enabled. */
164
conf_port->rx_setting = PJMEDIA_PORT_ENABLE;
165
conf_port->tx_setting = PJMEDIA_PORT_ENABLE;
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;
171
/* Create transmit flag array */
172
conf_port->listener_slots = (SLOT_TYPE*)
174
conf->max_ports * sizeof(SLOT_TYPE));
175
PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM);
177
/* Save some port's infos, for convenience. */
178
conf_port->port = port;
179
conf_port->info = &port->info;
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);
186
*p_conf_port = conf_port;
191
* Create port zero for the sound device.
193
static pj_status_t create_sound_port( pj_pool_t *pool,
196
struct conf_port *conf_port;
197
pj_str_t name = { "Master/sound", 12 };
200
status = create_conf_port(pool, conf, conf->master_port, &name, &conf_port);
201
if (status != PJ_SUCCESS)
204
/* Add the port to the bridge */
206
conf->ports[0] = conf_port;
209
PJ_LOG(5,(THIS_FILE, "Sound device successfully created for port 0"));
214
* Create conference bridge.
216
PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,
219
unsigned channel_count,
220
unsigned samples_per_frame,
221
unsigned bits_per_sample,
223
pjmedia_conf **p_conf )
226
const pj_str_t name = { "Conf", 4 };
229
/* Can only accept 16bits per sample, for now.. */
230
PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
232
PJ_LOG(5,(THIS_FILE, "Creating conference bridge with %d ports",
235
/* Create and init conf structure. */
236
conf = PJ_POOL_ZALLOC_T(pool, pjmedia_conf);
237
PJ_ASSERT_RETURN(conf, PJ_ENOMEM);
239
conf->ports = (struct conf_port**)
240
pj_pool_zalloc(pool, max_ports*sizeof(void*));
241
PJ_ASSERT_RETURN(conf->ports, PJ_ENOMEM);
243
conf->options = options;
244
conf->max_ports = max_ports;
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);
250
pjmedia_port_info_init(&conf->master_port->info, &name, SIGNATURE,
251
clock_rate, channel_count, bits_per_sample,
254
conf->master_port->port_data.pdata = conf;
255
conf->master_port->port_data.ldata = 0;
257
conf->master_port->get_frame = &get_frame;
258
conf->master_port->put_frame = &put_frame;
259
conf->master_port->on_destroy = &destroy_port;
262
/* Create port zero for sound device. */
263
status = create_sound_port(pool, conf);
264
if (status != PJ_SUCCESS)
268
status = pj_mutex_create_recursive(pool, "conf", &conf->mutex);
269
if (status != PJ_SUCCESS)
281
* Pause sound device.
283
static pj_status_t pause_sound( pjmedia_conf *conf )
291
* Resume sound device.
293
static pj_status_t resume_sound( pjmedia_conf *conf )
302
* Destroy conference bridge.
304
PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf )
306
PJ_ASSERT_RETURN(conf != NULL, PJ_EINVAL);
309
pj_mutex_destroy(conf->mutex);
316
* Destroy the master port (will destroy the conference)
318
static pj_status_t destroy_port(pjmedia_port *this_port)
320
pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
321
return pjmedia_conf_destroy(conf);
325
* Get port zero interface.
327
PJ_DEF(pjmedia_port*) pjmedia_conf_get_master_port(pjmedia_conf *conf)
330
PJ_ASSERT_RETURN(conf != NULL, NULL);
332
/* Can only return port interface when PJMEDIA_CONF_NO_DEVICE was
333
* present in the option.
335
PJ_ASSERT_RETURN((conf->options & PJMEDIA_CONF_NO_DEVICE) != 0, NULL);
337
return conf->master_port;
342
* Set master port name.
344
PJ_DEF(pj_status_t) pjmedia_conf_set_port0_name(pjmedia_conf *conf,
345
const pj_str_t *name)
350
PJ_ASSERT_RETURN(conf != NULL && name != NULL, PJ_EINVAL);
353
if (len > sizeof(conf->master_name_buf))
354
len = sizeof(conf->master_name_buf);
356
if (len > 0) pj_memcpy(conf->master_name_buf, name->ptr, len);
358
conf->ports[0]->name.ptr = conf->master_name_buf;
359
conf->ports[0]->name.slen = len;
361
conf->master_port->info.name = conf->ports[0]->name;
367
* Add stream port to the conference bridge.
369
PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf,
371
pjmedia_port *strm_port,
372
const pj_str_t *port_name,
375
struct conf_port *conf_port;
379
PJ_ASSERT_RETURN(conf && pool && strm_port, PJ_EINVAL);
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,
385
PJ_ASSERT_RETURN(conf->bits_per_sample == strm_port->info.bits_per_sample,
389
/* Port's samples per frame should be equal to or multiplication of
390
* conference's samples per frame.
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);
401
/* If port_name is not specified, use the port's name */
403
port_name = &strm_port->info.name;
405
pj_mutex_lock(conf->mutex);
407
if (conf->port_cnt >= conf->max_ports) {
408
pj_assert(!"Too many ports");
409
pj_mutex_unlock(conf->mutex);
413
/* Find empty port in the conference bridge. */
414
for (index=0; index < conf->max_ports; ++index) {
415
if (conf->ports[index] == NULL)
419
pj_assert(index != conf->max_ports);
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);
429
conf_port->slot = index;
430
conf->ports[index] = conf_port;
438
pj_mutex_unlock(conf->mutex);
447
PJ_DEF(pj_status_t) pjmedia_conf_add_passive_port( pjmedia_conf *conf,
449
const pj_str_t *name,
451
unsigned channel_count,
452
unsigned samples_per_frame,
453
unsigned bits_per_sample,
456
pjmedia_port **p_port )
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);
475
* Change TX and RX settings for the port.
477
PJ_DEF(pj_status_t) pjmedia_conf_configure_port( pjmedia_conf *conf,
482
struct conf_port *conf_port;
484
/* Check arguments */
485
PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
487
pj_mutex_lock(conf->mutex);
489
/* Port must be valid. */
490
conf_port = conf->ports[slot];
491
if (conf_port == NULL) {
492
pj_mutex_unlock(conf->mutex);
496
if (tx != PJMEDIA_PORT_NO_CHANGE)
497
conf_port->tx_setting = tx;
499
if (rx != PJMEDIA_PORT_NO_CHANGE)
500
conf_port->rx_setting = rx;
502
pj_mutex_unlock(conf->mutex);
511
PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf,
516
struct conf_port *src_port, *dst_port;
517
pj_bool_t start_sound = PJ_FALSE;
520
/* Check arguments */
521
PJ_ASSERT_RETURN(conf && src_slot<conf->max_ports &&
522
sink_slot<conf->max_ports, PJ_EINVAL);
524
/* For now, level MUST be zero. */
525
PJ_ASSERT_RETURN(level == 0, PJ_EINVAL);
527
pj_mutex_lock(conf->mutex);
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);
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)
541
pj_mutex_unlock(conf->mutex);
542
return PJMEDIA_ENOTCOMPATIBLE;
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;
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;
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))
563
pj_mutex_unlock(conf->mutex);
564
return PJMEDIA_ENCSAMPLESPFRAME;
567
/* If sink is currently listening to other ports, it needs to be released
568
* first before the new connection made.
570
if (dst_port->transmitter_cnt > 0) {
572
pj_bool_t transmitter_found = PJ_FALSE;
574
pj_assert(dst_port->transmitter_cnt == 1);
575
for (j=0; j<conf->max_ports && !transmitter_found; ++j) {
576
if (conf->ports[j]) {
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;
591
pj_assert(dst_port->transmitter_cnt == 0);
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)
600
if (i == src_port->listener_cnt) {
601
src_port->listener_slots[src_port->listener_cnt] = sink_slot;
603
++src_port->listener_cnt;
604
++dst_port->transmitter_cnt;
606
if (conf->connect_cnt == 1)
609
PJ_LOG(4,(THIS_FILE,"Port %d (%.*s) transmitting to port %d (%.*s)",
611
(int)src_port->name.slen,
614
(int)dst_port->name.slen,
615
dst_port->name.ptr));
618
pj_mutex_unlock(conf->mutex);
620
/* Sound device must be started without mutex, otherwise the
621
* sound thread will deadlock (?)
633
PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf,
637
struct conf_port *src_port, *dst_port;
640
/* Check arguments */
641
PJ_ASSERT_RETURN(conf && src_slot<conf->max_ports &&
642
sink_slot<conf->max_ports, PJ_EINVAL);
644
pj_mutex_lock(conf->mutex);
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);
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)
660
if (i != src_port->listener_cnt) {
661
pjmedia_frame_ext *f;
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);
670
--src_port->listener_cnt;
671
--dst_port->transmitter_cnt;
673
/* Cleanup listener TX buffer. */
674
f = (pjmedia_frame_ext*)dst_port->tx_buf;
675
f->base.type = PJMEDIA_FRAME_TYPE_NONE;
681
"Port %d (%.*s) stop transmitting to port %d (%.*s)",
683
(int)src_port->name.slen,
686
(int)dst_port->name.slen,
687
dst_port->name.ptr));
690
pj_mutex_unlock(conf->mutex);
692
if (conf->connect_cnt == 0) {
700
* Get number of ports currently registered to the conference bridge.
702
PJ_DEF(unsigned) pjmedia_conf_get_port_count(pjmedia_conf *conf)
704
return conf->port_cnt;
708
* Get total number of ports connections currently set up in the bridge.
710
PJ_DEF(unsigned) pjmedia_conf_get_connect_count(pjmedia_conf *conf)
712
return conf->connect_cnt;
717
* Remove the specified port.
719
PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,
722
struct conf_port *conf_port;
725
/* Check arguments */
726
PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL);
728
/* Suspend the sound devices.
729
* Don't want to remove port while port is being accessed by sound
733
pj_mutex_lock(conf->mutex);
735
/* Port must be valid. */
736
conf_port = conf->ports[port];
737
if (conf_port == NULL) {
738
pj_mutex_unlock(conf->mutex);
742
conf_port->tx_setting = PJMEDIA_PORT_DISABLE;
743
conf_port->rx_setting = PJMEDIA_PORT_DISABLE;
745
/* Remove this port from transmit array of other ports. */
746
for (i=0; i<conf->max_ports; ++i) {
748
struct conf_port *src_port;
750
src_port = conf->ports[i];
755
if (src_port->listener_cnt == 0)
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);
764
--src_port->listener_cnt;
770
/* Update transmitter_cnt of ports we're transmitting to */
771
while (conf_port->listener_cnt) {
773
struct conf_port *dst_port;
774
pjmedia_frame_ext *f;
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);
783
/* Cleanup & reinit listener TX buffer. */
784
f = (pjmedia_frame_ext*)dst_port->tx_buf;
785
f->base.type = PJMEDIA_FRAME_TYPE_NONE;
791
/* Remove the port. */
792
conf->ports[port] = NULL;
795
pj_mutex_unlock(conf->mutex);
798
/* Stop sound if there's no connection. */
799
if (conf->connect_cnt == 0) {
810
PJ_DEF(pj_status_t) pjmedia_conf_enum_ports( pjmedia_conf *conf,
816
PJ_ASSERT_RETURN(conf && p_count && ports, PJ_EINVAL);
819
pj_mutex_lock(conf->mutex);
821
for (i=0; i<conf->max_ports && count<*p_count; ++i) {
829
pj_mutex_unlock(conf->mutex);
838
PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf,
840
pjmedia_conf_port_info *info)
842
struct conf_port *conf_port;
844
/* Check arguments */
845
PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
848
pj_mutex_lock(conf->mutex);
850
/* Port must be valid. */
851
conf_port = conf->ports[slot];
852
if (conf_port == NULL) {
853
pj_mutex_unlock(conf->mutex);
857
pj_bzero(info, sizeof(pjmedia_conf_port_info));
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;
875
pj_mutex_unlock(conf->mutex);
881
PJ_DEF(pj_status_t) pjmedia_conf_get_ports_info(pjmedia_conf *conf,
883
pjmedia_conf_port_info info[])
887
PJ_ASSERT_RETURN(conf && size && info, PJ_EINVAL);
890
pj_mutex_lock(conf->mutex);
892
for (i=0; i<conf->max_ports && count<*size; ++i) {
896
pjmedia_conf_get_port_info(conf, i, &info[count]);
901
pj_mutex_unlock(conf->mutex);
911
PJ_DEF(pj_status_t) pjmedia_conf_get_signal_level( pjmedia_conf *conf,
916
struct conf_port *conf_port;
918
/* Check arguments */
919
PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
922
pj_mutex_lock(conf->mutex);
924
/* Port must be valid. */
925
conf_port = conf->ports[slot];
926
if (conf_port == NULL) {
927
pj_mutex_unlock(conf->mutex);
931
if (tx_level != NULL) {
932
*tx_level = conf_port->tx_level;
935
if (rx_level != NULL)
936
*rx_level = conf_port->rx_level;
939
pj_mutex_unlock(conf->mutex);
946
* Adjust RX level of individual port.
948
PJ_DEF(pj_status_t) pjmedia_conf_adjust_rx_level( pjmedia_conf *conf,
952
struct conf_port *conf_port;
954
/* Check arguments */
955
PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
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);
961
PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
964
pj_mutex_lock(conf->mutex);
966
/* Port must be valid. */
967
conf_port = conf->ports[slot];
968
if (conf_port == NULL) {
969
pj_mutex_unlock(conf->mutex);
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,
977
/* Set normalized adjustment level. */
978
conf_port->rx_adj_level = adj_level + NORMAL_LEVEL;
981
pj_mutex_unlock(conf->mutex);
988
* Adjust TX level of individual port.
990
PJ_DEF(pj_status_t) pjmedia_conf_adjust_tx_level( pjmedia_conf *conf,
994
struct conf_port *conf_port;
996
/* Check arguments */
997
PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
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);
1003
PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
1006
pj_mutex_lock(conf->mutex);
1008
/* Port must be valid. */
1009
conf_port = conf->ports[slot];
1010
if (conf_port == NULL) {
1011
pj_mutex_unlock(conf->mutex);
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,
1019
/* Set normalized adjustment level. */
1020
conf_port->tx_adj_level = adj_level + NORMAL_LEVEL;
1023
pj_mutex_unlock(conf->mutex);
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.
1031
static pj_status_t write_frame(struct conf_port *cport_dst,
1032
const pjmedia_frame *frm_src)
1034
pjmedia_frame *frm_dst = (pjmedia_frame*)cport_dst->tx_buf;
1036
PJ_TODO(MAKE_SURE_DEST_FRAME_HAS_ENOUGH_SPACE);
1038
frm_dst->type = frm_src->type;
1039
frm_dst->timestamp = cport_dst->ts_tx;
1041
if (frm_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1043
pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frm_src;
1044
pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
1047
for (i = 0; i < f_src->subframe_cnt; ++i) {
1048
pjmedia_frame_ext_subframe *sf;
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);
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.
1060
if (f_dst->samples_cnt >= cport_dst->info->samples_per_frame)
1062
if (cport_dst->slot) {
1063
pjmedia_port_put_frame(cport_dst->port,
1064
(pjmedia_frame*)f_dst);
1066
/* Reset TX buffer. */
1067
f_dst->subframe_cnt = 0;
1068
f_dst->samples_cnt = 0;
1071
/* Update TX timestamp. */
1072
pj_add_timestamp32(&cport_dst->ts_tx,
1073
cport_dst->info->samples_per_frame);
1077
} else if (frm_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
1079
pj_int16_t *f_start, *f_end;
1081
f_start = (pj_int16_t*)frm_src->buf;
1082
f_end = f_start + (frm_src->size >> 1);
1084
while (f_start < f_end) {
1085
unsigned nsamples_to_copy, nsamples_req;
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 -
1091
if (nsamples_to_copy > nsamples_req)
1092
nsamples_to_copy = nsamples_req;
1094
/* Adjust TX level. */
1095
if (cport_dst->tx_adj_level != NORMAL_LEVEL) {
1096
pj_int16_t *p, *p_end;
1099
p_end = p + nsamples_to_copy;
1101
pj_int32_t itemp = *p;
1103
/* Adjust the level */
1104
itemp = (itemp * cport_dst->tx_adj_level) >> 7;
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;
1110
/* Put back in the buffer. */
1111
*p = (pj_int16_t)itemp;
1116
pjmedia_copy_samples((pj_int16_t*)frm_dst->buf + (frm_dst->size>>1),
1119
frm_dst->size += nsamples_to_copy << 1;
1120
f_start += nsamples_to_copy;
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.
1126
if ((frm_dst->size >> 1) == cport_dst->info->samples_per_frame)
1128
if (cport_dst->slot) {
1129
pjmedia_port_put_frame(cport_dst->port, frm_dst);
1131
/* Reset TX buffer. */
1135
/* Update TX timestamp. */
1136
pj_add_timestamp32(&cport_dst->ts_tx,
1137
cport_dst->info->samples_per_frame);
1141
} else if (frm_src->type == PJMEDIA_FRAME_TYPE_NONE) {
1143
/* Check port format. */
1144
if (cport_dst->port &&
1145
cport_dst->port->info.format.id == PJMEDIA_FORMAT_L16)
1147
/* When there is already some samples in listener's TX buffer,
1148
* pad the buffer with "zero samples".
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));
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);
1160
/* Reset TX buffer. */
1164
/* Update TX timestamp. */
1165
pj_add_timestamp32(&cport_dst->ts_tx,
1166
cport_dst->info->samples_per_frame);
1169
pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
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);
1178
/* Reset TX buffer. */
1179
f_dst->subframe_cnt = 0;
1180
f_dst->samples_cnt = 0;
1183
/* Update TX timestamp. */
1184
pj_add_timestamp32(&cport_dst->ts_tx,
1185
cport_dst->info->samples_per_frame);
1189
/* Synchronize clock. */
1190
while (pj_cmp_timestamp(&cport_dst->ts_clock,
1191
&cport_dst->ts_tx) > 0)
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);
1198
/* Update TX timestamp. */
1199
pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->info->samples_per_frame);
1209
static pj_status_t get_frame(pjmedia_port *this_port,
1210
pjmedia_frame *frame)
1212
pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
1216
pj_mutex_lock(conf->mutex);
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.
1223
for (i=1, ci=1; i<conf->max_ports && ci<conf->port_cnt; ++i) {
1224
struct conf_port *cport = conf->ports[i];
1226
/* Skip empty port. */
1230
/* Var "ci" is to count how many ports have been visited so far. */
1233
/* Update clock of the port. */
1234
pj_add_timestamp32(&cport->ts_clock,
1235
conf->master_port->info.samples_per_frame);
1237
/* Skip if we're not allowed to receive from this port or
1238
* the port doesn't have listeners.
1240
if (cport->rx_setting == PJMEDIA_PORT_DISABLE ||
1241
cport->listener_cnt == 0)
1243
cport->rx_level = 0;
1244
pj_add_timestamp32(&cport->ts_rx,
1245
conf->master_port->info.samples_per_frame);
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.
1253
while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_rx) > 0) {
1254
pjmedia_frame *f = (pjmedia_frame*)conf->buf;
1257
pj_int32_t level = 0;
1259
pj_add_timestamp32(&cport->ts_rx, cport->info->samples_per_frame);
1261
f->buf = &conf->buf[sizeof(pjmedia_frame)];
1262
f->size = cport->info->samples_per_frame<<1;
1264
/* Get frame from port. */
1265
status = pjmedia_port_get_frame(cport->port, f);
1266
if (status != PJ_SUCCESS)
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;
1275
end = p + (f->size >> 1);
1277
pj_int32_t itemp = *p;
1279
/* Adjust the level */
1280
itemp = (itemp * cport->rx_adj_level) >> 7;
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;
1286
level += PJ_ABS(itemp);
1288
/* Put back in the buffer. */
1289
*p = (pj_int16_t)itemp;
1292
level /= (f->size >> 1);
1294
level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
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.
1301
level = NORMAL_LEVEL;
1304
cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff;
1306
/* Put the frame to all listeners. */
1307
for (j=0; j < cport->listener_cnt; ++j)
1309
struct conf_port *listener;
1311
listener = conf->ports[cport->listener_slots[j]];
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;
1321
status = write_frame(listener, f);
1322
if (status != PJ_SUCCESS) {
1323
listener->tx_level = 0;
1327
/* Set listener TX level based on transmitter RX level &
1328
* listener TX level.
1330
listener->tx_level = (cport->rx_level * listener->tx_adj_level)
1336
/* Keep alive. Update TX timestamp and send frame type NONE to all
1337
* underflow ports at their own clock.
1339
for (i=1, ci=1; i<conf->max_ports && ci<conf->port_cnt; ++i) {
1340
struct conf_port *cport = conf->ports[i];
1342
/* Skip empty port. */
1346
/* Var "ci" is to count how many ports have been visited so far. */
1349
if (cport->tx_setting==PJMEDIA_PORT_MUTE || cport->transmitter_cnt==0)
1351
pjmedia_frame_ext *f;
1353
/* Clear left-over samples in tx_buffer, if any, so that it won't
1354
* be transmitted next time we have audio signal.
1356
f = (pjmedia_frame_ext*)cport->tx_buf;
1357
f->base.type = PJMEDIA_FRAME_TYPE_NONE;
1360
f->subframe_cnt = 0;
1362
cport->tx_level = 0;
1364
while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_tx) > 0)
1366
if (cport->tx_setting == PJMEDIA_PORT_ENABLE) {
1367
pjmedia_frame tmp_f;
1369
tmp_f.timestamp = cport->ts_tx;
1370
tmp_f.type = PJMEDIA_FRAME_TYPE_NONE;
1374
pjmedia_port_put_frame(cport->port, &tmp_f);
1375
pj_add_timestamp32(&cport->ts_tx, cport->info->samples_per_frame);
1381
/* Return sound playback frame. */
1383
struct conf_port *this_cport = conf->ports[this_port->port_data.ldata];
1384
pjmedia_frame *f_src = (pjmedia_frame*) this_cport->tx_buf;
1386
frame->type = f_src->type;
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;
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;
1401
f_dst->samples_cnt = 0;
1402
f_dst->subframe_cnt = 0;
1404
samples_per_subframe = f_src_->samples_cnt / f_src_->subframe_cnt;
1407
while (f_dst->samples_cnt < this_cport->info->samples_per_frame) {
1408
sf = pjmedia_frame_ext_get_subframe(f_src_, i++);
1410
pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
1411
samples_per_subframe);
1414
/* Shift left TX buffer. */
1415
pjmedia_frame_ext_pop_subframes(f_src_, i);
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;
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;
1429
/* Shift left TX buffer. */
1430
f_src->size -= frame->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,
1436
} else { /* PJMEDIA_FRAME_TYPE_NONE */
1437
pjmedia_frame_ext *f_src_ = (pjmedia_frame_ext*)f_src;
1439
/* Reset source/TX buffer */
1440
f_src_->base.size = 0;
1441
f_src_->samples_cnt = 0;
1442
f_src_->subframe_cnt = 0;
1447
pj_mutex_unlock(conf->mutex);
1453
* Recorder callback.
1455
static pj_status_t put_frame(pjmedia_port *this_port,
1456
const pjmedia_frame *f)
1458
pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
1459
struct conf_port *cport;
1461
pj_int32_t level = 0;
1464
pj_mutex_lock(conf->mutex);
1466
/* Get conf port of this port */
1467
cport = conf->ports[this_port->port_data.ldata];
1468
if (cport == NULL) {
1470
pj_mutex_unlock(conf->mutex);
1474
pj_add_timestamp32(&cport->ts_rx, cport->info->samples_per_frame);
1476
/* Skip if this port is muted/disabled. */
1477
if (cport->rx_setting == PJMEDIA_PORT_DISABLE) {
1478
cport->rx_level = 0;
1480
pj_mutex_unlock(conf->mutex);
1484
/* Skip if no port is listening to the microphone */
1485
if (cport->listener_cnt == 0) {
1486
cport->rx_level = 0;
1488
pj_mutex_unlock(conf->mutex);
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;
1498
end = p + (f->size >> 1);
1500
pj_int32_t itemp = *p;
1502
/* Adjust the level */
1503
itemp = (itemp * cport->rx_adj_level) >> 7;
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;
1509
level += PJ_ABS(itemp);
1511
/* Put back in the buffer. */
1512
*p = (pj_int16_t)itemp;
1515
level /= (f->size >> 1);
1517
level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
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.
1524
level = NORMAL_LEVEL;
1527
cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff;
1529
/* Put the frame to all listeners. */
1530
for (j=0; j < cport->listener_cnt; ++j)
1532
struct conf_port *listener;
1535
listener = conf->ports[cport->listener_slots[j]];
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;
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;
1553
status = write_frame(listener, f);
1554
if (status != PJ_SUCCESS) {
1555
listener->tx_level = 0;
1559
/* Set listener TX level based on transmitter RX level & listener
1562
listener->tx_level = (cport->rx_level * listener->tx_adj_level) >> 8;
1566
pj_mutex_unlock(conf->mutex);