2

我一直在使用AVAssetReader讀取音頻資產,以便稍後可以使用帶有AudioUnit回調的AUGraph播放音頻。我有AUGraph和AudioUnit回調工作,但它從磁盤讀取文件,如果文件太大,它會佔用太多內存並導致應用程序崩潰。所以我直接閱讀資產,只有有限的尺寸。然後,我將把它作爲一個雙緩衝區來管理,並在需要時獲得AUGraph所需的內容。如何使用AVAssetReader添加到AudioBufferList?

(注:我很想知道我是否可以使用音頻隊列服務,並仍然使用與AudioUnit回調的AUGraph所以內存是由iOS的框架,爲我管理。)

我的問題是,我沒有對C中的數組,結構和指針有很好的理解。我需要幫助的部分是將單個AudioBufferList放到單個AudioBuffer中,並將該數據添加到另一個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位置加上幀的數量的數據的值,將指針在它應該寫入數據的位置。我對我需要改變的一些想法有所瞭解。

  • 由於空指針僅僅是1,而不是指針的AudioUnitSampleType我可能需要通過的sizeof(AudioUnitSampleType)也乘以它的大小來獲得的memcpy到正確的位置
  • 我可能不使用malloc妥善準備MDATA但因爲我不知道有多少幀會有我不知道做初始化

什麼目前,當我運行這個程序,結束該功能與bufferList無效指針。

感謝您的幫助,讓我更好地理解如何管理AudioBufferList。

+0

我一直在學習更多關於void指針,以及如何使用malloc和memcpy,這似乎是理解AudioBufferList結構的關鍵部分。我需要知道一種方法來將值保存到C中的一個動態數組中。我正在閱讀的這些功能可能已添加到C99中。我正在繼續閱讀它。 – Brennan 2011-04-11 08:09:09

+0

我在想,我會積累從AudioBuffer到一個數組與ralloc增長,但我可以做到這一點與100塊,所以realloc不會與每次迭代調用。然後我可以在循環之後創建一個動態調整大小的數組,使用memcpy使用累加值填充該數組,然後使用該數組在緩衝區上設置mData。 – Brennan 2011-04-11 08:21:42

回答

3

我想出了自己的答案。我決定使用一個NSMutableData對象,該對象允許我在調用CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer獲取AudioBufferList之後,從CMSampleBufferRef中追加BigBytes。

 [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的任何地方釋放結構體。

+0

可以請你看看這個: - https://stackoverflow.com/q/49003226/8527224 – 2018-02-28 12:26:45