音频解码器
官方文档:https://developer.apple.com/documentation/audiotoolbox/audio_converter_services?language=occ
1.创建音频转换器 AudioConverterNewSpecific
/*!
@function AudioConverterNewSpecific
@abstract Create a new AudioConverter using specific codecs.
@param inSourceFormat
The format of the source audio to be converted.
@param inDestinationFormat
The destination format to which the audio is to be converted.
@param inNumberClassDescriptions
The number of class descriptions.
@param inClassDescriptions
AudioClassDescriptions specifiying the codec to instantiate.
@param outAudioConverter
On successful return, points to a new AudioConverter instance.
@result An OSStatus result code.
@discussion
This function is identical to AudioConverterNew(), except that the client may
explicitly choose which codec to instantiate if there is more than one choice.
*
extern OSStatus
AudioConverterNewSpecific( const AudioStreamBasicDescription * inSourceFormat,
const AudioStreamBasicDescription * inDestinationFormat,
UInt32 inNumberClassDescriptions,
const AudioClassDescription * inClassDescriptions,
AudioConverterRef __nullable * __nonnull outAudioConverter)
例子
//1.音频来源格式
AudioStreamBasicDescription inSourceFormat = {
.mSampleRate = 48000,
.mFormatID = kAudioFormatMPEG4AAC,
.mChannelsPerFrame = 2,
.mFramesPerPacket = 1024,
};
//2.要转换成的音频格式
AudioStreamBasicDescription inDestinationFormat = {0};
inDestinationFormat.mSampleRate = 48000;
inDestinationFormat.mFormatID = kAudioFormatLinearPCM;
inDestinationFormat.mChannelsPerFrame = 1;
inDestinationFormat.mFormatID = kAudioFormatLinearPCM;
inDestinationFormat.mFormatFlags = (kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked);
inDestinationFormat.mFramesPerPacket = 1;
inDestinationFormat.mBitsPerChannel = 16;
inDestinationFormat.mBytesPerFrame = inDestinationFormat.mBitsPerChannel / 8 *inDestinationFormat.mChannelsPerFrame;
inDestinationFormat.mBytesPerPacket = inDestinationFormat.mBytesPerFrame * inDestinationFormat.mFramesPerPacket;
inDestinationFormat.mReserved = 0;
//3.创建AudioClassDescription
- (AudioClassDescription *)getAudioCalssDescriptionWithType:(AudioFormatID)type fromManufacture:(uint32_t)manufacture {
static AudioClassDescription desc;
UInt32 decoderSpecific = type;
UInt32 size;
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_Decoders,
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;
}
4.创建AuidoConverter
//获取解码器的描述信息
AudioClassDescription *audioClassDesc = [self getAudioCalssDescriptionWithType:destFormatID fromManufacture:kAppleHardwareAudioCodecManufacturer];
// Create the AudioConverterRef.
AudioConverterRef converter = NULL;
if (![self checkError:AudioConverterNewSpecific(&inSourceFormat, &inDestinationFormat, inDestinationFormat.mChannelsPerFrame, audioClassDesc, &converter) withErrorString:@"Audio Converter New failed"]) {
return NULL;
}else {
printf("Audio converter create successful \n");
}
2.解码 AudioConverterFillComplexBuffer
/*!
@function AudioConverterFillComplexBuffer
@abstract Converts data supplied by an input callback function, supporting non-interleaved
and packetized formats.
@param inAudioConverter
The AudioConverter to use.
@param inInputDataProc
A callback function which supplies the input data.
@param inInputDataProcUserData
A value for the use of the callback function.
@param ioOutputDataPacketSize
On entry, the capacity of outOutputData expressed in packets in the
converter's output format. On exit, the number of packets of converted
data that were written to outOutputData.
@param outOutputData
The converted output data is written to this buffer.
@param outPacketDescription
If non-null, and the converter's output uses packet descriptions, then
packet descriptions are written to this array. It must point to a memory
block capable of holding *ioOutputDataPacketSize packet descriptions.
(See AudioFormat.h for ways to determine whether an audio format
uses packet descriptions).
@result An OSStatus result code.
@discussion
Produces a buffer list of output data from an AudioConverter. The supplied input
callback function is called whenever necessary.
*/
extern OSStatus
AudioConverterFillComplexBuffer( AudioConverterRef inAudioConverter,
AudioConverterComplexInputDataProc inInputDataProc,
void * __nullable inInputDataProcUserData,
UInt32 * ioOutputDataPacketSize,
AudioBufferList * outOutputData,
AudioStreamPacketDescription * __nullable outPacketDescription)
例子
struct ConverterInfo {
UInt32 sourceChannelsPerFrame;
UInt32 sourceDataSize;
void *sourceBuffer;
AudioStreamPacketDescription packetDesc;
};
typedef struct ConverterInfo ConverterInfoType;
OSStatus DecodeConverterComplexInputDataProc(AudioConverterRef inAudioConverter,
UInt32 *ioNumberDataPackets,
AudioBufferList *ioData,
AudioStreamPacketDescription **outDataPacketDescription,
void *inUserData) {
ConverterInfoType *info = (ConverterInfoType *)inUserData;
if (info->sourceDataSize <= 0) {
ioNumberDataPackets = 0;
return -1;
}
*outDataPacketDescription = &info->packetDesc;
(*outDataPacketDescription)[0].mStartOffset = 0;
(*outDataPacketDescription)[0].mDataByteSize = info->sourceDataSize;
(*outDataPacketDescription)[0].mVariableFramesInPacket = 0;
ioData->mNumberBuffers = 1;
ioData->mBuffers[0].mData = info->sourceBuffer;
ioData->mBuffers[0].mNumberChannels = info->sourceChannelsPerFrame;
ioData->mBuffers[0].mDataByteSize = info->sourceDataSize;
return noErr;
}
- (void)decodeFormatByConverter:(AudioConverterRef)audioConverter sourceBuffer:(void *)sourceBuffer sourceBufferSize:(UInt32)sourceBufferSize sourceFormat:(AudioStreamBasicDescription)sourceFormat dest:(AudioStreamBasicDescription)destFormat completeHandler:(void(^)(AudioBufferList *destBufferList, UInt32 outputPackets, AudioStreamPacketDescription *outputPacketDescriptions))completeHandler {
// Note: audio convert must set 1024.
UInt32 ioOutputDataPackets = kIOOutputDataPackets;
UInt32 outputBufferSize = (UInt32)(ioOutputDataPackets * destFormat.mChannelsPerFrame * destFormat.mBytesPerFrame);
// Set up output buffer list.
AudioBufferList fillBufferList = {0};
fillBufferList.mNumberBuffers = 1;
fillBufferList.mBuffers[0].mNumberChannels = destFormat.mChannelsPerFrame;
fillBufferList.mBuffers[0].mDataByteSize = outputBufferSize;
fillBufferList.mBuffers[0].mData = malloc(outputBufferSize * sizeof(char));
ConverterInfoType userInfo = {0};
userInfo.sourceBuffer = sourceBuffer;
userInfo.sourceDataSize = sourceBufferSize;
userInfo.sourceChannelsPerFrame = sourceFormat.mChannelsPerFrame;
userInfo.packetDesc.mDataByteSize = (UInt32)sourceBufferSize;
userInfo.packetDesc.mStartOffset = 0;
userInfo.packetDesc.mVariableFramesInPacket = 0;
AudioStreamPacketDescription outputPacketDesc = {0};
OSStatus status = AudioConverterFillComplexBuffer(audioConverter,
DecodeConverterComplexInputDataProc,
&userInfo,
&ioOutputDataPackets,
&fillBufferList,
&outputPacketDesc);
// if interrupted in the process of the conversion call, we must handle the error appropriately
if (status != noErr) {
if (status == kAudioConverterErr_HardwareInUse) {
printf("Audio Converter returned kAudioConverterErr_HardwareInUse!\n");
} else {
if (![self checkError:status withErrorString:@"AudioConverterFillComplexBuffer error!"]) {
return;
}
}
} else {
if (ioOutputDataPackets == 0) {
// This is the EOF condition.
status = noErr;
}
if (completeHandler) {
completeHandler(&fillBufferList, ioOutputDataPackets, &outputPacketDesc);
}
}
}