objective-c - 如何使用 AVAssetReader 添加到 AudioBufferList?

标签 objective-c core-audio avfoundation void-pointers core-media

我一直致力于使用 AVAssetReader 读取音频 Assets ,以便稍后可以使用带有 AudioUnit 回调的 AUGraph 播放音频。我有 AUGraph 和 AudioUnit 回调工作,但它从磁盘读取文件,如果文件太大,它会占用太多内存并使应用程序崩溃。因此,我改为直接阅读 Assets ,而且大小有限。然后,我会将其作为双缓冲区进行管理,并在需要时获取 AUGraph 所需的内容。

(注意:我很想知道我是否可以使用音频队列服务并仍然使用带有 AudioUnit 回调的 AUGraph,以便 iOS 框架为我管理内存。)

我的问题是我对 C 中的数组、结构和指针没有很好的理解。我需要帮助的部分是获取保存在单个 AudioBuffer 上的单个 AudioBufferList 并将该数据添加到保存在以后要用到的所有数据。我相信我需要使用 memcpy 但不清楚如何使用它甚至为我的目的初始化 AudioBufferList。我正在使用 MixerHost供引用,这是 Apple 的示例项目,它从磁盘读取文件。

如果您想在 Xcode 中加载它,我已经上传了我正在进行的工作。我已经弄清楚完成这项工作所需的大部分内容,一旦我将所有数据收集到一个地方,我就可以开始了。

示例项目:MyAssetReader.zip

在 header 中,您可以看到我将 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。

最佳答案

我想出了我自己的答案。我决定使用 NSMutableData 对象,它允许我在调用 CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer 获取 AudioBufferList 后从 CMSampleBufferRef 追加字节。

        [data appendBytes:localBufferList.mBuffers[0].mData length:localBufferList.mBuffers[0].mDataByteSize];

一旦读取循环完成,我的 NSMutableData 对象中就有了所有数据。然后,我以这种方式创建并填充 AudioBufferList。

audioBufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList));
if (NULL == audioBufferList) {
    NSLog (@"*** malloc failure for allocating audioBufferList memory");
    [data release];
    return;
}
audioBufferList->mNumberBuffers = 1;
audioBufferList->mBuffers[0].mNumberChannels = channelCount;
audioBufferList->mBuffers[0].mDataByteSize = [data length];
audioBufferList->mBuffers[0].mData = (AudioUnitSampleType *)malloc([data length]);
if (NULL == audioBufferList->mBuffers[0].mData) {
    NSLog (@"*** malloc failure for allocating mData memory");
    [data release];
    return;
}
memcpy(audioBufferList->mBuffers[0].mData, [data mutableBytes], [data length]);
[data release];

如果我能对我如何使用 malloc 创建结构并填充它进行一些代码审查,我将不胜感激。我偶尔会收到 EXC_BAD_ACCESS 错误,但我无法确定错误的位置。因为我在结构上使用 malloc,所以我不必在任何地方保留它。我调用“free”来释放结构中的子元素,最后在我使用 malloc 的任何地方释放结构本身。

关于objective-c - 如何使用 AVAssetReader 添加到 AudioBufferList?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5615395/

相关文章:

ios - 将 AVAudioRecorderDelegate 分配给非 UIViewController

ios - AVFoundation人脸检测能定位眼睛和嘴巴位置吗?

ios - 类在两者中都实现。将使用两者之一

ios - 在 iOS 中立即开始播放简短的自制声音

iOS 强制音频输出仅至耳机插孔

ios - 视频不使用 AVMutableVideoCompositionLayerInstruction 旋转

ios - 将 CGPDFDocumentRef 添加到 NSDictionary 或 NSArray

objective-c - NSView 或 CALayer 的 subview ?

ios - 在 Deprecated sizeWithFont :minFontSize:actualFontSize to get the change in font size? 中复制 actualFontSize 指针返回

ios - 与 AudioUnit 一起播放声音文件