Opus是一个有损声音编码的格式,由Xiph.Org基金会开发,之后由互联网工程任务组(IETF)进行标准化,目标用希望用单一格式包含声音和语音,取代Speex和Vorbis,且适用于网络上低延迟的即时声音传输,标准格式定义于RFC 6716文件。Opus格式是一个开放格式,使用上没有任何专利或限制。
Opus集成了两种声音编码的技术:以语音编码为导向的SILK和低延迟的CELT。Opus可以无缝调节高低比特率。在编码器内部它在较低比特率时使用线性预测编码在高比特率时候使用变换编码(在高低比特率交界处也使用两者结合的编码方式)。Opus具有非常低的算法延迟(默认为22.5 ms),非常适合用于低延迟语音通话的编码,像是网络上的即时声音流、即时同步声音旁白等等,此外Opus也可以通过降低编码比特率,达成更低的算法延迟,最低可以到5 ms。在多个听觉盲测中,Opus都比MP3、AAC、HE-AAC等常见格式,有更低的延迟和更好的声音压缩率。
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusDecoder *opus_decoder_create(
opus_int32 Fs,//采样率,可以设置的大小为8000, 12000, 16000, 24000, 48000.
int channels,//声道数,网络中实时音频一般为单声道
int *error//是否创建失败,返回0表示创建成功
);
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decode(
OpusDecoder *st, //上一步的返回值
const unsigned char *data,//要解码的数据
opus_int32 len, //数据长度
opus_int16 *pcm, //解码后的数据,注意是一个以16位长度为基本单位的数组
int frame_size, //每个声道给pcm数组的长度
int decode_fec //是否需要fec,设置0为不需要,1为需要
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
//注意是大端传入,所以应该转为小端
//注:frameSize是上一步的返回值,即每个声道返回的以2字节为单位的数组长度
//pcm是解码后的数据,即上一步的参数4
char *pcmData = new char[frameSize * channels * sizeof(opus_int16)];
for (int i = 0; i < channels * frameSize; ++i)
{
pcmData[i * 2] = pcm[i] & 0xFF;
pcmData[i * 2 + 1] = (pcm[i] >> 8) & 0xFF;
}
OPUS_EXPORT void opus_decoder_destroy(OpusDecoder *st);
class HandlerOpusImpl
{
public:
//在构造函数中创建解码器
HandlerOpusImpl() : _perSecNum(0)
{
int sampleRate = 48000;
int channels = 1;
int err;
_decoder = opus_decoder_create(sampleRate, channels, &err);
}
//传入要解码的数据srcData, 以string类型返回解码后的数据result
bool DecodeData(std::string srcData, std::string &result)
{
int frameSize;
int channels = CHANNELS;
int sampleRate = SAMPLE_RATE;
opus_int16 *out;
char *pcmData;
out = new opus_int16[SAMPLE_RATE / 50 * CHANNELS];
//解码,如果frameSize小于0,那么说明解码失败
frameSize = opus_decode(_decoder, (const unsigned char *)srcData.data(), srcData.size(), out, SAMPLE_RATE / 50 * CHANNELS, 0);
if (frameSize <= 0)
{
return false;
}
pcmData = new char[frameSize * channels * sizeof(opus_int16)];
++_perSecNum;
for (int i = 0; i < channels * frameSize; ++i)
{
pcmData[i * 2] = out[i] & 0xFF;
pcmData[i * 2 + 1] = (out[i] >> 8) & 0xFF;
}
//把数据赋值给result
std::string(pcmData, sizeof(opus_int16) * channels * frameSize).swap(result);
delete[]out;
delete[]pcmData;
return true;
}
//使用fec找回丢失的包,函数内容获取解码数据函数内容类似,不过fec标记位需要设置为1
bool HandleLosePack(std::string srcData, std::string &result)
{
int frameSize;
int channels = CHANNELS;
int sampleRate = SAMPLE_RATE;
opus_int16 *out;
char *pcmData;
out = new opus_int16[SAMPLE_RATE / 50 * CHANNELS];
frameSize = opus_decode(_decoder, NULL, 0, out, SAMPLE_RATE / 50 * CHANNELS, 1);
if (frameSize <= 0)
{
return false;
}
pcmData = new char[frameSize * channels * sizeof(opus_int16)];
++_perSecNum;
for (int i = 0; i < channels * frameSize; ++i)
{
pcmData[i * 2] = out[i] & 0xFF;
pcmData[i * 2 + 1] = (out[i] >> 8) & 0xFF;
}
std::string(pcmData, sizeof(opus_int16) * channels * frameSize).swap(result);
delete[]out;
delete[]pcmData;
return true;
}
void DeleteCodec()
{
opus_decoder_destroy(_decoder);
_decoder = NULL;
}
private:
int _perSecNum;
OpusDecoder *_decoder;
};
opus的使用并不复杂,不过网络上资料有限,靠着大哥们之前写过的代码才稍微理解了一些,希望可以给大家一些帮助,上面的代码是程序的一部分,并不能直接使用