1
/* $Id: splitcomb.c 3664 2011-07-19 03:42:28Z nanang $ */
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/splitcomb.h>
21
#include <pjmedia/delaybuf.h>
22
#include <pjmedia/errno.h>
23
#include <pj/assert.h>
28
#define SIGNATURE PJMEDIA_SIG_PORT_SPLIT_COMB
29
#define SIGNATURE_PORT PJMEDIA_SIG_PORT_SPLIT_COMB_P
30
#define THIS_FILE "splitcomb.c"
31
#define TMP_SAMP_TYPE pj_int16_t
33
/* Maximum number of channels. */
34
#define MAX_CHANNELS 16
36
/* Maximum number of buffers to be accommodated by delaybuf */
37
#define MAX_BUF_CNT PJMEDIA_SOUND_BUFFER_COUNT
39
/* Maximum number of burst before we pause the media flow */
40
#define MAX_BURST (buf_cnt + 6)
42
/* Maximum number of NULL frames received before we pause the
45
#define MAX_NULL_FRAMES (rport->max_burst)
54
* Media flow directions:
57
* UPSTREAM ------------>|split|<--> DOWNSTREAM
58
* <------------|comb |
64
/* This is the media direction from the splitcomb to the
65
* downstream port(s), which happens when:
66
* - put_frame() is called to the splitcomb
67
* - get_frame() is called to the reverse channel port.
71
/* This is the media direction from the downstream port to
72
* the splitcomb, which happens when:
73
* - get_frame() is called to the splitcomb
74
* - put_frame() is called to the reverse channel port.
82
* This structure describes the splitter/combiner.
90
/* Array of ports, one for each channel */
94
} port_desc[MAX_CHANNELS];
96
/* Temporary buffers needed to extract mono frame from
97
* multichannel frame. We could use stack for this, but this
98
* way it should be safer for devices with small stack size.
100
TMP_SAMP_TYPE *get_buf;
101
TMP_SAMP_TYPE *put_buf;
106
* This structure describes reverse port.
111
struct splitcomb*parent;
114
/* Maximum burst before media flow is suspended.
115
* With reverse port, it's possible that either end of the
116
* port doesn't actually process the media flow (meaning, it
117
* stops calling get_frame()/put_frame()). When this happens,
118
* the other end will encounter excessive underflow or overflow,
119
* depending on which direction is not actively processed by
122
* To avoid excessive underflow/overflow, the media flow will
123
* be suspended once underflow/overflow goes over this max_burst
128
/* When the media interface port of the splitcomb or the reverse
129
* channel port is registered to conference bridge, the bridge
130
* will transmit NULL frames to the media port when the media
131
* port is not receiving any audio from other slots (for example,
132
* when no other slots are connected to the media port).
134
* When this happens, we will generate zero frame to our buffer,
135
* to avoid underflow/overflow. But after too many NULL frames
136
* are received, we will pause the media flow instead, to save
139
* This value controls how many NULL frames can be received
140
* before we suspend media flow for a particular direction.
142
unsigned max_null_frames;
144
/* A reverse port need a temporary buffer to store frames
145
* (because of the different phase, see splitcomb.h for details).
146
* Since we can not expect get_frame() and put_frame() to be
147
* called evenly one after another, we use delay buffers to
148
* accomodate the burst.
150
* We maintain state for each direction, hence the array. The
151
* array is indexed by direction (sc_dir).
155
/* The delay buffer where frames will be stored */
156
pjmedia_delay_buf *dbuf;
158
/* Flag to indicate that audio flow on this direction
159
* is currently being suspended (perhaps because nothing
160
* is processing the frame on the other end).
164
/* Operation level. When the level exceeds a maximum value,
165
* the media flow on this direction will be paused.
172
/* Number of NULL frames transmitted to this port so far.
173
* NULL frame indicate that nothing is transmitted, and
174
* once we get too many of this, we should pause the media
175
* flow to reduce processing.
181
/* Must have temporary put buffer for the delay buf,
184
pj_int16_t *tmp_up_buf;
191
static pj_status_t put_frame(pjmedia_port *this_port,
192
pjmedia_frame *frame);
193
static pj_status_t get_frame(pjmedia_port *this_port,
194
pjmedia_frame *frame);
195
static pj_status_t on_destroy(pjmedia_port *this_port);
197
static pj_status_t rport_put_frame(pjmedia_port *this_port,
198
pjmedia_frame *frame);
199
static pj_status_t rport_get_frame(pjmedia_port *this_port,
200
pjmedia_frame *frame);
201
static pj_status_t rport_on_destroy(pjmedia_port *this_port);
205
* Create the splitter/combiner.
207
PJ_DEF(pj_status_t) pjmedia_splitcomb_create( pj_pool_t *pool,
209
unsigned channel_count,
210
unsigned samples_per_frame,
211
unsigned bits_per_sample,
213
pjmedia_port **p_splitcomb)
215
const pj_str_t name = pj_str("splitcomb");
216
struct splitcomb *sc;
219
PJ_ASSERT_RETURN(pool && clock_rate && channel_count &&
220
samples_per_frame && bits_per_sample &&
221
p_splitcomb, PJ_EINVAL);
223
/* Only supports 16 bits per sample */
224
PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
228
/* Create the splitter/combiner structure */
229
sc = PJ_POOL_ZALLOC_T(pool, struct splitcomb);
230
PJ_ASSERT_RETURN(sc != NULL, PJ_ENOMEM);
232
/* Create temporary buffers */
233
sc->get_buf = (TMP_SAMP_TYPE*)
234
pj_pool_alloc(pool, samples_per_frame *
235
sizeof(TMP_SAMP_TYPE) /
237
PJ_ASSERT_RETURN(sc->get_buf, PJ_ENOMEM);
239
sc->put_buf = (TMP_SAMP_TYPE*)
240
pj_pool_alloc(pool, samples_per_frame *
241
sizeof(TMP_SAMP_TYPE) /
243
PJ_ASSERT_RETURN(sc->put_buf, PJ_ENOMEM);
247
sc->options = options;
249
/* Initialize port */
250
pjmedia_port_info_init(&sc->base.info, &name, SIGNATURE, clock_rate,
251
channel_count, bits_per_sample, samples_per_frame);
253
sc->base.put_frame = &put_frame;
254
sc->base.get_frame = &get_frame;
255
sc->base.on_destroy = &on_destroy;
257
/* Init ports array */
259
sc->port_desc = pj_pool_zalloc(pool, channel_count*sizeof(*sc->port_desc));
261
pj_bzero(sc->port_desc, sizeof(sc->port_desc));
264
*p_splitcomb = &sc->base;
271
* Attach media port with the same phase as the splitter/combiner.
273
PJ_DEF(pj_status_t) pjmedia_splitcomb_set_channel( pjmedia_port *splitcomb,
278
struct splitcomb *sc = (struct splitcomb*) splitcomb;
281
PJ_ASSERT_RETURN(splitcomb && port, PJ_EINVAL);
283
/* Make sure this is really a splitcomb port */
284
PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL);
286
/* Check the channel number */
287
PJ_ASSERT_RETURN(ch_num < PJMEDIA_PIA_CCNT(&sc->base.info), PJ_EINVAL);
289
/* options is unused for now */
290
PJ_UNUSED_ARG(options);
292
sc->port_desc[ch_num].port = port;
293
sc->port_desc[ch_num].reversed = PJ_FALSE;
300
* Create reverse phase port for the specified channel.
302
PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool,
303
pjmedia_port *splitcomb,
306
pjmedia_port **p_chport)
308
const pj_str_t name = pj_str("scomb-rev");
309
struct splitcomb *sc = (struct splitcomb*) splitcomb;
310
struct reverse_port *rport;
312
const pjmedia_audio_format_detail *sc_afd, *p_afd;
317
PJ_ASSERT_RETURN(pool && splitcomb, PJ_EINVAL);
319
/* Make sure this is really a splitcomb port */
320
PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL);
322
/* Check the channel number */
323
PJ_ASSERT_RETURN(ch_num < PJMEDIA_PIA_CCNT(&sc->base.info), PJ_EINVAL);
325
/* options is unused for now */
326
PJ_UNUSED_ARG(options);
328
sc_afd = pjmedia_format_get_audio_format_detail(&splitcomb->info.fmt, 1);
330
/* Create the port */
331
rport = PJ_POOL_ZALLOC_T(pool, struct reverse_port);
333
rport->ch_num = ch_num;
335
/* Initialize port info... */
337
pjmedia_port_info_init(&port->info, &name, SIGNATURE_PORT,
338
sc_afd->clock_rate, 1,
339
sc_afd->bits_per_sample,
340
PJMEDIA_PIA_SPF(&splitcomb->info) /
341
sc_afd->channel_count);
343
p_afd = pjmedia_format_get_audio_format_detail(&port->info.fmt, 1);
345
/* ... and the callbacks */
346
port->put_frame = &rport_put_frame;
347
port->get_frame = &rport_get_frame;
348
port->on_destroy = &rport_on_destroy;
350
/* Buffer settings */
351
buf_cnt = options & 0xFF;
353
buf_cnt = MAX_BUF_CNT;
355
rport->max_burst = MAX_BURST;
356
rport->max_null_frames = MAX_NULL_FRAMES;
358
/* Create downstream/put buffers */
359
status = pjmedia_delay_buf_create(pool, "scombdb-dn",
361
PJMEDIA_PIA_SPF(&port->info),
362
p_afd->channel_count,
363
buf_cnt * p_afd->frame_time_usec / 1000,
364
0, &rport->buf[DIR_DOWNSTREAM].dbuf);
365
if (status != PJ_SUCCESS) {
369
/* Create upstream/get buffers */
370
status = pjmedia_delay_buf_create(pool, "scombdb-up",
372
PJMEDIA_PIA_SPF(&port->info),
373
p_afd->channel_count,
374
buf_cnt * p_afd->frame_time_usec / 1000,
375
0, &rport->buf[DIR_UPSTREAM].dbuf);
376
if (status != PJ_SUCCESS) {
377
pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf);
381
/* And temporary upstream/get buffer */
382
rport->tmp_up_buf = (pj_int16_t*)
384
PJMEDIA_PIA_AVG_FSZ(&port->info));
386
/* Save port in the splitcomb */
387
sc->port_desc[ch_num].port = &rport->base;
388
sc->port_desc[ch_num].reversed = PJ_TRUE;
398
* Extract one mono frame from a multichannel frame.
400
static void extract_mono_frame( const pj_int16_t *in,
404
unsigned samples_count)
409
for (i=0; i<samples_count; ++i) {
417
* Put one mono frame into a multichannel frame
419
static void store_mono_frame( const pj_int16_t *in,
423
unsigned samples_count)
428
for (i=0; i<samples_count; ++i) {
434
/* Update operation on the specified direction */
435
static void op_update(struct reverse_port *rport, int dir, int op)
437
char *dir_name[2] = {"downstream", "upstream"};
439
rport->buf[dir].level += op;
442
rport->buf[dir].ts.u64 += PJMEDIA_PIA_SPF(&rport->base.info);
445
if (rport->buf[dir].paused) {
446
if (rport->buf[dir].level < -rport->max_burst) {
447
/* Prevent the level from overflowing and resets back to zero */
448
rport->buf[dir].level = -rport->max_burst;
449
} else if (rport->buf[dir].level > rport->max_burst) {
450
/* Prevent the level from overflowing and resets back to zero */
451
rport->buf[dir].level = rport->max_burst;
453
/* Level has fallen below max level, we can resume
456
PJ_LOG(5,(rport->base.info.name.ptr,
457
"Resuming media flow on %s direction (level=%d)",
458
dir_name[dir], rport->buf[dir].level));
459
rport->buf[dir].level = 0;
460
rport->buf[dir].paused = PJ_FALSE;
462
//This will cause disruption in audio, and it seems to be
463
//working fine without this anyway, so we disable it for now.
464
//pjmedia_delay_buf_learn(rport->buf[dir].dbuf);
468
if (rport->buf[dir].level >= rport->max_burst ||
469
rport->buf[dir].level <= -rport->max_burst)
471
/* Level has reached maximum level, the other side of
472
* rport is not sending/retrieving frames. Pause the
473
* rport on this direction.
475
PJ_LOG(5,(rport->base.info.name.ptr,
476
"Pausing media flow on %s direction (level=%d)",
477
dir_name[dir], rport->buf[dir].level));
478
rport->buf[dir].paused = PJ_TRUE;
485
* "Write" a multichannel frame downstream. This would split
486
* the multichannel frame into individual mono channel, and write
487
* it to the appropriate port.
489
static pj_status_t put_frame(pjmedia_port *this_port,
490
pjmedia_frame *frame)
492
struct splitcomb *sc = (struct splitcomb*) this_port;
495
/* Handle null frame */
496
if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
497
for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
498
pjmedia_port *port = sc->port_desc[ch].port;
502
if (!sc->port_desc[ch].reversed) {
503
pjmedia_port_put_frame(port, frame);
505
struct reverse_port *rport = (struct reverse_port*)port;
507
/* Update the number of NULL frames received. Once we have too
508
* many of this, we'll stop calling op_update() to let the
509
* media be suspended.
512
if (++rport->buf[DIR_DOWNSTREAM].null_cnt >
513
rport->max_null_frames)
515
/* Prevent the counter from overflowing and resetting
518
rport->buf[DIR_DOWNSTREAM].null_cnt =
519
rport->max_null_frames + 1;
523
/* Write zero port to delaybuf so that it doesn't underflow.
524
* If we don't do this, get_frame() on this direction will
525
* cause delaybuf to generate missing frame and the last
526
* frame transmitted to delaybuf will be replayed multiple
527
* times, which doesn't sound good.
530
/* Update rport state. */
531
op_update(rport, DIR_DOWNSTREAM, OP_PUT);
533
/* Discard frame if rport is paused on this direction */
534
if (rport->buf[DIR_DOWNSTREAM].paused)
537
/* Generate zero frame. */
538
pjmedia_zero_samples(sc->put_buf,
539
PJMEDIA_PIA_SPF(&this_port->info));
541
/* Put frame to delay buffer */
542
pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf,
550
/* Not sure how we would handle partial frame, so better reject
553
PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info),
557
* Write mono frame into each channels
559
for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
560
pjmedia_port *port = sc->port_desc[ch].port;
565
/* Extract the mono frame to temporary buffer */
566
extract_mono_frame((const pj_int16_t*)frame->buf, sc->put_buf, ch,
567
PJMEDIA_PIA_CCNT(&this_port->info),
569
PJMEDIA_PIA_BITS(&this_port->info) /
570
PJMEDIA_PIA_CCNT(&this_port->info));
572
if (!sc->port_desc[ch].reversed) {
573
/* Write to normal port */
574
pjmedia_frame mono_frame;
576
mono_frame.buf = sc->put_buf;
577
mono_frame.size = frame->size / PJMEDIA_PIA_CCNT(&this_port->info);
578
mono_frame.type = frame->type;
579
mono_frame.timestamp.u64 = frame->timestamp.u64;
582
pjmedia_port_put_frame(port, &mono_frame);
585
/* Write to reversed phase port */
586
struct reverse_port *rport = (struct reverse_port*)port;
588
/* Reset NULL frame counter */
589
rport->buf[DIR_DOWNSTREAM].null_cnt = 0;
591
/* Update rport state. */
592
op_update(rport, DIR_DOWNSTREAM, OP_PUT);
594
if (!rport->buf[DIR_DOWNSTREAM].paused) {
595
pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf,
606
* Get a multichannel frame upstream.
607
* This will get mono channel frame from each port and put the
608
* mono frame into the multichannel frame.
610
static pj_status_t get_frame(pjmedia_port *this_port,
611
pjmedia_frame *frame)
613
struct splitcomb *sc = (struct splitcomb*) this_port;
615
pj_bool_t has_frame = PJ_FALSE;
617
/* Read frame from each port */
618
for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
619
pjmedia_port *port = sc->port_desc[ch].port;
620
pjmedia_frame mono_frame;
624
pjmedia_zero_samples(sc->get_buf,
625
PJMEDIA_PIA_SPF(&this_port->info) /
626
PJMEDIA_PIA_CCNT(&this_port->info));
628
} else if (sc->port_desc[ch].reversed == PJ_FALSE) {
629
/* Read from normal port */
630
mono_frame.buf = sc->get_buf;
631
mono_frame.size = PJMEDIA_PIA_AVG_FSZ(&port->info);
632
mono_frame.timestamp.u64 = frame->timestamp.u64;
634
status = pjmedia_port_get_frame(port, &mono_frame);
635
if (status != PJ_SUCCESS ||
636
mono_frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
638
pjmedia_zero_samples(sc->get_buf,
639
PJMEDIA_PIA_SPF(&port->info));
642
frame->timestamp.u64 = mono_frame.timestamp.u64;
645
/* Read from temporary buffer for reverse port */
646
struct reverse_port *rport = (struct reverse_port*)port;
648
/* Update rport state. */
649
op_update(rport, DIR_UPSTREAM, OP_GET);
651
if (!rport->buf[DIR_UPSTREAM].paused) {
652
pjmedia_delay_buf_get(rport->buf[DIR_UPSTREAM].dbuf,
656
pjmedia_zero_samples(sc->get_buf,
657
PJMEDIA_PIA_SPF(&port->info));
660
frame->timestamp.u64 = rport->buf[DIR_UPSTREAM].ts.u64;
663
/* Combine the mono frame into multichannel frame */
664
store_mono_frame(sc->get_buf,
665
(pj_int16_t*)frame->buf, ch,
666
PJMEDIA_PIA_CCNT(&this_port->info),
667
PJMEDIA_PIA_SPF(&this_port->info) /
668
PJMEDIA_PIA_CCNT(&this_port->info));
673
/* Return NO_FRAME is we don't get any frames from downstream ports */
675
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
676
frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
678
frame->type = PJMEDIA_FRAME_TYPE_NONE;
684
static pj_status_t on_destroy(pjmedia_port *this_port)
686
/* Nothing to do for the splitcomb
687
* Reverse ports must be destroyed separately.
689
PJ_UNUSED_ARG(this_port);
696
* Put a frame in the reverse port (upstream direction). This frame
697
* will be picked up by get_frame() above.
699
static pj_status_t rport_put_frame(pjmedia_port *this_port,
700
pjmedia_frame *frame)
702
struct reverse_port *rport = (struct reverse_port*) this_port;
704
pj_assert(frame->size <= PJMEDIA_PIA_AVG_FSZ(&rport->base.info));
706
/* Handle NULL frame */
707
if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) {
708
/* Update the number of NULL frames received. Once we have too
709
* many of this, we'll stop calling op_update() to let the
710
* media be suspended.
712
if (++rport->buf[DIR_UPSTREAM].null_cnt > rport->max_null_frames) {
713
/* Prevent the counter from overflowing and resetting back
716
rport->buf[DIR_UPSTREAM].null_cnt = rport->max_null_frames + 1;
720
/* Write zero port to delaybuf so that it doesn't underflow.
721
* If we don't do this, get_frame() on this direction will
722
* cause delaybuf to generate missing frame and the last
723
* frame transmitted to delaybuf will be replayed multiple
724
* times, which doesn't sound good.
727
/* Update rport state. */
728
op_update(rport, DIR_UPSTREAM, OP_PUT);
730
/* Discard frame if rport is paused on this direction */
731
if (rport->buf[DIR_UPSTREAM].paused)
734
/* Generate zero frame. */
735
pjmedia_zero_samples(rport->tmp_up_buf,
736
PJMEDIA_PIA_SPF(&this_port->info));
738
/* Put frame to delay buffer */
739
return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf,
743
/* Not sure how to handle partial frame, so better reject for now */
744
PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info),
747
/* Reset NULL frame counter */
748
rport->buf[DIR_UPSTREAM].null_cnt = 0;
750
/* Update rport state. */
751
op_update(rport, DIR_UPSTREAM, OP_PUT);
753
/* Discard frame if rport is paused on this direction */
754
if (rport->buf[DIR_UPSTREAM].paused)
757
/* Unfortunately must copy to temporary buffer since delay buf
758
* modifies the frame content.
760
pjmedia_copy_samples(rport->tmp_up_buf, (const pj_int16_t*)frame->buf,
761
PJMEDIA_PIA_SPF(&this_port->info));
763
/* Put frame to delay buffer */
764
return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf,
769
/* Get a mono frame from a reversed phase channel (downstream direction).
770
* The frame is put by put_frame() call to the splitcomb.
772
static pj_status_t rport_get_frame(pjmedia_port *this_port,
773
pjmedia_frame *frame)
775
struct reverse_port *rport = (struct reverse_port*) this_port;
778
op_update(rport, DIR_DOWNSTREAM, OP_GET);
780
/* Return no frame if media flow on this direction is being
783
if (rport->buf[DIR_DOWNSTREAM].paused) {
784
frame->type = PJMEDIA_FRAME_TYPE_NONE;
788
/* Get frame from delay buffer */
789
frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
790
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
791
frame->timestamp.u64 = rport->buf[DIR_DOWNSTREAM].ts.u64;
793
return pjmedia_delay_buf_get(rport->buf[DIR_DOWNSTREAM].dbuf,
798
static pj_status_t rport_on_destroy(pjmedia_port *this_port)
800
struct reverse_port *rport = (struct reverse_port*) this_port;
802
pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf);
803
pjmedia_delay_buf_destroy(rport->buf[DIR_UPSTREAM].dbuf);