- 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:
parent
4906604fd7
commit
bfa860b133
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -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_;
|
||||
for (i = 0; i < input->subframe_cnt; ++i) {
|
||||
pjmedia_frame_ext_subframe *sf;
|
||||
pjmedia_codec_amr_bit_info *info;
|
||||
unsigned len;
|
||||
|
||||
info_ = *((pj_uint16_t*)r);
|
||||
eof = ((info_ & 0x40) != 0);
|
||||
sf = pjmedia_frame_ext_get_subframe(input, i);
|
||||
|
||||
info = (pjmedia_codec_amr_bit_info*) &frames[nframes].bit_info;
|
||||
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,29 +785,29 @@ 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),
|
||||
|
|
|
@ -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->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,
|
||||
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,
|
||||
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,7 +413,6 @@ 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;
|
||||
|
||||
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)
|
||||
|
@ -678,6 +682,9 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf,
|
|||
--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)",
|
||||
src_slot,
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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,20 +696,98 @@ 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_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, 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);
|
||||
frame->samples_cnt = 0;
|
||||
frame->subframe_cnt = 0;
|
||||
}
|
||||
}
|
||||
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.
|
||||
/* 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;
|
||||
|
@ -717,13 +796,13 @@ static void RecCb(TAPSCommBuffer &buf, void *user_data)
|
|||
}
|
||||
|
||||
/* 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.
|
||||
* 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;
|
||||
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);
|
||||
|
@ -738,6 +817,7 @@ static void RecCb(TAPSCommBuffer &buf, void *user_data)
|
|||
frame->subframe_cnt = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -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,11 @@ static void PlayCb(TAPSCommBuffer &buf, void *user_data)
|
|||
buf.iBuffer.Zero();
|
||||
|
||||
switch(strm->setting.format.u32) {
|
||||
case PJMEDIA_FOURCC_G711U:
|
||||
case PJMEDIA_FOURCC_G711A:
|
||||
case PJMEDIA_FOURCC_AMR:
|
||||
{
|
||||
/* Add header. */
|
||||
buf.iBuffer.Append(1);
|
||||
buf.iBuffer.Append(0);
|
||||
|
||||
/* Assume frame size is 10ms if frame size hasn't been known. */
|
||||
if (g711_frame_len == 0)
|
||||
g711_frame_len = 80;
|
||||
|
||||
/* Call parent stream callback to get samples to play. */
|
||||
while (samples_ready < g711_frame_len) {
|
||||
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 +852,172 @@ 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) {
|
||||
/* 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 {
|
||||
pj_uint8_t silence_code;
|
||||
} else {
|
||||
buf.iBuffer.Append(0);
|
||||
buf.iBuffer.Append(0);
|
||||
}
|
||||
|
||||
if (strm->setting.format.u32 == PJMEDIA_FOURCC_G711U)
|
||||
silence_code = pjmedia_linear2ulaw(0);
|
||||
else
|
||||
silence_code = pjmedia_linear2alaw(0);
|
||||
pjmedia_frame_ext_pop_subframes(frame, 1);
|
||||
|
||||
buf.iBuffer.AppendFill(silence_code, samples_cnt);
|
||||
} 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:
|
||||
{
|
||||
unsigned samples_ready = 0;
|
||||
unsigned samples_req = aps_g711_frame_len;
|
||||
|
||||
/* Assume frame size is 10ms if frame size hasn't been known. */
|
||||
if (samples_req == 0)
|
||||
samples_req = 80;
|
||||
|
||||
/* Call parent stream callback to get samples to play. */
|
||||
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, 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) {
|
||||
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);
|
||||
}
|
||||
samples_ready += samples_cnt;
|
||||
|
||||
pjmedia_frame_ext_pop_subframes(frame, 1);
|
||||
|
||||
} else { /* PJMEDIA_FRAME_TYPE_NONE */
|
||||
pj_uint8_t silence_code;
|
||||
buf.iBuffer.Append(0);
|
||||
buf.iBuffer.Append(0);
|
||||
|
||||
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;
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
/* Reopen sound device. */
|
||||
/* 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();
|
||||
|
||||
samples_per_frame = conf->info.samples_per_frame;
|
||||
/* 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. */
|
||||
status = pjmedia_snd_port_create2(app_pool,
|
||||
PJMEDIA_DIR_CAPTURE_PLAYBACK,
|
||||
0,
|
||||
|
|
Loading…
Reference in New Issue