我一直在使用 AVAssetReader 读取音频资产,以便以后可以使用带有 AudioUnit 回调的 AUGraph 播放音频。我有 AUGraph 和 AudioUnit 回调工作,但它从磁盘读取文件,如果文件太大,它会占用太多内存并使应用程序崩溃。所以我转而直接阅读资产,并且只有有限的大小。然后,我会将其作为双缓冲区进行管理,并在需要时获取 AUGraph 所需的内容。
(注意:我很想知道我是否可以使用音频队列服务并且仍然使用带有 AudioUnit 回调的 AUGraph,以便 iOS 框架为我管理内存。)
我的问题是我对 C 中的数组、结构和指针没有很好的理解。我需要帮助的部分是将保存在单个 AudioBuffer 上的单个 AudioBufferList 并将该数据添加到另一个保存所有 AudioBufferList 的 AudioBufferList以后使用的数据。我相信我需要使用 memcpy,但不清楚如何使用它,甚至为我的目的初始化一个 AudioBufferList。我正在使用MixerHost作为参考,它是来自 Apple 的示例项目,它从磁盘读取文件。
如果您想在 Xcode 中加载它,我已经上传了我正在进行的工作。我已经弄清楚了完成这项工作所需的大部分内容,一旦我将数据全部收集在一个地方,我就可以开始了。
示例项目:MyAssetReader.zip
在标题中,您可以看到我将 bufferList 声明为指向结构的指针。
@interface MyAssetReader : NSObject {
BOOL reading;
signed long sampleTotal;
Float64 totalDuration;
AudioBufferList *bufferList; // How should this be handled?
}
然后我以这种方式分配bufferList,主要是从MixerHost借来的......
UInt32 channelCount = [asset.tracks count];
if (channelCount > 1) {
NSLog(@"We have more than 1 channel!");
}
bufferList = (AudioBufferList *) malloc (
sizeof (AudioBufferList) + sizeof (AudioBuffer) * (channelCount - 1)
);
if (NULL == bufferList) {NSLog (@"*** malloc failure for allocating bufferList memory"); return;}
// initialize the mNumberBuffers member
bufferList->mNumberBuffers = channelCount;
// initialize the mBuffers member to 0
AudioBuffer emptyBuffer = {0};
size_t arrayIndex;
for (arrayIndex = 0; arrayIndex < channelCount; arrayIndex++) {
// set up the AudioBuffer structs in the buffer list
bufferList->mBuffers[arrayIndex] = emptyBuffer;
bufferList->mBuffers[arrayIndex].mNumberChannels = 1;
// How should mData be initialized???
bufferList->mBuffers[arrayIndex].mData = malloc(sizeof(AudioUnitSampleType));
}
最后我循环阅读。
int frameCount = 0;
CMSampleBufferRef nextBuffer;
while (assetReader.status == AVAssetReaderStatusReading) {
nextBuffer = [assetReaderOutput copyNextSampleBuffer];
AudioBufferList localBufferList;
CMBlockBufferRef blockBuffer;
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(nextBuffer, NULL, &localBufferList, sizeof(localBufferList), NULL, NULL,
kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBuffer);
// increase the number of total bites
bufferList->mBuffers[0].mDataByteSize += localBufferList.mBuffers[0].mDataByteSize;
// carefully copy the data into the buffer list
memcpy(bufferList->mBuffers[0].mData + frameCount, localBufferList.mBuffers[0].mData, sizeof(AudioUnitSampleType));
// get information about duration and position
//CMSampleBufferGet
CMItemCount sampleCount = CMSampleBufferGetNumSamples(nextBuffer);
Float64 duration = CMTimeGetSeconds(CMSampleBufferGetDuration(nextBuffer));
Float64 presTime = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(nextBuffer));
if (isnan(duration)) duration = 0.0;
if (isnan(presTime)) presTime = 0.0;
//NSLog(@"sampleCount: %ld", sampleCount);
//NSLog(@"duration: %f", duration);
//NSLog(@"presTime: %f", presTime);
self.sampleTotal += sampleCount;
self.totalDuration += duration;
frameCount++;
free(nextBuffer);
}
我不确定我处理 mDataByteSize 和 mData 的内容,尤其是使用 memcpy。由于 mData 是一个空指针,这是一个额外的棘手区域。
memcpy(bufferList->mBuffers[0].mData + frameCount, localBufferList.mBuffers[0].mData, sizeof(AudioUnitSampleType));
在这一行中,我认为它应该将值从 localBufferList 中的数据复制到 bufferList 中的位置加上帧数以将指针定位在它应该写入数据的位置。我有几个想法,我需要改变什么才能让它工作。
- 由于 void 指针只是 1 而不是 AudioUnitSampleType 指针的大小,我可能还需要将它乘以 sizeof(AudioUnitSampleType) 以使 memcpy 进入正确的位置
- 我可能没有正确使用 malloc 来准备 mData 但由于我不确定会有多少帧我不知道该怎么做才能初始化它
目前,当我运行这个应用程序时,它会以一个无效的 bufferList 指针结束这个函数。
感谢您帮助我更好地了解如何管理 AudioBufferList。