10

AVAssetReader を使用してビデオ ファイルを読み込んで、オーディオを CoreAudio に渡して処理 (エフェクトなどを追加) してから、AVAssetWriter を使用してディスクに保存しようとしています。出力ノードの AudioComponentDescription に componentSubType を RemoteIO として設定すると、スピーカーから正しく再生されることを指摘したいと思います。これにより、AUGraph が適切にセットアップされていることを確信できます。動作が聞こえるからです。ただし、subType を GenericOutput に設定しているので、自分でレンダリングを行い、調整されたオーディオを取り戻すことができます。

オーディオを読み込んでいて、CMSampleBufferRef を copyBuffer に渡します。これにより、オーディオが循環バッファに入れられ、後で読み込まれます。

- (void)copyBuffer:(CMSampleBufferRef)buf {  
    if (_readyForMoreBytes == NO)  
    {  
        return;  
    }  

    AudioBufferList abl;  
    CMBlockBufferRef blockBuffer;  
    CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(buf, NULL, &abl, sizeof(abl), NULL, NULL, kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBuffer);  

    UInt32 size = (unsigned int)CMSampleBufferGetTotalSampleSize(buf);  
    BOOL bytesCopied = TPCircularBufferProduceBytes(&circularBuffer, abl.mBuffers[0].mData, size);  

    if (!bytesCopied){  
        /  
        _readyForMoreBytes = NO;  

        if (size > kRescueBufferSize){  
            NSLog(@"Unable to allocate enought space for rescue buffer, dropping audio frame");  
        } else {  
            if (rescueBuffer == nil) {  
                rescueBuffer = malloc(kRescueBufferSize);  
            }  

            rescueBufferSize = size;  
            memcpy(rescueBuffer, abl.mBuffers[0].mData, size);  
        }  
    }  

    CFRelease(blockBuffer);  
    if (!self.hasBuffer && bytesCopied > 0)  
    {  
        self.hasBuffer = YES;  
    }  
} 

次に、processOutput を呼び出します。これにより、outputUnit で手動の reder が実行されます。AudioUnitRender が呼び出されると、以下の playbackCallback が呼び出されます。これは、最初のノードで入力コールバックとして接続されているものです。PlaybackCallback は循環バッファからデータを取得し、渡された audioBufferList にフィードします。前に述べたように、出力が RemoteIO として設定されている場合、オーディオはスピーカーで正しく再生されます。AudioUnitRender が終了すると、noErr が返され、bufferList オブジェクトには有効なデータが含まれます。 CMSampleBufferSetDataBufferFromAudioBufferList を呼び出すと、 kCMSampleBufferError_RequiredParameterMissing (-12731) が取得されます

-(CMSampleBufferRef)processOutput  
{  
    if(self.offline == NO)  
    {  
        return NULL;  
    }  

    AudioUnitRenderActionFlags flags = 0;  
    AudioTimeStamp inTimeStamp;  
    memset(&inTimeStamp, 0, sizeof(AudioTimeStamp));  
    inTimeStamp.mFlags = kAudioTimeStampSampleTimeValid;  
    UInt32 busNumber = 0;  

    UInt32 numberFrames = 512;  
    inTimeStamp.mSampleTime = 0;  
    UInt32 channelCount = 2;  

    AudioBufferList *bufferList = (AudioBufferList*)malloc(sizeof(AudioBufferList)+sizeof(AudioBuffer)*(channelCount-1));  
    bufferList->mNumberBuffers = channelCount;  
    for (int j=0; j<channelCount; j++)  
    {  
        AudioBuffer buffer = {0};  
        buffer.mNumberChannels = 1;  
        buffer.mDataByteSize = numberFrames*sizeof(SInt32);  
        buffer.mData = calloc(numberFrames,sizeof(SInt32));  

        bufferList->mBuffers[j] = buffer;  

    }  
    CheckError(AudioUnitRender(outputUnit, &flags, &inTimeStamp, busNumber, numberFrames, bufferList), @"AudioUnitRender outputUnit");  

    CMSampleBufferRef sampleBufferRef = NULL;  
    CMFormatDescriptionRef format = NULL;  
    CMSampleTimingInfo timing = { CMTimeMake(1, 44100), kCMTimeZero, kCMTimeInvalid };  
    AudioStreamBasicDescription audioFormat = self.audioFormat;  
    CheckError(CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &audioFormat, 0, NULL, 0, NULL, NULL, &format), @"CMAudioFormatDescriptionCreate");  
    CheckError(CMSampleBufferCreate(kCFAllocatorDefault, NULL, false, NULL, NULL, format, numberFrames, 1, &timing, 0, NULL, &sampleBufferRef), @"CMSampleBufferCreate");  
    CheckError(CMSampleBufferSetDataBufferFromAudioBufferList(sampleBufferRef, kCFAllocatorDefault, kCFAllocatorDefault, 0, bufferList), @"CMSampleBufferSetDataBufferFromAudioBufferList");  

    return sampleBufferRef;  
} 


static OSStatus playbackCallback(void *inRefCon,  
                                 AudioUnitRenderActionFlags *ioActionFlags,  
                                 const AudioTimeStamp *inTimeStamp,  
                                 UInt32 inBusNumber,  
                                 UInt32 inNumberFrames,  
                                 AudioBufferList *ioData)  
{  
    int numberOfChannels = ioData->mBuffers[0].mNumberChannels;  
    SInt16 *outSample = (SInt16 *)ioData->mBuffers[0].mData;  

    /  
    memset(outSample, 0, ioData->mBuffers[0].mDataByteSize);  

    MyAudioPlayer *p = (__bridge MyAudioPlayer *)inRefCon;  

    if (p.hasBuffer){  
        int32_t availableBytes;  
        SInt16 *bufferTail = TPCircularBufferTail([p getBuffer], &availableBytes);  

        int32_t requestedBytesSize = inNumberFrames * kUnitSize * numberOfChannels;  

        int bytesToRead = MIN(availableBytes, requestedBytesSize);  
        memcpy(outSample, bufferTail, bytesToRead);  
        TPCircularBufferConsume([p getBuffer], bytesToRead);  

        if (availableBytes <= requestedBytesSize*2){  
            [p setReadyForMoreBytes];  
        }  

        if (availableBytes <= requestedBytesSize) {  
            p.hasBuffer = NO;  
        }    
    }  
    return noErr;  
} 

渡す CMSampleBufferRef は有効に見えます (以下は、デバッガーからのオブジェクトのダンプです)。

CMSampleBuffer 0x7f87d2a03120 retainCount: 1 allocator: 0x103333180  
  invalid = NO  
  dataReady = NO  
  makeDataReadyCallback = 0x0  
  makeDataReadyRefcon = 0x0  
  formatDescription = <CMAudioFormatDescription 0x7f87d2a02b20 [0x103333180]> {  
  mediaType:'soun'  
  mediaSubType:'lpcm'  
  mediaSpecific: {  
  ASBD: {  
  mSampleRate: 44100.000000  
  mFormatID: 'lpcm'  
  mFormatFlags: 0xc2c  
  mBytesPerPacket: 2  
  mFramesPerPacket: 1  
  mBytesPerFrame: 2  
  mChannelsPerFrame: 1  
  mBitsPerChannel: 16 }  
  cookie: {(null)}  
  ACL: {(null)}  
  }  
  extensions: {(null)}  
}  
  sbufToTrackReadiness = 0x0  
  numSamples = 512  
  sampleTimingArray[1] = {  
  {PTS = {0/1 = 0.000}, DTS = {INVALID}, duration = {1/44100 = 0.000}},  
  }  
  dataBuffer = 0x0  

バッファリストは次のようになります

Printing description of bufferList:  
(AudioBufferList *) bufferList = 0x00007f87d280b0a0  
Printing description of bufferList->mNumberBuffers:  
(UInt32) mNumberBuffers = 2  
Printing description of bufferList->mBuffers:  
(AudioBuffer [1]) mBuffers = {  
  [0] = (mNumberChannels = 1, mDataByteSize = 2048, mData = 0x00007f87d3008c00)  
}  

誰かが助けてくれることを願って、ここで本当に途方に暮れています。ありがとう、

問題がある場合は、これをios 8.3シミュレーターでデバッグしています。オーディオは、iPhone 6で撮影してラップトップに保存したmp4からのものです。

次の問題を読みましたが、まだ役に立たず、機能していません。

AudioBufferList を CMSampleBuffer に変換するには?

AudioBufferList を CMSampleBuffer に変換すると予期しない結果が生じる

CMSampleBufferSetDataBufferFromAudioBufferList がエラー 12731 を返す

コア オーディオのオフライン レンダリング

アップデート

さらに調べてみると、AudioUnitRender が実行される直前の AudioBufferList が次のようになっていることに気付きました。

bufferList->mNumberBuffers = 2,
bufferList->mBuffers[0].mNumberChannels = 1,
bufferList->mBuffers[0].mDataByteSize = 2048

mDataByteSize は numberFrames*sizeof(SInt32) で、512 * 4 です。playbackCallback で渡された AudioBufferList を見ると、リストは次のようになります。

bufferList->mNumberBuffers = 1,
bufferList->mBuffers[0].mNumberChannels = 1,
bufferList->mBuffers[0].mDataByteSize = 1024

その他のバッファがどこに行くのか、または他の1024バイトサイズが本当にわからない...

もし私がレッドナーに電話をかけ終わったら、もし私がこのようなことをしたら

AudioBufferList newbuff;
newbuff.mNumberBuffers = 1;
newbuff.mBuffers[0] = bufferList->mBuffers[0];
newbuff.mBuffers[0].mDataByteSize = 1024;

newbuff を CMSampleBufferSetDataBufferFromAudioBufferList に渡すと、エラーはなくなります。

BufferList のサイズを 1 mNumberBuffers に設定するか、その mDataByteSize を numberFrames*sizeof(SInt16) に設定しようとすると、AudioUnitRender を呼び出すときに -50 が返されます。

更新 2

スピーカーでサウンドを再生するときに出力を検査できるように、レンダリング コールバックを接続しました。スピーカーに送られる出力にも 2 つのバッファーを持つ AudioBufferList があり、入力コールバック中の mDataByteSize は 1024 で、レンダー コールバックでは 2048 であることに気付きました。これは、AudioUnitRender を手動で呼び出したときに見たものと同じです。レンダリングされた AudioBufferList のデータを調べると、2 つのバッファーのバイトが同じであることがわかります。つまり、2 番目のバッファーは無視できます。しかし、データが取り込まれているため、レンダリング後のサイズが 1024 ではなく 2048 であるという事実を処理する方法がわかりません。なぜそれが起こっているのかについてのアイデアはありますか? オーディオグラフを通過した後、それはより生の形であり、それがサイズが2倍になっている理由ですか?

4

1 に答える 1