~ubuntu-branches/ubuntu/trusty/sflphone/trusty

« 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): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (4.3.4 sid)
  • Revision ID: package-import@ubuntu.com-20140128182336-jrsv0k9u6cawc068
Tags: 1.3.0-1
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

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