FreeSwitch使用自带的AMR和AMR-WB编解码器BUG,双方通话协商为AMR或AMR-WB时,通话在录音的情况下可能变为杂音,导致无法正常通话。此问题可以通过修改FreeSwitch处理相关的代码来解决。
本文章是在FreeSwitch 1.10.9的代码基础上进行的改动,过早的版本,或者过于未来的版本可能有改动,如果时间不长应该大同小异。
- 问题原因
- 解决方案
- 代码改动
1.问题原因:
AMR和AMR-WB在RTP传输过程中,有两种模式:
字节对齐模式(OA)
带宽效率模式(BE)
当协商为带宽效率模式(BE)时,会对每个RTP报文的CMR后面的F、FT、Q位进行位移。
BE模式
OA模式
FreeSwitch在解码AMR和AMR-WB编码部分直接对读取到的switch_frame_t结构体内的数据进行unpack操作,对于将要传输的原始数据进行了位移修改,导致了被叫侧发出去的数据是被位移函数破坏的数据,产生了杂音。
mod_amr.c的switch_amr_decode函数:
static switch_status_t switch_amr_decode(switch_codec_t *codec,
switch_codec_t *other_codec,
void *encoded_data,
uint32_t encoded_data_len,
uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate,
unsigned int *flag)
{
#ifdef AMR_PASSTHROUGH
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This codec is only usable in passthrough mode!\n");
return SWITCH_STATUS_FALSE;
#else
struct amr_context *context = codec->private_info;
//此处直接将编码数据赋值给buf指针进行操作,造成了数据破坏
unsigned char *buf = encoded_data;
uint8_t tmp[SWITCH_AMR_OUT_MAX_SIZE];
if (!context) {
return SWITCH_STATUS_FALSE;
}
if (globals.debug) {
switch_amr_info(codec, buf, encoded_data_len, switch_test_flag(context, AMR_OPT_OCTET_ALIGN) ? 1 : 0, "AMR decoder");
}
if (switch_test_flag(context, AMR_OPT_OCTET_ALIGN)) {
/* Octed Aligned */
if (!switch_amr_unpack_oa(buf, tmp, encoded_data_len)) {
return SWITCH_STATUS_FALSE;
}
} else {
/* Bandwidth Efficient */
if (!switch_amr_unpack_be(buf, tmp, encoded_data_len)) {
return SWITCH_STATUS_FALSE;
}
}
Decoder_Interface_Decode(context->decoder_state, tmp, (int16_t *) decoded_data, 0);
*decoded_data_len = codec->implementation->decoded_bytes_per_packet;
return SWITCH_STATUS_SUCCESS;
#endif
}
mod_amrwb.c的switch_amrwb_decode函数:
static switch_status_t switch_amrwb_decode(switch_codec_t *codec,
switch_codec_t *other_codec,
void *encoded_data,
uint32_t encoded_data_len,
uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate,
unsigned int *flag)
{
#ifdef AMRWB_PASSTHROUGH
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This codec is only usable in passthrough mode!\n");
return SWITCH_STATUS_FALSE;
#else
struct amrwb_context *context = codec->private_info;
//此处直接将编码数据赋值给buf指针进行操作,造成了数据破坏
unsigned char *buf = encoded_data;
uint8_t tmp[SWITCH_AMRWB_OUT_MAX_SIZE];
if (!context) {
return SWITCH_STATUS_FALSE;
}
if (globals.debug) {
switch_amrwb_info(codec, buf, encoded_data_len, switch_test_flag(context, AMRWB_OPT_OCTET_ALIGN) ? 1 : 0, "AMRWB decoder");
}
if (switch_test_flag(context, AMRWB_OPT_OCTET_ALIGN)) {
/* Octed Aligned */
if (!switch_amrwb_unpack_oa(buf, tmp, encoded_data_len)) {
return SWITCH_STATUS_FALSE;
}
} else {
/* Bandwidth Efficient */
if (!switch_amrwb_unpack_be(buf, tmp, encoded_data_len)) {
return SWITCH_STATUS_FALSE;
}
}
D_IF_decode(context->decoder_state, tmp, (int16_t *) decoded_data, 0);
*decoded_data_len = codec->implementation->decoded_bytes_per_packet;
return SWITCH_STATUS_SUCCESS;
#endif
}
2.解决方案:
增加一个临时的buffer数组,将编码数据复制到buffer中,使解码操作只操作临时的buffer,不对原始数据造成影响。
3.代码改动:
将第一步的代码改为如下代码
mod_amr.c的switch_amr_decode函数:
static switch_status_t switch_amr_decode(switch_codec_t *codec,
switch_codec_t *other_codec,
void *encoded_data,
uint32_t encoded_data_len,
uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate,
unsigned int *flag)
{
#ifdef AMR_PASSTHROUGH
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This codec is only usable in passthrough mode!\n");
return SWITCH_STATUS_FALSE;
#else
struct amr_context *context = codec->private_info;
//此处直接将编码数据赋值给buf指针进行操作,造成了数据破坏
unsigned char *buf = encoded_data;
uint8_t tmp[SWITCH_AMR_OUT_MAX_SIZE];
uint8_t tmp_enc[128] = {0};
if (!context) {
return SWITCH_STATUS_FALSE;
}
if (encoded_data_len > 128) {
return SWITCH_STATUS_FALSE;
}
memcpy(tmp_enc, buf, encoded_data_len);
if (globals.debug) {
switch_amr_info(codec, buf, encoded_data_len, switch_test_flag(context, AMR_OPT_OCTET_ALIGN) ? 1 : 0, "AMR decoder");
}
if (switch_test_flag(context, AMR_OPT_OCTET_ALIGN)) {
/* Octed Aligned */
if (!switch_amr_unpack_oa(tmp_enc, tmp, encoded_data_len)) {
return SWITCH_STATUS_FALSE;
}
} else {
/* Bandwidth Efficient */
if (!switch_amr_unpack_be(tmp_enc, tmp, encoded_data_len)) {
return SWITCH_STATUS_FALSE;
}
}
Decoder_Interface_Decode(context->decoder_state, tmp, (int16_t *) decoded_data, 0);
*decoded_data_len = codec->implementation->decoded_bytes_per_packet;
return SWITCH_STATUS_SUCCESS;
#endif
}
mod_amrwb.c的switch_amrwb_decode函数:
static switch_status_t switch_amrwb_decode(switch_codec_t *codec,
switch_codec_t *other_codec,
void *encoded_data,
uint32_t encoded_data_len,
uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate,
unsigned int *flag)
{
#ifdef AMRWB_PASSTHROUGH
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This codec is only usable in passthrough mode!\n");
return SWITCH_STATUS_FALSE;
#else
struct amrwb_context *context = codec->private_info;
//此处直接将编码数据赋值给buf指针进行操作,造成了数据破坏
unsigned char *buf = encoded_data;
uint8_t tmp[SWITCH_AMRWB_OUT_MAX_SIZE];
uint8_t tmp_enc[128] = {0};
if (!context) {
return SWITCH_STATUS_FALSE;
}
if (globals.debug) {
switch_amrwb_info(codec, buf, encoded_data_len, switch_test_flag(context, AMRWB_OPT_OCTET_ALIGN) ? 1 : 0, "AMRWB decoder");
}
if (encoded_data_len > 128) {
return SWITCH_STATUS_FALSE;
}
memcpy(tmp_enc, buf, encoded_data_len);
if (switch_test_flag(context, AMRWB_OPT_OCTET_ALIGN)) {
/* Octed Aligned */
if (!switch_amrwb_unpack_oa(tmp_enc, tmp, encoded_data_len)) {
return SWITCH_STATUS_FALSE;
}
} else {
/* Bandwidth Efficient */
if (!switch_amrwb_unpack_be(tmp_enc, tmp, encoded_data_len)) {
return SWITCH_STATUS_FALSE;
}
}
D_IF_decode(context->decoder_state, tmp, (int16_t *) decoded_data, 0);
*decoded_data_len = codec->implementation->decoded_bytes_per_packet;
return SWITCH_STATUS_SUCCESS;
#endif
}
改动后,重新编译即可正常。