音频编码
- 根据音频输入和输出参数创建编码器, 并设置一些编码参数
- 根据待编码的数据, 创建一个AudioBufferList, 根据创建的AudioBufferList编码(编码中的回调函数是编码过程中不断用来填充参数的的函数)
- 将编码结果回调或者发送出去
[关键代码如下] :
// - 创建编码器
-(void)open{
/* - 输入的音频参数, 可以通过 CMSampleBufferRef 直接取到;
AudioStreamBasicDescription inputAduioDes = *CMAudioFormatDescriptionGetStreamBasicDescription(CMSampleBufferGetFormatDescription(sampleBuffer));
*/
AudioStreamBasicDescription inputAudioDes = {
.mFormatID = kAudioFormatLinearPCM,
.mSampleRate = self.audioConfig.sampleRate,
.mBitsPerChannel = (uint32_t)self.audioConfig.sampleSize,
.mFramesPerPacket = 1,
.mBytesPerFrame = 2,
.mBytesPerPacket = 2,
.mChannelsPerFrame = (uint32_t)self.audioConfig.channelCount,
.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsNonInterleaved,
.mReserved = 0
};
// - 输出音频参数
AudioStreamBasicDescription outputAudioDes = {
.mSampleRate = (Float64)self.audioConfig.sampleRate, //采样率
.mFormatID = kAudioFormatMPEG4AAC, //输出格式
.mFormatFlags = kMPEG4Object_AAC_LC, // 如果设为0 代表无损编码
.mBytesPerPacket = 0, //自己确定每个packet 大小
.mFramesPerPacket = 1024, //每一个packet帧数 AAC-1024;
.mBytesPerFrame = 0, //每一帧大小
.mChannelsPerFrame = (uint32_t)self.audioConfig.channelCount, //输出声道数
.mBitsPerChannel = 0, //数据帧中每个通道的采样位数。
.mReserved = 0 //对其方式 0(8字节对齐)
};
// - 填充输出的相关参数 测试 如果outputAudioDes 参数填充完全的话, 是不需要调用下边的函数的
// UInt32 outDesSize = sizeof(outputAudioDes);
// AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &outDesSize, &outputAudioDes);
// - 创建编码器
/* 此功能与AudioConverterNewSpecific功能相同,不同之处在于,AudioConverterNewSpecific可以显式选择要实例化的编解码器, 而 AudioConverterNew 不可以选择编码器
// - 使用软编码
AudioClassDescription *audioClassDesc = [self getAudioCalssDescriptionWithType:outputAudioDes.mFormatID fromManufacture:kAppleSoftwareAudioCodecManufacturer];
OSStatus status = AudioConverterNewSpecific(&inputAduioDes, &outputAudioDes, 1, audioClassDesc, &_audioConverter);
*/
OSStatus status = AudioConverterNew(&inputAudioDes, &outputAudioDes, &_aConverter);
if (status != noErr) {
[self onErrorWithCode:AWEncoderErrorCodeCreateAudioConverterFailed des:@"硬编码AAC创建失败"];
}
//设置码率
uint32_t aBitrate = (uint32_t)self.audioConfig.bitrate;
uint32_t aBitrateSize = sizeof(aBitrate);
status = AudioConverterSetProperty(_aConverter, kAudioConverterEncodeBitRate, aBitrateSize, &aBitrate);
//查询最大输出
uint32_t aMaxOutput = 0;
uint32_t aMaxOutputSize = sizeof(aMaxOutput);
AudioConverterGetProperty(_aConverter, kAudioConverterPropertyMaximumOutputPacketSize, &aMaxOutputSize, &aMaxOutput);
self.aMaxOutputFrameSize = aMaxOutput;
if (aMaxOutput == 0) {
[self onErrorWithCode:AWEncoderErrorCodeAudioConverterGetMaxFrameSizeFailed des:@"AAC 获取最大frame size失败"];
}
}
// - 编码音频参数
-(void)encodeSample:(CMSampleBuffer *)sampleBuffer{
CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
size_t pcmDataLen;
uint8_t *pcmData;
OSStatus status = CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, &pcmDataLen, &pcmData);
self.curFramePcmData = [NSData dataWithBytes:pcmData length:pcmDataLen];
// - 赋值outAudioBufferList
AudioBufferList outAudioBufferList = {0};
outAudioBufferList.mNumberBuffers = 1;
outAudioBufferList.mBuffers[0].mNumberChannels = (uint32_t)self.audioConfig.channelCount;
outAudioBufferList.mBuffers[0].mDataByteSize = self.aMaxOutputFrameSize;
outAudioBufferList.mBuffers[0].mData = malloc(self.aMaxOutputFrameSize);
UInt32 outputDataPacketSize = 1;
//配置填充函数,获取输出数据
//转换由输入回调函数提供的数据
/*
参数1: inAudioConverter 音频转换器
参数2: inInputDataProc 回调函数.提供要转换的音频数据的回调函数。当转换器准备好接受新的输入数据时,会重复调用此回调.
参数3: inInputDataProcUserData, 传给回调函数的参数
参数4: ioOutputDataPacketSize,输出缓冲区的大小
参数5: outOutputData,编码后输出的数据
参数6: outPacketDescription,输出包描述
*/
OSStatus status = AudioConverterFillComplexBuffer(_aConverter, aacEncodeInputDataProc, (__bridge void * _Nullable)(self), &outputDataPacketSize, &outAudioBufferList, NULL);
if (status == noErr) {
NSData *rawAAC = [NSData dataWithBytesNoCopy: outAudioBufferList.mBuffers[0].mData length:outAudioBufferList.mBuffers[0].mDataByteSize];
self.manager.timestamp += 1024 * 1000 / self.audioConfig.sampleRate;
}else{
[self onErrorWithCode:AWEncoderErrorCodeAudioEncoderFailed des:@"aac 编码错误"];
}
}
// - 回调函数(不断填充数据)
static OSStatus aacEncodeInputDataProc(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData){
AWHWAACEncoder *hwAacEncoder = (__bridge AWHWAACEncoder *)inUserData;
if (hwAacEncoder.curFramePcmData) {
// - 填充需要编码的数据
ioData->mBuffers[0].mData = (void *)hwAacEncoder.curFramePcmData.bytes;
ioData->mBuffers[0].mDataByteSize = (uint32_t)hwAacEncoder.curFramePcmData.length;
ioData->mNumberBuffers = 1;
ioData->mBuffers[0].mNumberChannels = (uint32_t)hwAacEncoder.audioConfig.channelCount;
return noErr;
}
return -1;
}
音频解码
- 根据音频输入和输出参数创建解码器, 并设置一些解码参数
- 根据待解码码的数据, 创建一个AudioBufferList, 根据创建的AudioBufferList解码(解码中的回调函数是解码过程中不断用来填充参数的的函数)
- 将解码结果回调或者发送出去
[关键代码如下] :
// - 创建解码器
setupEncoder {
//输出参数pcm
AudioStreamBasicDescription outputAudioDes = {0};
outputAudioDes.mSampleRate = (Float64)_config.sampleRate; //采样率
outputAudioDes.mChannelsPerFrame = (UInt32)_config.channelCount; //输出声道数
outputAudioDes.mFormatID = kAudioFormatLinearPCM; //输出格式
outputAudioDes.mFormatFlags = (kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked); //编码 12
outputAudioDes.mFramesPerPacket = 1; //每一个packet帧数 ;
outputAudioDes.mBitsPerChannel = 16; //数据帧中每个通道的采样位数。
outputAudioDes.mBytesPerFrame = outputAudioDes.mBitsPerChannel / 8 *outputAudioDes.mChannelsPerFrame; //每一帧大小(采样位数 / 8 *声道数)
outputAudioDes.mBytesPerPacket = outputAudioDes.mBytesPerFrame * outputAudioDes.mFramesPerPacket; //每个packet大小(帧大小 * 帧数)
outputAudioDes.mReserved = 0; //对其方式 0(8字节对齐)
//输入参数aac
AudioStreamBasicDescription inputAduioDes = {0};
inputAduioDes.mSampleRate = (Float64)_config.sampleRate;
inputAduioDes.mFormatID = kAudioFormatMPEG4AAC;
inputAduioDes.mFormatFlags = kMPEG4Object_AAC_LC;
inputAduioDes.mFramesPerPacket = 1024;
inputAduioDes.mChannelsPerFrame = (UInt32)_config.channelCount;
//填充输出相关信息
UInt32 inDesSize = sizeof(inputAduioDes);
AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &inDesSize, &inputAduioDes);
//获取解码器的描述信息(只能传入software)
AudioClassDescription *audioClassDesc = [self getAudioCalssDescriptionWithType:outputAudioDes.mFormatID fromManufacture:kAppleSoftwareAudioCodecManufacturer];
/** 创建converter
参数1:输入音频格式描述
参数2:输出音频格式描述
参数3:class desc的数量
参数4:class desc
参数5:创建的解码器
*/
OSStatus status = AudioConverterNewSpecific(&inputAduioDes, &outputAudioDes, 1, audioClassDesc, &_audioConverter);
if (status != noErr) {
NSLog(@"Error!:硬解码AAC创建失败, status= %d", (int)status);
return;
}
}
/**
获取解码器类型描述
参数1:类型
*/
- (AudioClassDescription *)getAudioCalssDescriptionWithType: (AudioFormatID)type fromManufacture: (uint32_t)manufacture {
static AudioClassDescription desc;
UInt32 decoderSpecific = type;
//获取满足AAC解码器的总大小
UInt32 size;
/**
参数1:编码器类型(解码)
参数2:类型描述大小
参数3:类型描述
参数4:大小
*/
OSStatus status = AudioFormatGetPropertyInfo(kAudioFormatProperty_Decoders, sizeof(decoderSpecific), &decoderSpecific, &size);
if (status != noErr) {
NSLog(@"Error!:硬解码AAC get info 失败, status= %d", (int)status);
return nil;
}
//计算aac解码器的个数
unsigned int count = size / sizeof(AudioClassDescription);
//创建一个包含count个解码器的数组
AudioClassDescription description[count];
//将满足aac解码的解码器的信息写入数组
status = AudioFormatGetProperty(kAudioFormatProperty_Encoders, sizeof(decoderSpecific), &decoderSpecific, &size, &description);
if (status != noErr) {
NSLog(@"Error!:硬解码AAC get propery 失败, status= %d", (int)status);
return nil;
}
for (unsigned int i = 0; i < count; i++) {
if (type == description[i].mSubType && manufacture == description[i].mManufacturer) {
desc = description[i];
return &desc;
}
}
return nil;
}
// - 解码数据
- (void)decodeAudioAACData:(NSData *)aacData {
if (!_audioConverter) { return; }
dispatch_async(_decoderQueue, ^{
//记录aac 作为参数参入解码回调函数, 在回调函数中使用userData来不断填充数据
CCAudioUserData userData = {0};
userData.channelCount = (UInt32)_config.channelCount;
userData.data = (char *)[aacData bytes];
userData.size = (UInt32)aacData.length;
userData.packetDesc.mDataByteSize = (UInt32)aacData.length;
userData.packetDesc.mStartOffset = 0;
userData.packetDesc.mVariableFramesInPacket = 0;
//输出大小和packet个数
UInt32 pcmDataPacketSize = 1024;
// - 数据包数量*每个数据包中字节数*声道数.
UInt32 pcmBufferSize = (UInt32)(pcmDataPacketSize * (_config.sampleSize / 8) * _config.channelCount);
//创建临时容器pcm
uint8_t *pcmBuffer = malloc(pcmBufferSize);
memset(pcmBuffer, 0, pcmBufferSize);
//输出buffer
AudioBufferList outAudioBufferList = {0};
outAudioBufferList.mNumberBuffers = 1;
outAudioBufferList.mBuffers[0].mNumberChannels = (uint32_t)_config.channelCount;
outAudioBufferList.mBuffers[0].mDataByteSize = (UInt32)pcmBufferSize;
outAudioBufferList.mBuffers[0].mData = pcmBuffer;
//配置填充函数,获取输出数据
OSStatus status = AudioConverterFillComplexBuffer(_audioConverter, &AudioDecoderConverterComplexInputDataProc, &userData, &pcmDataPacketSize, &outAudioBufferList, NULL);
if (status != noErr) {
NSLog(@"Error: AAC Decoder error, status=%d",(int)status);
return;
}
//如果获取到数据
if (outAudioBufferList.mBuffers[0].mDataByteSize > 0) {
NSData *rawData = [NSData dataWithBytes:outAudioBufferList.mBuffers[0].mData length:outAudioBufferList.mBuffers[0].mDataByteSize];
dispatch_async(_callbackQueue, ^{
[_delegate audioDecodeCallback:rawData];
});
}
free(pcmBuffer);
});
}
// - 回调函数, 不断的填充数据
//解码器回调函数
static OSStatus AudioDecoderConverterComplexInputDataProc( AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData) {
CCAudioUserData *audioDecoder = (CCAudioUserData *)(inUserData);
if (audioDecoder->size <= 0) {
ioNumberDataPackets = 0;
return -1;
}
ioData->mBuffers[0].mData = audioDecoder->data;
ioData->mBuffers[0].mDataByteSize = audioDecoder->size;
ioData->mBuffers[0].mNumberChannels = audioDecoder->channelCount;
return noErr;
}