- Added support for codec ILBC, G729, and AMR.

- Updated audio switch board to make user possible to update its port 0 (master port) attributes, this is needed since sound device need to be reopened (e.g: for changing ptime or codec) while conf is not recreated.
- Added new API to AMR helper to resolve mode/frame-type based on frame len.
- Updated pmedia_frame_ext helper functions for a bit optimization.



git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/aps-direct@2444 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Nanang Izzuddin 2009-02-09 10:39:58 +00:00
parent 4906604fd7
commit bfa860b133
8 changed files with 668 additions and 222 deletions

View File

@ -632,7 +632,6 @@ typedef struct pjmedia_codec_amr_pack_setting {
*
* @return AMR mode.
*/
PJ_INLINE(pj_int8_t) pjmedia_codec_amr_get_mode(unsigned bitrate)
{
pj_int8_t mode = -1;
@ -677,6 +676,35 @@ PJ_INLINE(pj_int8_t) pjmedia_codec_amr_get_mode(unsigned bitrate)
return mode;
}
/**
* Get AMR mode based on frame length.
*
* @param amrnb Set to PJ_TRUE for AMR-NB domain or PJ_FALSE for AMR-WB.
* @param frame_len The frame length.
*
* @return AMR mode.
*/
PJ_INLINE(pj_int8_t) pjmedia_codec_amr_get_mode2(pj_bool_t amrnb,
unsigned frame_len)
{
unsigned i;
if (amrnb) {
for (i = 0; i < 9; ++i)
if (frame_len == pjmedia_codec_amrnb_framelen[i])
return i;
} else {
for (i = 0; i < 10; ++i) {
if (frame_len == pjmedia_codec_amrwb_framelen[i])
return i;
}
}
pj_assert(!"Invalid AMR frame length");
return -1;
}
/**
* Prepare a frame before pass it to decoder. This function will do:
* - reorder AMR bitstream from descending sensitivity order into

View File

@ -310,13 +310,14 @@ PJ_INLINE(void) pjmedia_frame_ext_append_subframe(pjmedia_frame_ext *frm,
for (i = 0; i < frm->subframe_cnt; ++i) {
pjmedia_frame_ext_subframe *fsub;
fsub = (pjmedia_frame_ext_subframe*) p;
p += sizeof(fsub->bitlen) + fsub->bitlen / 8;
if (fsub->bitlen % 8)
p += sizeof(fsub->bitlen) + (fsub->bitlen >> 3);
if (fsub->bitlen & 0x07)
++p;
}
tmp = bitlen / 8;
if (bitlen % 8) ++tmp;
tmp = bitlen >> 3;
if (bitlen & 0x07)
++tmp;
pj_memcpy(p, &bitlen, sizeof(bitlen));
if (tmp)
@ -347,8 +348,8 @@ PJ_INLINE(pjmedia_frame_ext_subframe*)
p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext);
for (i = 0; i < n; ++i) {
sf = (pjmedia_frame_ext_subframe*) p;
p += sizeof(sf->bitlen) + sf->bitlen / 8;
if (sf->bitlen % 8)
p += sizeof(sf->bitlen) + (sf->bitlen >> 3);
if (sf->bitlen & 0x07)
++p;
}
@ -381,8 +382,9 @@ PJ_INLINE(pj_status_t)
move_src = (pj_uint8_t*)pjmedia_frame_ext_get_subframe(frm, n);
sf = pjmedia_frame_ext_get_subframe(frm, frm->subframe_cnt-1);
move_len = (pj_uint8_t*)sf - move_src + sizeof(sf->bitlen) + sf->bitlen/8;
if (sf->bitlen % 8 != 0)
move_len = (pj_uint8_t*)sf - move_src + sizeof(sf->bitlen) +
(sf->bitlen >> 3);
if (sf->bitlen & 0x07)
++move_len;
pj_memmove((pj_uint8_t*)frm+sizeof(pjmedia_frame_ext),
move_src, move_len);

View File

@ -200,7 +200,7 @@ typedef union pjmedia_fourcc {
#define PJMEDIA_FOURCC_G711U PJMEDIA_FOURCC_PACK('U', 'L', 'A', 'W')
#define PJMEDIA_FOURCC_AMR PJMEDIA_FOURCC_PACK(' ', 'A', 'M', 'R')
#define PJMEDIA_FOURCC_G729 PJMEDIA_FOURCC_PACK('G', '7', '2', '9')
#define PJMEDIA_FOURCC_ILBC PJMEDIA_FOURCC_PACK('i', 'L', 'B', 'C')
#define PJMEDIA_FOURCC_ILBC PJMEDIA_FOURCC_PACK('I', 'L', 'B', 'C')
/**

View File

@ -170,45 +170,46 @@ static struct codec_desc {
codec_desc[] =
{
# if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR
{1, "AMR", PJMEDIA_RTP_PT_AMR, PJMEDIA_FOURCC_AMR,
{1, "AMR", PJMEDIA_RTP_PT_AMR, {PJMEDIA_FOURCC_AMR},
8000, 1, 160,
5900, 12200, 4,
12200, 12200, 2,
&parse_amr, &pack_amr
/*, {1, {{{"octet-align", 11}, {"1", 1}}} } */
},
# endif
# if PJMEDIA_HAS_PASSTHROUGH_CODEC_G729
{1, "G729", PJMEDIA_RTP_PT_G729, PJMEDIA_FOURCC_G729,
{1, "G729", PJMEDIA_RTP_PT_G729, {PJMEDIA_FOURCC_G729},
8000, 1, 80,
8000, 11800, 2
8000, 8000, 2
},
# endif
# if PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC
{1, "iLBC", PJMEDIA_RTP_PT_ILBC, PJMEDIA_FOURCC_ILBC,
8000, 1, 80,
8000, 11800, 2,
{1, "iLBC", PJMEDIA_RTP_PT_ILBC, {PJMEDIA_FOURCC_ILBC},
8000, 1, 240,
13333, 15200, 2,
NULL, NULL,
{1, {{{"mode", 4}, {"30", 2}}} }
},
# endif
# if PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU
{1, "PCMU", PJMEDIA_RTP_PT_PCMU, PJMEDIA_FOURCC_G711U,
{1, "PCMU", PJMEDIA_RTP_PT_PCMU, {PJMEDIA_FOURCC_G711U},
8000, 1, 80,
64000, 64000, 2
},
# endif
# if PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA
{1, "PCMA", PJMEDIA_RTP_PT_PCMA, PJMEDIA_FOURCC_G711A,
{1, "PCMA", PJMEDIA_RTP_PT_PCMA, {PJMEDIA_FOURCC_G711A},
8000, 1, 80,
64000, 64000, 2
},
# endif
};
#if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR
#include <pjmedia-codec/amr_helper.h>
@ -226,61 +227,45 @@ static pj_status_t pack_amr ( codec_private_t *codec_data,
unsigned output_buf_len,
struct pjmedia_frame *output)
{
enum {MAX_FRAMES_PER_PACKET = 16};
enum {MAX_FRAMES_PER_PACKET = 8};
pjmedia_frame frames[MAX_FRAMES_PER_PACKET];
unsigned nframes = 0;
pjmedia_codec_amr_bit_info *info;
PJ_TODO(DEFINE_AMR_FRAME_INFO_FOR_PJMEDIA_FRAME_EXT);
#if 0
pj_uint8_t *r; /* Read cursor */
amr_settings_t* setting = (amr_settings_t*)codec_data->codec_setting;
pjmedia_codec_amr_pack_setting *enc_setting = &setting->enc_setting;
pj_uint8_t SID_FT;
pjmedia_codec_amr_pack_setting *setting;
unsigned i;
setting = &((amr_settings_t*)codec_data->codec_setting)->enc_setting;
pj_assert(input->subframe_cnt <= MAX_FRAMES_PER_PACKET);
SID_FT = (pj_uint8_t)(setting->amr_nb? 8 : 9);
/* Align pkt buf right */
r = (pj_uint8_t*)pkt + max_pkt_size - *pkt_size;
pj_memmove(r, pkt, *pkt_size);
SID_FT = (pj_uint8_t)(enc_setting->amr_nb? 8 : 9);
/* Get frames */
for (;;) {
pj_bool_t eof;
pj_uint16_t info_;
info_ = *((pj_uint16_t*)r);
eof = ((info_ & 0x40) != 0);
info = (pjmedia_codec_amr_bit_info*) &frames[nframes].bit_info;
for (i = 0; i < input->subframe_cnt; ++i) {
pjmedia_frame_ext_subframe *sf;
pjmedia_codec_amr_bit_info *info;
unsigned len;
sf = pjmedia_frame_ext_get_subframe(input, i);
len = sf->bitlen >> 3;
if (sf->bitlen & 0x07)
++len;
info = (pjmedia_codec_amr_bit_info*) &frames[i].bit_info;
pj_bzero(info, sizeof(*info));
info->frame_type = (pj_uint8_t)(info_ & 0x0F);
info->frame_type = pjmedia_codec_amr_get_mode2(enc_setting->amr_nb,
len);
info->good_quality = 1;
info->mode = (pj_int8_t) ((info_ >> 8) & 0x0F);
info->mode = setting->enc_mode;
frames[nframes].buf = r + 2;
frames[nframes].size = info->frame_type <= SID_FT ?
pjmedia_codec_amrnb_framelen[info->frame_type] :
0;
r += frames[nframes].size + 2;
/* Last frame */
if (++nframes >= MAX_FRAMES_PER_PACKET || eof)
break;
frames[i].buf = sf->data;
frames[i].size = len;
}
/* Pack */
*pkt_size = max_pkt_size;
output->size = output_buf_len;
return pjmedia_codec_amr_pack(frames, nframes, setting, pkt, pkt_size);
#endif
return PJ_ENOTSUP;
return pjmedia_codec_amr_pack(frames, input->subframe_cnt, enc_setting,
output->buf, &output->size);
}
@ -301,10 +286,11 @@ static pj_status_t parse_amr(codec_private_t *codec_data, void *pkt,
if (status != PJ_SUCCESS)
return status;
// CMR is not supported for now.
/* Check Change Mode Request. */
if ((setting->amr_nb && cmr <= 7) || (!setting->amr_nb && cmr <= 8)) {
s->enc_mode = cmr;
}
//if ((setting->amr_nb && cmr <= 7) || (!setting->amr_nb && cmr <= 8)) {
// s->enc_mode = cmr;
//}
return PJ_SUCCESS;
}
@ -799,30 +785,30 @@ static pj_status_t codec_decode( pjmedia_codec *codec,
struct codec_desc *desc = &codec_desc[codec_data->codec_idx];
pjmedia_frame_ext *output_ = (pjmedia_frame_ext*) output;
pj_assert(input && input->size > 0);
pj_assert(input);
#if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR
/* Need to rearrange the AMR bitstream, since the bitstream may not be
* started from bit 0 or may need to be reordered from sensitivity order
* into encoder bits order.
*/
if (desc->pt == PJMEDIA_RTP_PT_AMR) {
pjmedia_frame frame;
if (desc->pt == PJMEDIA_RTP_PT_AMR || desc->pt == PJMEDIA_RTP_PT_AMRWB) {
pjmedia_frame input_;
pjmedia_codec_amr_pack_setting *setting;
setting = &((amr_settings_t*)codec_data->codec_setting)->dec_setting;
frame = *input;
pjmedia_codec_amr_predecode(input, setting, &frame);
input_ = *input;
pjmedia_codec_amr_predecode(input, setting, &input_);
pjmedia_frame_ext_append_subframe(output_, input_.buf,
(pj_uint16_t)(input_.size << 3),
(pj_uint16_t)desc->samples_per_frame);
return PJ_SUCCESS;
}
#endif
/*
PJ_ASSERT_RETURN(output_buf_len >= sizeof(pjmedia_frame_ext) +
sizeof(pjmedia_frame_ext_subframe) +
input->size,
PJMEDIA_CODEC_EFRMTOOSHORT);
*/
pjmedia_frame_ext_append_subframe(output_, input->buf,
(pj_uint16_t)(input->size << 3),
(pj_uint16_t)desc->samples_per_frame);

View File

@ -67,6 +67,7 @@
*/
struct conf_port
{
SLOT_TYPE slot; /**< Array of listeners. */
pj_str_t name; /**< Port name. */
pjmedia_port *port; /**< get_frame() and put_frame() */
pjmedia_port_op rx_setting; /**< Can we receive from this port */
@ -76,9 +77,7 @@ struct conf_port
unsigned transmitter_cnt;/**<Number of transmitters. */
/* Shortcut for port info. */
unsigned clock_rate; /**< Port's clock rate. */
unsigned samples_per_frame; /**< Port's samples per frame. */
unsigned channel_count; /**< Port's channel count. */
pjmedia_port_info *info;
/* Calculated signal levels: */
unsigned tx_level; /**< Last tx level to this port. */
@ -117,10 +116,6 @@ struct pjmedia_conf
char master_name_buf[80]; /**< Port0 name buffer. */
pj_mutex_t *mutex; /**< Conference mutex. */
struct conf_port **ports; /**< Array of ports. */
unsigned clock_rate; /**< Sampling rate. */
unsigned channel_count;/**< Number of channels (1=mono). */
unsigned samples_per_frame; /**< Samples per frame. */
unsigned bits_per_sample; /**< Bits per sample. */
pj_uint8_t buf[BUFFER_SIZE]; /**< Common buffer. */
};
@ -145,6 +140,8 @@ static pj_status_t create_conf_port( pj_pool_t *pool,
struct conf_port *conf_port;
pjmedia_frame *f;
PJ_ASSERT_RETURN(pool && conf && port && name && p_conf_port, PJ_EINVAL);
/* Create port. */
conf_port = PJ_POOL_ZALLOC_T(pool, struct conf_port);
@ -162,17 +159,8 @@ static pj_status_t create_conf_port( pj_pool_t *pool,
PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM);
/* Save some port's infos, for convenience. */
if (port) {
conf_port->port = port;
conf_port->clock_rate = port->info.clock_rate;
conf_port->samples_per_frame = port->info.samples_per_frame;
conf_port->channel_count = port->info.channel_count;
} else {
conf_port->port = NULL;
conf_port->clock_rate = conf->clock_rate;
conf_port->samples_per_frame = conf->samples_per_frame;
conf_port->channel_count = conf->channel_count;
}
conf_port->port = port;
conf_port->info = &port->info;
/* Init pjmedia_frame structure in the TX buffer. */
f = (pjmedia_frame*)conf_port->tx_buf;
@ -195,7 +183,7 @@ static pj_status_t create_sound_port( pj_pool_t *pool,
pj_status_t status;
status = create_conf_port(pool, conf, NULL, &name, &conf_port);
status = create_conf_port(pool, conf, conf->master_port, &name, &conf_port);
if (status != PJ_SUCCESS)
return status;
@ -205,27 +193,32 @@ static pj_status_t create_sound_port( pj_pool_t *pool,
if ((conf->options & PJMEDIA_CONF_NO_DEVICE) == 0) {
pjmedia_snd_stream *strm;
pjmedia_snd_stream_info si;
pjmedia_port_info *master_port_info = (pjmedia_port_info*)
&conf->master_port->info;
/*
* If capture is disabled then create player only port.
* Otherwise create bidirectional sound device port.
*/
if (conf->options & PJMEDIA_CONF_NO_MIC) {
status = pjmedia_snd_port_create_player(pool, -1, conf->clock_rate,
conf->channel_count,
conf->samples_per_frame,
conf->bits_per_sample,
0, /* options */
&conf->snd_dev_port);
status = pjmedia_snd_port_create_player(
pool, -1,
master_port_info->clock_rate,
master_port_info->channel_count,
master_port_info->samples_per_frame,
master_port_info->bits_per_sample,
0, /* options */
&conf->snd_dev_port);
} else {
status = pjmedia_snd_port_create( pool, -1, -1, conf->clock_rate,
conf->channel_count,
conf->samples_per_frame,
conf->bits_per_sample,
0, /* Options */
&conf->snd_dev_port);
status = pjmedia_snd_port_create(
pool, -1, -1,
master_port_info->clock_rate,
master_port_info->channel_count,
master_port_info->samples_per_frame,
master_port_info->bits_per_sample,
0, /* Options */
&conf->snd_dev_port);
}
if (status != PJ_SUCCESS)
@ -245,6 +238,7 @@ static pj_status_t create_sound_port( pj_pool_t *pool,
/* Add the port to the bridge */
conf_port->slot = 0;
conf->ports[0] = conf_port;
conf->port_cnt++;
@ -285,11 +279,6 @@ PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,
conf->options = options;
conf->max_ports = max_ports;
conf->clock_rate = clock_rate;
conf->channel_count = channel_count;
conf->samples_per_frame = samples_per_frame;
conf->bits_per_sample = bits_per_sample;
/* Create and initialize the master port interface. */
conf->master_port = PJ_POOL_ZALLOC_T(pool, pjmedia_port);
@ -424,8 +413,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_set_port0_name(pjmedia_conf *conf,
conf->ports[0]->name.ptr = conf->master_name_buf;
conf->ports[0]->name.slen = len;
if (conf->master_port)
conf->master_port->info.name = conf->ports[0]->name;
conf->master_port->info.name = conf->ports[0]->name;
return PJ_SUCCESS;
}
@ -444,21 +432,26 @@ PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf,
pj_status_t status;
PJ_ASSERT_RETURN(conf && pool && strm_port, PJ_EINVAL);
/*
PJ_ASSERT_RETURN(conf->clock_rate == strm_port->info.clock_rate,
PJMEDIA_ENCCLOCKRATE);
PJ_ASSERT_RETURN(conf->channel_count == strm_port->info.channel_count,
PJMEDIA_ENCCHANNEL);
PJ_ASSERT_RETURN(conf->bits_per_sample == strm_port->info.bits_per_sample,
PJMEDIA_ENCBITS);
*/
/* Port's samples per frame should be equal to or multiplication of
* conference's samples per frame.
*/
/*
Not sure if this is needed!
PJ_ASSERT_RETURN((conf->samples_per_frame %
strm_port->info.samples_per_frame==0) ||
(strm_port->info.samples_per_frame %
conf->samples_per_frame==0),
PJMEDIA_ENCSAMPLESPFRAME);
*/
/* If port_name is not specified, use the port's name */
if (!port_name)
@ -488,6 +481,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf,
}
/* Put the port. */
conf_port->slot = index;
conf->ports[index] = conf_port;
conf->port_cnt++;
@ -588,6 +582,16 @@ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf,
src_port = conf->ports[src_slot];
dst_port = conf->ports[sink_slot];
/* Source and sink ptime must be equal or a multiplication factor. */
if ((src_port->info->samples_per_frame %
dst_port->info->samples_per_frame != 0) &&
(dst_port->info->samples_per_frame %
src_port->info->samples_per_frame != 0))
{
pj_mutex_unlock(conf->mutex);
return PJMEDIA_ENCSAMPLESPFRAME;
}
/* Check if source and sink has compatible format */
if (src_slot != 0 && sink_slot != 0 &&
src_port->port->info.format.u32 != dst_port->port->info.format.u32)
@ -677,6 +681,9 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf,
--conf->connect_cnt;
--src_port->listener_cnt;
--dst_port->transmitter_cnt;
/* Clean up sink TX buffer. */
pj_bzero(dst_port->tx_buf, sizeof(pjmedia_frame_ext));
PJ_LOG(4,(THIS_FILE,
"Port %d (%.*s) stop transmitting to port %d (%.*s)",
@ -776,6 +783,9 @@ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,
--conf_port->listener_cnt;
pj_assert(conf->connect_cnt > 0);
--conf->connect_cnt;
/* Clean up TX buffer. */
pj_bzero(dst_port->tx_buf, sizeof(pjmedia_frame_ext));
}
/* Remove the port. */
@ -841,12 +851,11 @@ PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf,
info->rx_setting = conf_port->rx_setting;
info->listener_cnt = conf_port->listener_cnt;
info->listener_slots = conf_port->listener_slots;
info->clock_rate = conf_port->clock_rate;
info->channel_count = conf_port->channel_count;
info->samples_per_frame = conf_port->samples_per_frame;
info->bits_per_sample = conf->bits_per_sample;
info->format = slot? conf_port->port->info.format :
conf->master_port->info.format;
info->clock_rate = conf_port->info->clock_rate;
info->channel_count = conf_port->info->channel_count;
info->samples_per_frame = conf_port->info->samples_per_frame;
info->bits_per_sample = conf_port->info->bits_per_sample;
info->format = conf_port->port->info.format;
return PJ_SUCCESS;
}
@ -990,9 +999,9 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
* i.e: samples count in TX buffer equal to listener's
* samples per frame.
*/
if (f_dst->samples_cnt == cport_dst->samples_per_frame)
if (f_dst->samples_cnt == cport_dst->info->samples_per_frame)
{
if (cport_dst->port) {
if (cport_dst->slot) {
pjmedia_port_put_frame(cport_dst->port,
(pjmedia_frame*)f_dst);
@ -1003,8 +1012,7 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
/* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx,
cport_dst->samples_per_frame);
cport_dst->info->samples_per_frame);
}
}
@ -1019,9 +1027,11 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
/* Copy frame to listener's TX buffer. */
nsamples_to_copy = f_end - f_start;
nsamples_req = cport_dst->samples_per_frame - (frm_dst->size>>1);
nsamples_req = cport_dst->info->samples_per_frame -
(frm_dst->size>>1);
if (nsamples_to_copy > nsamples_req)
nsamples_to_copy = nsamples_req;
pjmedia_copy_samples((pj_int16_t*)frm_dst->buf + (frm_dst->size>>1),
f_start,
nsamples_to_copy);
@ -1032,9 +1042,9 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
* i.e: samples count in TX buffer equal to listener's
* samples per frame.
*/
if ((frm_dst->size >> 1) == cport_dst->samples_per_frame)
if ((frm_dst->size >> 1) == cport_dst->info->samples_per_frame)
{
if (cport_dst->port) {
if (cport_dst->slot) {
pjmedia_port_put_frame(cport_dst->port, frm_dst);
/* Reset TX buffer. */
@ -1043,7 +1053,7 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
/* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx,
cport_dst->samples_per_frame);
cport_dst->info->samples_per_frame);
}
}
@ -1058,12 +1068,12 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
*/
if (frm_dst->size != 0) {
pjmedia_zero_samples((pj_int16_t*)frm_dst->buf,
cport_dst->samples_per_frame -
cport_dst->info->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) {
frm_dst->size = cport_dst->info->samples_per_frame << 1;
if (cport_dst->slot) {
pjmedia_port_put_frame(cport_dst->port, frm_dst);
/* Reset TX buffer. */
@ -1072,7 +1082,7 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
/* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx,
cport_dst->samples_per_frame);
cport_dst->info->samples_per_frame);
}
} else {
pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
@ -1080,9 +1090,8 @@ static pj_status_t write_frame(struct conf_port *cport_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) {
(cport_dst->info->samples_per_frame - f_dst->samples_cnt));
if (cport_dst->slot) {
pjmedia_port_put_frame(cport_dst->port, frm_dst);
/* Reset TX buffer. */
@ -1092,7 +1101,7 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
/* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx,
cport_dst->samples_per_frame);
cport_dst->info->samples_per_frame);
}
}
@ -1102,11 +1111,11 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
{
frm_dst->type = PJMEDIA_FRAME_TYPE_NONE;
frm_dst->timestamp = cport_dst->ts_tx;
if (cport_dst->port)
if (cport_dst->slot)
pjmedia_port_put_frame(cport_dst->port, frm_dst);
/* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->samples_per_frame);
pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->info->samples_per_frame);
}
}
@ -1143,7 +1152,8 @@ static pj_status_t get_frame(pjmedia_port *this_port,
++ci;
/* Update clock of the port. */
pj_add_timestamp32(&cport->ts_clock, conf->samples_per_frame);
pj_add_timestamp32(&cport->ts_clock,
conf->master_port->info.samples_per_frame);
/* Skip if we're not allowed to receive from this port or
* the port doesn't have listeners.
@ -1165,7 +1175,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
unsigned j;
pj_int32_t level;
pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
pj_add_timestamp32(&cport->ts_rx, cport->info->samples_per_frame);
f->buf = &conf->buf[sizeof(pjmedia_frame)];
f->size = BUFFER_SIZE - sizeof(pjmedia_frame);
@ -1199,7 +1209,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
/* Skip if this listener doesn't want to receive audio */
if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
pj_add_timestamp32(&listener->ts_tx,
listener->samples_per_frame);
listener->info->samples_per_frame);
listener->tx_level = 0;
continue;
}
@ -1249,7 +1259,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
tmp_f.size = 0;
pjmedia_port_put_frame(cport->port, &tmp_f);
pj_add_timestamp32(&cport->ts_tx, cport->samples_per_frame);
pj_add_timestamp32(&cport->ts_tx, cport->info->samples_per_frame);
}
}
}
@ -1268,7 +1278,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
pjmedia_frame_ext_subframe *sf;
pj_uint16_t samples_per_subframe;
if (f_src_->samples_cnt < this_cport->samples_per_frame) {
if (f_src_->samples_cnt < this_cport->info->samples_per_frame) {
pj_bzero(this_cport->tx_buf, sizeof(pjmedia_frame_ext));
frame->type = PJMEDIA_FRAME_TYPE_NONE;
break;
@ -1280,7 +1290,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
samples_per_subframe = f_src_->samples_cnt / f_src_->subframe_cnt;
while (f_dst->samples_cnt < this_cport->samples_per_frame) {
while (f_dst->samples_cnt < this_cport->info->samples_per_frame) {
sf = pjmedia_frame_ext_get_subframe(f_src_, i++);
pj_assert(sf);
pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
@ -1291,7 +1301,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
pjmedia_frame_ext_pop_subframes(f_src_, i);
} else if (f_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
if ((f_src->size>>1) < this_cport->samples_per_frame) {
if ((f_src->size>>1) < this_cport->info->samples_per_frame) {
pj_bzero(this_cport->tx_buf, sizeof(pjmedia_frame_ext));
frame->type = PJMEDIA_FRAME_TYPE_NONE;
break;
@ -1299,15 +1309,15 @@ static pj_status_t get_frame(pjmedia_port *this_port,
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;
this_cport->info->samples_per_frame);
frame->size = this_cport->info->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,
this_cport->info->samples_per_frame,
f_src->size >> 1);
} else { /* PJMEDIA_FRAME_TYPE_NONE */
/* Reset TX buffer */
@ -1333,7 +1343,7 @@ static pj_status_t put_frame(pjmedia_port *this_port,
unsigned j;
pj_int32_t level;
pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
pj_add_timestamp32(&cport->ts_rx, cport->info->samples_per_frame);
/* Skip if this port is muted/disabled. */
if (cport->rx_setting == PJMEDIA_PORT_DISABLE) {
@ -1372,7 +1382,7 @@ static pj_status_t put_frame(pjmedia_port *this_port,
/* Skip if this listener doesn't want to receive audio */
if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
pj_add_timestamp32(&listener->ts_tx,
listener->samples_per_frame);
listener->info->samples_per_frame);
listener->tx_level = 0;
continue;
}
@ -1380,7 +1390,7 @@ static pj_status_t put_frame(pjmedia_port *this_port,
/* Skip loopback for now. */
if (listener == cport) {
pj_add_timestamp32(&listener->ts_tx,
listener->samples_per_frame);
listener->info->samples_per_frame);
listener->tx_level = 0;
continue;
}

View File

@ -0,0 +1,171 @@
#ifndef __BITSTREAM_H_
#define __BITSTREAM_H_
#define KPackedFrameLen 10
#define KUnpackedFrameLen 22
// Below values are taken from the APS design document
const TUint8 KG729FullPayloadBits[] = { 8, 10, 8, 1, 13, 4, 7, 5, 13, 4, 7 };
const TUint KNumFullFrameParams = 11;
const TUint8 KG729SIDPayloadBits[] = { 1, 5, 4, 5 };
const TUint KNumSIDFrameParams = 4;
/*!
@class TBitStream
@discussion Provides compression from 16-bit-word-aligned G.729 audio frames
(used in S60 G.729 DSP codec) to 8-bit stream, and vice versa.
*/
class TBitStream
{
public:
/*!
@function TBitStream
@discussion Constructor
*/
TBitStream():iDes(iData,KUnpackedFrameLen){}
/*!
@function CompressG729Frame
@discussion Compress either a 22-byte G.729 full rate frame to 10 bytes
or a 8-byte G.729 Annex.B SID frame to 2 bytes.
@param aSrc Reference to the uncompressed source frame data
@param aIsSIDFrame True if the source is a SID frame
@result a reference to the compressed frame
*/
const TDesC8& CompressG729Frame( const TDesC8& aSrc, TBool aIsSIDFrame = EFalse );
/*!
@function ExpandG729Frame
@discussion Expand a 10-byte G.729 full rate frame to 22 bytes
or a 2-byte G.729 Annex.B SID frame to 8(22) bytes.
@param aSrc Reference to the compressed source frame data
@param aIsSIDFrame True if the source is a SID frame
@result a reference to a descriptor representing the uncompressed frame.
Note that SID frames are zero-padded to 22 bytes as well.
*/
const TDesC8& ExpandG729Frame( const TDesC8& aSrc, TBool aIsSIDFrame = EFalse );
private:
void Compress( TUint8 aValue, TUint8 aNumOfBits );
void Expand( const TUint8* aSrc, TInt aDstIdx, TUint8 aNumOfBits );
private:
TUint8 iData[KUnpackedFrameLen];
TPtr8 iDes;
TInt iIdx;
TInt iBitOffset;
};
const TDesC8& TBitStream::CompressG729Frame( const TDesC8& aSrc, TBool aIsSIDFrame )
{
// reset data
iDes.FillZ(iDes.MaxLength());
iIdx = iBitOffset = 0;
TInt numParams = (aIsSIDFrame) ? KNumSIDFrameParams : KNumFullFrameParams;
const TUint8* p = const_cast<TUint8*>(aSrc.Ptr());
for(TInt i = 0, pIdx = 0; i < numParams; i++, pIdx += 2)
{
TUint8 paramBits = (aIsSIDFrame) ? KG729SIDPayloadBits[i] : KG729FullPayloadBits[i];
if(paramBits > 8)
{
Compress(p[pIdx+1], paramBits - 8); // msb
paramBits = 8;
}
Compress(p[pIdx], paramBits); // lsb
}
if( iBitOffset )
iIdx++;
iDes.SetLength(iIdx);
return iDes;
}
const TDesC8& TBitStream::ExpandG729Frame( const TDesC8& aSrc, TBool aIsSIDFrame )
{
// reset data
iDes.FillZ(iDes.MaxLength());
iIdx = iBitOffset = 0;
TInt numParams = (aIsSIDFrame) ? KNumSIDFrameParams : KNumFullFrameParams;
const TUint8* p = const_cast<TUint8*>(aSrc.Ptr());
for(TInt i = 0, dIdx = 0; i < numParams; i++, dIdx += 2)
{
TUint8 paramBits = (aIsSIDFrame) ? KG729SIDPayloadBits[i] : KG729FullPayloadBits[i];
if(paramBits > 8)
{
Expand(p, dIdx+1, paramBits - 8); // msb
paramBits = 8;
}
Expand(p, dIdx, paramBits); // lsb
}
iDes.SetLength(KUnpackedFrameLen);
return iDes;
}
void TBitStream::Compress( TUint8 aValue, TUint8 aNumOfBits )
{
// clear bits that will be discarded
aValue &= (0xff >> (8 - aNumOfBits));
// calculate required bitwise left shift
TInt shl = 8 - (iBitOffset + aNumOfBits);
if (shl == 0) // no shift required
{
iData[iIdx++] |= aValue;
iBitOffset = 0;
}
else if (shl > 0) // bits fit into current byte
{
iData[iIdx] |= (aValue << shl);
iBitOffset += aNumOfBits;
}
else
{
iBitOffset = -shl;
iData[iIdx] |= (aValue >> iBitOffset); // right shift
iData[++iIdx] |= (aValue << (8-iBitOffset)); // push remaining bits to next byte
}
}
void TBitStream::Expand( const TUint8* aSrc, TInt aDstIdx, TUint8 aNumOfBits )
{
TUint8 aValue = aSrc[iIdx] & (0xff >> iBitOffset);
// calculate required bitwise right shift
TInt shr = 8 - (iBitOffset + aNumOfBits);
if (shr == 0) // no shift required
{
iData[aDstIdx] = aValue;
iIdx++;
iBitOffset = 0;
}
else if (shr > 0) // right shift
{
iData[aDstIdx] = (aValue >> shr);
iBitOffset += aNumOfBits;
}
else // shift left and take remaining bits from the next src byte
{
iBitOffset = -shr;
iData[aDstIdx] = aValue << iBitOffset;
iData[aDstIdx] |= aSrc[++iIdx] >> (8 - iBitOffset);
}
}
#endif // __BITSTREAM_H_
// eof

View File

@ -98,6 +98,7 @@ struct pjmedia_snd_stream
pj_uint16_t play_buf_start;
pj_int16_t *rec_buf;
pj_uint16_t rec_buf_len;
void *strm_data;
};
@ -695,42 +696,24 @@ static void PlayCbPcm(TAPSCommBuffer &buf, void *user_data)
}
}
/* Pack/unpack G.729 frame of S60 DSP codec, taken from:
* http://wiki.forum.nokia.com/index.php/TSS000776_-_Payload_conversion_for_G.729_audio_format
*/
#include "s60_g729_bitstream.h"
#include <pjmedia-codec/amr_helper.h>
static void RecCb(TAPSCommBuffer &buf, void *user_data)
{
pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data;
pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->rec_buf;
unsigned samples_processed = 0;
switch(strm->setting.format.u32) {
case PJMEDIA_FOURCC_G711U:
case PJMEDIA_FOURCC_G711A:
pj_assert(buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0);
/* Detect the recorder G.711 frame size, player frame size will follow
* this recorder frame size.
*/
if (aps_g711_frame_len == 0) {
aps_g711_frame_len = buf.iBuffer.Length() < 160? 80 : 160;
TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples",
aps_g711_frame_len));
}
/* Convert APS buffer format into pjmedia_frame_ext. Whenever
* samples count in the frame is equal to stream's samples per frame,
* call parent stream callback.
*/
while (samples_processed < aps_g711_frame_len) {
unsigned tmp;
const pj_uint8_t *pb = (const pj_uint8_t*)buf.iBuffer.Ptr() + 2 +
samples_processed;
tmp = PJ_MIN(strm->samples_per_frame - frame->samples_cnt,
aps_g711_frame_len - samples_processed);
switch(strm->setting.format.u32) {
case PJMEDIA_FOURCC_AMR:
{
const pj_uint8_t *p = (const pj_uint8_t*)buf.iBuffer.Ptr() + 1;
unsigned len = buf.iBuffer.Length() - 1;
pjmedia_frame_ext_append_subframe(frame, pb, tmp << 3, tmp);
samples_processed += tmp;
pjmedia_frame_ext_append_subframe(frame, p, len << 3, 160);
if (frame->samples_cnt == strm->samples_per_frame) {
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
strm->rec_cb(strm->user_data, 0, strm->rec_buf, 0);
@ -740,6 +723,103 @@ static void RecCb(TAPSCommBuffer &buf, void *user_data)
}
break;
case PJMEDIA_FOURCC_G729:
{
/* Check if we got a normal or SID frame. */
if (buf.iBuffer[0] != 0 || buf.iBuffer[1] != 0) {
enum { NORMAL_LEN = 22, SID_LEN = 8 };
TBitStream *bitstream = (TBitStream*)strm->strm_data;
unsigned src_len = buf.iBuffer.Length()- 2;
pj_assert(src_len == NORMAL_LEN || src_len == SID_LEN);
const TDesC8& p = bitstream->CompressG729Frame(
buf.iBuffer.Right(src_len),
src_len == SID_LEN);
pjmedia_frame_ext_append_subframe(frame, p.Ptr(),
p.Length() << 3, 80);
} else { /* We got null frame. */
pjmedia_frame_ext_append_subframe(frame, NULL, 0, 80);
}
if (frame->samples_cnt == strm->samples_per_frame) {
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
strm->rec_cb(strm->user_data, 0, strm->rec_buf, 0);
frame->samples_cnt = 0;
frame->subframe_cnt = 0;
}
}
break;
case PJMEDIA_FOURCC_ILBC:
{
unsigned samples_got;
samples_got = strm->setting.mode == 30? 240 : 160;
/* Check if we got a normal frame. */
if (buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0) {
const pj_uint8_t *p = (const pj_uint8_t*)buf.iBuffer.Ptr() + 2;
unsigned len = buf.iBuffer.Length() - 2;
pjmedia_frame_ext_append_subframe(frame, p, len << 3,
samples_got);
} else { /* We got null frame. */
pjmedia_frame_ext_append_subframe(frame, NULL, 0, samples_got);
}
if (frame->samples_cnt == strm->samples_per_frame) {
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
strm->rec_cb(strm->user_data, 0, strm->rec_buf, 0);
frame->samples_cnt = 0;
frame->subframe_cnt = 0;
}
}
break;
case PJMEDIA_FOURCC_G711U:
case PJMEDIA_FOURCC_G711A:
{
unsigned samples_processed = 0;
/* Make sure it is normal frame. */
pj_assert(buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0);
/* Detect the recorder G.711 frame size, player frame size will
* follow this recorder frame size.
*/
if (aps_g711_frame_len == 0) {
aps_g711_frame_len = buf.iBuffer.Length() < 160? 80 : 160;
TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples",
aps_g711_frame_len));
}
/* Convert APS buffer format into pjmedia_frame_ext. Whenever
* samples count in the frame is equal to stream's samples per
* frame, call parent stream callback.
*/
while (samples_processed < aps_g711_frame_len) {
unsigned tmp;
const pj_uint8_t *pb = (const pj_uint8_t*)buf.iBuffer.Ptr() +
2 + samples_processed;
tmp = PJ_MIN(strm->samples_per_frame - frame->samples_cnt,
aps_g711_frame_len - samples_processed);
pjmedia_frame_ext_append_subframe(frame, pb, tmp << 3, tmp);
samples_processed += tmp;
if (frame->samples_cnt == strm->samples_per_frame) {
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
strm->rec_cb(strm->user_data, 0, strm->rec_buf, 0);
frame->samples_cnt = 0;
frame->subframe_cnt = 0;
}
}
}
break;
default:
break;
}
@ -749,8 +829,6 @@ static void PlayCb(TAPSCommBuffer &buf, void *user_data)
{
pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data;
pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->play_buf;
unsigned g711_frame_len = aps_g711_frame_len;
unsigned samples_ready = 0;
/* Init buffer attributes and header. */
buf.iCommand = CQueueHandler::EAPSPlayData;
@ -758,23 +836,160 @@ static void PlayCb(TAPSCommBuffer &buf, void *user_data)
buf.iBuffer.Zero();
switch(strm->setting.format.u32) {
case PJMEDIA_FOURCC_AMR:
{
if (frame->samples_cnt == 0) {
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
strm->play_cb(strm->user_data, 0, strm->play_buf, 0);
pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
}
if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
pjmedia_frame_ext_subframe *sf;
unsigned samples_cnt;
sf = pjmedia_frame_ext_get_subframe(frame, 0);
samples_cnt = frame->samples_cnt / frame->subframe_cnt;
if (sf->data && sf->bitlen) {
/* AMR header for APS is one byte, the format (may be!):
* 0xxxxy00, where xxxx:frame type, y:not sure.
*/
unsigned len = sf->bitlen>>3;
enum {SID_FT = 8 };
pj_uint8_t amr_header = 4, ft = SID_FT;
if (sf->bitlen & 0x07)
++len;
if (len >= pjmedia_codec_amrnb_framelen[0])
ft = pjmedia_codec_amr_get_mode2(PJ_TRUE, len);
amr_header |= ft << 3;
buf.iBuffer.Append(amr_header);
buf.iBuffer.Append((TUint8*)sf->data, len);
} else {
buf.iBuffer.Append(0);
}
pjmedia_frame_ext_pop_subframes(frame, 1);
} else { /* PJMEDIA_FRAME_TYPE_NONE */
buf.iBuffer.Append(0);
frame->samples_cnt = 0;
frame->subframe_cnt = 0;
}
}
break;
case PJMEDIA_FOURCC_G729:
{
if (frame->samples_cnt == 0) {
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
strm->play_cb(strm->user_data, 0, strm->play_buf, 0);
pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
}
if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
pjmedia_frame_ext_subframe *sf;
unsigned samples_cnt;
sf = pjmedia_frame_ext_get_subframe(frame, 0);
samples_cnt = frame->samples_cnt / frame->subframe_cnt;
if (sf->data && sf->bitlen) {
enum { NORMAL_LEN = 10, SID_LEN = 2 };
pj_bool_t sid_frame = ((sf->bitlen >> 3) == SID_LEN);
TBitStream *bitstream = (TBitStream*)strm->strm_data;
const TPtrC8 src(sf->data, sf->bitlen>>3);
const TDesC8 &dst = bitstream->ExpandG729Frame(src,
sid_frame);
if (sid_frame) {
buf.iBuffer.Append(0);
buf.iBuffer.Append(1);
} else {
buf.iBuffer.Append(1);
buf.iBuffer.Append(0);
}
buf.iBuffer.Append(dst);
} else {
buf.iBuffer.Append(0);
buf.iBuffer.Append(0);
}
pjmedia_frame_ext_pop_subframes(frame, 1);
} else { /* PJMEDIA_FRAME_TYPE_NONE */
buf.iBuffer.Append(0);
buf.iBuffer.Append(0);
frame->samples_cnt = 0;
frame->subframe_cnt = 0;
}
}
break;
case PJMEDIA_FOURCC_ILBC:
{
if (frame->samples_cnt == 0) {
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
strm->play_cb(strm->user_data, 0, strm->play_buf, 0);
pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
}
if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
pjmedia_frame_ext_subframe *sf;
unsigned samples_cnt;
sf = pjmedia_frame_ext_get_subframe(frame, 0);
samples_cnt = frame->samples_cnt / frame->subframe_cnt;
pj_assert((strm->setting.mode == 30 && samples_cnt == 240) ||
(strm->setting.mode == 20 && samples_cnt == 160));
if (sf->data && sf->bitlen) {
buf.iBuffer.Append(1);
buf.iBuffer.Append(0);
buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3);
} else {
buf.iBuffer.Append(0);
buf.iBuffer.Append(0);
}
pjmedia_frame_ext_pop_subframes(frame, 1);
} else { /* PJMEDIA_FRAME_TYPE_NONE */
buf.iBuffer.Append(0);
buf.iBuffer.Append(0);
frame->samples_cnt = 0;
frame->subframe_cnt = 0;
}
}
break;
case PJMEDIA_FOURCC_G711U:
case PJMEDIA_FOURCC_G711A:
{
/* Add header. */
buf.iBuffer.Append(1);
buf.iBuffer.Append(0);
unsigned samples_ready = 0;
unsigned samples_req = aps_g711_frame_len;
/* Assume frame size is 10ms if frame size hasn't been known. */
if (g711_frame_len == 0)
g711_frame_len = 80;
if (samples_req == 0)
samples_req = 80;
/* Call parent stream callback to get samples to play. */
while (samples_ready < g711_frame_len) {
while (samples_ready < samples_req) {
if (frame->samples_cnt == 0) {
frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
strm->play_cb(strm->user_data, 0, strm->play_buf,
strm->samples_per_frame<<1);
strm->play_cb(strm->user_data, 0, strm->play_buf, 0);
pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
@ -786,33 +1001,23 @@ static void PlayCb(TAPSCommBuffer &buf, void *user_data)
sf = pjmedia_frame_ext_get_subframe(frame, 0);
samples_cnt = frame->samples_cnt / frame->subframe_cnt;
if (sf->data && sf->bitlen)
if (sf->data && sf->bitlen) {
buf.iBuffer.Append(1);
buf.iBuffer.Append(0);
buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3);
else {
pj_uint8_t silence_code;
if (strm->setting.format.u32 == PJMEDIA_FOURCC_G711U)
silence_code = pjmedia_linear2ulaw(0);
else
silence_code = pjmedia_linear2alaw(0);
buf.iBuffer.AppendFill(silence_code, samples_cnt);
} else {
buf.iBuffer.Append(0);
buf.iBuffer.Append(0);
}
samples_ready += samples_cnt;
pjmedia_frame_ext_pop_subframes(frame, 1);
} else { /* PJMEDIA_FRAME_TYPE_NONE */
pj_uint8_t silence_code;
if (strm->setting.format.u32 == PJMEDIA_FOURCC_G711U)
silence_code = pjmedia_linear2ulaw(0);
else
silence_code = pjmedia_linear2alaw(0);
buf.iBuffer.AppendFill(silence_code,
g711_frame_len - samples_ready);
samples_ready = g711_frame_len;
buf.iBuffer.Append(0);
buf.iBuffer.Append(0);
samples_ready = samples_req;
frame->samples_cnt = 0;
frame->subframe_cnt = 0;
}
@ -921,8 +1126,16 @@ static pj_status_t sound_open(pjmedia_dir dir,
aps_setting.mode = EALawOr20ms;
}
aps_setting.vad = strm->setting.format.u32==PJMEDIA_FOURCC_L16?
EFalse : strm->setting.vad;
/* Disable VAD on L16 and G711. */
if (strm->setting.format.u32 == PJMEDIA_FOURCC_L16 ||
strm->setting.format.u32 == PJMEDIA_FOURCC_G711U ||
strm->setting.format.u32 == PJMEDIA_FOURCC_G711A)
{
aps_setting.vad = EFalse;
} else {
aps_setting.vad = strm->setting.vad;
}
aps_setting.plc = strm->setting.plc;
aps_setting.cng = strm->setting.cng;
aps_setting.loudspk = strm->setting.loudspk;
@ -957,6 +1170,13 @@ static pj_status_t sound_open(pjmedia_dir dir,
strm->rec_buf = (pj_int16_t*)pj_pool_zalloc(pool, samples_per_frame << 1);
strm->rec_buf_len = 0;
if (strm->setting.format.u32 == PJMEDIA_FOURCC_G729) {
TBitStream *g729_bitstream = new TBitStream;
PJ_ASSERT_RETURN(g729_bitstream, PJ_ENOMEM);
strm->strm_data = (void*)g729_bitstream;
}
// Done.
*p_snd_strm = strm;
return PJ_SUCCESS;
@ -983,7 +1203,6 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index,
pj_bzero(&setting, sizeof(setting));
setting.format.u32 = PJMEDIA_FOURCC_L16;
setting.bitrate = 128000;
return sound_open(PJMEDIA_DIR_CAPTURE, clock_rate, channel_count,
samples_per_frame, bits_per_sample, rec_cb, NULL,
@ -1006,7 +1225,6 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
pj_bzero(&setting, sizeof(setting));
setting.format.u32 = PJMEDIA_FOURCC_L16;
setting.bitrate = 128000;
return sound_open(PJMEDIA_DIR_PLAYBACK, clock_rate, channel_count,
samples_per_frame, bits_per_sample, NULL, play_cb,
@ -1032,7 +1250,6 @@ PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
pj_bzero(&setting, sizeof(setting));
setting.format.u32 = PJMEDIA_FOURCC_L16;
setting.bitrate = 128000;
return sound_open(PJMEDIA_DIR_CAPTURE_PLAYBACK, clock_rate, channel_count,
samples_per_frame, bits_per_sample, rec_cb, play_cb,
@ -1120,6 +1337,12 @@ PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream)
delete stream->engine;
stream->engine = NULL;
if (stream->setting.format.u32 == PJMEDIA_FOURCC_G729) {
TBitStream *g729_bitstream = (TBitStream*)stream->strm_data;
stream->strm_data = NULL;
delete g729_bitstream;
}
pool = stream->pool;
if (pool) {
stream->pool = NULL;

View File

@ -280,18 +280,44 @@ static void on_stream_created(pjsua_call_id call_id,
if (strm_info->type != PJMEDIA_TYPE_AUDIO)
return;
/* Init sound device setting based on stream info. */
pj_bzero(&setting, sizeof(setting));
setting.format = strm_info->param->info.format;
setting.bitrate = strm_info->param->info.avg_bps;
setting.cng = strm_info->param->setting.cng;
setting.vad = strm_info->param->setting.vad;
setting.plc = strm_info->param->setting.plc;
if (setting.format.u32 == PJMEDIA_FOURCC_ILBC) {
unsigned i;
pjmedia_codec_fmtp *fmtp = &strm_info->param->setting.dec_fmtp;
/* Initialize mode. */
setting.mode = 30;
/* Get mode. */
for (i = 0; i < fmtp->cnt; ++i) {
if (pj_stricmp2(&fmtp->param[i].name, "mode") == 0) {
setting.mode = (pj_uint32_t) pj_strtoul(&fmtp->param[i].val);
break;
}
}
}
samples_per_frame = strm_info->param->info.clock_rate *
strm_info->param->info.frm_ptime *
strm_info->param->info.channel_cnt /
1000;
/* Close sound device. */
conf = pjsua_set_no_snd_dev();
/* Reset conference attributes. */
conf->info.samples_per_frame = samples_per_frame;
conf->info.clock_rate = 8000;
conf->info.channel_count = 1;
conf->info.bits_per_sample = 16;
/* Reopen sound device. */
conf = pjsua_set_no_snd_dev();
samples_per_frame = conf->info.samples_per_frame;
status = pjmedia_snd_port_create2(app_pool,
PJMEDIA_DIR_CAPTURE_PLAYBACK,
0,