上一回说到音频AAC编码,这回我们把编码后的文件解码,解码的文件就是上一章录制的音频。
在iOS下进行音频解码及播放的大体流程如下:
1、打开 AAC 文件。
2、获取音频格式信息。如通道数,采样率等。
3、从 AAC 文件中取出一帧 AAC 数据。
4、使用 AudioToolbox 解码 AAC 数据包。
5、将解码后的 PCM 数据送给 AudioUnit 播放声音。
6、重复 3-5 步,直到整个 AAC 文件被读完。
AACDecoder.m
- (void)customAudioConfig {
//NSURL *url = [[NSBundle mainBundle] URLForResource:@"abc" withExtension:@"aac"];
NSFileManager *manager = [NSFileManager defaultManager];
NSURL *path = [[manager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject];
NSURL *url = [path URLByAppendingPathComponent:@"abc.aac"];
// 打开 AAC 文件。
OSStatus status = AudioFileOpenURL((__bridge CFURLRef)url, kAudioFileReadPermission, 0, &audioFileID); //Open an existing audio file specified by a URL.
if (status != noErr) {
NSLog(@"打开文件失败 %@", url);
return ;
}
uint32_t size = sizeof(audioStreamBasicDescrpition);
status = AudioFileGetProperty(audioFileID, kAudioFilePropertyDataFormat, &size, &audioStreamBasicDescrpition); // Gets the value of an audio file property.
NSAssert(status == noErr, @"error");
status = AudioQueueNewOutput(&audioStreamBasicDescrpition, bufferReady, (__bridge void * _Nullable)(self), NULL, NULL, 0, &audioQueue); // Creates a new playback audio queue object.
NSAssert(status == noErr, @"error");
if (audioStreamBasicDescrpition.mBytesPerPacket == 0 || audioStreamBasicDescrpition.mFramesPerPacket == 0) {
uint32_t maxSize;
size = sizeof(maxSize);
AudioFileGetProperty(audioFileID, kAudioFilePropertyPacketSizeUpperBound, &size, &maxSize); // The theoretical maximum packet size in the file.
if (maxSize > CONST_BUFFER_SIZE) {
maxSize = CONST_BUFFER_SIZE;
}
packetNums = CONST_BUFFER_SIZE / maxSize;
audioStreamPacketDescrption = malloc(sizeof(AudioStreamPacketDescription) * packetNums);
}
else {
packetNums = CONST_BUFFER_SIZE / audioStreamBasicDescrpition.mBytesPerPacket;
audioStreamPacketDescrption = nil;
}
char cookies[100];
memset(cookies, 0, sizeof(cookies));
// 这里的100 有问题
AudioFileGetProperty(audioFileID, kAudioFilePropertyMagicCookieData, &size, cookies); // Some file types require that a magic cookie be provided before packets can be written to an audio file.
if (size > 0) {
AudioQueueSetProperty(audioQueue, kAudioQueueProperty_MagicCookie, cookies, size); // Sets an audio queue property value.
}
readedPacket = 0;
// 循环执行 3-5步,直到文件结束。
for (int i = 0; i < CONST_BUFFER_COUNT; ++i) {
AudioQueueAllocateBuffer(audioQueue, CONST_BUFFER_SIZE, &audioBuffers[i]); // Asks an audio queue object to allocate an audio queue buffer.
if ([self fillBuffer:audioBuffers[i]]) {
// full
break;
}
NSLog(@"buffer%d full", i);
}
}
void bufferReady(void *inUserData,AudioQueueRef inAQ,
AudioQueueBufferRef buffer){
NSLog(@"refresh buffer");
AACDecoder* player = (__bridge AACDecoder *)inUserData;
if (!player) {
NSLog(@"player nil");
return ;
}
if ([player fillBuffer:buffer]) {
NSLog(@"play end");
}
}
- (bool)fillBuffer:(AudioQueueBufferRef)buffer {
bool full = NO;
uint32_t bytes = 0, packets = (uint32_t)packetNums;
OSStatus status = AudioFileReadPackets(audioFileID, NO, &bytes, audioStreamPacketDescrption, readedPacket, &packets, buffer->mAudioData); // Reads packets of audio data from an audio file.
NSAssert(status == noErr, ([NSString stringWithFormat:@"error status %d", status]) );
if (packets > 0) {
buffer->mAudioDataByteSize = bytes;
AudioQueueEnqueueBuffer(audioQueue, buffer, packets, audioStreamPacketDescrption);
readedPacket += packets;
}
else {
AudioQueueStop(audioQueue, NO);
full = YES;
}
return full;
}
- (void)play {
AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1.0); // Sets a playback audio queue parameter value.
AudioQueueStart(audioQueue, NULL); // Begins playing or recording audio.
}
ViewController.m
- (void)decoderPlay{
// 播放
self.player = [[AACDecoder alloc] init];
[self.player play];
}
到这里就把AAC编码的文件给解码播放了。
了解音频AAC编码
Demo下载地址