- Fixed pjmedia_frame_ext helper functions, it didn't involve sizeof(bitlen) in calculating subframe address.

- Fixed audio switch board to handle such case that transmitter of port 0 has greater ptime, so it could save the remaining data in the TX buffer.
 - Fixed audio swtich board in handling FRAME_TYPE_NONE.
 - Updated audio switch board to handle keep alive mechanism.



git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/aps-direct@2437 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Nanang Izzuddin 2009-02-01 14:10:49 +00:00
parent 8cdba46cca
commit 389d3264b7
2 changed files with 286 additions and 118 deletions

View File

@ -264,6 +264,8 @@ typedef struct pjmedia_frame
* subframe immediately follows the previous subframe, and all subframes * subframe immediately follows the previous subframe, and all subframes
* are byte-aligned although its payload may not be byte-aligned. * are byte-aligned although its payload may not be byte-aligned.
*/ */
#pragma pack(1)
typedef struct pjmedia_frame_ext { typedef struct pjmedia_frame_ext {
pjmedia_frame base; /**< Base frame info */ pjmedia_frame base; /**< Base frame info */
pj_uint16_t samples_cnt; /**< Number of samples in this frame */ pj_uint16_t samples_cnt; /**< Number of samples in this frame */
@ -273,16 +275,20 @@ typedef struct pjmedia_frame_ext {
* each will be represented by pjmedia_frame_ext_subframe * each will be represented by pjmedia_frame_ext_subframe
*/ */
} pjmedia_frame_ext; } pjmedia_frame_ext;
#pragma pack()
/** /**
* This structure represents the individual subframes in the * This structure represents the individual subframes in the
* pjmedia_frame_ext structure. * pjmedia_frame_ext structure.
*/ */
#pragma pack(1)
typedef struct pjmedia_frame_ext_subframe { typedef struct pjmedia_frame_ext_subframe {
pj_uint16_t bitlen; /**< Number of bits in the data */ pj_uint16_t bitlen; /**< Number of bits in the data */
pj_uint8_t data[1]; /**< Start of encoded data */ pj_uint8_t data[1]; /**< Start of encoded data */
} pjmedia_frame_ext_subframe; } pjmedia_frame_ext_subframe;
#pragma pack()
/** /**
* Append one subframe to #pjmedia_frame_ext. * Append one subframe to #pjmedia_frame_ext.
@ -304,15 +310,18 @@ PJ_INLINE(void) pjmedia_frame_ext_append_subframe(pjmedia_frame_ext *frm,
for (i = 0; i < frm->subframe_cnt; ++i) { for (i = 0; i < frm->subframe_cnt; ++i) {
pjmedia_frame_ext_subframe *fsub; pjmedia_frame_ext_subframe *fsub;
fsub = (pjmedia_frame_ext_subframe*) p; fsub = (pjmedia_frame_ext_subframe*) p;
p += fsub->bitlen / 8; p += sizeof(fsub->bitlen) + fsub->bitlen / 8;
if (fsub->bitlen % 8) if (fsub->bitlen % 8)
++p; ++p;
} }
tmp = bitlen / 8; tmp = bitlen / 8;
if (bitlen % 8) ++tmp; if (bitlen % 8) ++tmp;
pj_memcpy(p, &bitlen, sizeof(bitlen)); pj_memcpy(p, &bitlen, sizeof(bitlen));
pj_memcpy(p + sizeof(bitlen), src, tmp); if (tmp)
pj_memcpy(p + sizeof(bitlen), src, tmp);
frm->subframe_cnt++; frm->subframe_cnt++;
frm->samples_cnt = frm->samples_cnt + samples_cnt; frm->samples_cnt = frm->samples_cnt + samples_cnt;
} }
@ -338,7 +347,7 @@ PJ_INLINE(pjmedia_frame_ext_subframe*)
p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext); p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext);
for (i = 0; i < n; ++i) { for (i = 0; i < n; ++i) {
sf = (pjmedia_frame_ext_subframe*) p; sf = (pjmedia_frame_ext_subframe*) p;
p += sf->bitlen / 8; p += sizeof(sf->bitlen) + sf->bitlen / 8;
if (sf->bitlen % 8) if (sf->bitlen % 8)
++p; ++p;
} }

View File

@ -18,8 +18,10 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include <pjmedia/conference.h> #include <pjmedia/conference.h>
#include <pjmedia/alaw_ulaw.h>
#include <pjmedia/errno.h> #include <pjmedia/errno.h>
#include <pjmedia/port.h> #include <pjmedia/port.h>
#include <pjmedia/silencedet.h>
#include <pjmedia/sound_port.h> #include <pjmedia/sound_port.h>
#include <pjmedia/stream.h> #include <pjmedia/stream.h>
#include <pj/array.h> #include <pj/array.h>
@ -41,18 +43,6 @@
# define TRACE_(x) # define TRACE_(x)
#endif #endif
/* REC_FILE macro enables recording of the samples written to the sound
* device. The file contains RAW PCM data with no header, and has the
* same settings (clock rate etc) as the conference bridge.
* This should only be enabled when debugging audio quality *only*.
*/
//#define REC_FILE "confrec.pcm"
#ifdef REC_FILE
static FILE *fhnd_rec;
#endif
#define THIS_FILE "conf_switch.c" #define THIS_FILE "conf_switch.c"
#define SIGNATURE PJMEDIA_PORT_SIGNATURE('S', 'W', 'T', 'C') #define SIGNATURE PJMEDIA_PORT_SIGNATURE('S', 'W', 'T', 'C')
@ -102,6 +92,7 @@ struct conf_port
pj_timestamp ts_clock; pj_timestamp ts_clock;
pj_timestamp ts_rx; pj_timestamp ts_rx;
pj_timestamp ts_tx;
/* Tx buffer is a temporary buffer to be used when there's mismatch /* Tx buffer is a temporary buffer to be used when there's mismatch
* between port's ptime with conference's ptime. This buffer is used as * between port's ptime with conference's ptime. This buffer is used as
@ -109,23 +100,6 @@ struct conf_port
* fulfill a complete frame to be transmitted to the port. * fulfill a complete frame to be transmitted to the port.
*/ */
pj_uint8_t tx_buf[BUFFER_SIZE]; /**< Tx buffer. */ pj_uint8_t tx_buf[BUFFER_SIZE]; /**< Tx buffer. */
/* When the port is not receiving signal from any other ports (e.g. when
* no other ports is transmitting to this port), the bridge periodically
* transmit NULL frame to the port to keep the port "alive" (for example,
* a stream port needs this heart-beat to periodically transmit silence
* frame to keep NAT binding alive).
*
* This NULL frame should be sent to the port at the port's ptime rate.
* So if the port's ptime is greater than the bridge's ptime, the bridge
* needs to delay the NULL frame until it's the right time to do so.
*
* This variable keeps track of how many pending NULL samples are being
* "held" for this port. Once this value reaches samples_per_frame
* value of the port, a NULL frame is sent. The samples value on this
* variable is clocked at the port's clock rate.
*/
unsigned tx_heart_beat;
}; };
@ -476,6 +450,10 @@ PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf,
PJMEDIA_ENCCHANNEL); PJMEDIA_ENCCHANNEL);
PJ_ASSERT_RETURN(conf->bits_per_sample == strm_port->info.bits_per_sample, PJ_ASSERT_RETURN(conf->bits_per_sample == strm_port->info.bits_per_sample,
PJMEDIA_ENCBITS); PJMEDIA_ENCBITS);
/* Port's samples per frame should be equal to or multiplication of
* conference's samples per frame.
*/
PJ_ASSERT_RETURN((conf->samples_per_frame % PJ_ASSERT_RETURN((conf->samples_per_frame %
strm_port->info.samples_per_frame==0) || strm_port->info.samples_per_frame==0) ||
(strm_port->info.samples_per_frame % (strm_port->info.samples_per_frame %
@ -980,25 +958,29 @@ PJ_DEF(pj_status_t) pjmedia_conf_adjust_tx_level( pjmedia_conf *conf,
return PJ_SUCCESS; return PJ_SUCCESS;
} }
/* Deliver frm_src to a conference port (via frm_dst), eventually call /* Deliver frm_src to a listener port, eventually call port's put_frame()
* port's put_frame() when samples count in the frm_dst are equal to * when samples count in the frm_dst are equal to port's samples_per_frame.
* port's samples_per_frame.
*/ */
static pj_status_t deliver_frame(struct conf_port *cport_dst, static pj_status_t write_frame(struct conf_port *cport_dst,
pjmedia_frame *frm_dst, const pjmedia_frame *frm_src)
const pjmedia_frame *frm_src)
{ {
pjmedia_frame *frm_dst = (pjmedia_frame*)cport_dst->tx_buf;
PJ_TODO(MAKE_SURE_DEST_FRAME_HAS_ENOUGH_SPACE); PJ_TODO(MAKE_SURE_DEST_FRAME_HAS_ENOUGH_SPACE);
frm_dst->type = frm_src->type;
frm_dst->timestamp = cport_dst->ts_tx;
if (frm_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) { if (frm_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frm_src; pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frm_src;
pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst; pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
unsigned i; unsigned i;
/* Copy frame to listener's TX buffer. */
for (i = 0; i < f_src->subframe_cnt; ++i) { for (i = 0; i < f_src->subframe_cnt; ++i) {
pjmedia_frame_ext_subframe *sf; pjmedia_frame_ext_subframe *sf;
/* Copy frame to listener's TX buffer. */
sf = pjmedia_frame_ext_get_subframe(f_src, i); sf = pjmedia_frame_ext_get_subframe(f_src, i);
pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen, pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
f_src->samples_cnt / f_src->samples_cnt /
@ -1010,9 +992,13 @@ static pj_status_t deliver_frame(struct conf_port *cport_dst,
*/ */
if (f_dst->samples_cnt == cport_dst->samples_per_frame) if (f_dst->samples_cnt == cport_dst->samples_per_frame)
{ {
f_dst->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
if (cport_dst->port) { if (cport_dst->port) {
pjmedia_port_put_frame(cport_dst->port, (pjmedia_frame*)f_dst); pjmedia_port_put_frame(cport_dst->port,
(pjmedia_frame*)f_dst);
/* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx,
cport_dst->samples_per_frame);
/* Reset TX buffer. */ /* Reset TX buffer. */
f_dst->subframe_cnt = 0; f_dst->subframe_cnt = 0;
@ -1021,9 +1007,8 @@ static pj_status_t deliver_frame(struct conf_port *cport_dst,
} }
} }
} else { } else if (frm_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
pjmedia_frame *f_dst = (pjmedia_frame*)frm_dst;
pj_int16_t *f_start, *f_end; pj_int16_t *f_start, *f_end;
f_start = (pj_int16_t*)frm_src->buf; f_start = (pj_int16_t*)frm_src->buf;
@ -1031,32 +1016,94 @@ static pj_status_t deliver_frame(struct conf_port *cport_dst,
while (f_start < f_end) { while (f_start < f_end) {
unsigned nsamples_to_copy, nsamples_req; unsigned nsamples_to_copy, nsamples_req;
/* Copy frame to listener's TX buffer. */
nsamples_to_copy = f_end - f_start; nsamples_to_copy = f_end - f_start;
nsamples_req = cport_dst->samples_per_frame - (f_dst->size >> 1); nsamples_req = cport_dst->samples_per_frame - (frm_dst->size>>1);
if (nsamples_to_copy > nsamples_req) if (nsamples_to_copy > nsamples_req)
nsamples_to_copy = nsamples_req; nsamples_to_copy = nsamples_req;
pjmedia_copy_samples((pj_int16_t*)f_dst->buf + (f_dst->size >> 1), pjmedia_copy_samples((pj_int16_t*)frm_dst->buf + (frm_dst->size>>1),
f_start, f_start,
nsamples_to_copy); nsamples_to_copy);
f_dst->size += nsamples_to_copy << 1; frm_dst->size += nsamples_to_copy << 1;
f_start += nsamples_to_copy; f_start += nsamples_to_copy;
/* Check if it's time to deliver the TX buffer to listener, /* Check if it's time to deliver the TX buffer to listener,
* i.e: samples count in TX buffer equal to listener's * i.e: samples count in TX buffer equal to listener's
* samples per frame. * samples per frame.
*/ */
if ((f_dst->size >> 1) == cport_dst->samples_per_frame) if ((frm_dst->size >> 1) == cport_dst->samples_per_frame)
{ {
f_dst->type = PJMEDIA_FRAME_TYPE_AUDIO;
if (cport_dst->port) { if (cport_dst->port) {
pjmedia_port_put_frame(cport_dst->port, f_dst); pjmedia_port_put_frame(cport_dst->port, frm_dst);
/* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx,
cport_dst->samples_per_frame);
/* Reset TX buffer. */ /* Reset TX buffer. */
f_dst->size = 0; frm_dst->size = 0;
} }
} }
} }
} else if (frm_src->type == PJMEDIA_FRAME_TYPE_NONE) {
/* Check port format. */
if (cport_dst->port && (cport_dst->port->info.format.u32==0 ||
cport_dst->port->info.format.u32 == PJMEDIA_FOURCC_L16))
{
/* When there is already some samples in listener's TX buffer,
* pad the buffer with "zero samples".
*/
if (frm_dst->size != 0) {
pjmedia_zero_samples((pj_int16_t*)frm_dst->buf,
cport_dst->samples_per_frame -
(frm_dst->size>>1));
frm_dst->type = PJMEDIA_FRAME_TYPE_AUDIO;
frm_dst->size = cport_dst->samples_per_frame << 1;
if (cport_dst->port)
pjmedia_port_put_frame(cport_dst->port, frm_dst);
/* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx,
cport_dst->samples_per_frame);
/* Reset TX buffer. */
frm_dst->size = 0;
}
} else {
pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
if (f_dst->samples_cnt != 0) {
frm_dst->type = PJMEDIA_FRAME_TYPE_EXTENDED;
pjmedia_frame_ext_append_subframe(f_dst, NULL, 0, (pj_uint16_t)
(cport_dst->samples_per_frame-
f_dst->samples_cnt));
if (cport_dst->port)
pjmedia_port_put_frame(cport_dst->port, frm_dst);
/* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx,
cport_dst->samples_per_frame);
/* Reset TX buffer. */
f_dst->subframe_cnt = 0;
f_dst->samples_cnt = 0;
}
}
/* Synchronize clock. */
while (pj_cmp_timestamp(&cport_dst->ts_clock,
&cport_dst->ts_tx) >= 0)
{
if (cport_dst->port) {
frm_dst->type = PJMEDIA_FRAME_TYPE_NONE;
frm_dst->timestamp = cport_dst->ts_tx;
pjmedia_port_put_frame(cport_dst->port, frm_dst);
}
pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->samples_per_frame);
}
} }
return PJ_SUCCESS; return PJ_SUCCESS;
@ -1071,9 +1118,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata; pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
unsigned ci, i; unsigned ci, i;
PJ_TODO(ADJUST_AND_CALC_RX_TX_LEVEL_FOR_PCM_FRAMES); PJ_TODO(ADJUST_RX_TX_LEVEL_FOR_PCM_FRAMES);
TRACE_((THIS_FILE, "- clock -"));
/* Must lock mutex */ /* Must lock mutex */
pj_mutex_lock(conf->mutex); pj_mutex_lock(conf->mutex);
@ -1093,27 +1138,28 @@ static pj_status_t get_frame(pjmedia_port *this_port,
/* Var "ci" is to count how many ports have been visited so far. */ /* Var "ci" is to count how many ports have been visited so far. */
++ci; ++ci;
/* Skip if we're not allowed to receive from this port. */ /* Update clock of the port. */
if (cport->rx_setting == PJMEDIA_PORT_DISABLE) {
cport->rx_level = 0;
continue;
}
/* Also skip if this port doesn't have listeners. */
if (cport->listener_cnt == 0) {
cport->rx_level = 0;
continue;
}
pj_add_timestamp32(&cport->ts_clock, conf->samples_per_frame); pj_add_timestamp32(&cport->ts_clock, conf->samples_per_frame);
/* This loop will make sure the ptime between port & conf port /* Skip if we're not allowed to receive from this port or
* are synchronized. * the port doesn't have listeners.
*/
if (cport->rx_setting == PJMEDIA_PORT_DISABLE ||
cport->listener_cnt == 0)
{
cport->rx_level = 0;
continue;
}
/* Get frame from each port, put it to the listener TX buffer,
* and eventually call put_frame() of the listener. This loop
* will also make sure the ptime between conf & port synchronized.
*/ */
while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_rx) > 0) { while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_rx) > 0) {
pjmedia_frame *f = (pjmedia_frame*) conf->buf; pjmedia_frame *f = (pjmedia_frame*)conf->buf;
pj_status_t status; pj_status_t status;
unsigned j; unsigned j;
pj_int32_t level;
pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame); pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
@ -1125,45 +1171,144 @@ static pj_status_t get_frame(pjmedia_port *this_port,
if (status != PJ_SUCCESS) if (status != PJ_SUCCESS)
continue; continue;
if (f->type == PJMEDIA_FRAME_TYPE_NONE) { /* Calculate RX level. */
if (cport->port->info.format.u32 == PJMEDIA_FOURCC_L16) { if (f->type == PJMEDIA_FRAME_TYPE_AUDIO) {
pjmedia_zero_samples((pj_int16_t*)f->buf, level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
cport->samples_per_frame); f->size >>1 );
f->size = cport->samples_per_frame << 1; } else if (f->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
f->type = PJMEDIA_FRAME_TYPE_AUDIO; /* For extended frame, TX level is unknown, so we just set
} else { * it to NORMAL_LEVEL.
/* Handle DTX */ */
PJ_TODO(HANDLE_DTX); level = NORMAL_LEVEL;
} } else { /* PJMEDIA_FRAME_TYPE_NONE */
level = 0;
} }
cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff;
/* Put the frame to all listeners. */ /* Put the frame to all listeners. */
for (j=0; j < cport->listener_cnt; ++j) for (j=0; j < cport->listener_cnt; ++j)
{ {
struct conf_port *listener; struct conf_port *listener;
pjmedia_frame *frm_dst;
listener = conf->ports[cport->listener_slots[j]]; listener = conf->ports[cport->listener_slots[j]];
/* Skip if this listener doesn't want to receive audio */ /* Skip if this listener doesn't want to receive audio */
if (listener->tx_setting != PJMEDIA_PORT_ENABLE) if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
pj_add_timestamp32(&listener->ts_tx,
listener->samples_per_frame);
listener->tx_level = 0;
continue; continue;
}
if (listener->port) status = write_frame(listener, f);
frm_dst = frame; if (status != PJ_SUCCESS) {
else listener->tx_level = 0;
frm_dst = (pjmedia_frame*)listener->tx_buf;
status = deliver_frame(listener, frm_dst, f);
if (status != PJ_SUCCESS)
continue; continue;
}
/* Set listener TX level equal to transmitter RX level. */
listener->tx_level = cport->rx_level;
} }
} }
/* Keep alive mechanism. */
PJ_TODO(SEND_KEEP_ALIVE_WHEN_NEEDED);
} }
/* Keep alive. Update TX timestamp and send frame type NONE to all
* underflow ports at their own clock.
*/
for (i=1, ci=1; i<conf->max_ports && ci<conf->port_cnt; ++i) {
struct conf_port *cport = conf->ports[i];
/* Skip empty port. */
if (!cport)
continue;
/* Var "ci" is to count how many ports have been visited so far. */
++ci;
if (cport->tx_setting==PJMEDIA_PORT_MUTE || cport->transmitter_cnt==0)
{
/* Clear left-over samples in tx_buffer, if any, so that it won't
* be transmitted next time we have audio signal.
*/
pj_bzero(cport->tx_buf, sizeof(pjmedia_frame_ext));
cport->tx_level = 0;
while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_tx) >= 0)
{
if (cport->tx_setting == PJMEDIA_PORT_ENABLE) {
pjmedia_frame tmp_f;
tmp_f.timestamp = cport->ts_tx;
tmp_f.type = PJMEDIA_FRAME_TYPE_NONE;
tmp_f.buf = NULL;
tmp_f.size = 0;
pjmedia_port_put_frame(cport->port, &tmp_f);
pj_add_timestamp32(&cport->ts_tx, cport->samples_per_frame);
}
}
}
}
/* Return sound playback frame. */
do {
struct conf_port *this_cport = conf->ports[this_port->port_data.ldata];
pjmedia_frame *f_src = (pjmedia_frame*) this_cport->tx_buf;
frame->type = f_src->type;
if (f_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
pjmedia_frame_ext *f_src_ = (pjmedia_frame_ext*)f_src;
pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frame;
pjmedia_frame_ext_subframe *sf;
pj_uint16_t samples_per_subframe;
f_dst->samples_cnt = 0;
f_dst->subframe_cnt = 0;
i = 0;
samples_per_subframe = f_src_->samples_cnt / f_src_->subframe_cnt;
while (f_dst->samples_cnt < this_cport->samples_per_frame) {
sf = pjmedia_frame_ext_get_subframe(f_src_, i++);
pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
samples_per_subframe);
}
/* Shift left TX buffer. */
sf = pjmedia_frame_ext_get_subframe(f_src_, i);
if (sf) {
pjmedia_frame_ext_subframe *sf_end;
unsigned len;
sf_end = pjmedia_frame_ext_get_subframe(f_src_,
f_src_->subframe_cnt -1);
len = (pj_uint8_t*)sf_end - (pj_uint8_t*)sf + sf_end->bitlen/8;
if (sf_end->bitlen % 8 != 0)
++len;
pj_memmove(this_cport->tx_buf + sizeof(pjmedia_frame_ext), sf,
len);
}
f_src_->samples_cnt = f_src_->samples_cnt -
(pj_uint16_t)(i * samples_per_subframe);
f_src_->subframe_cnt = f_src_->subframe_cnt - (pj_uint16_t)i;
} else if (f_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
pjmedia_copy_samples((pj_int16_t*)frame->buf,
(pj_int16_t*)f_src->buf,
this_cport->samples_per_frame);
frame->size = this_cport->samples_per_frame << 1;
/* Shift left TX buffer. */
f_src->size -= frame->size;
if (f_src->size)
pjmedia_move_samples((pj_int16_t*)f_src->buf,
(pj_int16_t*)f_src->buf +
this_cport->samples_per_frame,
f_src->size >> 1);
}
} while (0);
/* Unlock mutex */ /* Unlock mutex */
pj_mutex_unlock(conf->mutex); pj_mutex_unlock(conf->mutex);
@ -1174,64 +1319,78 @@ static pj_status_t get_frame(pjmedia_port *this_port,
* Recorder callback. * Recorder callback.
*/ */
static pj_status_t put_frame(pjmedia_port *this_port, static pj_status_t put_frame(pjmedia_port *this_port,
const pjmedia_frame *frame) const pjmedia_frame *f)
{ {
pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata; pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
struct conf_port *port = conf->ports[this_port->port_data.ldata]; struct conf_port *cport = conf->ports[this_port->port_data.ldata];
unsigned j; unsigned j;
pj_int32_t level;
/* Check for correct size. */ /* Check for correct size. */
PJ_ASSERT_RETURN( frame->size == conf->samples_per_frame * PJ_ASSERT_RETURN( f->size == conf->samples_per_frame *
conf->bits_per_sample / 8, conf->bits_per_sample / 8,
PJMEDIA_ENCSAMPLESPFRAME); PJMEDIA_ENCSAMPLESPFRAME);
pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
/* Skip if this port is muted/disabled. */ /* Skip if this port is muted/disabled. */
if (port->rx_setting != PJMEDIA_PORT_ENABLE) { if (cport->rx_setting == PJMEDIA_PORT_DISABLE) {
cport->rx_level = 0;
return PJ_SUCCESS; return PJ_SUCCESS;
} }
/* Skip if no port is listening to the microphone */ /* Skip if no port is listening to the microphone */
if (port->listener_cnt == 0) { if (cport->listener_cnt == 0) {
cport->rx_level = 0;
return PJ_SUCCESS; return PJ_SUCCESS;
} }
if (frame->type == PJMEDIA_FRAME_TYPE_NONE) { /* Calculate RX level. */
if (this_port->info.format.u32 == PJMEDIA_FOURCC_L16) { if (f->type == PJMEDIA_FRAME_TYPE_AUDIO) {
pjmedia_frame *f = (pjmedia_frame*)port->tx_buf; level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
f->size >>1 );
pjmedia_zero_samples((pj_int16_t*)f->buf, } else if (f->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
port->samples_per_frame); /* For extended frame, TX level is unknown, so we just set
f->size = port->samples_per_frame << 1; * it to NORMAL_LEVEL.
f->type = PJMEDIA_FRAME_TYPE_AUDIO; */
frame = f; level = NORMAL_LEVEL;
} else { } else { /* PJMEDIA_FRAME_TYPE_NONE. */
/* Handle DTX */ level = 0;
PJ_TODO(HANDLE_DTX);
}
} }
cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff;
/* Put the frame to all listeners. */ /* Put the frame to all listeners. */
for (j=0; j < port->listener_cnt; ++j) for (j=0; j < cport->listener_cnt; ++j)
{ {
struct conf_port *listener; struct conf_port *listener;
pjmedia_frame *frm_dst;
pj_status_t status; pj_status_t status;
listener = conf->ports[port->listener_slots[j]]; listener = conf->ports[cport->listener_slots[j]];
/* Skip if this listener doesn't want to receive audio */ /* Skip if this listener doesn't want to receive audio */
if (listener->tx_setting != PJMEDIA_PORT_ENABLE) if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
pj_add_timestamp32(&listener->ts_tx,
listener->samples_per_frame);
listener->tx_level = 0;
continue; continue;
}
/* Skip loopback for now. */ /* Skip loopback for now. */
if (listener == port) if (listener == cport) {
pj_add_timestamp32(&listener->ts_tx,
listener->samples_per_frame);
listener->tx_level = 0;
continue; continue;
}
frm_dst = (pjmedia_frame*)listener->tx_buf; status = write_frame(listener, f);
if (status != PJ_SUCCESS) {
status = deliver_frame(listener, frm_dst, frame); listener->tx_level = 0;
if (status != PJ_SUCCESS)
continue; continue;
}
/* Set listener TX level equal to transmitter RX level. */
listener->tx_level = cport->rx_level;
} }
return PJ_SUCCESS; return PJ_SUCCESS;