因做语音在线识别就了解一下pcm做opus压缩。
opus是一种声音编码格式,Opus的前身是celt编码器。是由IETF开发,适用于网络上的实时声音传输,标准格式为RFC 6716。这是一种有损的声音编码格式。
#一、首先获取opus压缩的iOS库
先从GitHub下载国外大神Opus-iOS编译工程,然后修改需要运行的脚本文件 (build-libopus.sh)
##1、一般需要修改这几个地方
VERSION=”1.3” ##当前编译opus版本基本上不用修改
SDKVERSION=”10.2” ##你用的xcode sdk版本
MINIOSVERSION=”8.0” ##最低支持的iOS系统
##2、在 build-libopus.sh 文件中107行 的脚本代码 EXTRA_CONFIG=”” 修改为 EXTRA_CONFIG=”–host=x86_64” 注意,这个地方是和电脑的CPU相关的
##3、最后编译脚本生成需要的库;
$ ./build-libopus.sh
运行成功下面文件夹里面的就是需要的库,至于怎么放进工程随自己喜好。
#二、获取pcm(纯语音数据)语音数据
获取pcm格式的语音数据,这里的pcm数据的采样率要注意要和后面opus压缩的采样率一样。
我们做的语音在线识别,边录音变上传边识别。用的
{
//如果录音时同时需要播放媒体,那么必须加上这两行代码
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
memset(&_recordFormat, 0, sizeof(_recordFormat));
_recordFormat.mSampleRate = kDefaultSampleRate;
_recordFormat.mChannelsPerFrame = 1;
_recordFormat.mFormatID = kAudioFormatLinearPCM;
_recordFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
_recordFormat.mBitsPerChannel = 16;
_recordFormat.mBytesPerPacket = _recordFormat.mBytesPerFrame = (_recordFormat.mBitsPerChannel / 8) * _recordFormat.mChannelsPerFrame;
_recordFormat.mFramesPerPacket = 1;
//初始化音频输入队列
AudioQueueNewInput(&_recordFormat, inputBufferHandler, (__bridge void *)(self), NULL, NULL, 0, &_audioQueue);
//计算估算的缓存区大小
int frames = (int)ceil(kDefaultBufferDurationSeconds * _recordFormat.mSampleRate);
int bufferByteSize = frames * _recordFormat.mBytesPerFrame;
//创建缓冲器
for (int i = 0; i < QUEUE_BUFFER_SIZE; i++){
AudioQueueAllocateBuffer(_audioQueue, bufferByteSize, &_audioBuffers[i]);
AudioQueueEnqueueBuffer(_audioQueue, _audioBuffers[i], 0, NULL);
}
}
#三、opus压缩pcm
认识几个参数
##1、设置参数
enc = opus_encoder_create(kDefaultSampleRate, 1, OPUS_APPLICATION_VOIP, &error);//(采样率,声道数,,)
dec = opus_decoder_create(kDefaultSampleRate, 1, &error);
opus_encoder_ctl(enc, OPUS_SET_BITRATE(kDefaultSampleRate));//比特率
opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_AUTO));//OPUS_BANDWIDTH_NARROWBAND 宽带窄带
opus_encoder_ctl(enc, OPUS_SET_VBR(0));
opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(1));
opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(8));//录制质量 1-10
opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(0));
opus_encoder_ctl(enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));//信号
##2、压缩函数的函数以及返回值
//传入要压缩的数据
-(NSData *)encodePCMData:(NSData*)data{
return [self encode:(short *)[data bytes] length:[data length]];
}
-(NSData *)encode:(short *)pcmBuffer length:(NSInteger)lengthOfShorts{
opus_int16 *PCMPtr = pcmBuffer;
int PCMSize = (int)lengthOfShorts / sizeof(opus_int16);
opus_int16 *PCMEnd = PCMPtr + PCMSize;
NSMutableData *mutData = [NSMutableData data];
unsigned char encodedPacket[MAX_PACKET_BYTES];
// 记录opus块大小
OPUS_DATA_SIZE_T encodedBytes = 0;
while (PCMPtr + WB_FRAME_SIZE < PCMEnd) {
encodedBytes = opus_encode(enc, PCMPtr, WB_FRAME_SIZE, encodedPacket, MAX_PACKET_BYTES);
if (encodedBytes <= 0) {
return nil;
}
// 大端转小端 这个要根据解析要求是大段还是小段存储
// encodedBytes = CFSwapInt32HostToBig(encodedBytes)
// 保存opus块大小
[mutData appendBytes:&encodedBytes length:sizeof(encodedBytes)];
// 保存opus数据
[mutData appendBytes:encodedPacket length:encodedBytes];
PCMPtr += WB_FRAME_SIZE;
}
return mutData.length > 0 ? mutData : nil;
}
##3、解压函数的函数以及返回值
-(NSData )decodeOpusData:(NSData)data{
unsigned char opusPtr = (unsigned char )data.bytes;
int opusSize = (int)data.length;
unsigned char opusEnd = opusPtr + opusSize;
NSMutableData mutData = [[NSMutableData alloc] init];
opus_int16 decodedPacket[MAX_PACKET_BYTES];
int decodedSamples = 0;
// 保存opus块大小的数据
OPUS_DATA_SIZE_T nBytes = 0;
while (opusPtr < opusEnd) {
// 取出opus块大小的数据
nBytes = (OPUS_DATA_SIZE_T )opusPtr;
opusPtr += sizeof(nBytes);
decodedSamples = opus_decode(dec, opusPtr, nBytes, decodedPacket, MAX_PACKET_BYTES, 0);
if (decodedSamples <= 0) {
return nil;
}
[mutData appendBytes:decodedPacket length:decodedSamples * sizeof(opus_int16)];
opusPtr += nBytes;
}
return mutData.length > 0 ? mutData : nil;
}
以上是opus的压缩和解压,数据处理看自己需要;
这是自己的拙见,欢迎指正,🙏。