如何使用 AVAssetReader 添加到 AudioBufferList ?

发布于 2024-10-31 14:43:42 字数 4227 浏览 1 评论 0原文

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

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

我的问题是我对数组、结构体和我需要帮助的部分是获取保存单个 AudioBuffer 的单个 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。

I have been working on reading in an audio asset using AVAssetReader so that I can later play back the audio with an AUGraph with an AudioUnit callback. I have the AUGraph and AudioUnit callback working but it reads files from disk and if the file is too big it would take up too much memory and crash the app. So I am instead reading the asset directly and only a limited size. I will then manage it as a double buffer and get the AUGraph what it needs when it needs it.

(Note: I would love know if I can use Audio Queue Services and still use an AUGraph with AudioUnit callback so memory is managed for me by the iOS frameworks.)

My problem is that I do not have a good understanding of arrays, structs and pointers in C. The part where I need help is taking the individual AudioBufferList which holds onto a single AudioBuffer and add that data to another AudioBufferList which holds onto all of the data to be used later. I believe I need to use memcpy but it is not clear how to use it or even initialize an AudioBufferList for my purposes. I am using MixerHost for reference which is the sample project from Apple which reads in the file from disk.

I have uploaded my work in progress if you would like to load it up in Xcode. I've figured out most of what I need to get this done and once I have the data being collected all in one place I should be good to go.

Sample Project: MyAssetReader.zip

In the header you can see I declare the bufferList as a pointer to the struct.

@interface MyAssetReader : NSObject {
    BOOL reading;
    signed long sampleTotal;
    Float64 totalDuration;

    AudioBufferList *bufferList; // How should this be handled?
}

Then I allocate bufferList this way, largely borrowing from 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)); 
}

Finally I loop through the reads.

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);
}

I am unsure about the what that I handle mDataByteSize and mData, especially with memcpy. Since mData is a void pointer this is an extra tricky area.

    memcpy(bufferList->mBuffers[0].mData + frameCount, localBufferList.mBuffers[0].mData, sizeof(AudioUnitSampleType));

In this line I think it should be copying the value from the data in localBufferList to the position in the bufferList plus the number of frames to position the pointer where it should write the data. I have a couple of ideas on what I need to change to get this to work.

  • Since a void pointer is just 1 and not the size of the pointer for an AudioUnitSampleType I may need to multiply it also by sizeof(AudioUnitSampleType) to get the memcpy into the right position
  • I may not be using malloc properly to prepare mData but since I am not sure how many frames there will be I am not sure what to do to initialize it

Currently when I run this app it ends this function with an invalid pointer for bufferList.

I appreciate your help with making me better understand how to manage an AudioBufferList.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

过气美图社 2024-11-07 14:43:42

我已经想出了自己的答案。我决定使用 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 的任何地方释放结构本身。

I've come up with my own answer. I decided to use an NSMutableData object which allows me to appendBytes from the CMSampleBufferRef after calling CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer to get an AudioBufferList.

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

Once the read loop is done I have all of the data in my NSMutableData object. I then create and populate the AudioBufferList this way.

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];

I'd appreciate a little code review on how I use malloc to create the struct and populate it. I am getting a EXC_BAD_ACCESS error sporadically but I cannot pinpoint where the error is just yet. Since I am using malloc on the struct I should not have to retain it anywhere. I do call "free" to release child elements within the struct and finally the struct itself everywhere that I use malloc.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文