~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/pjmedia/src/pjmedia/splitcomb.c

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2015-01-07 14:51:16 UTC
  • mfrom: (4.3.5 sid)
  • Revision ID: package-import@ubuntu.com-20150107145116-yxnafinf4lrdvrmx
Tags: 1.4.1-0.1ubuntu1
* Merge with Debian, remaining changes:
 - Drop soprano, nepomuk build-dep
* Drop ubuntu patches, now upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: splitcomb.c 3664 2011-07-19 03:42:28Z nanang $ */
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/splitcomb.h>
21
 
#include <pjmedia/delaybuf.h>
22
 
#include <pjmedia/errno.h>
23
 
#include <pj/assert.h>
24
 
#include <pj/log.h>
25
 
#include <pj/pool.h>
26
 
 
27
 
 
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
32
 
 
33
 
/* Maximum number of channels. */
34
 
#define MAX_CHANNELS        16
35
 
 
36
 
/* Maximum number of buffers to be accommodated by delaybuf */
37
 
#define MAX_BUF_CNT         PJMEDIA_SOUND_BUFFER_COUNT
38
 
 
39
 
/* Maximum number of burst before we pause the media flow */
40
 
#define MAX_BURST           (buf_cnt + 6)
41
 
 
42
 
/* Maximum number of NULL frames received before we pause the
43
 
 * media flow.
44
 
 */
45
 
#define MAX_NULL_FRAMES     (rport->max_burst)
46
 
 
47
 
 
48
 
/* Operations */
49
 
#define OP_PUT              (1)
50
 
#define OP_GET              (-1)
51
 
 
52
 
 
53
 
/* 
54
 
 * Media flow directions:
55
 
 *
56
 
 *             put_frame() +-----+
57
 
 *  UPSTREAM  ------------>|split|<--> DOWNSTREAM
58
 
 *            <------------|comb |
59
 
 *             get_frame() +-----+
60
 
 *
61
 
 */
62
 
enum sc_dir
63
 
{
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.
68
 
     */
69
 
    DIR_DOWNSTREAM,
70
 
 
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.
75
 
     */
76
 
    DIR_UPSTREAM
77
 
};
78
 
 
79
 
 
80
 
 
81
 
/*
82
 
 * This structure describes the splitter/combiner.
83
 
 */
84
 
struct splitcomb
85
 
{
86
 
    pjmedia_port      base;
87
 
 
88
 
    unsigned          options;
89
 
 
90
 
    /* Array of ports, one for each channel */
91
 
    struct {
92
 
        pjmedia_port *port;
93
 
        pj_bool_t     reversed;
94
 
    } port_desc[MAX_CHANNELS];
95
 
 
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.
99
 
     */
100
 
    TMP_SAMP_TYPE    *get_buf;
101
 
    TMP_SAMP_TYPE    *put_buf;
102
 
};
103
 
 
104
 
 
105
 
/*
106
 
 * This structure describes reverse port.
107
 
 */
108
 
struct reverse_port
109
 
{
110
 
    pjmedia_port     base;
111
 
    struct splitcomb*parent;
112
 
    unsigned         ch_num;
113
 
 
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
120
 
     * the stopping end.
121
 
     *
122
 
     * To avoid excessive underflow/overflow, the media flow will
123
 
     * be suspended once underflow/overflow goes over this max_burst
124
 
     * limit.
125
 
     */
126
 
    int              max_burst;
127
 
 
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).
133
 
     *
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
137
 
     * some processing.
138
 
     *
139
 
     * This value controls how many NULL frames can be received
140
 
     * before we suspend media flow for a particular direction.
141
 
     */
142
 
    unsigned         max_null_frames;
143
 
 
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.
149
 
     *
150
 
     * We maintain state for each direction, hence the array. The
151
 
     * array is indexed by direction (sc_dir).
152
 
     */
153
 
    struct {
154
 
 
155
 
        /* The delay buffer where frames will be stored */
156
 
        pjmedia_delay_buf   *dbuf;
157
 
 
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).
161
 
         */
162
 
        pj_bool_t       paused;
163
 
 
164
 
        /* Operation level. When the level exceeds a maximum value,
165
 
         * the media flow on this direction will be paused.
166
 
         */
167
 
        int             level;
168
 
 
169
 
        /* Timestamp. */
170
 
        pj_timestamp    ts;
171
 
 
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.
176
 
         */
177
 
        unsigned        null_cnt;
178
 
 
179
 
    } buf[2];
180
 
 
181
 
    /* Must have temporary put buffer for the delay buf,
182
 
     * unfortunately.
183
 
     */
184
 
    pj_int16_t        *tmp_up_buf;
185
 
};
186
 
 
187
 
 
188
 
/*
189
 
 * Prototypes.
190
 
 */
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);
196
 
 
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);
202
 
 
203
 
 
204
 
/*
205
 
 * Create the splitter/combiner.
206
 
 */
207
 
PJ_DEF(pj_status_t) pjmedia_splitcomb_create( pj_pool_t *pool,
208
 
                                              unsigned clock_rate,
209
 
                                              unsigned channel_count,
210
 
                                              unsigned samples_per_frame,
211
 
                                              unsigned bits_per_sample,
212
 
                                              unsigned options,
213
 
                                              pjmedia_port **p_splitcomb)
214
 
{
215
 
    const pj_str_t name = pj_str("splitcomb");
216
 
    struct splitcomb *sc;
217
 
 
218
 
    /* Sanity check */
219
 
    PJ_ASSERT_RETURN(pool && clock_rate && channel_count &&
220
 
                     samples_per_frame && bits_per_sample &&
221
 
                     p_splitcomb, PJ_EINVAL);
222
 
 
223
 
    /* Only supports 16 bits per sample */
224
 
    PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
225
 
 
226
 
    *p_splitcomb = NULL;
227
 
 
228
 
    /* Create the splitter/combiner structure */
229
 
    sc = PJ_POOL_ZALLOC_T(pool, struct splitcomb);
230
 
    PJ_ASSERT_RETURN(sc != NULL, PJ_ENOMEM);
231
 
 
232
 
    /* Create temporary buffers */
233
 
    sc->get_buf = (TMP_SAMP_TYPE*)
234
 
                  pj_pool_alloc(pool, samples_per_frame * 
235
 
                                      sizeof(TMP_SAMP_TYPE) /
236
 
                                      channel_count);
237
 
    PJ_ASSERT_RETURN(sc->get_buf, PJ_ENOMEM);
238
 
 
239
 
    sc->put_buf = (TMP_SAMP_TYPE*)
240
 
                  pj_pool_alloc(pool, samples_per_frame * 
241
 
                                      sizeof(TMP_SAMP_TYPE) /
242
 
                                      channel_count);
243
 
    PJ_ASSERT_RETURN(sc->put_buf, PJ_ENOMEM);
244
 
 
245
 
 
246
 
    /* Save options */
247
 
    sc->options = options;
248
 
 
249
 
    /* Initialize port */
250
 
    pjmedia_port_info_init(&sc->base.info, &name, SIGNATURE, clock_rate,
251
 
                           channel_count, bits_per_sample, samples_per_frame);
252
 
 
253
 
    sc->base.put_frame = &put_frame;
254
 
    sc->base.get_frame = &get_frame;
255
 
    sc->base.on_destroy = &on_destroy;
256
 
 
257
 
    /* Init ports array */
258
 
    /*
259
 
    sc->port_desc = pj_pool_zalloc(pool, channel_count*sizeof(*sc->port_desc));
260
 
    */
261
 
    pj_bzero(sc->port_desc, sizeof(sc->port_desc));
262
 
 
263
 
    /* Done for now */
264
 
    *p_splitcomb = &sc->base;
265
 
 
266
 
    return PJ_SUCCESS;
267
 
}
268
 
 
269
 
 
270
 
/*
271
 
 * Attach media port with the same phase as the splitter/combiner.
272
 
 */
273
 
PJ_DEF(pj_status_t) pjmedia_splitcomb_set_channel( pjmedia_port *splitcomb,
274
 
                                                   unsigned ch_num,
275
 
                                                   unsigned options,
276
 
                                                   pjmedia_port *port)
277
 
{
278
 
    struct splitcomb *sc = (struct splitcomb*) splitcomb;
279
 
 
280
 
    /* Sanity check */
281
 
    PJ_ASSERT_RETURN(splitcomb && port, PJ_EINVAL);
282
 
 
283
 
    /* Make sure this is really a splitcomb port */
284
 
    PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL);
285
 
 
286
 
    /* Check the channel number */
287
 
    PJ_ASSERT_RETURN(ch_num < PJMEDIA_PIA_CCNT(&sc->base.info), PJ_EINVAL);
288
 
 
289
 
    /* options is unused for now */
290
 
    PJ_UNUSED_ARG(options);
291
 
 
292
 
    sc->port_desc[ch_num].port = port;
293
 
    sc->port_desc[ch_num].reversed = PJ_FALSE;
294
 
 
295
 
    return PJ_SUCCESS;
296
 
}
297
 
 
298
 
 
299
 
/*
300
 
 * Create reverse phase port for the specified channel.
301
 
 */
302
 
PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool,
303
 
                                      pjmedia_port *splitcomb,
304
 
                                      unsigned ch_num,
305
 
                                      unsigned options,
306
 
                                      pjmedia_port **p_chport)
307
 
{
308
 
    const pj_str_t name = pj_str("scomb-rev");
309
 
    struct splitcomb *sc = (struct splitcomb*) splitcomb;
310
 
    struct reverse_port *rport;
311
 
    unsigned buf_cnt;
312
 
    const pjmedia_audio_format_detail *sc_afd, *p_afd;
313
 
    pjmedia_port *port;
314
 
    pj_status_t status;
315
 
 
316
 
    /* Sanity check */
317
 
    PJ_ASSERT_RETURN(pool && splitcomb, PJ_EINVAL);
318
 
 
319
 
    /* Make sure this is really a splitcomb port */
320
 
    PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL);
321
 
 
322
 
    /* Check the channel number */
323
 
    PJ_ASSERT_RETURN(ch_num < PJMEDIA_PIA_CCNT(&sc->base.info), PJ_EINVAL);
324
 
 
325
 
    /* options is unused for now */
326
 
    PJ_UNUSED_ARG(options);
327
 
 
328
 
    sc_afd = pjmedia_format_get_audio_format_detail(&splitcomb->info.fmt, 1);
329
 
 
330
 
    /* Create the port */
331
 
    rport = PJ_POOL_ZALLOC_T(pool, struct reverse_port);
332
 
    rport->parent = sc;
333
 
    rport->ch_num = ch_num;
334
 
 
335
 
    /* Initialize port info... */
336
 
    port = &rport->base;
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);
342
 
 
343
 
    p_afd = pjmedia_format_get_audio_format_detail(&port->info.fmt, 1);
344
 
 
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;
349
 
 
350
 
    /* Buffer settings */
351
 
    buf_cnt = options & 0xFF;
352
 
    if (buf_cnt == 0)
353
 
        buf_cnt = MAX_BUF_CNT;
354
 
 
355
 
    rport->max_burst = MAX_BURST;
356
 
    rport->max_null_frames = MAX_NULL_FRAMES;
357
 
 
358
 
    /* Create downstream/put buffers */
359
 
    status = pjmedia_delay_buf_create(pool, "scombdb-dn",
360
 
                                      p_afd->clock_rate,
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) {
366
 
        return status;
367
 
    }
368
 
 
369
 
    /* Create upstream/get buffers */
370
 
    status = pjmedia_delay_buf_create(pool, "scombdb-up",
371
 
                                      p_afd->clock_rate,
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);
378
 
        return status;
379
 
    }
380
 
 
381
 
    /* And temporary upstream/get buffer */
382
 
    rport->tmp_up_buf = (pj_int16_t*)
383
 
                        pj_pool_alloc(pool,
384
 
                                      PJMEDIA_PIA_AVG_FSZ(&port->info));
385
 
 
386
 
    /* Save port in the splitcomb */
387
 
    sc->port_desc[ch_num].port = &rport->base;
388
 
    sc->port_desc[ch_num].reversed = PJ_TRUE;
389
 
 
390
 
 
391
 
    /* Done */
392
 
    *p_chport = port;
393
 
    return status;
394
 
}
395
 
 
396
 
 
397
 
/* 
398
 
 * Extract one mono frame from a multichannel frame. 
399
 
 */
400
 
static void extract_mono_frame( const pj_int16_t *in,
401
 
                                pj_int16_t *out,
402
 
                                unsigned ch,
403
 
                                unsigned ch_cnt,
404
 
                                unsigned samples_count)
405
 
{
406
 
    unsigned i;
407
 
 
408
 
    in += ch;
409
 
    for (i=0; i<samples_count; ++i) {
410
 
        *out++ = *in;
411
 
        in += ch_cnt;
412
 
    }
413
 
}
414
 
 
415
 
 
416
 
/* 
417
 
 * Put one mono frame into a multichannel frame 
418
 
 */
419
 
static void store_mono_frame( const pj_int16_t *in,
420
 
                              pj_int16_t *out,
421
 
                              unsigned ch,
422
 
                              unsigned ch_cnt,
423
 
                              unsigned samples_count)
424
 
{
425
 
    unsigned i;
426
 
 
427
 
    out += ch;
428
 
    for (i=0; i<samples_count; ++i) {
429
 
        *out = *in++;
430
 
        out += ch_cnt;
431
 
    }
432
 
}
433
 
 
434
 
/* Update operation on the specified direction  */
435
 
static void op_update(struct reverse_port *rport, int dir, int op)
436
 
{
437
 
    char *dir_name[2] = {"downstream", "upstream"};
438
 
 
439
 
    rport->buf[dir].level += op;
440
 
 
441
 
    if (op == OP_PUT) {
442
 
        rport->buf[dir].ts.u64 += PJMEDIA_PIA_SPF(&rport->base.info);
443
 
    }
444
 
 
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;
452
 
        } else {
453
 
            /* Level has fallen below max level, we can resume
454
 
             * media flow.
455
 
             */
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;
461
 
 
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);
465
 
 
466
 
        }
467
 
    } else {
468
 
        if (rport->buf[dir].level >= rport->max_burst ||
469
 
            rport->buf[dir].level <= -rport->max_burst) 
470
 
        {
471
 
            /* Level has reached maximum level, the other side of
472
 
             * rport is not sending/retrieving frames. Pause the
473
 
             * rport on this direction.
474
 
             */
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;
479
 
        }
480
 
    }
481
 
}
482
 
 
483
 
 
484
 
/*
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.
488
 
 */
489
 
static pj_status_t put_frame(pjmedia_port *this_port, 
490
 
                             pjmedia_frame *frame)
491
 
{
492
 
    struct splitcomb *sc = (struct splitcomb*) this_port;
493
 
    unsigned ch;
494
 
 
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;
499
 
 
500
 
            if (!port) continue;
501
 
 
502
 
            if (!sc->port_desc[ch].reversed) {
503
 
                pjmedia_port_put_frame(port, frame);
504
 
            } else {
505
 
                struct reverse_port *rport = (struct reverse_port*)port;
506
 
 
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.
510
 
                 */
511
 
 
512
 
                if (++rport->buf[DIR_DOWNSTREAM].null_cnt > 
513
 
                        rport->max_null_frames) 
514
 
                {
515
 
                    /* Prevent the counter from overflowing and resetting
516
 
                     * back to zero
517
 
                     */
518
 
                    rport->buf[DIR_DOWNSTREAM].null_cnt = 
519
 
                        rport->max_null_frames + 1;
520
 
                    continue;
521
 
                }
522
 
 
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.
528
 
                 */
529
 
 
530
 
                /* Update rport state. */
531
 
                op_update(rport, DIR_DOWNSTREAM, OP_PUT);
532
 
 
533
 
                /* Discard frame if rport is paused on this direction */
534
 
                if (rport->buf[DIR_DOWNSTREAM].paused)
535
 
                    continue;
536
 
 
537
 
                /* Generate zero frame. */
538
 
                pjmedia_zero_samples(sc->put_buf, 
539
 
                                     PJMEDIA_PIA_SPF(&this_port->info));
540
 
 
541
 
                /* Put frame to delay buffer */
542
 
                pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf,
543
 
                                      sc->put_buf);
544
 
 
545
 
            }
546
 
        }
547
 
        return PJ_SUCCESS;
548
 
    }
549
 
 
550
 
    /* Not sure how we would handle partial frame, so better reject
551
 
     * it for now.
552
 
     */
553
 
    PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info),
554
 
                     PJ_EINVAL);
555
 
 
556
 
    /* 
557
 
     * Write mono frame into each channels 
558
 
     */
559
 
    for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
560
 
        pjmedia_port *port = sc->port_desc[ch].port;
561
 
 
562
 
        if (!port)
563
 
            continue;
564
 
 
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),
568
 
                           frame->size * 8 / 
569
 
                             PJMEDIA_PIA_BITS(&this_port->info) /
570
 
                             PJMEDIA_PIA_CCNT(&this_port->info));
571
 
 
572
 
        if (!sc->port_desc[ch].reversed) {
573
 
            /* Write to normal port */
574
 
            pjmedia_frame mono_frame;
575
 
 
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;
580
 
 
581
 
            /* Write */
582
 
            pjmedia_port_put_frame(port, &mono_frame);
583
 
 
584
 
        } else {
585
 
            /* Write to reversed phase port */
586
 
            struct reverse_port *rport = (struct reverse_port*)port;
587
 
 
588
 
            /* Reset NULL frame counter */
589
 
            rport->buf[DIR_DOWNSTREAM].null_cnt = 0;
590
 
 
591
 
            /* Update rport state. */
592
 
            op_update(rport, DIR_DOWNSTREAM, OP_PUT);
593
 
 
594
 
            if (!rport->buf[DIR_DOWNSTREAM].paused) {
595
 
                pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf, 
596
 
                                      sc->put_buf);
597
 
            }
598
 
        }
599
 
    }
600
 
 
601
 
    return PJ_SUCCESS;
602
 
}
603
 
 
604
 
 
605
 
/*
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.
609
 
 */
610
 
static pj_status_t get_frame(pjmedia_port *this_port, 
611
 
                             pjmedia_frame *frame)
612
 
{
613
 
    struct splitcomb *sc = (struct splitcomb*) this_port;
614
 
    unsigned ch;
615
 
    pj_bool_t has_frame = PJ_FALSE;
616
 
 
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;
621
 
        pj_status_t status;
622
 
 
623
 
        if (!port) {
624
 
            pjmedia_zero_samples(sc->get_buf, 
625
 
                                 PJMEDIA_PIA_SPF(&this_port->info) /
626
 
                                  PJMEDIA_PIA_CCNT(&this_port->info));
627
 
 
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;
633
 
 
634
 
            status = pjmedia_port_get_frame(port, &mono_frame);
635
 
            if (status != PJ_SUCCESS || 
636
 
                mono_frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
637
 
            {
638
 
                pjmedia_zero_samples(sc->get_buf, 
639
 
                                     PJMEDIA_PIA_SPF(&port->info));
640
 
            }
641
 
 
642
 
            frame->timestamp.u64 = mono_frame.timestamp.u64;
643
 
 
644
 
        } else {
645
 
            /* Read from temporary buffer for reverse port */
646
 
            struct reverse_port *rport = (struct reverse_port*)port;
647
 
 
648
 
            /* Update rport state. */
649
 
            op_update(rport, DIR_UPSTREAM, OP_GET);
650
 
 
651
 
            if (!rport->buf[DIR_UPSTREAM].paused) {
652
 
                pjmedia_delay_buf_get(rport->buf[DIR_UPSTREAM].dbuf, 
653
 
                                      sc->get_buf);
654
 
 
655
 
            } else {
656
 
                pjmedia_zero_samples(sc->get_buf, 
657
 
                                     PJMEDIA_PIA_SPF(&port->info));
658
 
            }
659
 
 
660
 
            frame->timestamp.u64 = rport->buf[DIR_UPSTREAM].ts.u64;
661
 
        }
662
 
 
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));
669
 
 
670
 
        has_frame = PJ_TRUE;
671
 
    }
672
 
 
673
 
    /* Return NO_FRAME is we don't get any frames from downstream ports */
674
 
    if (has_frame) {
675
 
        frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
676
 
        frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
677
 
    } else
678
 
        frame->type = PJMEDIA_FRAME_TYPE_NONE;
679
 
 
680
 
    return PJ_SUCCESS;
681
 
}
682
 
 
683
 
 
684
 
static pj_status_t on_destroy(pjmedia_port *this_port)
685
 
{
686
 
    /* Nothing to do for the splitcomb
687
 
     * Reverse ports must be destroyed separately.
688
 
     */
689
 
    PJ_UNUSED_ARG(this_port);
690
 
 
691
 
    return PJ_SUCCESS;
692
 
}
693
 
 
694
 
 
695
 
/*
696
 
 * Put a frame in the reverse port (upstream direction). This frame
697
 
 * will be picked up by get_frame() above.
698
 
 */
699
 
static pj_status_t rport_put_frame(pjmedia_port *this_port, 
700
 
                                   pjmedia_frame *frame)
701
 
{
702
 
    struct reverse_port *rport = (struct reverse_port*) this_port;
703
 
 
704
 
    pj_assert(frame->size <= PJMEDIA_PIA_AVG_FSZ(&rport->base.info));
705
 
 
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.
711
 
         */
712
 
        if (++rport->buf[DIR_UPSTREAM].null_cnt > rport->max_null_frames) {
713
 
            /* Prevent the counter from overflowing and resetting back 
714
 
             * to zero
715
 
             */
716
 
            rport->buf[DIR_UPSTREAM].null_cnt = rport->max_null_frames + 1;
717
 
            return PJ_SUCCESS;
718
 
        }
719
 
 
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.
725
 
         */
726
 
 
727
 
        /* Update rport state. */
728
 
        op_update(rport, DIR_UPSTREAM, OP_PUT);
729
 
 
730
 
        /* Discard frame if rport is paused on this direction */
731
 
        if (rport->buf[DIR_UPSTREAM].paused)
732
 
            return PJ_SUCCESS;
733
 
 
734
 
        /* Generate zero frame. */
735
 
        pjmedia_zero_samples(rport->tmp_up_buf, 
736
 
                             PJMEDIA_PIA_SPF(&this_port->info));
737
 
 
738
 
        /* Put frame to delay buffer */
739
 
        return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf, 
740
 
                                     rport->tmp_up_buf);
741
 
    }
742
 
 
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),
745
 
                     PJ_EINVAL);
746
 
 
747
 
    /* Reset NULL frame counter */
748
 
    rport->buf[DIR_UPSTREAM].null_cnt = 0;
749
 
 
750
 
    /* Update rport state. */
751
 
    op_update(rport, DIR_UPSTREAM, OP_PUT);
752
 
 
753
 
    /* Discard frame if rport is paused on this direction */
754
 
    if (rport->buf[DIR_UPSTREAM].paused)
755
 
        return PJ_SUCCESS;
756
 
 
757
 
    /* Unfortunately must copy to temporary buffer since delay buf
758
 
     * modifies the frame content.
759
 
     */
760
 
    pjmedia_copy_samples(rport->tmp_up_buf, (const pj_int16_t*)frame->buf,
761
 
                         PJMEDIA_PIA_SPF(&this_port->info));
762
 
 
763
 
    /* Put frame to delay buffer */
764
 
    return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf, 
765
 
                                 rport->tmp_up_buf);
766
 
}
767
 
 
768
 
 
769
 
/* Get a mono frame from a reversed phase channel (downstream direction).
770
 
 * The frame is put by put_frame() call to the splitcomb.
771
 
 */
772
 
static pj_status_t rport_get_frame(pjmedia_port *this_port, 
773
 
                                   pjmedia_frame *frame)
774
 
{
775
 
    struct reverse_port *rport = (struct reverse_port*) this_port;
776
 
 
777
 
    /* Update state */
778
 
    op_update(rport, DIR_DOWNSTREAM, OP_GET);
779
 
 
780
 
    /* Return no frame if media flow on this direction is being
781
 
     * paused.
782
 
     */
783
 
    if (rport->buf[DIR_DOWNSTREAM].paused) {
784
 
        frame->type = PJMEDIA_FRAME_TYPE_NONE;
785
 
        return PJ_SUCCESS;
786
 
    }
787
 
 
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;
792
 
 
793
 
    return pjmedia_delay_buf_get(rport->buf[DIR_DOWNSTREAM].dbuf, 
794
 
                                 (short*)frame->buf);
795
 
}
796
 
 
797
 
 
798
 
static pj_status_t rport_on_destroy(pjmedia_port *this_port)
799
 
{
800
 
    struct reverse_port *rport = (struct reverse_port*) this_port;
801
 
 
802
 
    pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf);
803
 
    pjmedia_delay_buf_destroy(rport->buf[DIR_UPSTREAM].dbuf);
804
 
 
805
 
    return PJ_SUCCESS;
806
 
}
807