diff options
author | Benny Prijono <bennylp@teluu.com> | 2006-07-27 22:03:51 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2006-07-27 22:03:51 +0000 |
commit | 9716653fda5019f1c944b0e1dafe304ab14e5aa3 (patch) | |
tree | 70c64b3db887daf41b04d4bcae4961a02e8ce93b /pjmedia | |
parent | f3610aa459cf39dcb6707f2062be88d8880a123e (diff) |
- Added splitter/combiner port (splitcomb.c)
- Added function to initialize pjmedia_port_info (still unused though)
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@631 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia')
-rw-r--r-- | pjmedia/build/Makefile | 2 | ||||
-rw-r--r-- | pjmedia/build/pjmedia.dsp | 4 | ||||
-rw-r--r-- | pjmedia/include/pjmedia/port.h | 36 | ||||
-rw-r--r-- | pjmedia/include/pjmedia/splitcomb.h | 28 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/port.c | 31 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/splitcomb.c | 656 |
6 files changed, 742 insertions, 15 deletions
diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile index b9861951..7e183502 100644 --- a/pjmedia/build/Makefile +++ b/pjmedia/build/Makefile @@ -68,7 +68,7 @@ export PJMEDIA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ clock_thread.o codec.o conference.o endpoint.o errno.o \ g711.o jbuf.o master_port.o mem_capture.o mem_player.o \ null_port.o plc_common.o plc_g711.o \ - port.o resample.o \ + port.o splitcomb.o resample.o \ resample_port.o rtcp.o rtp.o sdp.o sdp_cmp.o sdp_neg.o \ session.o silencedet.o sound_port.o stream.o \ transport_udp.o wav_player.o wav_writer.o wave.o \ diff --git a/pjmedia/build/pjmedia.dsp b/pjmedia/build/pjmedia.dsp index 0149b16a..e024f655 100644 --- a/pjmedia/build/pjmedia.dsp +++ b/pjmedia/build/pjmedia.dsp @@ -199,6 +199,10 @@ SOURCE=..\src\pjmedia\sound_port.c # End Source File
# Begin Source File
+SOURCE=..\src\pjmedia\splitcomb.c
+# End Source File
+# Begin Source File
+
SOURCE=..\src\pjmedia\stream.c
# End Source File
# Begin Source File
diff --git a/pjmedia/include/pjmedia/port.h b/pjmedia/include/pjmedia/port.h index 794b3139..fb8bd839 100644 --- a/pjmedia/include/pjmedia/port.h +++ b/pjmedia/include/pjmedia/port.h @@ -356,13 +356,45 @@ struct pjmedia_port /** + * This is an auxiliary function to initialize port info for + * ports which deal with PCM audio. + * + * @param info The port info to be initialized. + * @param name Port name. + * @param signature Port signature. + * @param channel_count Number of channels. + * @param bits_per_sample Bits per sample. + * @param samples_per_frame Number of samples per frame. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_port_info_init( pjmedia_port_info *info, + const pj_str_t *name, + unsigned signature, + unsigned clock_rate, + unsigned channel_count, + unsigned bits_per_sample, + unsigned samples_per_frame); + + +/** * Get a frame from the port (and subsequent downstream ports). + * + * @param port The media port. + * @param frame Frame to store samples. + * + * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_port_get_frame( pjmedia_port *port, pjmedia_frame *frame ); /** * Put a frame to the port (and subsequent downstream ports). + * + * @param port The media port. + * @param frame Frame to the put to the port. + * + * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_port_put_frame( pjmedia_port *port, const pjmedia_frame *frame ); @@ -370,6 +402,10 @@ PJ_DECL(pj_status_t) pjmedia_port_put_frame( pjmedia_port *port, /** * Destroy port (and subsequent downstream ports) + * + * @param port The media port. + * + * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_port_destroy( pjmedia_port *port ); diff --git a/pjmedia/include/pjmedia/splitcomb.h b/pjmedia/include/pjmedia/splitcomb.h index d1104488..4911499c 100644 --- a/pjmedia/include/pjmedia/splitcomb.h +++ b/pjmedia/include/pjmedia/splitcomb.h @@ -24,7 +24,7 @@ * @file splitcomb.h * @brief Media channel splitter/combiner port. */ -#include <pjmedia/types.h> +#include <pjmedia/port.h> /** @@ -34,6 +34,12 @@ * @{ * This section describes media port to split and combine media * channels in the stream. + * + * A splitter/combiner splits a single stereo/multichannels audio frame into + * multiple audio frames to each channel when put_frame() is called, + * and combines mono frames from each channel into a stereo/multichannel + * frame when get_frame() is called. A common application for the splitter/ + * combiner is to split frames from stereo to mono and vise versa. */ PJ_BEGIN_DECL @@ -41,11 +47,6 @@ PJ_BEGIN_DECL /** * Create a media splitter/combiner with the specified parameters. - * A splitter/combiner splits a single stereo/multichannel audio frame into - * multiple mono audio frames to each channel when put_frame() is called, - * and combines mono frames from each channel into a stereo/multichannel - * frame when get_frame() is called. - * * When the splitter/combiner is created, it creates an instance of * pjmedia_port. This media port represents the stereo/multichannel side * of the splitter/combiner. Application needs to supply the splitter/ @@ -79,10 +80,8 @@ PJ_DECL(pj_status_t) pjmedia_splitcomb_create(pj_pool_t *pool, * for all ports that have the same phase. And similarly for put_frame(). * * @param splitcomb The splitter/combiner. - * @param ch_num Audio channel number. - * @param options Valid options are: - * - PJMEDIA_SPLITCOMB_AUTO_DESTROY to destroy the - * media port when the splitter is destroyed. + * @param ch_num Audio channel starting number (zero based). + * @param options Must be zero at the moment. * @param port The media port. * * @return PJ_SUCCESS on success, or the appropriate error @@ -107,10 +106,11 @@ PJ_DECL(pj_status_t) pjmedia_splitcomb_set_channel(pjmedia_port *splitcomb, * @param pool The pool to allocate memory for the port and * buffers. * @param splitcomb The splitter/combiner. - * @param ch_num Audio channel number. - * @param options Valid options are: - * - PJMEDIA_SPLITCOMB_AUTO_DESTROY to destroy the - * media port when the splitter is destroyed. + * @param ch_num Audio channel starting number (zero based). + * @param options Normally is zero, but the lower 8-bit of the + * options can be used to specify the number of + * buffers in the circular buffer. If zero, then + * default number will be used (default: 8). * @param p_chport The media port created with reverse phase for * the specified audio channel. * diff --git a/pjmedia/src/pjmedia/port.c b/pjmedia/src/pjmedia/port.c index 96c07c08..f1baa98a 100644 --- a/pjmedia/src/pjmedia/port.c +++ b/pjmedia/src/pjmedia/port.c @@ -25,6 +25,37 @@ /** + * This is an auxiliary function to initialize port info for + * ports which deal with PCM audio. + */ +PJ_DEF(pj_status_t) pjmedia_port_info_init( pjmedia_port_info *info, + const pj_str_t *name, + unsigned signature, + unsigned clock_rate, + unsigned channel_count, + unsigned bits_per_sample, + unsigned samples_per_frame) +{ + pj_bzero(info, sizeof(*info)); + + info->name = *name; + info->signature = signature; + info->type = PJMEDIA_TYPE_AUDIO; + info->has_info = PJ_TRUE; + info->need_info = PJ_FALSE; + info->pt = 0xFF; + info->encoding_name = pj_str("pcm"); + info->clock_rate = clock_rate; + info->channel_count = channel_count; + info->bits_per_sample = bits_per_sample; + info->samples_per_frame = samples_per_frame; + info->bytes_per_frame = samples_per_frame * bits_per_sample / 8; + + return PJ_SUCCESS; +} + + +/** * Get a frame from the port (and subsequent downstream ports). */ PJ_DEF(pj_status_t) pjmedia_port_get_frame( pjmedia_port *port, diff --git a/pjmedia/src/pjmedia/splitcomb.c b/pjmedia/src/pjmedia/splitcomb.c new file mode 100644 index 00000000..5130c73a --- /dev/null +++ b/pjmedia/src/pjmedia/splitcomb.c @@ -0,0 +1,656 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjmedia/splitcomb.h> +#include <pjmedia/errno.h> +#include <pj/assert.h> +#include <pj/log.h> +#include <pj/pool.h> + + +#define SIGNATURE PJMEDIA_PORT_SIGNATURE('S', 'p', 'C', 'b') +#define THIS_FILE "splitcomb.c" +#define TMP_SAMP_TYPE pj_int16_t +#define MAX_BUF_CNT 8 + +#if 0 +# define TRACE_UP_(x) PJ_LOG(5,x) +# define TRACE_DN_(x) PJ_LOG(5,x) +#else +# define TRACE_UP_(x) +# define TRACE_DN_(x) +#endif + +#if 1 +# define LOG_UP_(x) PJ_LOG(5,x) +# define LOG_DN_(x) PJ_LOG(5,x) +#else +# define LOG_UP_(x) +# define LOG_DN_(x) +#endif + +/* + * This structure describes the splitter/combiner. + */ +struct splitcomb +{ + pjmedia_port base; + + unsigned options; + + /* Array of ports, one for each channel */ + struct { + pjmedia_port *port; + pj_bool_t reversed; + } *port_desc; + + /* Temporary buffers needed to extract mono frame from + * multichannel frame. We could use stack for this, but this + * way it should be safer for devices with small stack size. + */ + TMP_SAMP_TYPE *get_buf; + TMP_SAMP_TYPE *put_buf; +}; + + +/* + * This structure describes reverse port. + */ +struct reverse_port +{ + pjmedia_port base; + struct splitcomb*parent; + unsigned ch_num; + + /* A reverse port need a temporary buffer to store frame + * (because of the different phase, see splitcomb.h for details). + * Since we can not expect get_frame() and put_frame() to be + * called evenly one after another, we use circular buffers to + * accomodate the "jitter". + */ + unsigned buf_cnt; + + /* Downstream is the direction when get_frame() is called to the + * splitter/combiner port. + */ + unsigned dn_read_pos, dn_write_pos, + dn_overflow_pos, dn_underflow_pos; + pj_int16_t *dnstream_buf[MAX_BUF_CNT]; + + /* Upstream is the direction when put_frame() is called to the + * splitter/combiner port. + */ + unsigned up_read_pos, up_write_pos, + up_overflow_pos, up_underflow_pos; + pj_int16_t *upstream_buf[MAX_BUF_CNT]; +}; + + +/* + * Prototypes. + */ +static pj_status_t put_frame(pjmedia_port *this_port, + const pjmedia_frame *frame); +static pj_status_t get_frame(pjmedia_port *this_port, + pjmedia_frame *frame); +static pj_status_t on_destroy(pjmedia_port *this_port); + +static pj_status_t rport_put_frame(pjmedia_port *this_port, + const pjmedia_frame *frame); +static pj_status_t rport_get_frame(pjmedia_port *this_port, + pjmedia_frame *frame); +static pj_status_t rport_on_destroy(pjmedia_port *this_port); + + +/* + * Create the splitter/combiner. + */ +PJ_DEF(pj_status_t) 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) +{ + struct splitcomb *sc; + + /* Sanity check */ + PJ_ASSERT_RETURN(pool && clock_rate && channel_count && + samples_per_frame && bits_per_sample && + p_splitcomb, PJ_EINVAL); + + /* Only supports 16 bits per sample */ + PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); + + *p_splitcomb = NULL; + + /* Create the splitter/combiner structure */ + sc = pj_pool_zalloc(pool, sizeof(struct splitcomb)); + PJ_ASSERT_RETURN(sc != NULL, PJ_ENOMEM); + + /* Create temporary buffers */ + sc->get_buf = pj_pool_alloc(pool, samples_per_frame * + sizeof(TMP_SAMP_TYPE) / + channel_count); + PJ_ASSERT_RETURN(sc->get_buf, PJ_ENOMEM); + + sc->put_buf = pj_pool_alloc(pool, samples_per_frame * + sizeof(TMP_SAMP_TYPE) / + channel_count); + PJ_ASSERT_RETURN(sc->put_buf, PJ_ENOMEM); + + + /* Save options */ + sc->options = options; + + /* Initialize port */ + sc->base.info.name = pj_str("splitcomb"); + sc->base.info.signature = SIGNATURE; + sc->base.info.type = PJMEDIA_TYPE_AUDIO; + sc->base.info.has_info = PJ_TRUE; + sc->base.info.need_info = PJ_FALSE; + sc->base.info.pt = 0xFF; + sc->base.info.encoding_name = pj_str("pcm"); + sc->base.info.clock_rate = clock_rate; + sc->base.info.channel_count = channel_count; + sc->base.info.bits_per_sample = bits_per_sample; + sc->base.info.samples_per_frame = samples_per_frame; + sc->base.info.bytes_per_frame = samples_per_frame * bits_per_sample / 8; + + sc->base.put_frame = &put_frame; + sc->base.get_frame = &get_frame; + sc->base.on_destroy = &on_destroy; + + /* Init ports array */ + sc->port_desc = pj_pool_zalloc(pool, channel_count*sizeof(*sc->port_desc)); + + /* Done for now */ + *p_splitcomb = &sc->base; + + return PJ_SUCCESS; +} + + +/* + * Attach media port with the same phase as the splitter/combiner. + */ +PJ_DEF(pj_status_t) pjmedia_splitcomb_set_channel( pjmedia_port *splitcomb, + unsigned ch_num, + unsigned options, + pjmedia_port *port) +{ + struct splitcomb *sc = (struct splitcomb*) splitcomb; + + /* Sanity check */ + PJ_ASSERT_RETURN(splitcomb && port, PJ_EINVAL); + + /* Make sure this is really a splitcomb port */ + PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL); + + /* Check the channel number */ + PJ_ASSERT_RETURN(ch_num < sc->base.info.channel_count, PJ_EINVAL); + + /* options is unused for now */ + PJ_UNUSED_ARG(options); + + sc->port_desc[ch_num].port = port; + sc->port_desc[ch_num].reversed = PJ_FALSE; + + return PJ_SUCCESS; +} + + +/* + * Create reverse phase port for the specified channel. + */ +PJ_DEF(pj_status_t) +pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, + pjmedia_port *splitcomb, + unsigned ch_num, + unsigned options, + pjmedia_port **p_chport) +{ + struct splitcomb *sc = (struct splitcomb*) splitcomb; + struct reverse_port *rport; + unsigned i; + pjmedia_port *port; + + /* Sanity check */ + PJ_ASSERT_RETURN(pool && splitcomb, PJ_EINVAL); + + /* Make sure this is really a splitcomb port */ + PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL); + + /* Check the channel number */ + PJ_ASSERT_RETURN(ch_num < sc->base.info.channel_count, PJ_EINVAL); + + /* options is unused for now */ + PJ_UNUSED_ARG(options); + + /* Create the port */ + rport = pj_pool_zalloc(pool, sizeof(struct reverse_port)); + rport->parent = sc; + rport->ch_num = ch_num; + + /* Initialize port info... */ + port = &rport->base; + port->info.name = pj_str("splitcomb-ch"); + port->info.signature = 0; + port->info.type = PJMEDIA_TYPE_AUDIO; + port->info.has_info = PJ_TRUE; + port->info.need_info = PJ_FALSE; + port->info.pt = 0xFF; + port->info.encoding_name = pj_str("pcm"); + port->info.clock_rate = splitcomb->info.clock_rate; + port->info.channel_count = 1; + port->info.bits_per_sample = splitcomb->info.bits_per_sample; + port->info.samples_per_frame = splitcomb->info.samples_per_frame / + splitcomb->info.channel_count; + port->info.bytes_per_frame = port->info.samples_per_frame * + port->info.bits_per_sample / 8; + + /* ... and the callbacks */ + port->put_frame = &rport_put_frame; + port->get_frame = &rport_get_frame; + port->on_destroy = &rport_on_destroy; + + + rport->buf_cnt = options & 0xFF; + if (rport->buf_cnt == 0) + rport->buf_cnt = MAX_BUF_CNT; + + /* Create put buffers */ + for (i=0; i<rport->buf_cnt; ++i) { + rport->dnstream_buf[i] = pj_pool_zalloc(pool, port->info.bytes_per_frame); + PJ_ASSERT_RETURN(rport->dnstream_buf[i], PJ_ENOMEM); + } + rport->dn_write_pos = rport->buf_cnt/2; + + /* Create get buffers */ + for (i=0; i<rport->buf_cnt; ++i) { + rport->upstream_buf[i] = pj_pool_zalloc(pool, + port->info.bytes_per_frame); + PJ_ASSERT_RETURN(rport->upstream_buf[i], PJ_ENOMEM); + } + rport->up_write_pos = rport->buf_cnt/2; + + + /* Save port in the splitcomb */ + sc->port_desc[ch_num].port = &rport->base; + sc->port_desc[ch_num].reversed = PJ_TRUE; + + + /* Done */ + *p_chport = port; + return PJ_SUCCESS; +} + + +/* + * Extract one mono frame from a multichannel frame. + */ +static void extract_mono_frame( const pj_int16_t *in, + pj_int16_t *out, + unsigned ch, + unsigned ch_cnt, + unsigned samples_count) +{ + unsigned i; + + in += ch; + for (i=0; i<samples_count; ++i) { + *out++ = *in; + in += ch_cnt; + } +} + + +/* + * Put one mono frame into a multichannel frame + */ +static void store_mono_frame( const pj_int16_t *in, + pj_int16_t *out, + unsigned ch, + unsigned ch_cnt, + unsigned samples_count) +{ + unsigned i; + + out += ch; + for (i=0; i<samples_count; ++i) { + *out = *in++; + out += ch_cnt; + } +} + + +/* + * "Write" a multichannel frame. This would split the multichannel frame + * into individual mono channel, and write it to the appropriate port. + */ +static pj_status_t put_frame(pjmedia_port *this_port, + const pjmedia_frame *frame) +{ + struct splitcomb *sc = (struct splitcomb*) this_port; + unsigned ch; + + /* Handle null frame */ + if (frame->type == PJMEDIA_FRAME_TYPE_NONE) { + for (ch=0; ch < this_port->info.channel_count; ++ch) { + pjmedia_port *port = sc->port_desc[ch].port; + + if (!port) continue; + + pjmedia_port_put_frame(port, frame); + } + return PJ_SUCCESS; + } + + /* Not sure how we would handle partial frame, so better reject + * it for now. + */ + PJ_ASSERT_RETURN(frame->size == this_port->info.bytes_per_frame, + PJ_EINVAL); + + /* + * Write mono frame into each channels + */ + for (ch=0; ch < this_port->info.channel_count; ++ch) { + pjmedia_port *port = sc->port_desc[ch].port; + + if (!port) + continue; + + if (!sc->port_desc[ch].reversed) { + /* Write to normal port */ + pjmedia_frame mono_frame; + + /* Extract the mono frame */ + extract_mono_frame(frame->buf, sc->put_buf, ch, + this_port->info.channel_count, + frame->size * 8 / + this_port->info.bits_per_sample / + this_port->info.channel_count); + + mono_frame.buf = sc->put_buf; + mono_frame.size = frame->size / this_port->info.channel_count; + mono_frame.type = frame->type; + mono_frame.timestamp.u64 = frame->timestamp.u64; + + /* Write */ + pjmedia_port_put_frame(port, &mono_frame); + + } else { + /* Write to reversed phase port */ + struct reverse_port *rport = (struct reverse_port*)port; + + if (rport->dn_write_pos == rport->dn_read_pos) { + + /* Only report overflow if the frame is constantly read + * by the 'consumer' of the reverse port. + * It is possible that nobody reads the buffer, so causing + * overflow to happen rapidly, and writing log message this + * way does not seem to be wise. + */ + if (rport->dn_read_pos != rport->dn_overflow_pos) { + rport->dn_overflow_pos = rport->dn_read_pos; + LOG_DN_((THIS_FILE, "Overflow in downstream direction")); + } + + /* Adjust write position */ + rport->dn_write_pos = + (rport->dn_write_pos + rport->buf_cnt/2) % + rport->buf_cnt; + } + + /* Extract mono-frame and put it in downstream buffer */ + extract_mono_frame(frame->buf, + rport->dnstream_buf[rport->dn_write_pos], + ch, this_port->info.channel_count, + frame->size * 8 / + this_port->info.bits_per_sample / + this_port->info.channel_count); + + rport->dn_write_pos = (rport->dn_write_pos + 1) % + rport->buf_cnt; + } + } + + return PJ_SUCCESS; +} + + +/* + * Get a multichannel frame. + * This will get mono channel frame from each port and put the + * mono frame into the multichannel frame. + */ +static pj_status_t get_frame(pjmedia_port *this_port, + pjmedia_frame *frame) +{ + struct splitcomb *sc = (struct splitcomb*) this_port; + unsigned ch; + pj_bool_t has_frame = PJ_FALSE; + + /* Clear output frame */ + pjmedia_zero_samples(frame->buf, this_port->info.samples_per_frame); + + /* Read frame from each port */ + for (ch=0; ch < this_port->info.channel_count; ++ch) { + pjmedia_port *port = sc->port_desc[ch].port; + pjmedia_frame mono_frame; + pj_status_t status; + + if (!port) + continue; + + /* Read from the port */ + if (sc->port_desc[ch].reversed == PJ_FALSE) { + /* Read from normal port */ + mono_frame.buf = sc->get_buf; + mono_frame.size = port->info.bytes_per_frame; + mono_frame.timestamp.u64 = frame->timestamp.u64; + + status = pjmedia_port_get_frame(port, &mono_frame); + if (status != PJ_SUCCESS || + mono_frame.type != PJMEDIA_FRAME_TYPE_AUDIO) + { + continue; + } + + /* Combine the mono frame into multichannel frame */ + store_mono_frame(mono_frame.buf, frame->buf, ch, + this_port->info.channel_count, + mono_frame.size * 8 / + this_port->info.bits_per_sample); + + frame->timestamp.u64 = mono_frame.timestamp.u64; + + } else { + /* Read from temporary buffer for reverse port */ + struct reverse_port *rport = (struct reverse_port*)port; + + /* Check for underflows */ + if (rport->up_read_pos == rport->up_write_pos) { + + /* Only report underflow if the buffer is constantly filled + * up at the other side. + * It is possible that nobody writes the buffer, so causing + * underflow to happen rapidly, and writing log message this + * way does not seem to be wise. + */ + if (rport->up_write_pos != rport->up_underflow_pos) { + rport->up_underflow_pos = rport->up_write_pos; + LOG_UP_((THIS_FILE, "Underflow in upstream direction")); + } + + /* Adjust read position */ + rport->up_read_pos = + (rport->up_write_pos - rport->buf_cnt/2) % + rport->buf_cnt; + } + + TRACE_UP_((THIS_FILE, "Upstream read at buffer pos %d", + rport->up_read_pos)); + + /* Combine the mono frame into multichannel frame */ + store_mono_frame(rport->upstream_buf[rport->up_read_pos], + frame->buf, ch, + this_port->info.channel_count, + port->info.samples_per_frame); + + rport->up_read_pos = (rport->up_read_pos + 1) % + rport->buf_cnt; + } + + + has_frame = PJ_TRUE; + } + + /* Return NO_FRAME is we don't get any frames from downstream ports */ + if (has_frame) { + frame->type = PJMEDIA_FRAME_TYPE_AUDIO; + frame->size = this_port->info.bytes_per_frame; + } else + frame->type = PJMEDIA_FRAME_TYPE_NONE; + + return PJ_SUCCESS; +} + + +static pj_status_t on_destroy(pjmedia_port *this_port) +{ + /* Nothing to do */ + PJ_UNUSED_ARG(this_port); + + return PJ_SUCCESS; +} + + +/* + * Get a mono frame from a reversed phase channel. + */ +static pj_status_t rport_put_frame(pjmedia_port *this_port, + const pjmedia_frame *frame) +{ + struct reverse_port *rport = (struct reverse_port*) this_port; + unsigned count; + + pj_assert(frame->size <= rport->base.info.bytes_per_frame); + + /* Check for overflows */ + if (rport->up_write_pos == rport->up_read_pos) { + + /* Only report overflow if the frame is constantly read + * at the other end of the buffer (the multichannel side). + * It is possible that nobody reads the buffer, so causing + * overflow to happen rapidly, and writing log message this + * way does not seem to be wise. + */ + if (rport->up_read_pos != rport->up_overflow_pos) { + rport->up_overflow_pos = rport->up_read_pos; + LOG_UP_((THIS_FILE, "Overflow in upstream direction")); + } + + /* Adjust the write position */ + rport->up_write_pos = (rport->up_read_pos + rport->buf_cnt/2) % + rport->buf_cnt; + } + + /* Handle NULL frame */ + if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) { + TRACE_UP_((THIS_FILE, "Upstream write %d null samples at buf pos %d", + this_port->info.samples_per_frame, rport->up_write_pos)); + pjmedia_zero_samples(rport->upstream_buf[rport->up_write_pos], + this_port->info.samples_per_frame); + rport->up_write_pos = (rport->up_write_pos+1) % rport->buf_cnt; + return PJ_SUCCESS; + } + + /* Not sure how to handle partial frame, so better reject for now */ + PJ_ASSERT_RETURN(frame->size == this_port->info.bytes_per_frame, + PJ_EINVAL); + + /* Copy normal frame to curcular buffer */ + count = frame->size * 8 / this_port->info.bits_per_sample; + + TRACE_UP_((THIS_FILE, "Upstream write %d samples at buf pos %d", + count, rport->up_write_pos)); + + + pjmedia_copy_samples(rport->upstream_buf[rport->up_write_pos], + frame->buf, count); + rport->up_write_pos = (rport->up_write_pos+1) % rport->buf_cnt; + + return PJ_SUCCESS; +} + + +/* + * Get a mono frame from a reversed phase channel. + */ +static pj_status_t rport_get_frame(pjmedia_port *this_port, + pjmedia_frame *frame) +{ + struct reverse_port *rport = (struct reverse_port*) this_port; + unsigned count; + + count = rport->base.info.samples_per_frame; + + frame->size = this_port->info.bytes_per_frame; + frame->type = PJMEDIA_FRAME_TYPE_AUDIO; + + /* Check for underflows */ + if (rport->dn_read_pos == rport->dn_write_pos) { + + /* Only report underflow if the buffer is constantly filled + * up at the other side. + * It is possible that nobody writes the buffer, so causing + * underflow to happen rapidly, and writing log message this + * way does not seem to be wise. + */ + if (rport->dn_write_pos != rport->dn_underflow_pos) { + rport->dn_underflow_pos = rport->dn_write_pos; + LOG_DN_((THIS_FILE, "Underflow in downstream direction")); + } + + /* Adjust read position */ + rport->dn_read_pos = + (rport->dn_write_pos - rport->buf_cnt/2) % rport->buf_cnt; + + } + + /* Get the samples from the circular buffer */ + pjmedia_copy_samples(frame->buf, + rport->dnstream_buf[rport->dn_read_pos], + count); + rport->dn_read_pos = (rport->dn_read_pos+1) % rport->buf_cnt; + + return PJ_SUCCESS; +} + + +static pj_status_t rport_on_destroy(pjmedia_port *this_port) +{ + /* Nothing to do */ + PJ_UNUSED_ARG(this_port); + + return PJ_SUCCESS; +} + |