3

後でAudioUnitコールバックを使用してAUGraphでオーディオを再生できるように、AVAssetReaderを使用してオーディオアセットの読み込みに取り組んでいます。AUGraphとAudioUnitのコールバックが機能していますが、ディスクからファイルを読み取ります。ファイルが大きすぎると、メモリを大量に消費してアプリがクラッシュします。そのため、代わりにアセットを直接読み取り、限られたサイズのみを読み取っています。次に、それをダブルバッファーとして管理し、必要なときに必要なAUGraphを取得します。

(注:オーディオキューサービスを使用でき、AudioUnitコールバックでAUGraphを使用できるかどうかを知りたいので、iOSフレームワークによってメモリが管理されます。)

私の問題は、Cの配列、構造体、およびポインターについて十分に理解していないことです。ヘルプが必要な部分は、単一のAudioBufferを保持する個々のAudioBufferListを取得し、そのデータをすべてを保持する別のAudioBufferListに追加することです。後で使用するデータ。memcpyを使用する必要があると思いますが、memcpyの使用方法や、目的のためにAudioBufferListを初期化する方法が明確ではありません。ディスクからファイルを読み込むAppleのサンプルプロジェクトであるMixerHostを参照用に使用しています。

Xcodeにロードしたい場合は、進行中の作業をアップロードしました。私はこれを行うために必要なことのほとんどを理解しました。データがすべて1つの場所に収集されたら、準備は整っているはずです。

サンプルプロジェクト: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);
}

特にmemcpyで、mDataByteSizeとmDataを何を処理するのかわかりません。mDataはvoidポインターであるため、これは非常に注意が必要な領域です。

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

この行では、localBufferListのデータから、bufferListの位置に、データを書き込む場所にポインターを配置するためのフレーム数を加えた値をコピーする必要があると思います。これを機能させるために何を変更する必要があるかについて、いくつかのアイデアがあります。

  • voidポインターは1であり、AudioUnitSampleTypeのポインターのサイズではないため、memcpyを正しい位置に配置するには、sizeof(AudioUnitSampleType)も掛ける必要がある場合があります。
  • mDataを準備するためにmallocを適切に使用していない可能性がありますが、フレーム数がわからないため、初期化するために何をすべきかわかりません。

現在、このアプリを実行すると、bufferListへの無効なポインターでこの関数が終了します。

AudioBufferListの管理方法をよりよく理解するためにご協力いただきありがとうございます。

4

1 に答える 1

3

私は自分の答えを思いついた。私は、CMSampleBufferGetAudioBufferListWithRetainedBlockBufferを呼び出してAudioBufferListを取得した後、CMSampleBufferRefからBytesを追加できるNSMutableDataオブジェクトを使用することにしました。

        [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を使用しているので、どこにも保持する必要はありません。私は「無料」と呼んで、構造体内の子要素を解放し、最後に、mallocを使用するすべての場所で構造体自体を解放します。

于 2011-04-21T07:41:21.860 に答える