1 /* $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/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 */
pjmedia_splitcomb_create(pj_pool_t * pool,unsigned clock_rate,unsigned channel_count,unsigned samples_per_frame,unsigned bits_per_sample,unsigned options,pjmedia_port ** p_splitcomb)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 */
pjmedia_splitcomb_set_channel(pjmedia_port * splitcomb,unsigned ch_num,unsigned options,pjmedia_port * port)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 */
pjmedia_splitcomb_create_rev_channel(pj_pool_t * pool,pjmedia_port * splitcomb,unsigned ch_num,unsigned options,pjmedia_port ** p_chport)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 unsigned buf_options;
313 const pjmedia_audio_format_detail *sc_afd, *p_afd;
314 pjmedia_port *port;
315 pj_status_t status;
316
317 /* Sanity check */
318 PJ_ASSERT_RETURN(pool && splitcomb, PJ_EINVAL);
319
320 /* Make sure this is really a splitcomb port */
321 PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL);
322
323 /* Check the channel number */
324 PJ_ASSERT_RETURN(ch_num < PJMEDIA_PIA_CCNT(&sc->base.info), PJ_EINVAL);
325
326 /* options is unused for now */
327 PJ_UNUSED_ARG(options);
328
329 sc_afd = pjmedia_format_get_audio_format_detail(&splitcomb->info.fmt, 1);
330
331 /* Create the port */
332 rport = PJ_POOL_ZALLOC_T(pool, struct reverse_port);
333 rport->parent = sc;
334 rport->ch_num = ch_num;
335
336 /* Initialize port info... */
337 port = &rport->base;
338 pjmedia_port_info_init(&port->info, &name, SIGNATURE_PORT,
339 sc_afd->clock_rate, 1,
340 sc_afd->bits_per_sample,
341 PJMEDIA_PIA_SPF(&splitcomb->info) /
342 sc_afd->channel_count);
343
344 p_afd = pjmedia_format_get_audio_format_detail(&port->info.fmt, 1);
345
346 /* ... and the callbacks */
347 port->put_frame = &rport_put_frame;
348 port->get_frame = &rport_get_frame;
349 port->on_destroy = &rport_on_destroy;
350
351 /* Buffer settings */
352 buf_cnt = options & 0xFF;
353 if (buf_cnt == 0)
354 buf_cnt = MAX_BUF_CNT;
355
356 /* Buffer options */
357 buf_options = (options >> 8U) & 0xFF;
358
359 rport->max_burst = MAX_BURST;
360 rport->max_null_frames = MAX_NULL_FRAMES;
361
362 /* Create downstream/put buffers */
363 status = pjmedia_delay_buf_create(pool, "scombdb-dn",
364 p_afd->clock_rate,
365 PJMEDIA_PIA_SPF(&port->info),
366 p_afd->channel_count,
367 buf_cnt * p_afd->frame_time_usec / 1000,
368 buf_options,
369 &rport->buf[DIR_DOWNSTREAM].dbuf);
370 if (status != PJ_SUCCESS) {
371 return status;
372 }
373
374 /* Create upstream/get buffers */
375 status = pjmedia_delay_buf_create(pool, "scombdb-up",
376 p_afd->clock_rate,
377 PJMEDIA_PIA_SPF(&port->info),
378 p_afd->channel_count,
379 buf_cnt * p_afd->frame_time_usec / 1000,
380 buf_options,
381 &rport->buf[DIR_UPSTREAM].dbuf);
382 if (status != PJ_SUCCESS) {
383 pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf);
384 return status;
385 }
386
387 /* And temporary upstream/get buffer */
388 rport->tmp_up_buf = (pj_int16_t*)
389 pj_pool_alloc(pool,
390 PJMEDIA_PIA_AVG_FSZ(&port->info));
391
392 /* Save port in the splitcomb */
393 sc->port_desc[ch_num].port = &rport->base;
394 sc->port_desc[ch_num].reversed = PJ_TRUE;
395
396
397 /* Done */
398 *p_chport = port;
399 return status;
400 }
401
402
403 /*
404 * Extract one mono frame from a multichannel frame.
405 */
extract_mono_frame(const pj_int16_t * in,pj_int16_t * out,unsigned ch,unsigned ch_cnt,unsigned samples_count)406 static void extract_mono_frame( const pj_int16_t *in,
407 pj_int16_t *out,
408 unsigned ch,
409 unsigned ch_cnt,
410 unsigned samples_count)
411 {
412 unsigned i;
413
414 in += ch;
415 for (i=0; i<samples_count; ++i) {
416 *out++ = *in;
417 in += ch_cnt;
418 }
419 }
420
421
422 /*
423 * Put one mono frame into a multichannel frame
424 */
store_mono_frame(const pj_int16_t * in,pj_int16_t * out,unsigned ch,unsigned ch_cnt,unsigned samples_count)425 static void store_mono_frame( const pj_int16_t *in,
426 pj_int16_t *out,
427 unsigned ch,
428 unsigned ch_cnt,
429 unsigned samples_count)
430 {
431 unsigned i;
432
433 out += ch;
434 for (i=0; i<samples_count; ++i) {
435 *out = *in++;
436 out += ch_cnt;
437 }
438 }
439
440 /* Update operation on the specified direction */
op_update(struct reverse_port * rport,int dir,int op)441 static void op_update(struct reverse_port *rport, int dir, int op)
442 {
443 char *dir_name[2] = {"downstream", "upstream"};
444
445 rport->buf[dir].level += op;
446
447 if (op == OP_PUT) {
448 rport->buf[dir].ts.u64 += PJMEDIA_PIA_SPF(&rport->base.info);
449 }
450
451 if (rport->buf[dir].paused) {
452 if (rport->buf[dir].level < -rport->max_burst) {
453 /* Prevent the level from overflowing and resets back to zero */
454 rport->buf[dir].level = -rport->max_burst;
455 } else if (rport->buf[dir].level > rport->max_burst) {
456 /* Prevent the level from overflowing and resets back to zero */
457 rport->buf[dir].level = rport->max_burst;
458 } else {
459 /* Level has fallen below max level, we can resume
460 * media flow.
461 */
462 PJ_LOG(5,(rport->base.info.name.ptr,
463 "Resuming media flow on %s direction (level=%d)",
464 dir_name[dir], rport->buf[dir].level));
465 rport->buf[dir].level = 0;
466 rport->buf[dir].paused = PJ_FALSE;
467
468 //This will cause disruption in audio, and it seems to be
469 //working fine without this anyway, so we disable it for now.
470 //pjmedia_delay_buf_learn(rport->buf[dir].dbuf);
471
472 }
473 } else {
474 if (rport->buf[dir].level >= rport->max_burst ||
475 rport->buf[dir].level <= -rport->max_burst)
476 {
477 /* Level has reached maximum level, the other side of
478 * rport is not sending/retrieving frames. Pause the
479 * rport on this direction.
480 */
481 PJ_LOG(5,(rport->base.info.name.ptr,
482 "Pausing media flow on %s direction (level=%d)",
483 dir_name[dir], rport->buf[dir].level));
484 rport->buf[dir].paused = PJ_TRUE;
485 }
486 }
487 }
488
489
490 /*
491 * "Write" a multichannel frame downstream. This would split
492 * the multichannel frame into individual mono channel, and write
493 * it to the appropriate port.
494 */
put_frame(pjmedia_port * this_port,pjmedia_frame * frame)495 static pj_status_t put_frame(pjmedia_port *this_port,
496 pjmedia_frame *frame)
497 {
498 struct splitcomb *sc = (struct splitcomb*) this_port;
499 unsigned ch;
500
501 /* Handle null frame */
502 if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
503 for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
504 pjmedia_port *port = sc->port_desc[ch].port;
505
506 if (!port) continue;
507
508 if (!sc->port_desc[ch].reversed) {
509 pjmedia_port_put_frame(port, frame);
510 } else {
511 struct reverse_port *rport = (struct reverse_port*)port;
512
513 /* Update the number of NULL frames received. Once we have too
514 * many of this, we'll stop calling op_update() to let the
515 * media be suspended.
516 */
517
518 if (++rport->buf[DIR_DOWNSTREAM].null_cnt >
519 rport->max_null_frames)
520 {
521 /* Prevent the counter from overflowing and resetting
522 * back to zero
523 */
524 rport->buf[DIR_DOWNSTREAM].null_cnt =
525 rport->max_null_frames + 1;
526 continue;
527 }
528
529 /* Write zero port to delaybuf so that it doesn't underflow.
530 * If we don't do this, get_frame() on this direction will
531 * cause delaybuf to generate missing frame and the last
532 * frame transmitted to delaybuf will be replayed multiple
533 * times, which doesn't sound good.
534 */
535
536 /* Update rport state. */
537 op_update(rport, DIR_DOWNSTREAM, OP_PUT);
538
539 /* Discard frame if rport is paused on this direction */
540 if (rport->buf[DIR_DOWNSTREAM].paused)
541 continue;
542
543 /* Generate zero frame. */
544 pjmedia_zero_samples(sc->put_buf,
545 PJMEDIA_PIA_SPF(&port->info));
546
547 /* Put frame to delay buffer */
548 pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf,
549 sc->put_buf);
550
551 }
552 }
553 return PJ_SUCCESS;
554 }
555
556 /* Not sure how we would handle partial frame, so better reject
557 * it for now.
558 */
559 PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info),
560 PJ_EINVAL);
561
562 /*
563 * Write mono frame into each channels
564 */
565 for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
566 pjmedia_port *port = sc->port_desc[ch].port;
567
568 if (!port)
569 continue;
570
571 /* Extract the mono frame to temporary buffer */
572 extract_mono_frame((const pj_int16_t*)frame->buf, sc->put_buf, ch,
573 PJMEDIA_PIA_CCNT(&this_port->info),
574 (unsigned)frame->size * 8 /
575 PJMEDIA_PIA_BITS(&this_port->info) /
576 PJMEDIA_PIA_CCNT(&this_port->info));
577
578 if (!sc->port_desc[ch].reversed) {
579 /* Write to normal port */
580 pjmedia_frame mono_frame;
581
582 mono_frame.buf = sc->put_buf;
583 mono_frame.size = frame->size / PJMEDIA_PIA_CCNT(&this_port->info);
584 mono_frame.type = frame->type;
585 mono_frame.timestamp.u64 = frame->timestamp.u64;
586
587 /* Write */
588 pjmedia_port_put_frame(port, &mono_frame);
589
590 } else {
591 /* Write to reversed phase port */
592 struct reverse_port *rport = (struct reverse_port*)port;
593
594 /* Reset NULL frame counter */
595 rport->buf[DIR_DOWNSTREAM].null_cnt = 0;
596
597 /* Update rport state. */
598 op_update(rport, DIR_DOWNSTREAM, OP_PUT);
599
600 if (!rport->buf[DIR_DOWNSTREAM].paused) {
601 pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf,
602 sc->put_buf);
603 }
604 }
605 }
606
607 return PJ_SUCCESS;
608 }
609
610
611 /*
612 * Get a multichannel frame upstream.
613 * This will get mono channel frame from each port and put the
614 * mono frame into the multichannel frame.
615 */
get_frame(pjmedia_port * this_port,pjmedia_frame * frame)616 static pj_status_t get_frame(pjmedia_port *this_port,
617 pjmedia_frame *frame)
618 {
619 struct splitcomb *sc = (struct splitcomb*) this_port;
620 unsigned ch;
621 pj_bool_t has_frame = PJ_FALSE;
622
623 /* Read frame from each port */
624 for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
625 pjmedia_port *port = sc->port_desc[ch].port;
626 pjmedia_frame mono_frame;
627 pj_status_t status;
628
629 if (!port) {
630 pjmedia_zero_samples(sc->get_buf,
631 PJMEDIA_PIA_SPF(&this_port->info) /
632 PJMEDIA_PIA_CCNT(&this_port->info));
633
634 } else if (sc->port_desc[ch].reversed == PJ_FALSE) {
635 /* Read from normal port */
636 mono_frame.buf = sc->get_buf;
637 mono_frame.size = PJMEDIA_PIA_AVG_FSZ(&port->info);
638 mono_frame.timestamp.u64 = frame->timestamp.u64;
639
640 status = pjmedia_port_get_frame(port, &mono_frame);
641 if (status != PJ_SUCCESS ||
642 mono_frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
643 {
644 pjmedia_zero_samples(sc->get_buf,
645 PJMEDIA_PIA_SPF(&port->info));
646 }
647
648 frame->timestamp.u64 = mono_frame.timestamp.u64;
649
650 } else {
651 /* Read from temporary buffer for reverse port */
652 struct reverse_port *rport = (struct reverse_port*)port;
653
654 /* Update rport state. */
655 op_update(rport, DIR_UPSTREAM, OP_GET);
656
657 if (!rport->buf[DIR_UPSTREAM].paused) {
658 pjmedia_delay_buf_get(rport->buf[DIR_UPSTREAM].dbuf,
659 sc->get_buf);
660
661 } else {
662 pjmedia_zero_samples(sc->get_buf,
663 PJMEDIA_PIA_SPF(&port->info));
664 }
665
666 frame->timestamp.u64 = rport->buf[DIR_UPSTREAM].ts.u64;
667 }
668
669 /* Combine the mono frame into multichannel frame */
670 store_mono_frame(sc->get_buf,
671 (pj_int16_t*)frame->buf, ch,
672 PJMEDIA_PIA_CCNT(&this_port->info),
673 PJMEDIA_PIA_SPF(&this_port->info) /
674 PJMEDIA_PIA_CCNT(&this_port->info));
675
676 has_frame = PJ_TRUE;
677 }
678
679 /* Return NO_FRAME is we don't get any frames from downstream ports */
680 if (has_frame) {
681 frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
682 frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
683 } else
684 frame->type = PJMEDIA_FRAME_TYPE_NONE;
685
686 return PJ_SUCCESS;
687 }
688
689
on_destroy(pjmedia_port * this_port)690 static pj_status_t on_destroy(pjmedia_port *this_port)
691 {
692 /* Nothing to do for the splitcomb
693 * Reverse ports must be destroyed separately.
694 */
695 PJ_UNUSED_ARG(this_port);
696
697 return PJ_SUCCESS;
698 }
699
700
701 /*
702 * Put a frame in the reverse port (upstream direction). This frame
703 * will be picked up by get_frame() above.
704 */
rport_put_frame(pjmedia_port * this_port,pjmedia_frame * frame)705 static pj_status_t rport_put_frame(pjmedia_port *this_port,
706 pjmedia_frame *frame)
707 {
708 struct reverse_port *rport = (struct reverse_port*) this_port;
709
710 pj_assert(frame->size <= PJMEDIA_PIA_AVG_FSZ(&rport->base.info));
711
712 /* Handle NULL frame */
713 if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) {
714 /* Update the number of NULL frames received. Once we have too
715 * many of this, we'll stop calling op_update() to let the
716 * media be suspended.
717 */
718 if (++rport->buf[DIR_UPSTREAM].null_cnt > rport->max_null_frames) {
719 /* Prevent the counter from overflowing and resetting back
720 * to zero
721 */
722 rport->buf[DIR_UPSTREAM].null_cnt = rport->max_null_frames + 1;
723 return PJ_SUCCESS;
724 }
725
726 /* Write zero port to delaybuf so that it doesn't underflow.
727 * If we don't do this, get_frame() on this direction will
728 * cause delaybuf to generate missing frame and the last
729 * frame transmitted to delaybuf will be replayed multiple
730 * times, which doesn't sound good.
731 */
732
733 /* Update rport state. */
734 op_update(rport, DIR_UPSTREAM, OP_PUT);
735
736 /* Discard frame if rport is paused on this direction */
737 if (rport->buf[DIR_UPSTREAM].paused)
738 return PJ_SUCCESS;
739
740 /* Generate zero frame. */
741 pjmedia_zero_samples(rport->tmp_up_buf,
742 PJMEDIA_PIA_SPF(&this_port->info));
743
744 /* Put frame to delay buffer */
745 return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf,
746 rport->tmp_up_buf);
747 }
748
749 /* Not sure how to handle partial frame, so better reject for now */
750 PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info),
751 PJ_EINVAL);
752
753 /* Reset NULL frame counter */
754 rport->buf[DIR_UPSTREAM].null_cnt = 0;
755
756 /* Update rport state. */
757 op_update(rport, DIR_UPSTREAM, OP_PUT);
758
759 /* Discard frame if rport is paused on this direction */
760 if (rport->buf[DIR_UPSTREAM].paused)
761 return PJ_SUCCESS;
762
763 /* Unfortunately must copy to temporary buffer since delay buf
764 * modifies the frame content.
765 */
766 pjmedia_copy_samples(rport->tmp_up_buf, (const pj_int16_t*)frame->buf,
767 PJMEDIA_PIA_SPF(&this_port->info));
768
769 /* Put frame to delay buffer */
770 return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf,
771 rport->tmp_up_buf);
772 }
773
774
775 /* Get a mono frame from a reversed phase channel (downstream direction).
776 * The frame is put by put_frame() call to the splitcomb.
777 */
rport_get_frame(pjmedia_port * this_port,pjmedia_frame * frame)778 static pj_status_t rport_get_frame(pjmedia_port *this_port,
779 pjmedia_frame *frame)
780 {
781 struct reverse_port *rport = (struct reverse_port*) this_port;
782
783 /* Update state */
784 op_update(rport, DIR_DOWNSTREAM, OP_GET);
785
786 /* Return no frame if media flow on this direction is being
787 * paused.
788 */
789 if (rport->buf[DIR_DOWNSTREAM].paused) {
790 frame->type = PJMEDIA_FRAME_TYPE_NONE;
791 return PJ_SUCCESS;
792 }
793
794 /* Get frame from delay buffer */
795 frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
796 frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
797 frame->timestamp.u64 = rport->buf[DIR_DOWNSTREAM].ts.u64;
798
799 return pjmedia_delay_buf_get(rport->buf[DIR_DOWNSTREAM].dbuf,
800 (short*)frame->buf);
801 }
802
803
rport_on_destroy(pjmedia_port * this_port)804 static pj_status_t rport_on_destroy(pjmedia_port *this_port)
805 {
806 struct reverse_port *rport = (struct reverse_port*) this_port;
807
808 pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf);
809 pjmedia_delay_buf_destroy(rport->buf[DIR_UPSTREAM].dbuf);
810
811 return PJ_SUCCESS;
812 }
813
814