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