blob: 114b48a04f3bd3acb527ed42da4a9d52b83b3f93 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjmedia/conference.h>
21#include <pjmedia/alaw_ulaw.h>
22#include <pjmedia/errno.h>
23#include <pjmedia/port.h>
24#include <pjmedia/silencedet.h>
25#include <pjmedia/sound_port.h>
26#include <pj/array.h>
27#include <pj/assert.h>
28#include <pj/log.h>
29#include <pj/math.h>
30#include <pj/pool.h>
31#include <pj/string.h>
32
33#if defined(PJMEDIA_CONF_USE_SWITCH_BOARD) && PJMEDIA_CONF_USE_SWITCH_BOARD!=0
34
35/* CONF_DEBUG enables detailed operation of the conference bridge.
36 * Beware that it prints large amounts of logs (several lines per frame).
37 */
38//#define CONF_DEBUG
39#ifdef CONF_DEBUG
40# include <stdio.h>
41# define TRACE_(x) PJ_LOG(5,x)
42#else
43# define TRACE_(x)
44#endif
45
46#define THIS_FILE "conf_switch.c"
47
48#define SIGNATURE PJMEDIA_CONF_SWITCH_SIGNATURE
49#define SIGNATURE_PORT PJMEDIA_PORT_SIGNATURE('S', 'W', 'T', 'P')
50#define NORMAL_LEVEL 128
51#define SLOT_TYPE unsigned
52#define INVALID_SLOT ((SLOT_TYPE)-1)
53#define BUFFER_SIZE PJMEDIA_CONF_SWITCH_BOARD_BUF_SIZE
54#define MAX_LEVEL (32767)
55#define MIN_LEVEL (-32768)
56
57/*
58 * DON'T GET CONFUSED WITH TX/RX!!
59 *
60 * TX and RX directions are always viewed from the conference bridge's point
61 * of view, and NOT from the port's point of view. So TX means the bridge
62 * is transmitting to the port, RX means the bridge is receiving from the
63 * port.
64 */
65
66
67/**
68 * This is a port connected to conference bridge.
69 */
70struct conf_port
71{
72 SLOT_TYPE slot; /**< Array of listeners. */
73 pj_str_t name; /**< Port name. */
74 pjmedia_port *port; /**< get_frame() and put_frame() */
75 pjmedia_port_op rx_setting; /**< Can we receive from this port */
76 pjmedia_port_op tx_setting; /**< Can we transmit to this port */
77 unsigned listener_cnt; /**< Number of listeners. */
78 SLOT_TYPE *listener_slots;/**< Array of listeners. */
79 unsigned transmitter_cnt;/**<Number of transmitters. */
80
81 /* Shortcut for port info. */
82 pjmedia_port_info *info;
83 unsigned samples_per_frame;
84
85 /* Calculated signal levels: */
86 unsigned tx_level; /**< Last tx level to this port. */
87 unsigned rx_level; /**< Last rx level from this port. */
88
89 /* The normalized signal level adjustment.
90 * A value of 128 (NORMAL_LEVEL) means there's no adjustment.
91 */
92 unsigned tx_adj_level; /**< Adjustment for TX. */
93 unsigned rx_adj_level; /**< Adjustment for RX. */
94
95 pj_timestamp ts_clock;
96 pj_timestamp ts_rx;
97 pj_timestamp ts_tx;
98
99 /* Tx buffer is a temporary buffer to be used when there's mismatch
100 * between port's ptime with conference's ptime. This buffer is used as
101 * the source to buffer the samples until there are enough samples to
102 * fulfill a complete frame to be transmitted to the port.
103 */
104 pj_uint8_t tx_buf[BUFFER_SIZE]; /**< Tx buffer. */
105};
106
107
108/*
109 * Conference bridge.
110 */
111struct pjmedia_conf
112{
113 unsigned options; /**< Bitmask options. */
114 unsigned max_ports; /**< Maximum ports. */
115 unsigned port_cnt; /**< Current number of ports. */
116 unsigned connect_cnt; /**< Total number of connections */
117 pjmedia_port *master_port; /**< Port zero's port. */
118 char master_name_buf[80]; /**< Port0 name buffer. */
119 pj_mutex_t *mutex; /**< Conference mutex. */
120 struct conf_port **ports; /**< Array of ports. */
121 pj_uint8_t buf[BUFFER_SIZE]; /**< Common buffer. */
122};
123
124
125/* Prototypes */
126static pj_status_t put_frame(pjmedia_port *this_port,
127 pjmedia_frame *frame);
128static pj_status_t get_frame(pjmedia_port *this_port,
129 pjmedia_frame *frame);
130static pj_status_t destroy_port(pjmedia_port *this_port);
131
132
133/*
134 * Create port.
135 */
136static pj_status_t create_conf_port( pj_pool_t *pool,
137 pjmedia_conf *conf,
138 pjmedia_port *port,
139 const pj_str_t *name,
140 struct conf_port **p_conf_port)
141{
142 struct conf_port *conf_port;
143 pjmedia_frame *f;
144
145 PJ_ASSERT_RETURN(pool && conf && port && name && p_conf_port, PJ_EINVAL);
146
147 /* Check port's buffer size */
148 if (port->info.fmt.id == PJMEDIA_FORMAT_PCM &&
149 PJMEDIA_PIA_SPF(&port->info)*2 > BUFFER_SIZE - sizeof(pjmedia_frame))
150 {
151 pj_assert(!"Too small buffer size for audio switchboard. "
152 "Try increase PJMEDIA_CONF_SWITCH_BOARD_BUF_SIZE");
153 return PJ_ETOOSMALL;
154 }
155
156 /* Create port. */
157 conf_port = PJ_POOL_ZALLOC_T(pool, struct conf_port);
158
159 /* Set name */
160 pj_strdup_with_null(pool, &conf_port->name, name);
161
162 /* Default has tx and rx enabled. */
163 conf_port->rx_setting = PJMEDIA_PORT_ENABLE;
164 conf_port->tx_setting = PJMEDIA_PORT_ENABLE;
165
166 /* Default level adjustment is 128 (which means no adjustment) */
167 conf_port->tx_adj_level = NORMAL_LEVEL;
168 conf_port->rx_adj_level = NORMAL_LEVEL;
169
170 /* Create transmit flag array */
171 conf_port->listener_slots = (SLOT_TYPE*)
172 pj_pool_zalloc(pool,
173 conf->max_ports * sizeof(SLOT_TYPE));
174 PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM);
175
176 /* Save some port's infos, for convenience. */
177 conf_port->port = port;
178 conf_port->info = &port->info;
179 conf_port->samples_per_frame = PJMEDIA_PIA_SPF(&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 */
193static 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 */
216PJ_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 */
283static 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 */
293static 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 */
304PJ_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 */
318static 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 */
327PJ_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 */
344PJ_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 */
369PJ_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 */
447PJ_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 */
477PJ_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 */
511PJ_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 pjmedia_audio_format_detail *src_afd, *dst_afd;
519 unsigned i;
520
521 /* Check arguments */
522 PJ_ASSERT_RETURN(conf && src_slot<conf->max_ports &&
523 sink_slot<conf->max_ports, PJ_EINVAL);
524
525 /* For now, level MUST be zero. */
526 PJ_ASSERT_RETURN(level == 0, PJ_EINVAL);
527
528 pj_mutex_lock(conf->mutex);
529
530 /* Ports must be valid. */
531 src_port = conf->ports[src_slot];
532 dst_port = conf->ports[sink_slot];
533 if (!src_port || !dst_port) {
534 pj_mutex_unlock(conf->mutex);
535 return PJ_EINVAL;
536 }
537
538 src_afd = pjmedia_format_get_audio_format_detail(&src_port->info->fmt, 1);
539 dst_afd = pjmedia_format_get_audio_format_detail(&dst_port->info->fmt, 1);
540
541 /* Format must match. */
542 if (src_port->info->fmt.id != dst_port->info->fmt.id ||
543 src_afd->avg_bps != dst_afd->avg_bps)
544 {
545 pj_mutex_unlock(conf->mutex);
546 return PJMEDIA_ENOTCOMPATIBLE;
547 }
548
549 /* Clock rate must match. */
550 if (src_afd->clock_rate != dst_afd->clock_rate) {
551 pj_mutex_unlock(conf->mutex);
552 return PJMEDIA_ENCCLOCKRATE;
553 }
554
555 /* Channel count must match. */
556 if (src_afd->channel_count != dst_afd->channel_count) {
557 pj_mutex_unlock(conf->mutex);
558 return PJMEDIA_ENCCHANNEL;
559 }
560
561 /* Source and sink ptime must be equal or a multiplication factor. */
562 if ((src_afd->frame_time_usec % dst_afd->frame_time_usec != 0) &&
563 (dst_afd->frame_time_usec % src_afd->frame_time_usec != 0))
564 {
565 pj_mutex_unlock(conf->mutex);
566 return PJMEDIA_ENCSAMPLESPFRAME;
567 }
568
569 /* If sink is currently listening to other ports, it needs to be released
570 * first before the new connection made.
571 */
572 if (dst_port->transmitter_cnt > 0) {
573 unsigned j;
574 pj_bool_t transmitter_found = PJ_FALSE;
575
576 pj_assert(dst_port->transmitter_cnt == 1);
577 for (j=0; j<conf->max_ports && !transmitter_found; ++j) {
578 if (conf->ports[j]) {
579 unsigned k;
580
581 for (k=0; k < conf->ports[j]->listener_cnt; ++k) {
582 if (conf->ports[j]->listener_slots[k] == sink_slot) {
583 PJ_LOG(2,(THIS_FILE, "Connection [%d->%d] is "
584 "disconnected for new connection [%d->%d]",
585 j, sink_slot, src_slot, sink_slot));
586 pjmedia_conf_disconnect_port(conf, j, sink_slot);
587 transmitter_found = PJ_TRUE;
588 break;
589 }
590 }
591 }
592 }
593 pj_assert(dst_port->transmitter_cnt == 0);
594 }
595
596 /* Check if connection has been made */
597 for (i=0; i<src_port->listener_cnt; ++i) {
598 if (src_port->listener_slots[i] == sink_slot)
599 break;
600 }
601
602 /* Update master port info shortcut, note that application may update
603 * the master port info when the audio device needs to be reopened with
604 * a new format to match to ports connection format.
605 */
606 conf->ports[0]->samples_per_frame = PJMEDIA_PIA_SPF(conf->ports[0]->info);
607
608 if (i == src_port->listener_cnt) {
609 src_port->listener_slots[src_port->listener_cnt] = sink_slot;
610 ++conf->connect_cnt;
611 ++src_port->listener_cnt;
612 ++dst_port->transmitter_cnt;
613
614 if (conf->connect_cnt == 1)
615 start_sound = 1;
616
617 PJ_LOG(4,(THIS_FILE,"Port %d (%.*s) transmitting to port %d (%.*s)",
618 src_slot,
619 (int)src_port->name.slen,
620 src_port->name.ptr,
621 sink_slot,
622 (int)dst_port->name.slen,
623 dst_port->name.ptr));
624 }
625
626 pj_mutex_unlock(conf->mutex);
627
628 /* Sound device must be started without mutex, otherwise the
629 * sound thread will deadlock (?)
630 */
631 if (start_sound)
632 resume_sound(conf);
633
634 return PJ_SUCCESS;
635}
636
637
638/*
639 * Disconnect port
640 */
641PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf,
642 unsigned src_slot,
643 unsigned sink_slot )
644{
645 struct conf_port *src_port, *dst_port;
646 unsigned i;
647
648 /* Check arguments */
649 PJ_ASSERT_RETURN(conf && src_slot<conf->max_ports &&
650 sink_slot<conf->max_ports, PJ_EINVAL);
651
652 pj_mutex_lock(conf->mutex);
653
654 /* Ports must be valid. */
655 src_port = conf->ports[src_slot];
656 dst_port = conf->ports[sink_slot];
657 if (!src_port || !dst_port) {
658 pj_mutex_unlock(conf->mutex);
659 return PJ_EINVAL;
660 }
661
662 /* Check if connection has been made */
663 for (i=0; i<src_port->listener_cnt; ++i) {
664 if (src_port->listener_slots[i] == sink_slot)
665 break;
666 }
667
668 if (i != src_port->listener_cnt) {
669 pjmedia_frame_ext *f;
670
671 pj_assert(src_port->listener_cnt > 0 &&
672 src_port->listener_cnt < conf->max_ports);
673 pj_assert(dst_port->transmitter_cnt > 0 &&
674 dst_port->transmitter_cnt < conf->max_ports);
675 pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE),
676 src_port->listener_cnt, i);
677 --conf->connect_cnt;
678 --src_port->listener_cnt;
679 --dst_port->transmitter_cnt;
680
681 /* Cleanup listener TX buffer. */
682 f = (pjmedia_frame_ext*)dst_port->tx_buf;
683 f->base.type = PJMEDIA_FRAME_TYPE_NONE;
684 f->base.size = 0;
685 f->samples_cnt = 0;
686 f->subframe_cnt = 0;
687
688 PJ_LOG(4,(THIS_FILE,
689 "Port %d (%.*s) stop transmitting to port %d (%.*s)",
690 src_slot,
691 (int)src_port->name.slen,
692 src_port->name.ptr,
693 sink_slot,
694 (int)dst_port->name.slen,
695 dst_port->name.ptr));
696 }
697
698 pj_mutex_unlock(conf->mutex);
699
700 if (conf->connect_cnt == 0) {
701 pause_sound(conf);
702 }
703
704 return PJ_SUCCESS;
705}
706
707/*
708 * Get number of ports currently registered to the conference bridge.
709 */
710PJ_DEF(unsigned) pjmedia_conf_get_port_count(pjmedia_conf *conf)
711{
712 return conf->port_cnt;
713}
714
715/*
716 * Get total number of ports connections currently set up in the bridge.
717 */
718PJ_DEF(unsigned) pjmedia_conf_get_connect_count(pjmedia_conf *conf)
719{
720 return conf->connect_cnt;
721}
722
723
724/*
725 * Remove the specified port.
726 */
727PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,
728 unsigned port )
729{
730 struct conf_port *conf_port;
731 unsigned i;
732
733 /* Check arguments */
734 PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL);
735
736 /* Suspend the sound devices.
737 * Don't want to remove port while port is being accessed by sound
738 * device's threads!
739 */
740
741 pj_mutex_lock(conf->mutex);
742
743 /* Port must be valid. */
744 conf_port = conf->ports[port];
745 if (conf_port == NULL) {
746 pj_mutex_unlock(conf->mutex);
747 return PJ_EINVAL;
748 }
749
750 conf_port->tx_setting = PJMEDIA_PORT_DISABLE;
751 conf_port->rx_setting = PJMEDIA_PORT_DISABLE;
752
753 /* Remove this port from transmit array of other ports. */
754 for (i=0; i<conf->max_ports; ++i) {
755 unsigned j;
756 struct conf_port *src_port;
757
758 src_port = conf->ports[i];
759
760 if (!src_port)
761 continue;
762
763 if (src_port->listener_cnt == 0)
764 continue;
765
766 for (j=0; j<src_port->listener_cnt; ++j) {
767 if (src_port->listener_slots[j] == port) {
768 pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE),
769 src_port->listener_cnt, j);
770 pj_assert(conf->connect_cnt > 0);
771 --conf->connect_cnt;
772 --src_port->listener_cnt;
773 break;
774 }
775 }
776 }
777
778 /* Update transmitter_cnt of ports we're transmitting to */
779 while (conf_port->listener_cnt) {
780 unsigned dst_slot;
781 struct conf_port *dst_port;
782 pjmedia_frame_ext *f;
783
784 dst_slot = conf_port->listener_slots[conf_port->listener_cnt-1];
785 dst_port = conf->ports[dst_slot];
786 --dst_port->transmitter_cnt;
787 --conf_port->listener_cnt;
788 pj_assert(conf->connect_cnt > 0);
789 --conf->connect_cnt;
790
791 /* Cleanup & reinit listener TX buffer. */
792 f = (pjmedia_frame_ext*)dst_port->tx_buf;
793 f->base.type = PJMEDIA_FRAME_TYPE_NONE;
794 f->base.size = 0;
795 f->samples_cnt = 0;
796 f->subframe_cnt = 0;
797 }
798
799 /* Remove the port. */
800 conf->ports[port] = NULL;
801 --conf->port_cnt;
802
803 pj_mutex_unlock(conf->mutex);
804
805
806 /* Stop sound if there's no connection. */
807 if (conf->connect_cnt == 0) {
808 pause_sound(conf);
809 }
810
811 return PJ_SUCCESS;
812}
813
814
815/*
816 * Enum ports.
817 */
818PJ_DEF(pj_status_t) pjmedia_conf_enum_ports( pjmedia_conf *conf,
819 unsigned ports[],
820 unsigned *p_count )
821{
822 unsigned i, count=0;
823
824 PJ_ASSERT_RETURN(conf && p_count && ports, PJ_EINVAL);
825
826 /* Lock mutex */
827 pj_mutex_lock(conf->mutex);
828
829 for (i=0; i<conf->max_ports && count<*p_count; ++i) {
830 if (!conf->ports[i])
831 continue;
832
833 ports[count++] = i;
834 }
835
836 /* Unlock mutex */
837 pj_mutex_unlock(conf->mutex);
838
839 *p_count = count;
840 return PJ_SUCCESS;
841}
842
843/*
844 * Get port info
845 */
846PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf,
847 unsigned slot,
848 pjmedia_conf_port_info *info)
849{
850 struct conf_port *conf_port;
851 const pjmedia_audio_format_detail *afd;
852
853 /* Check arguments */
854 PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
855
856 /* Lock mutex */
857 pj_mutex_lock(conf->mutex);
858
859 /* Port must be valid. */
860 conf_port = conf->ports[slot];
861 if (conf_port == NULL) {
862 pj_mutex_unlock(conf->mutex);
863 return PJ_EINVAL;
864 }
865
866 afd = pjmedia_format_get_audio_format_detail(&conf_port->info->fmt, 1);
867
868 pj_bzero(info, sizeof(pjmedia_conf_port_info));
869
870 info->slot = slot;
871 info->name = conf_port->name;
872 info->tx_setting = conf_port->tx_setting;
873 info->rx_setting = conf_port->rx_setting;
874 info->listener_cnt = conf_port->listener_cnt;
875 info->listener_slots = conf_port->listener_slots;
876 info->transmitter_cnt = conf_port->transmitter_cnt;
877 info->clock_rate = afd->clock_rate;
878 info->channel_count = afd->channel_count;
879 info->samples_per_frame = conf_port->samples_per_frame;
880 info->bits_per_sample = afd->bits_per_sample;
881 info->format = conf_port->port->info.fmt;
882 info->tx_adj_level = conf_port->tx_adj_level - NORMAL_LEVEL;
883 info->rx_adj_level = conf_port->rx_adj_level - NORMAL_LEVEL;
884
885 /* Unlock mutex */
886 pj_mutex_unlock(conf->mutex);
887
888 return PJ_SUCCESS;
889}
890
891
892PJ_DEF(pj_status_t) pjmedia_conf_get_ports_info(pjmedia_conf *conf,
893 unsigned *size,
894 pjmedia_conf_port_info info[])
895{
896 unsigned i, count=0;
897
898 PJ_ASSERT_RETURN(conf && size && info, PJ_EINVAL);
899
900 /* Lock mutex */
901 pj_mutex_lock(conf->mutex);
902
903 for (i=0; i<conf->max_ports && count<*size; ++i) {
904 if (!conf->ports[i])
905 continue;
906
907 pjmedia_conf_get_port_info(conf, i, &info[count]);
908 ++count;
909 }
910
911 /* Unlock mutex */
912 pj_mutex_unlock(conf->mutex);
913
914 *size = count;
915 return PJ_SUCCESS;
916}
917
918
919/*
920 * Get signal level.
921 */
922PJ_DEF(pj_status_t) pjmedia_conf_get_signal_level( pjmedia_conf *conf,
923 unsigned slot,
924 unsigned *tx_level,
925 unsigned *rx_level)
926{
927 struct conf_port *conf_port;
928
929 /* Check arguments */
930 PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
931
932 /* Lock mutex */
933 pj_mutex_lock(conf->mutex);
934
935 /* Port must be valid. */
936 conf_port = conf->ports[slot];
937 if (conf_port == NULL) {
938 pj_mutex_unlock(conf->mutex);
939 return PJ_EINVAL;
940 }
941
942 if (tx_level != NULL) {
943 *tx_level = conf_port->tx_level;
944 }
945
946 if (rx_level != NULL)
947 *rx_level = conf_port->rx_level;
948
949 /* Unlock mutex */
950 pj_mutex_unlock(conf->mutex);
951
952 return PJ_SUCCESS;
953}
954
955
956/*
957 * Adjust RX level of individual port.
958 */
959PJ_DEF(pj_status_t) pjmedia_conf_adjust_rx_level( pjmedia_conf *conf,
960 unsigned slot,
961 int adj_level )
962{
963 struct conf_port *conf_port;
964
965 /* Check arguments */
966 PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
967
968 /* Value must be from -128 to +127 */
969 /* Disabled, you can put more than +127, at your own risk:
970 PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
971 */
972 PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
973
974 /* Lock mutex */
975 pj_mutex_lock(conf->mutex);
976
977 /* Port must be valid. */
978 conf_port = conf->ports[slot];
979 if (conf_port == NULL) {
980 pj_mutex_unlock(conf->mutex);
981 return PJ_EINVAL;
982 }
983
984 /* Level adjustment is applicable only for ports that work with raw PCM. */
985 PJ_ASSERT_RETURN(conf_port->info->fmt.id == PJMEDIA_FORMAT_L16,
986 PJ_EIGNORED);
987
988 /* Set normalized adjustment level. */
989 conf_port->rx_adj_level = adj_level + NORMAL_LEVEL;
990
991 /* Unlock mutex */
992 pj_mutex_unlock(conf->mutex);
993
994 return PJ_SUCCESS;
995}
996
997
998/*
999 * Adjust TX level of individual port.
1000 */
1001PJ_DEF(pj_status_t) pjmedia_conf_adjust_tx_level( pjmedia_conf *conf,
1002 unsigned slot,
1003 int adj_level )
1004{
1005 struct conf_port *conf_port;
1006
1007 /* Check arguments */
1008 PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
1009
1010 /* Value must be from -128 to +127 */
1011 /* Disabled, you can put more than +127,, at your own risk:
1012 PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
1013 */
1014 PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
1015
1016 /* Lock mutex */
1017 pj_mutex_lock(conf->mutex);
1018
1019 /* Port must be valid. */
1020 conf_port = conf->ports[slot];
1021 if (conf_port == NULL) {
1022 pj_mutex_unlock(conf->mutex);
1023 return PJ_EINVAL;
1024 }
1025
1026 /* Level adjustment is applicable only for ports that work with raw PCM. */
1027 PJ_ASSERT_RETURN(conf_port->info->fmt.id == PJMEDIA_FORMAT_L16,
1028 PJ_EIGNORED);
1029
1030 /* Set normalized adjustment level. */
1031 conf_port->tx_adj_level = adj_level + NORMAL_LEVEL;
1032
1033 /* Unlock mutex */
1034 pj_mutex_unlock(conf->mutex);
1035
1036 return PJ_SUCCESS;
1037}
1038
1039/* Deliver frm_src to a listener port, eventually call port's put_frame()
1040 * when samples count in the frm_dst are equal to port's samples_per_frame.
1041 */
1042static pj_status_t write_frame(struct conf_port *cport_dst,
1043 const pjmedia_frame *frm_src)
1044{
1045 pjmedia_frame *frm_dst = (pjmedia_frame*)cport_dst->tx_buf;
1046
1047 PJ_TODO(MAKE_SURE_DEST_FRAME_HAS_ENOUGH_SPACE);
1048
1049 frm_dst->type = frm_src->type;
1050 frm_dst->timestamp = cport_dst->ts_tx;
1051
1052 if (frm_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1053
1054 pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frm_src;
1055 pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
1056 unsigned i;
1057
1058 for (i = 0; i < f_src->subframe_cnt; ++i) {
1059 pjmedia_frame_ext_subframe *sf;
1060
1061 /* Copy frame to listener's TX buffer. */
1062 sf = pjmedia_frame_ext_get_subframe(f_src, i);
1063 pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
1064 f_src->samples_cnt /
1065 f_src->subframe_cnt);
1066
1067 /* Check if it's time to deliver the TX buffer to listener,
1068 * i.e: samples count in TX buffer equal to listener's
1069 * samples per frame.
1070 */
1071 if (f_dst->samples_cnt >= cport_dst->samples_per_frame)
1072 {
1073 if (cport_dst->slot) {
1074 pjmedia_port_put_frame(cport_dst->port,
1075 (pjmedia_frame*)f_dst);
1076
1077 /* Reset TX buffer. */
1078 f_dst->subframe_cnt = 0;
1079 f_dst->samples_cnt = 0;
1080 }
1081
1082 /* Update TX timestamp. */
1083 pj_add_timestamp32(&cport_dst->ts_tx,
1084 cport_dst->samples_per_frame);
1085 }
1086 }
1087
1088 } else if (frm_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
1089
1090 pj_int16_t *f_start, *f_end;
1091
1092 f_start = (pj_int16_t*)frm_src->buf;
1093 f_end = f_start + (frm_src->size >> 1);
1094
1095 while (f_start < f_end) {
1096 unsigned nsamples_to_copy, nsamples_req;
1097
1098 /* Copy frame to listener's TX buffer.
1099 * Note that if the destination is port 0, just copy the whole
1100 * available samples.
1101 */
1102 nsamples_to_copy = f_end - f_start;
1103 nsamples_req = cport_dst->samples_per_frame -
1104 (frm_dst->size>>1);
1105 if (cport_dst->slot && nsamples_to_copy > nsamples_req)
1106 nsamples_to_copy = nsamples_req;
1107
1108 /* Adjust TX level. */
1109 if (cport_dst->tx_adj_level != NORMAL_LEVEL) {
1110 pj_int16_t *p, *p_end;
1111
1112 p = f_start;
1113 p_end = p + nsamples_to_copy;
1114 while (p < p_end) {
1115 pj_int32_t itemp = *p;
1116
1117 /* Adjust the level */
1118 itemp = (itemp * cport_dst->tx_adj_level) >> 7;
1119
1120 /* Clip the signal if it's too loud */
1121 if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
1122 else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
1123
1124 /* Put back in the buffer. */
1125 *p = (pj_int16_t)itemp;
1126 ++p;
1127 }
1128 }
1129
1130 pjmedia_copy_samples((pj_int16_t*)frm_dst->buf + (frm_dst->size>>1),
1131 f_start,
1132 nsamples_to_copy);
1133 frm_dst->size += nsamples_to_copy << 1;
1134 f_start += nsamples_to_copy;
1135
1136 /* Check if it's time to deliver the TX buffer to listener,
1137 * i.e: samples count in TX buffer equal to listener's
1138 * samples per frame. Note that for destination port 0 this
1139 * function will just populate all samples in the TX buffer.
1140 */
1141 if (cport_dst->slot == 0) {
1142 /* Update TX timestamp. */
1143 pj_add_timestamp32(&cport_dst->ts_tx, nsamples_to_copy);
1144 } else if ((frm_dst->size >> 1) ==
1145 cport_dst->samples_per_frame)
1146 {
1147 pjmedia_port_put_frame(cport_dst->port, frm_dst);
1148
1149 /* Reset TX buffer. */
1150 frm_dst->size = 0;
1151
1152 /* Update TX timestamp. */
1153 pj_add_timestamp32(&cport_dst->ts_tx,
1154 cport_dst->samples_per_frame);
1155 }
1156 }
1157
1158 } else if (frm_src->type == PJMEDIA_FRAME_TYPE_NONE) {
1159
1160 /* Check port format. */
1161 if (cport_dst->port &&
1162 cport_dst->port->info.fmt.id == PJMEDIA_FORMAT_L16)
1163 {
1164 /* When there is already some samples in listener's TX buffer,
1165 * pad the buffer with "zero samples".
1166 */
1167 if (frm_dst->size != 0) {
1168 pjmedia_zero_samples((pj_int16_t*)frm_dst->buf,
1169 cport_dst->samples_per_frame -
1170 (frm_dst->size>>1));
1171
1172 frm_dst->type = PJMEDIA_FRAME_TYPE_AUDIO;
1173 frm_dst->size = cport_dst->samples_per_frame << 1;
1174 if (cport_dst->slot) {
1175 pjmedia_port_put_frame(cport_dst->port, frm_dst);
1176
1177 /* Reset TX buffer. */
1178 frm_dst->size = 0;
1179 }
1180
1181 /* Update TX timestamp. */
1182 pj_add_timestamp32(&cport_dst->ts_tx,
1183 cport_dst->samples_per_frame);
1184 }
1185 } else {
1186 pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
1187
1188 if (f_dst->samples_cnt != 0) {
1189 frm_dst->type = PJMEDIA_FRAME_TYPE_EXTENDED;
1190 pjmedia_frame_ext_append_subframe(f_dst, NULL, 0, (pj_uint16_t)
1191 (cport_dst->samples_per_frame - f_dst->samples_cnt));
1192 if (cport_dst->slot) {
1193 pjmedia_port_put_frame(cport_dst->port, frm_dst);
1194
1195 /* Reset TX buffer. */
1196 f_dst->subframe_cnt = 0;
1197 f_dst->samples_cnt = 0;
1198 }
1199
1200 /* Update TX timestamp. */
1201 pj_add_timestamp32(&cport_dst->ts_tx,
1202 cport_dst->samples_per_frame);
1203 }
1204 }
1205
1206 /* Synchronize clock. */
1207 while (pj_cmp_timestamp(&cport_dst->ts_clock,
1208 &cport_dst->ts_tx) > 0)
1209 {
1210 frm_dst->type = PJMEDIA_FRAME_TYPE_NONE;
1211 frm_dst->timestamp = cport_dst->ts_tx;
1212 if (cport_dst->slot)
1213 pjmedia_port_put_frame(cport_dst->port, frm_dst);
1214
1215 /* Update TX timestamp. */
1216 pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->samples_per_frame);
1217 }
1218 }
1219
1220 return PJ_SUCCESS;
1221}
1222
1223/*
1224 * Player callback.
1225 */
1226static pj_status_t get_frame(pjmedia_port *this_port,
1227 pjmedia_frame *frame)
1228{
1229 pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
1230 unsigned ci, i;
1231
1232 /* Lock mutex */
1233 pj_mutex_lock(conf->mutex);
1234
1235 /* Call get_frame() from all ports (except port 0) that has
1236 * receiver and distribute the frame (put the frame to the destination
1237 * port's buffer to accommodate different ptime, and ultimately call
1238 * put_frame() of that port) to ports that are receiving from this port.
1239 */
1240 for (i=1, ci=1; i<conf->max_ports && ci<conf->port_cnt; ++i) {
1241 struct conf_port *cport = conf->ports[i];
1242 unsigned master_samples_per_frame;
1243
1244 /* Skip empty port. */
1245 if (!cport)
1246 continue;
1247
1248 /* Var "ci" is to count how many ports have been visited so far. */
1249 ++ci;
1250
1251 master_samples_per_frame = PJMEDIA_PIA_SPF(&conf->master_port->info);
1252
1253 /* Update clock of the port. */
1254 pj_add_timestamp32(&cport->ts_clock, master_samples_per_frame);
1255
1256 /* Skip if we're not allowed to receive from this port or
1257 * the port doesn't have listeners.
1258 */
1259 if (cport->rx_setting == PJMEDIA_PORT_DISABLE ||
1260 cport->listener_cnt == 0)
1261 {
1262 cport->rx_level = 0;
1263 pj_add_timestamp32(&cport->ts_rx, master_samples_per_frame);
1264 continue;
1265 }
1266
1267 /* Get frame from each port, put it to the listener TX buffer,
1268 * and eventually call put_frame() of the listener. This loop
1269 * will also make sure the ptime between conf & port synchronized.
1270 */
1271 while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_rx) > 0) {
1272 pjmedia_frame *f = (pjmedia_frame*)conf->buf;
1273 pj_status_t status;
1274 unsigned j;
1275 pj_int32_t level = 0;
1276
1277 pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
1278
1279 f->buf = &conf->buf[sizeof(pjmedia_frame)];
1280 f->size = cport->samples_per_frame<<1;
1281
1282 /* Get frame from port. */
1283 status = pjmedia_port_get_frame(cport->port, f);
1284 if (status != PJ_SUCCESS)
1285 continue;
1286
1287 /* Calculate & adjust RX level. */
1288 if (f->type == PJMEDIA_FRAME_TYPE_AUDIO) {
1289 if (cport->rx_adj_level != NORMAL_LEVEL) {
1290 pj_int16_t *p = (pj_int16_t*)f->buf;
1291 pj_int16_t *end;
1292
1293 end = p + (f->size >> 1);
1294 while (p < end) {
1295 pj_int32_t itemp = *p;
1296
1297 /* Adjust the level */
1298 itemp = (itemp * cport->rx_adj_level) >> 7;
1299
1300 /* Clip the signal if it's too loud */
1301 if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
1302 else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
1303
1304 level += PJ_ABS(itemp);
1305
1306 /* Put back in the buffer. */
1307 *p = (pj_int16_t)itemp;
1308 ++p;
1309 }
1310 level /= (f->size >> 1);
1311 } else {
1312 level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
1313 f->size >> 1);
1314 }
1315 } else if (f->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1316 /* For extended frame, level is unknown, so we just set
1317 * it to NORMAL_LEVEL.
1318 */
1319 level = NORMAL_LEVEL;
1320 }
1321
1322 cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff;
1323
1324 /* Put the frame to all listeners. */
1325 for (j=0; j < cport->listener_cnt; ++j)
1326 {
1327 struct conf_port *listener;
1328
1329 listener = conf->ports[cport->listener_slots[j]];
1330
1331 /* Skip if this listener doesn't want to receive audio */
1332 if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
1333 pj_add_timestamp32(&listener->ts_tx,
1334 listener->samples_per_frame);
1335 listener->tx_level = 0;
1336 continue;
1337 }
1338
1339 status = write_frame(listener, f);
1340 if (status != PJ_SUCCESS) {
1341 listener->tx_level = 0;
1342 continue;
1343 }
1344
1345 /* Set listener TX level based on transmitter RX level &
1346 * listener TX level.
1347 */
1348 listener->tx_level = (cport->rx_level * listener->tx_adj_level)
1349 >> 8;
1350 }
1351 }
1352 }
1353
1354 /* Keep alive. Update TX timestamp and send frame type NONE to all
1355 * underflow ports at their own clock.
1356 */
1357 for (i=1, ci=1; i<conf->max_ports && ci<conf->port_cnt; ++i) {
1358 struct conf_port *cport = conf->ports[i];
1359
1360 /* Skip empty port. */
1361 if (!cport)
1362 continue;
1363
1364 /* Var "ci" is to count how many ports have been visited so far. */
1365 ++ci;
1366
1367 if (cport->tx_setting==PJMEDIA_PORT_MUTE || cport->transmitter_cnt==0)
1368 {
1369 pjmedia_frame_ext *f;
1370
1371 /* Clear left-over samples in tx_buffer, if any, so that it won't
1372 * be transmitted next time we have audio signal.
1373 */
1374 f = (pjmedia_frame_ext*)cport->tx_buf;
1375 f->base.type = PJMEDIA_FRAME_TYPE_NONE;
1376 f->base.size = 0;
1377 f->samples_cnt = 0;
1378 f->subframe_cnt = 0;
1379
1380 cport->tx_level = 0;
1381
1382 while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_tx) > 0)
1383 {
1384 if (cport->tx_setting == PJMEDIA_PORT_ENABLE) {
1385 pjmedia_frame tmp_f;
1386
1387 tmp_f.timestamp = cport->ts_tx;
1388 tmp_f.type = PJMEDIA_FRAME_TYPE_NONE;
1389 tmp_f.buf = NULL;
1390 tmp_f.size = 0;
1391
1392 pjmedia_port_put_frame(cport->port, &tmp_f);
1393 pj_add_timestamp32(&cport->ts_tx, cport->samples_per_frame);
1394 }
1395 }
1396 }
1397 }
1398
1399 /* Return sound playback frame. */
1400 do {
1401 struct conf_port *this_cport = conf->ports[this_port->port_data.ldata];
1402 pjmedia_frame *f_src = (pjmedia_frame*) this_cport->tx_buf;
1403
1404 frame->type = f_src->type;
1405
1406 if (f_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1407 pjmedia_frame_ext *f_src_ = (pjmedia_frame_ext*)f_src;
1408 pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frame;
1409 pjmedia_frame_ext_subframe *sf;
1410 unsigned samples_per_subframe;
1411
1412 if (f_src_->samples_cnt < this_cport->samples_per_frame) {
1413 f_dst->base.type = PJMEDIA_FRAME_TYPE_NONE;
1414 f_dst->samples_cnt = 0;
1415 f_dst->subframe_cnt = 0;
1416 break;
1417 }
1418
1419 f_dst->samples_cnt = 0;
1420 f_dst->subframe_cnt = 0;
1421 i = 0;
1422 samples_per_subframe = f_src_->samples_cnt / f_src_->subframe_cnt;
1423
1424
1425 while (f_dst->samples_cnt < this_cport->samples_per_frame) {
1426 sf = pjmedia_frame_ext_get_subframe(f_src_, i++);
1427 pj_assert(sf);
1428 pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
1429 samples_per_subframe);
1430 }
1431
1432 /* Shift left TX buffer. */
1433 pjmedia_frame_ext_pop_subframes(f_src_, i);
1434
1435 } else if (f_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
1436 if ((f_src->size>>1) < this_cport->samples_per_frame) {
1437 frame->type = PJMEDIA_FRAME_TYPE_NONE;
1438 frame->size = 0;
1439 break;
1440 }
1441
1442 pjmedia_copy_samples((pj_int16_t*)frame->buf,
1443 (pj_int16_t*)f_src->buf,
1444 this_cport->samples_per_frame);
1445 frame->size = this_cport->samples_per_frame << 1;
1446
1447 /* Shift left TX buffer. */
1448 f_src->size -= frame->size;
1449 if (f_src->size)
1450 pjmedia_move_samples((pj_int16_t*)f_src->buf,
1451 (pj_int16_t*)f_src->buf +
1452 this_cport->samples_per_frame,
1453 f_src->size >> 1);
1454 } else { /* PJMEDIA_FRAME_TYPE_NONE */
1455 pjmedia_frame_ext *f_src_ = (pjmedia_frame_ext*)f_src;
1456
1457 /* Reset source/TX buffer */
1458 f_src_->base.size = 0;
1459 f_src_->samples_cnt = 0;
1460 f_src_->subframe_cnt = 0;
1461 }
1462 } while (0);
1463
1464 /* Unlock mutex */
1465 pj_mutex_unlock(conf->mutex);
1466
1467 return PJ_SUCCESS;
1468}
1469
1470/*
1471 * Recorder callback.
1472 */
1473static pj_status_t put_frame(pjmedia_port *this_port,
1474 pjmedia_frame *f)
1475{
1476 pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
1477 struct conf_port *cport;
1478 unsigned j;
1479 pj_int32_t level = 0;
1480
1481 /* Lock mutex */
1482 pj_mutex_lock(conf->mutex);
1483
1484 /* Get conf port of this port */
1485 cport = conf->ports[this_port->port_data.ldata];
1486 if (cport == NULL) {
1487 /* Unlock mutex */
1488 pj_mutex_unlock(conf->mutex);
1489 return PJ_SUCCESS;
1490 }
1491
1492 pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
1493
1494 /* Skip if this port is muted/disabled. */
1495 if (cport->rx_setting == PJMEDIA_PORT_DISABLE) {
1496 cport->rx_level = 0;
1497 /* Unlock mutex */
1498 pj_mutex_unlock(conf->mutex);
1499 return PJ_SUCCESS;
1500 }
1501
1502 /* Skip if no port is listening to the microphone */
1503 if (cport->listener_cnt == 0) {
1504 cport->rx_level = 0;
1505 /* Unlock mutex */
1506 pj_mutex_unlock(conf->mutex);
1507 return PJ_SUCCESS;
1508 }
1509
1510 /* Calculate & adjust RX level. */
1511 if (f->type == PJMEDIA_FRAME_TYPE_AUDIO) {
1512 if (cport->rx_adj_level != NORMAL_LEVEL) {
1513 pj_int16_t *p = (pj_int16_t*)f->buf;
1514 pj_int16_t *end;
1515
1516 end = p + (f->size >> 1);
1517 while (p < end) {
1518 pj_int32_t itemp = *p;
1519
1520 /* Adjust the level */
1521 itemp = (itemp * cport->rx_adj_level) >> 7;
1522
1523 /* Clip the signal if it's too loud */
1524 if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
1525 else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
1526
1527 level += PJ_ABS(itemp);
1528
1529 /* Put back in the buffer. */
1530 *p = (pj_int16_t)itemp;
1531 ++p;
1532 }
1533 level /= (f->size >> 1);
1534 } else {
1535 level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
1536 f->size >> 1);
1537 }
1538 } else if (f->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1539 /* For extended frame, level is unknown, so we just set
1540 * it to NORMAL_LEVEL.
1541 */
1542 level = NORMAL_LEVEL;
1543 }
1544
1545 cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff;
1546
1547 /* Put the frame to all listeners. */
1548 for (j=0; j < cport->listener_cnt; ++j)
1549 {
1550 struct conf_port *listener;
1551 pj_status_t status;
1552
1553 listener = conf->ports[cport->listener_slots[j]];
1554
1555 /* Skip if this listener doesn't want to receive audio */
1556 if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
1557 pj_add_timestamp32(&listener->ts_tx,
1558 listener->samples_per_frame);
1559 listener->tx_level = 0;
1560 continue;
1561 }
1562
1563 /* Skip loopback for now. */
1564 if (listener == cport) {
1565 pj_add_timestamp32(&listener->ts_tx,
1566 listener->samples_per_frame);
1567 listener->tx_level = 0;
1568 continue;
1569 }
1570
1571 status = write_frame(listener, f);
1572 if (status != PJ_SUCCESS) {
1573 listener->tx_level = 0;
1574 continue;
1575 }
1576
1577 /* Set listener TX level based on transmitter RX level & listener
1578 * TX level.
1579 */
1580 listener->tx_level = (cport->rx_level * listener->tx_adj_level) >> 8;
1581 }
1582
1583 /* Unlock mutex */
1584 pj_mutex_unlock(conf->mutex);
1585
1586 return PJ_SUCCESS;
1587}
1588
1589#endif