1
/* $Id: conf_switch.c 4122 2012-05-14 11:04:46Z bennylp $ */
3
* Copyright (C) 2008-2011 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
#include <pjmedia/conference.h>
21
#include <pjmedia/alaw_ulaw.h>
22
#include <pjmedia/errno.h>
23
#include <pjmedia/port.h>
24
#include <pjmedia/silencedet.h>
25
#include <pjmedia/sound_port.h>
27
#include <pj/assert.h>
31
#include <pj/string.h>
33
#if defined(PJMEDIA_CONF_USE_SWITCH_BOARD) && PJMEDIA_CONF_USE_SWITCH_BOARD!=0
35
/* CONF_DEBUG enables detailed operation of the conference bridge.
36
* Beware that it prints large amounts of logs (several lines per frame).
41
# define TRACE_(x) PJ_LOG(5,x)
46
#define THIS_FILE "conf_switch.c"
48
#define SIGNATURE PJMEDIA_CONF_SWITCH_SIGNATURE
49
#define SIGNATURE_PORT PJMEDIA_PORT_SIGNATURE('S', 'W', 'T', 'P')
50
#define NORMAL_LEVEL 128
51
#define SLOT_TYPE unsigned
52
#define INVALID_SLOT ((SLOT_TYPE)-1)
53
#define BUFFER_SIZE PJMEDIA_MAX_MTU
54
#define MAX_LEVEL (32767)
55
#define MIN_LEVEL (-32768)
58
* DON'T GET CONFUSED WITH TX/RX!!
60
* TX and RX directions are always viewed from the conference bridge's point
61
* of view, and NOT from the port's point of view. So TX means the bridge
62
* is transmitting to the port, RX means the bridge is receiving from the
68
* This is a port connected to conference bridge.
72
SLOT_TYPE slot; /**< Array of listeners. */
73
pj_str_t name; /**< Port name. */
74
pjmedia_port *port; /**< get_frame() and put_frame() */
75
pjmedia_port_op rx_setting; /**< Can we receive from this port */
76
pjmedia_port_op tx_setting; /**< Can we transmit to this port */
77
unsigned listener_cnt; /**< Number of listeners. */
78
SLOT_TYPE *listener_slots;/**< Array of listeners. */
79
unsigned transmitter_cnt;/**<Number of transmitters. */
81
/* Shortcut for port info. */
82
pjmedia_port_info *info;
83
unsigned samples_per_frame;
85
/* Calculated signal levels: */
86
unsigned tx_level; /**< Last tx level to this port. */
87
unsigned rx_level; /**< Last rx level from this port. */
89
/* The normalized signal level adjustment.
90
* A value of 128 (NORMAL_LEVEL) means there's no adjustment.
92
unsigned tx_adj_level; /**< Adjustment for TX. */
93
unsigned rx_adj_level; /**< Adjustment for RX. */
95
pj_timestamp ts_clock;
99
/* Tx buffer is a temporary buffer to be used when there's mismatch
100
* between port's ptime with conference's ptime. This buffer is used as
101
* the source to buffer the samples until there are enough samples to
102
* fulfill a complete frame to be transmitted to the port.
104
pj_uint8_t tx_buf[BUFFER_SIZE]; /**< Tx buffer. */
113
unsigned options; /**< Bitmask options. */
114
unsigned max_ports; /**< Maximum ports. */
115
unsigned port_cnt; /**< Current number of ports. */
116
unsigned connect_cnt; /**< Total number of connections */
117
pjmedia_port *master_port; /**< Port zero's port. */
118
char master_name_buf[80]; /**< Port0 name buffer. */
119
pj_mutex_t *mutex; /**< Conference mutex. */
120
struct conf_port **ports; /**< Array of ports. */
121
pj_uint8_t buf[BUFFER_SIZE]; /**< Common buffer. */
126
static pj_status_t put_frame(pjmedia_port *this_port,
127
pjmedia_frame *frame);
128
static pj_status_t get_frame(pjmedia_port *this_port,
129
pjmedia_frame *frame);
130
static pj_status_t destroy_port(pjmedia_port *this_port);
136
static pj_status_t create_conf_port( pj_pool_t *pool,
139
const pj_str_t *name,
140
struct conf_port **p_conf_port)
142
struct conf_port *conf_port;
145
PJ_ASSERT_RETURN(pool && conf && port && name && p_conf_port, PJ_EINVAL);
148
conf_port = PJ_POOL_ZALLOC_T(pool, struct conf_port);
151
pj_strdup_with_null(pool, &conf_port->name, name);
153
/* Default has tx and rx enabled. */
154
conf_port->rx_setting = PJMEDIA_PORT_ENABLE;
155
conf_port->tx_setting = PJMEDIA_PORT_ENABLE;
157
/* Default level adjustment is 128 (which means no adjustment) */
158
conf_port->tx_adj_level = NORMAL_LEVEL;
159
conf_port->rx_adj_level = NORMAL_LEVEL;
161
/* Create transmit flag array */
162
conf_port->listener_slots = (SLOT_TYPE*)
164
conf->max_ports * sizeof(SLOT_TYPE));
165
PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM);
167
/* Save some port's infos, for convenience. */
168
conf_port->port = port;
169
conf_port->info = &port->info;
170
conf_port->samples_per_frame = PJMEDIA_PIA_SPF(&port->info);
172
/* Init pjmedia_frame structure in the TX buffer. */
173
f = (pjmedia_frame*)conf_port->tx_buf;
174
f->buf = conf_port->tx_buf + sizeof(pjmedia_frame);
177
*p_conf_port = conf_port;
182
* Create port zero for the sound device.
184
static pj_status_t create_sound_port( pj_pool_t *pool,
187
struct conf_port *conf_port;
188
pj_str_t name = { "Master/sound", 12 };
191
status = create_conf_port(pool, conf, conf->master_port, &name, &conf_port);
192
if (status != PJ_SUCCESS)
195
/* Add the port to the bridge */
197
conf->ports[0] = conf_port;
200
PJ_LOG(5,(THIS_FILE, "Sound device successfully created for port 0"));
205
* Create conference bridge.
207
PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,
210
unsigned channel_count,
211
unsigned samples_per_frame,
212
unsigned bits_per_sample,
214
pjmedia_conf **p_conf )
217
const pj_str_t name = { "Conf", 4 };
220
/* Can only accept 16bits per sample, for now.. */
221
PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
223
PJ_LOG(5,(THIS_FILE, "Creating conference bridge with %d ports",
226
/* Create and init conf structure. */
227
conf = PJ_POOL_ZALLOC_T(pool, pjmedia_conf);
228
PJ_ASSERT_RETURN(conf, PJ_ENOMEM);
230
conf->ports = (struct conf_port**)
231
pj_pool_zalloc(pool, max_ports*sizeof(void*));
232
PJ_ASSERT_RETURN(conf->ports, PJ_ENOMEM);
234
conf->options = options;
235
conf->max_ports = max_ports;
237
/* Create and initialize the master port interface. */
238
conf->master_port = PJ_POOL_ZALLOC_T(pool, pjmedia_port);
239
PJ_ASSERT_RETURN(conf->master_port, PJ_ENOMEM);
241
pjmedia_port_info_init(&conf->master_port->info, &name, SIGNATURE,
242
clock_rate, channel_count, bits_per_sample,
245
conf->master_port->port_data.pdata = conf;
246
conf->master_port->port_data.ldata = 0;
248
conf->master_port->get_frame = &get_frame;
249
conf->master_port->put_frame = &put_frame;
250
conf->master_port->on_destroy = &destroy_port;
253
/* Create port zero for sound device. */
254
status = create_sound_port(pool, conf);
255
if (status != PJ_SUCCESS)
259
status = pj_mutex_create_recursive(pool, "conf", &conf->mutex);
260
if (status != PJ_SUCCESS)
272
* Pause sound device.
274
static pj_status_t pause_sound( pjmedia_conf *conf )
282
* Resume sound device.
284
static pj_status_t resume_sound( pjmedia_conf *conf )
293
* Destroy conference bridge.
295
PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf )
297
PJ_ASSERT_RETURN(conf != NULL, PJ_EINVAL);
300
pj_mutex_destroy(conf->mutex);
307
* Destroy the master port (will destroy the conference)
309
static pj_status_t destroy_port(pjmedia_port *this_port)
311
pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
312
return pjmedia_conf_destroy(conf);
316
* Get port zero interface.
318
PJ_DEF(pjmedia_port*) pjmedia_conf_get_master_port(pjmedia_conf *conf)
321
PJ_ASSERT_RETURN(conf != NULL, NULL);
323
/* Can only return port interface when PJMEDIA_CONF_NO_DEVICE was
324
* present in the option.
326
PJ_ASSERT_RETURN((conf->options & PJMEDIA_CONF_NO_DEVICE) != 0, NULL);
328
return conf->master_port;
333
* Set master port name.
335
PJ_DEF(pj_status_t) pjmedia_conf_set_port0_name(pjmedia_conf *conf,
336
const pj_str_t *name)
341
PJ_ASSERT_RETURN(conf != NULL && name != NULL, PJ_EINVAL);
344
if (len > sizeof(conf->master_name_buf))
345
len = sizeof(conf->master_name_buf);
347
if (len > 0) pj_memcpy(conf->master_name_buf, name->ptr, len);
349
conf->ports[0]->name.ptr = conf->master_name_buf;
350
conf->ports[0]->name.slen = len;
352
conf->master_port->info.name = conf->ports[0]->name;
358
* Add stream port to the conference bridge.
360
PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf,
362
pjmedia_port *strm_port,
363
const pj_str_t *port_name,
366
struct conf_port *conf_port;
370
PJ_ASSERT_RETURN(conf && pool && strm_port, PJ_EINVAL);
372
PJ_ASSERT_RETURN(conf->clock_rate == strm_port->info.clock_rate,
373
PJMEDIA_ENCCLOCKRATE);
374
PJ_ASSERT_RETURN(conf->channel_count == strm_port->info.channel_count,
376
PJ_ASSERT_RETURN(conf->bits_per_sample == strm_port->info.bits_per_sample,
380
/* Port's samples per frame should be equal to or multiplication of
381
* conference's samples per frame.
384
Not sure if this is needed!
385
PJ_ASSERT_RETURN((conf->samples_per_frame %
386
strm_port->info.samples_per_frame==0) ||
387
(strm_port->info.samples_per_frame %
388
conf->samples_per_frame==0),
389
PJMEDIA_ENCSAMPLESPFRAME);
392
/* If port_name is not specified, use the port's name */
394
port_name = &strm_port->info.name;
396
pj_mutex_lock(conf->mutex);
398
if (conf->port_cnt >= conf->max_ports) {
399
pj_assert(!"Too many ports");
400
pj_mutex_unlock(conf->mutex);
404
/* Find empty port in the conference bridge. */
405
for (index=0; index < conf->max_ports; ++index) {
406
if (conf->ports[index] == NULL)
410
pj_assert(index != conf->max_ports);
412
/* Create conf port structure. */
413
status = create_conf_port(pool, conf, strm_port, port_name, &conf_port);
414
if (status != PJ_SUCCESS) {
415
pj_mutex_unlock(conf->mutex);
420
conf_port->slot = index;
421
conf->ports[index] = conf_port;
429
pj_mutex_unlock(conf->mutex);
438
PJ_DEF(pj_status_t) pjmedia_conf_add_passive_port( pjmedia_conf *conf,
440
const pj_str_t *name,
442
unsigned channel_count,
443
unsigned samples_per_frame,
444
unsigned bits_per_sample,
447
pjmedia_port **p_port )
452
PJ_UNUSED_ARG(clock_rate);
453
PJ_UNUSED_ARG(channel_count);
454
PJ_UNUSED_ARG(samples_per_frame);
455
PJ_UNUSED_ARG(bits_per_sample);
456
PJ_UNUSED_ARG(options);
457
PJ_UNUSED_ARG(p_slot);
458
PJ_UNUSED_ARG(p_port);
466
* Change TX and RX settings for the port.
468
PJ_DEF(pj_status_t) pjmedia_conf_configure_port( pjmedia_conf *conf,
473
struct conf_port *conf_port;
475
/* Check arguments */
476
PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
478
pj_mutex_lock(conf->mutex);
480
/* Port must be valid. */
481
conf_port = conf->ports[slot];
482
if (conf_port == NULL) {
483
pj_mutex_unlock(conf->mutex);
487
if (tx != PJMEDIA_PORT_NO_CHANGE)
488
conf_port->tx_setting = tx;
490
if (rx != PJMEDIA_PORT_NO_CHANGE)
491
conf_port->rx_setting = rx;
493
pj_mutex_unlock(conf->mutex);
502
PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf,
507
struct conf_port *src_port, *dst_port;
508
pj_bool_t start_sound = PJ_FALSE;
509
pjmedia_audio_format_detail *src_afd, *dst_afd;
512
/* Check arguments */
513
PJ_ASSERT_RETURN(conf && src_slot<conf->max_ports &&
514
sink_slot<conf->max_ports, PJ_EINVAL);
516
/* For now, level MUST be zero. */
517
PJ_ASSERT_RETURN(level == 0, PJ_EINVAL);
519
pj_mutex_lock(conf->mutex);
521
/* Ports must be valid. */
522
src_port = conf->ports[src_slot];
523
dst_port = conf->ports[sink_slot];
524
if (!src_port || !dst_port) {
525
pj_mutex_unlock(conf->mutex);
529
src_afd = pjmedia_format_get_audio_format_detail(&src_port->info->fmt, 1);
530
dst_afd = pjmedia_format_get_audio_format_detail(&dst_port->info->fmt, 1);
532
/* Format must match. */
533
if (src_port->info->fmt.id != dst_port->info->fmt.id ||
534
src_afd->avg_bps != dst_afd->avg_bps)
536
pj_mutex_unlock(conf->mutex);
537
return PJMEDIA_ENOTCOMPATIBLE;
540
/* Clock rate must match. */
541
if (src_afd->clock_rate != dst_afd->clock_rate) {
542
pj_mutex_unlock(conf->mutex);
543
return PJMEDIA_ENCCLOCKRATE;
546
/* Channel count must match. */
547
if (src_afd->channel_count != dst_afd->channel_count) {
548
pj_mutex_unlock(conf->mutex);
549
return PJMEDIA_ENCCHANNEL;
552
/* Source and sink ptime must be equal or a multiplication factor. */
553
if ((src_afd->frame_time_usec % dst_afd->frame_time_usec != 0) &&
554
(dst_afd->frame_time_usec % src_afd->frame_time_usec != 0))
556
pj_mutex_unlock(conf->mutex);
557
return PJMEDIA_ENCSAMPLESPFRAME;
560
/* If sink is currently listening to other ports, it needs to be released
561
* first before the new connection made.
563
if (dst_port->transmitter_cnt > 0) {
565
pj_bool_t transmitter_found = PJ_FALSE;
567
pj_assert(dst_port->transmitter_cnt == 1);
568
for (j=0; j<conf->max_ports && !transmitter_found; ++j) {
569
if (conf->ports[j]) {
572
for (k=0; k < conf->ports[j]->listener_cnt; ++k) {
573
if (conf->ports[j]->listener_slots[k] == sink_slot) {
574
PJ_LOG(2,(THIS_FILE, "Connection [%d->%d] is "
575
"disconnected for new connection [%d->%d]",
576
j, sink_slot, src_slot, sink_slot));
577
pjmedia_conf_disconnect_port(conf, j, sink_slot);
578
transmitter_found = PJ_TRUE;
584
pj_assert(dst_port->transmitter_cnt == 0);
587
/* Check if connection has been made */
588
for (i=0; i<src_port->listener_cnt; ++i) {
589
if (src_port->listener_slots[i] == sink_slot)
593
/* Update master port info shortcut, note that application may update
594
* the master port info when the audio device needs to be reopened with
595
* a new format to match to ports connection format.
597
conf->ports[0]->samples_per_frame = PJMEDIA_PIA_SPF(conf->ports[0]->info);
599
if (i == src_port->listener_cnt) {
600
src_port->listener_slots[src_port->listener_cnt] = sink_slot;
602
++src_port->listener_cnt;
603
++dst_port->transmitter_cnt;
605
if (conf->connect_cnt == 1)
608
PJ_LOG(4,(THIS_FILE,"Port %d (%.*s) transmitting to port %d (%.*s)",
610
(int)src_port->name.slen,
613
(int)dst_port->name.slen,
614
dst_port->name.ptr));
617
pj_mutex_unlock(conf->mutex);
619
/* Sound device must be started without mutex, otherwise the
620
* sound thread will deadlock (?)
632
PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf,
636
struct conf_port *src_port, *dst_port;
639
/* Check arguments */
640
PJ_ASSERT_RETURN(conf && src_slot<conf->max_ports &&
641
sink_slot<conf->max_ports, PJ_EINVAL);
643
pj_mutex_lock(conf->mutex);
645
/* Ports must be valid. */
646
src_port = conf->ports[src_slot];
647
dst_port = conf->ports[sink_slot];
648
if (!src_port || !dst_port) {
649
pj_mutex_unlock(conf->mutex);
653
/* Check if connection has been made */
654
for (i=0; i<src_port->listener_cnt; ++i) {
655
if (src_port->listener_slots[i] == sink_slot)
659
if (i != src_port->listener_cnt) {
660
pjmedia_frame_ext *f;
662
pj_assert(src_port->listener_cnt > 0 &&
663
src_port->listener_cnt < conf->max_ports);
664
pj_assert(dst_port->transmitter_cnt > 0 &&
665
dst_port->transmitter_cnt < conf->max_ports);
666
pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE),
667
src_port->listener_cnt, i);
669
--src_port->listener_cnt;
670
--dst_port->transmitter_cnt;
672
/* Cleanup listener TX buffer. */
673
f = (pjmedia_frame_ext*)dst_port->tx_buf;
674
f->base.type = PJMEDIA_FRAME_TYPE_NONE;
680
"Port %d (%.*s) stop transmitting to port %d (%.*s)",
682
(int)src_port->name.slen,
685
(int)dst_port->name.slen,
686
dst_port->name.ptr));
689
pj_mutex_unlock(conf->mutex);
691
if (conf->connect_cnt == 0) {
699
* Get number of ports currently registered to the conference bridge.
701
PJ_DEF(unsigned) pjmedia_conf_get_port_count(pjmedia_conf *conf)
703
return conf->port_cnt;
707
* Get total number of ports connections currently set up in the bridge.
709
PJ_DEF(unsigned) pjmedia_conf_get_connect_count(pjmedia_conf *conf)
711
return conf->connect_cnt;
716
* Remove the specified port.
718
PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,
721
struct conf_port *conf_port;
724
/* Check arguments */
725
PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL);
727
/* Suspend the sound devices.
728
* Don't want to remove port while port is being accessed by sound
732
pj_mutex_lock(conf->mutex);
734
/* Port must be valid. */
735
conf_port = conf->ports[port];
736
if (conf_port == NULL) {
737
pj_mutex_unlock(conf->mutex);
741
conf_port->tx_setting = PJMEDIA_PORT_DISABLE;
742
conf_port->rx_setting = PJMEDIA_PORT_DISABLE;
744
/* Remove this port from transmit array of other ports. */
745
for (i=0; i<conf->max_ports; ++i) {
747
struct conf_port *src_port;
749
src_port = conf->ports[i];
754
if (src_port->listener_cnt == 0)
757
for (j=0; j<src_port->listener_cnt; ++j) {
758
if (src_port->listener_slots[j] == port) {
759
pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE),
760
src_port->listener_cnt, j);
761
pj_assert(conf->connect_cnt > 0);
763
--src_port->listener_cnt;
769
/* Update transmitter_cnt of ports we're transmitting to */
770
while (conf_port->listener_cnt) {
772
struct conf_port *dst_port;
773
pjmedia_frame_ext *f;
775
dst_slot = conf_port->listener_slots[conf_port->listener_cnt-1];
776
dst_port = conf->ports[dst_slot];
777
--dst_port->transmitter_cnt;
778
--conf_port->listener_cnt;
779
pj_assert(conf->connect_cnt > 0);
782
/* Cleanup & reinit listener TX buffer. */
783
f = (pjmedia_frame_ext*)dst_port->tx_buf;
784
f->base.type = PJMEDIA_FRAME_TYPE_NONE;
790
/* Remove the port. */
791
conf->ports[port] = NULL;
794
pj_mutex_unlock(conf->mutex);
797
/* Stop sound if there's no connection. */
798
if (conf->connect_cnt == 0) {
809
PJ_DEF(pj_status_t) pjmedia_conf_enum_ports( pjmedia_conf *conf,
815
PJ_ASSERT_RETURN(conf && p_count && ports, PJ_EINVAL);
818
pj_mutex_lock(conf->mutex);
820
for (i=0; i<conf->max_ports && count<*p_count; ++i) {
828
pj_mutex_unlock(conf->mutex);
837
PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf,
839
pjmedia_conf_port_info *info)
841
struct conf_port *conf_port;
842
const pjmedia_audio_format_detail *afd;
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
afd = pjmedia_format_get_audio_format_detail(&conf_port->info->fmt, 1);
859
pj_bzero(info, sizeof(pjmedia_conf_port_info));
862
info->name = conf_port->name;
863
info->tx_setting = conf_port->tx_setting;
864
info->rx_setting = conf_port->rx_setting;
865
info->listener_cnt = conf_port->listener_cnt;
866
info->listener_slots = conf_port->listener_slots;
867
info->transmitter_cnt = conf_port->transmitter_cnt;
868
info->clock_rate = afd->clock_rate;
869
info->channel_count = afd->channel_count;
870
info->samples_per_frame = conf_port->samples_per_frame;
871
info->bits_per_sample = afd->bits_per_sample;
872
info->format = conf_port->port->info.fmt;
873
info->tx_adj_level = conf_port->tx_adj_level - NORMAL_LEVEL;
874
info->rx_adj_level = conf_port->rx_adj_level - NORMAL_LEVEL;
877
pj_mutex_unlock(conf->mutex);
883
PJ_DEF(pj_status_t) pjmedia_conf_get_ports_info(pjmedia_conf *conf,
885
pjmedia_conf_port_info info[])
889
PJ_ASSERT_RETURN(conf && size && info, PJ_EINVAL);
892
pj_mutex_lock(conf->mutex);
894
for (i=0; i<conf->max_ports && count<*size; ++i) {
898
pjmedia_conf_get_port_info(conf, i, &info[count]);
903
pj_mutex_unlock(conf->mutex);
913
PJ_DEF(pj_status_t) pjmedia_conf_get_signal_level( pjmedia_conf *conf,
918
struct conf_port *conf_port;
920
/* Check arguments */
921
PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
924
pj_mutex_lock(conf->mutex);
926
/* Port must be valid. */
927
conf_port = conf->ports[slot];
928
if (conf_port == NULL) {
929
pj_mutex_unlock(conf->mutex);
933
if (tx_level != NULL) {
934
*tx_level = conf_port->tx_level;
937
if (rx_level != NULL)
938
*rx_level = conf_port->rx_level;
941
pj_mutex_unlock(conf->mutex);
948
* Adjust RX level of individual port.
950
PJ_DEF(pj_status_t) pjmedia_conf_adjust_rx_level( pjmedia_conf *conf,
954
struct conf_port *conf_port;
956
/* Check arguments */
957
PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
959
/* Value must be from -128 to +127 */
960
/* Disabled, you can put more than +127, at your own risk:
961
PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
963
PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
966
pj_mutex_lock(conf->mutex);
968
/* Port must be valid. */
969
conf_port = conf->ports[slot];
970
if (conf_port == NULL) {
971
pj_mutex_unlock(conf->mutex);
975
/* Level adjustment is applicable only for ports that work with raw PCM. */
976
PJ_ASSERT_RETURN(conf_port->info->fmt.id == PJMEDIA_FORMAT_L16,
979
/* Set normalized adjustment level. */
980
conf_port->rx_adj_level = adj_level + NORMAL_LEVEL;
983
pj_mutex_unlock(conf->mutex);
990
* Adjust TX level of individual port.
992
PJ_DEF(pj_status_t) pjmedia_conf_adjust_tx_level( pjmedia_conf *conf,
996
struct conf_port *conf_port;
998
/* Check arguments */
999
PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
1001
/* Value must be from -128 to +127 */
1002
/* Disabled, you can put more than +127,, at your own risk:
1003
PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
1005
PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
1008
pj_mutex_lock(conf->mutex);
1010
/* Port must be valid. */
1011
conf_port = conf->ports[slot];
1012
if (conf_port == NULL) {
1013
pj_mutex_unlock(conf->mutex);
1017
/* Level adjustment is applicable only for ports that work with raw PCM. */
1018
PJ_ASSERT_RETURN(conf_port->info->fmt.id == PJMEDIA_FORMAT_L16,
1021
/* Set normalized adjustment level. */
1022
conf_port->tx_adj_level = adj_level + NORMAL_LEVEL;
1025
pj_mutex_unlock(conf->mutex);
1030
/* Deliver frm_src to a listener port, eventually call port's put_frame()
1031
* when samples count in the frm_dst are equal to port's samples_per_frame.
1033
static pj_status_t write_frame(struct conf_port *cport_dst,
1034
const pjmedia_frame *frm_src)
1036
pjmedia_frame *frm_dst = (pjmedia_frame*)cport_dst->tx_buf;
1038
PJ_TODO(MAKE_SURE_DEST_FRAME_HAS_ENOUGH_SPACE);
1040
frm_dst->type = frm_src->type;
1041
frm_dst->timestamp = cport_dst->ts_tx;
1043
if (frm_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1045
pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frm_src;
1046
pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
1049
for (i = 0; i < f_src->subframe_cnt; ++i) {
1050
pjmedia_frame_ext_subframe *sf;
1052
/* Copy frame to listener's TX buffer. */
1053
sf = pjmedia_frame_ext_get_subframe(f_src, i);
1054
pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
1055
f_src->samples_cnt /
1056
f_src->subframe_cnt);
1058
/* Check if it's time to deliver the TX buffer to listener,
1059
* i.e: samples count in TX buffer equal to listener's
1060
* samples per frame.
1062
if (f_dst->samples_cnt >= cport_dst->samples_per_frame)
1064
if (cport_dst->slot) {
1065
pjmedia_port_put_frame(cport_dst->port,
1066
(pjmedia_frame*)f_dst);
1068
/* Reset TX buffer. */
1069
f_dst->subframe_cnt = 0;
1070
f_dst->samples_cnt = 0;
1073
/* Update TX timestamp. */
1074
pj_add_timestamp32(&cport_dst->ts_tx,
1075
cport_dst->samples_per_frame);
1079
} else if (frm_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
1081
pj_int16_t *f_start, *f_end;
1083
f_start = (pj_int16_t*)frm_src->buf;
1084
f_end = f_start + (frm_src->size >> 1);
1086
while (f_start < f_end) {
1087
unsigned nsamples_to_copy, nsamples_req;
1089
/* Copy frame to listener's TX buffer.
1090
* Note that if the destination is port 0, just copy the whole
1091
* available samples.
1093
nsamples_to_copy = f_end - f_start;
1094
nsamples_req = cport_dst->samples_per_frame -
1096
if (cport_dst->slot && nsamples_to_copy > nsamples_req)
1097
nsamples_to_copy = nsamples_req;
1099
/* Adjust TX level. */
1100
if (cport_dst->tx_adj_level != NORMAL_LEVEL) {
1101
pj_int16_t *p, *p_end;
1104
p_end = p + nsamples_to_copy;
1106
pj_int32_t itemp = *p;
1108
/* Adjust the level */
1109
itemp = (itemp * cport_dst->tx_adj_level) >> 7;
1111
/* Clip the signal if it's too loud */
1112
if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
1113
else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
1115
/* Put back in the buffer. */
1116
*p = (pj_int16_t)itemp;
1121
pjmedia_copy_samples((pj_int16_t*)frm_dst->buf + (frm_dst->size>>1),
1124
frm_dst->size += nsamples_to_copy << 1;
1125
f_start += nsamples_to_copy;
1127
/* Check if it's time to deliver the TX buffer to listener,
1128
* i.e: samples count in TX buffer equal to listener's
1129
* samples per frame. Note that for destination port 0 this
1130
* function will just populate all samples in the TX buffer.
1132
if (cport_dst->slot == 0) {
1133
/* Update TX timestamp. */
1134
pj_add_timestamp32(&cport_dst->ts_tx, nsamples_to_copy);
1135
} else if ((frm_dst->size >> 1) ==
1136
cport_dst->samples_per_frame)
1138
pjmedia_port_put_frame(cport_dst->port, frm_dst);
1140
/* Reset TX buffer. */
1143
/* Update TX timestamp. */
1144
pj_add_timestamp32(&cport_dst->ts_tx,
1145
cport_dst->samples_per_frame);
1149
} else if (frm_src->type == PJMEDIA_FRAME_TYPE_NONE) {
1151
/* Check port format. */
1152
if (cport_dst->port &&
1153
cport_dst->port->info.fmt.id == PJMEDIA_FORMAT_L16)
1155
/* When there is already some samples in listener's TX buffer,
1156
* pad the buffer with "zero samples".
1158
if (frm_dst->size != 0) {
1159
pjmedia_zero_samples((pj_int16_t*)frm_dst->buf,
1160
cport_dst->samples_per_frame -
1161
(frm_dst->size>>1));
1163
frm_dst->type = PJMEDIA_FRAME_TYPE_AUDIO;
1164
frm_dst->size = cport_dst->samples_per_frame << 1;
1165
if (cport_dst->slot) {
1166
pjmedia_port_put_frame(cport_dst->port, frm_dst);
1168
/* Reset TX buffer. */
1172
/* Update TX timestamp. */
1173
pj_add_timestamp32(&cport_dst->ts_tx,
1174
cport_dst->samples_per_frame);
1177
pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
1179
if (f_dst->samples_cnt != 0) {
1180
frm_dst->type = PJMEDIA_FRAME_TYPE_EXTENDED;
1181
pjmedia_frame_ext_append_subframe(f_dst, NULL, 0, (pj_uint16_t)
1182
(cport_dst->samples_per_frame - f_dst->samples_cnt));
1183
if (cport_dst->slot) {
1184
pjmedia_port_put_frame(cport_dst->port, frm_dst);
1186
/* Reset TX buffer. */
1187
f_dst->subframe_cnt = 0;
1188
f_dst->samples_cnt = 0;
1191
/* Update TX timestamp. */
1192
pj_add_timestamp32(&cport_dst->ts_tx,
1193
cport_dst->samples_per_frame);
1197
/* Synchronize clock. */
1198
while (pj_cmp_timestamp(&cport_dst->ts_clock,
1199
&cport_dst->ts_tx) > 0)
1201
frm_dst->type = PJMEDIA_FRAME_TYPE_NONE;
1202
frm_dst->timestamp = cport_dst->ts_tx;
1203
if (cport_dst->slot)
1204
pjmedia_port_put_frame(cport_dst->port, frm_dst);
1206
/* Update TX timestamp. */
1207
pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->samples_per_frame);
1217
static pj_status_t get_frame(pjmedia_port *this_port,
1218
pjmedia_frame *frame)
1220
pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
1224
pj_mutex_lock(conf->mutex);
1226
/* Call get_frame() from all ports (except port 0) that has
1227
* receiver and distribute the frame (put the frame to the destination
1228
* port's buffer to accommodate different ptime, and ultimately call
1229
* put_frame() of that port) to ports that are receiving from this port.
1231
for (i=1, ci=1; i<conf->max_ports && ci<conf->port_cnt; ++i) {
1232
struct conf_port *cport = conf->ports[i];
1233
unsigned master_samples_per_frame;
1235
/* Skip empty port. */
1239
/* Var "ci" is to count how many ports have been visited so far. */
1242
master_samples_per_frame = PJMEDIA_PIA_SPF(&conf->master_port->info);
1244
/* Update clock of the port. */
1245
pj_add_timestamp32(&cport->ts_clock, master_samples_per_frame);
1247
/* Skip if we're not allowed to receive from this port or
1248
* the port doesn't have listeners.
1250
if (cport->rx_setting == PJMEDIA_PORT_DISABLE ||
1251
cport->listener_cnt == 0)
1253
cport->rx_level = 0;
1254
pj_add_timestamp32(&cport->ts_rx, master_samples_per_frame);
1258
/* Get frame from each port, put it to the listener TX buffer,
1259
* and eventually call put_frame() of the listener. This loop
1260
* will also make sure the ptime between conf & port synchronized.
1262
while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_rx) > 0) {
1263
pjmedia_frame *f = (pjmedia_frame*)conf->buf;
1266
pj_int32_t level = 0;
1268
pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
1270
f->buf = &conf->buf[sizeof(pjmedia_frame)];
1271
f->size = cport->samples_per_frame<<1;
1273
/* Get frame from port. */
1274
status = pjmedia_port_get_frame(cport->port, f);
1275
if (status != PJ_SUCCESS)
1278
/* Calculate & adjust RX level. */
1279
if (f->type == PJMEDIA_FRAME_TYPE_AUDIO) {
1280
if (cport->rx_adj_level != NORMAL_LEVEL) {
1281
pj_int16_t *p = (pj_int16_t*)f->buf;
1284
end = p + (f->size >> 1);
1286
pj_int32_t itemp = *p;
1288
/* Adjust the level */
1289
itemp = (itemp * cport->rx_adj_level) >> 7;
1291
/* Clip the signal if it's too loud */
1292
if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
1293
else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
1295
level += PJ_ABS(itemp);
1297
/* Put back in the buffer. */
1298
*p = (pj_int16_t)itemp;
1301
level /= (f->size >> 1);
1303
level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
1306
} else if (f->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1307
/* For extended frame, level is unknown, so we just set
1308
* it to NORMAL_LEVEL.
1310
level = NORMAL_LEVEL;
1313
cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff;
1315
/* Put the frame to all listeners. */
1316
for (j=0; j < cport->listener_cnt; ++j)
1318
struct conf_port *listener;
1320
listener = conf->ports[cport->listener_slots[j]];
1322
/* Skip if this listener doesn't want to receive audio */
1323
if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
1324
pj_add_timestamp32(&listener->ts_tx,
1325
listener->samples_per_frame);
1326
listener->tx_level = 0;
1330
status = write_frame(listener, f);
1331
if (status != PJ_SUCCESS) {
1332
listener->tx_level = 0;
1336
/* Set listener TX level based on transmitter RX level &
1337
* listener TX level.
1339
listener->tx_level = (cport->rx_level * listener->tx_adj_level)
1345
/* Keep alive. Update TX timestamp and send frame type NONE to all
1346
* underflow ports at their own clock.
1348
for (i=1, ci=1; i<conf->max_ports && ci<conf->port_cnt; ++i) {
1349
struct conf_port *cport = conf->ports[i];
1351
/* Skip empty port. */
1355
/* Var "ci" is to count how many ports have been visited so far. */
1358
if (cport->tx_setting==PJMEDIA_PORT_MUTE || cport->transmitter_cnt==0)
1360
pjmedia_frame_ext *f;
1362
/* Clear left-over samples in tx_buffer, if any, so that it won't
1363
* be transmitted next time we have audio signal.
1365
f = (pjmedia_frame_ext*)cport->tx_buf;
1366
f->base.type = PJMEDIA_FRAME_TYPE_NONE;
1369
f->subframe_cnt = 0;
1371
cport->tx_level = 0;
1373
while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_tx) > 0)
1375
if (cport->tx_setting == PJMEDIA_PORT_ENABLE) {
1376
pjmedia_frame tmp_f;
1378
tmp_f.timestamp = cport->ts_tx;
1379
tmp_f.type = PJMEDIA_FRAME_TYPE_NONE;
1383
pjmedia_port_put_frame(cport->port, &tmp_f);
1384
pj_add_timestamp32(&cport->ts_tx, cport->samples_per_frame);
1390
/* Return sound playback frame. */
1392
struct conf_port *this_cport = conf->ports[this_port->port_data.ldata];
1393
pjmedia_frame *f_src = (pjmedia_frame*) this_cport->tx_buf;
1395
frame->type = f_src->type;
1397
if (f_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1398
pjmedia_frame_ext *f_src_ = (pjmedia_frame_ext*)f_src;
1399
pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frame;
1400
pjmedia_frame_ext_subframe *sf;
1401
unsigned samples_per_subframe;
1403
if (f_src_->samples_cnt < this_cport->samples_per_frame) {
1404
f_dst->base.type = PJMEDIA_FRAME_TYPE_NONE;
1405
f_dst->samples_cnt = 0;
1406
f_dst->subframe_cnt = 0;
1410
f_dst->samples_cnt = 0;
1411
f_dst->subframe_cnt = 0;
1413
samples_per_subframe = f_src_->samples_cnt / f_src_->subframe_cnt;
1416
while (f_dst->samples_cnt < this_cport->samples_per_frame) {
1417
sf = pjmedia_frame_ext_get_subframe(f_src_, i++);
1419
pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
1420
samples_per_subframe);
1423
/* Shift left TX buffer. */
1424
pjmedia_frame_ext_pop_subframes(f_src_, i);
1426
} else if (f_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
1427
if ((f_src->size>>1) < this_cport->samples_per_frame) {
1428
frame->type = PJMEDIA_FRAME_TYPE_NONE;
1433
pjmedia_copy_samples((pj_int16_t*)frame->buf,
1434
(pj_int16_t*)f_src->buf,
1435
this_cport->samples_per_frame);
1436
frame->size = this_cport->samples_per_frame << 1;
1438
/* Shift left TX buffer. */
1439
f_src->size -= frame->size;
1441
pjmedia_move_samples((pj_int16_t*)f_src->buf,
1442
(pj_int16_t*)f_src->buf +
1443
this_cport->samples_per_frame,
1445
} else { /* PJMEDIA_FRAME_TYPE_NONE */
1446
pjmedia_frame_ext *f_src_ = (pjmedia_frame_ext*)f_src;
1448
/* Reset source/TX buffer */
1449
f_src_->base.size = 0;
1450
f_src_->samples_cnt = 0;
1451
f_src_->subframe_cnt = 0;
1456
pj_mutex_unlock(conf->mutex);
1462
* Recorder callback.
1464
static pj_status_t put_frame(pjmedia_port *this_port,
1467
pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
1468
struct conf_port *cport;
1470
pj_int32_t level = 0;
1473
pj_mutex_lock(conf->mutex);
1475
/* Get conf port of this port */
1476
cport = conf->ports[this_port->port_data.ldata];
1477
if (cport == NULL) {
1479
pj_mutex_unlock(conf->mutex);
1483
pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
1485
/* Skip if this port is muted/disabled. */
1486
if (cport->rx_setting == PJMEDIA_PORT_DISABLE) {
1487
cport->rx_level = 0;
1489
pj_mutex_unlock(conf->mutex);
1493
/* Skip if no port is listening to the microphone */
1494
if (cport->listener_cnt == 0) {
1495
cport->rx_level = 0;
1497
pj_mutex_unlock(conf->mutex);
1501
/* Calculate & adjust RX level. */
1502
if (f->type == PJMEDIA_FRAME_TYPE_AUDIO) {
1503
if (cport->rx_adj_level != NORMAL_LEVEL) {
1504
pj_int16_t *p = (pj_int16_t*)f->buf;
1507
end = p + (f->size >> 1);
1509
pj_int32_t itemp = *p;
1511
/* Adjust the level */
1512
itemp = (itemp * cport->rx_adj_level) >> 7;
1514
/* Clip the signal if it's too loud */
1515
if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
1516
else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
1518
level += PJ_ABS(itemp);
1520
/* Put back in the buffer. */
1521
*p = (pj_int16_t)itemp;
1524
level /= (f->size >> 1);
1526
level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
1529
} else if (f->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1530
/* For extended frame, level is unknown, so we just set
1531
* it to NORMAL_LEVEL.
1533
level = NORMAL_LEVEL;
1536
cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff;
1538
/* Put the frame to all listeners. */
1539
for (j=0; j < cport->listener_cnt; ++j)
1541
struct conf_port *listener;
1544
listener = conf->ports[cport->listener_slots[j]];
1546
/* Skip if this listener doesn't want to receive audio */
1547
if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
1548
pj_add_timestamp32(&listener->ts_tx,
1549
listener->samples_per_frame);
1550
listener->tx_level = 0;
1554
/* Skip loopback for now. */
1555
if (listener == cport) {
1556
pj_add_timestamp32(&listener->ts_tx,
1557
listener->samples_per_frame);
1558
listener->tx_level = 0;
1562
status = write_frame(listener, f);
1563
if (status != PJ_SUCCESS) {
1564
listener->tx_level = 0;
1568
/* Set listener TX level based on transmitter RX level & listener
1571
listener->tx_level = (cport->rx_level * listener->tx_adj_level) >> 8;
1575
pj_mutex_unlock(conf->mutex);