https://blog.csdn.net/wirelessdisplay/article/details/77801825
官网http://www.opus-codec.org/
opus是一个有损声音编码的格式,由IETF开发,没有任何专利或限制,适用于网络上的实时声音传输,标准格式为RFC 6716,其技术来源于Skype的SILK及Xiph.Org的CELT编码
主要特性如下:
基于OPUS强大的PLC能力以及良好的VOIP音质, 我们决定在我们的视频会议中引入OPUS编码,用于Android/iOS/Windows视频会议客户端,以及视频会议媒体服务器中.
本文主要介绍OPUS在服务器端的引入过程,内容涵盖下载编译/SIP SDP/RTP/编解码
我司的会议服务器使用的CentOS7.2 64bit的开发环境, 以下就以此环境做例子说明
使用当前最新的1.2.1版本下载编译:
wget https://archive.mozilla.org/pub/opus/opus-1.2.1.tar.gz
tar -zxf opus-1.2.1.tar.gz
cd opus-1.2.1
./configure --prefix=$your_install_dir
make; make install
编译完之后, 在$your_install_dir目录下就有存在这三个文件夹,后续将此三个文件夹导入我们的服务器代码工程总:include lib share
我司的会议系统支持IMS/SIP, 所以Android/iOS/Windows客户端都是基于IMS/SIP做信令呼叫协议, 所以需要在SIP/SDP中支持OPUS
参考RFC7587,
其中 101是payload type值, 这个值没有保留值,可以在35-127之间随意取值, 只要和其他codec不冲突即可;48000是采样率, opus支持8k, 12k,16k,24k和48k, 2为双声道.useinbandfec为是否启用opus的内置的plc算法
RTP的封包可以参考RFC7587,没有什么特殊性,主要是ts的打包需要注意下
OPUS支持的包间隔从20ms到120ms, 视频会议对实时性要求比较高, 所以我们采用的20ms, 48k采样率的ts递增值为960
解码器初始化:
pDecoder = opus_decoder_create(sample_rate_, channels_, &error);
if (OPUS_OK != error || NULL == pDecoder)
{
TRACE_ERR("Could not open opus decoder codec %d", error);
return false;
}
解码:
if (NULL == data || 0 == size)
{
outputSamples = opus_decode(pDecoder,
NULL,
0,
pDecodedData,
frame_size_,
1);
}
else
{
outputSamples = opus_decode(pDecoder,
(const unsigned char*)data,
size,
pDecodedData,
frame_size_,
0);
}
输入的数据是RTP payload数据解码, 如果是丢包, 则输入的NULL指针. 由于我们采用的inbandfec,所有丢包情况下最后一个参数是1. frame_size_为48k/50=980
输出的数据在data中,格式为PCM S16.
初始化:
OpusEncoder* opusEncoder = (OpusEncoder*)opus_encoder_;
int error;
opusEncoder = opus_encoder_create (sample_rate_, channels_, OPUS_APPLICATION_VOIP, &error);
if (error != OPUS_OK || opusEncoder == NULL)
{
TRACE_ERR("create opus encoder failed %d", error);
return false;
}
opus_encoder_ctl(opusEncoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
opus_encoder_ctl(opusEncoder, OPUS_SET_BITRATE(OPUS_AUTO)); ///使用AUTO主要是应为视频会议过程中,大部分场景是不说话的,减少带宽占用
opus_encoder_ctl(opusEncoder, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND));
opus_encoder_ctl(opusEncoder, OPUS_SET_VBR(1));//0:CBR, 1:VBR
opus_encoder_ctl(opusEncoder, OPUS_SET_VBR_CONSTRAINT(0));//0:Unconstrained VBR., 1:Constrained VBR.
opus_encoder_ctl(opusEncoder, OPUS_SET_COMPLEXITY(5));//range:0~10
opus_encoder_ctl(opusEncoder, OPUS_SET_FORCE_CHANNELS(channels_)); //1:Forced mono, 2:Forced stereo
opus_encoder_ctl(opusEncoder, OPUS_SET_APPLICATION(OPUS_APPLICATION_VOIP));//
opus_encoder_ctl(opusEncoder, OPUS_SET_INBAND_FEC(1));//0:Disable, 1:Enable
opus_encoder_ctl(opusEncoder, OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_20_MS));
编码:
outputLen = opus_encode(pEncoder, (const opus_int16*)data, frame_size_, pkt.data, MAX_DECODED_BUFFER_LENGTH);
if (outputLen <= 0)
{
MEDIA_TRACE_ERR("opus encode failed %d", outputLen);
return outputLen;
}
输入的是PCM S16数据,输出则为OPUS包, 一个RTP封包就够了
如果调试过程中,声音不正常, 这需要定位问题. 声音不正常, 主要需要确认: 终端发过来的流/RTP收包部分/解码部分/重采样/混音部分/编码部分/发包部分
定位问题,需要在每个地方都打桩写文件来确认.
文件写下来之后, PCM数据只是使用专业软件来听(我司使用的GoldWave); OPUS数据的话,可以使用opus_demo(在上面所述的libopus代码中)来解码再听,写opus文件的时候,需要注意格式(加8字节的头).
使用OPUS之后, 效果尤其是公网上传输的效果比较好, 30%以内的丢包, 都能够保证语音能够正常听清楚
技术交流有兴趣请加:
音视频技术交流群:308601278
无线投屏技术交流群:582349005
我司有成熟的视频会议/视频监控/视频调度/无线投屏盒子销售,也可做音视频相关产品和技术的定制化开发
有需要可发邮件[email protected]