2017-10-24 18 views
10

captureOutputから返されたCMSampleBufferのコピーをAVCaptureAudioDataOutputSampleBufferDelegateに作成しようとしています。オーディオの深いコピーCMSampleBuffer

私が抱えている問題は、デリゲートメソッドcaptureOutput:didOutputSampleBuffer:fromConnection:から来るフレームが、長時間にわたってCFArrayに保持された後に削除されてしまうことです。

明らかに、今後の処理のために着信バッファのディープコピーを作成する必要があります。 CMSampleBufferCreateCopyは浅いコピーのみを作成することも知っています。

関連するいくつかの質問がありますがSOに尋ねた:

しかし、それらのどれも12個のパラメータで正しくCMSampleBufferCreate機能を使用するために私を助けていません。

CMSampleBufferRef copyBuffer; 

    CMBlockBufferRef data = CMSampleBufferGetDataBuffer(sampleBuffer); 
    CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer); 
    CMItemCount itemCount = CMSampleBufferGetNumSamples(sampleBuffer); 

    CMTime duration = CMSampleBufferGetDuration(sampleBuffer); 
    CMTime presentationStamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); 
    CMSampleTimingInfo timingInfo; 
    timingInfo.duration = duration; 
    timingInfo.presentationTimeStamp = presentationStamp; 
    timingInfo.decodeTimeStamp = CMSampleBufferGetDecodeTimeStamp(sampleBuffer); 


    size_t sampleSize = CMBlockBufferGetDataLength(data); 
    CMBlockBufferRef sampleData; 

    if (CMBlockBufferCopyDataBytes(data, 0, sampleSize, &sampleData) != kCMBlockBufferNoErr) { 
    VLog(@"error during copying sample buffer"); 
    } 

    // Here I tried data and sampleData CMBlockBuffer instance, but no success 
    OSStatus status = CMSampleBufferCreate(kCFAllocatorDefault, data, isDataReady, nil, nil, formatDescription, itemCount, 1, &timingInfo, 1, &sampleSize, &copyBuffer); 

    if (!self.sampleBufferArray) { 
    self.sampleBufferArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 
    //EXC_BAD_ACCESS crash when trying to add sampleBuffer to the array 
    CFArrayAppendValue(self.sampleBufferArray, copyBuffer); 
    } else { 
    CFArrayAppendValue(self.sampleBufferArray, copyBuffer); 
    } 

どのようにオーディオCMSampleBufferを詳細にコピーしますか?あなたの答えには、どんな言葉を使ってもかまいません。

+0

ディープコピーが必要なのは明らかですか? 'CMSampleBufferCreateCopy'を使うとどうなりますか? 'CMSampleBufferCopySampleBufferForRange'は深いコピーを与えますか?さらなる処理のために 'CMSampleBuffer'sが本当に必要ですか?独自の処理をしている場合は、length + pointerが便利です。 –

+0

@RhythmicFistmanはい、 'CMSampleBufferCreateCopy'を使用して、コピーされたサンプルを' CFArray'に1秒以上保持すると、 'didOutputSampleBuffer'が呼び出されるのを止める必要があることは明らかです。この[質問](https://stackoverflow.com/questions/30850676/avcaptureoutput-didoutputsamplebuffer-stops-getting-called)で簡単に再現することができます。私は 'CMSampleBufferCopySampleBufferForRange'で動作をチェックし、あなたを更新します。 –

+0

ああ、大丈夫、その保持バッファがあなたの代理コールバックをブロックすることが重要な情報です。上記のコードの実行可能なバージョンへのリンクがありますか? –

答えて

6

私は最終的に実装された実用的な解決策です。このスニペットをApple Developerテクニカルサポートに送って、受信したサンプルバッファをコピーする正しい方法かどうかを確認するよう依頼しました。基本的な考え方はコピーAudioBufferListであり、次にCMSampleBufferを作成し、AudioBufferListをこのサンプルに設定します。

 AudioBufferList audioBufferList; 
     CMBlockBufferRef blockBuffer; 
     //Create an AudioBufferList containing the data from the CMSampleBuffer, 
     //and a CMBlockBuffer which references the data in that AudioBufferList. 
     CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer, NULL, &audioBufferList, sizeof(audioBufferList), NULL, NULL, 0, &blockBuffer); 
     NSUInteger size = sizeof(audioBufferList); 
     char buffer[size]; 

     memcpy(buffer, &audioBufferList, size); 
     //This is the Audio data. 
     NSData *bufferData = [NSData dataWithBytes:buffer length:size]; 

     const void *copyBufferData = [bufferData bytes]; 
     copyBufferData = (char *)copyBufferData; 

     CMSampleBufferRef copyBuffer = NULL; 
     OSStatus status = -1; 

     /* Format Description */ 

     AudioStreamBasicDescription audioFormat = *CMAudioFormatDescriptionGetStreamBasicDescription((CMAudioFormatDescriptionRef) CMSampleBufferGetFormatDescription(sampleBuffer)); 

     CMFormatDescriptionRef format = NULL; 
     status = CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &audioFormat, 0, nil, 0, nil, nil, &format); 

     CMFormatDescriptionRef formatdes = NULL; 
     status = CMFormatDescriptionCreate(NULL, kCMMediaType_Audio, 'lpcm', NULL, &formatdes); 
     if (status != noErr) 
     { 
     NSLog(@"Error in CMAudioFormatDescriptionCreator"); 
     CFRelease(blockBuffer); 
     return; 
     } 

     /* Create sample Buffer */ 
     CMItemCount framesCount = CMSampleBufferGetNumSamples(sampleBuffer); 
     CMSampleTimingInfo timing = {.duration= CMTimeMake(1, 44100), .presentationTimeStamp= CMSampleBufferGetPresentationTimeStamp(sampleBuffer), .decodeTimeStamp= CMSampleBufferGetDecodeTimeStamp(sampleBuffer)}; 

     status = CMSampleBufferCreate(kCFAllocatorDefault, nil , NO,nil,nil,format, framesCount, 1, &timing, 0, nil, &copyBuffer); 

     if(status != noErr) { 
     NSLog(@"Error in CMSampleBufferCreate"); 
     CFRelease(blockBuffer); 
     return; 
     } 

     /* Copy BufferList to Sample Buffer */ 
     AudioBufferList receivedAudioBufferList; 
     memcpy(&receivedAudioBufferList, copyBufferData, sizeof(receivedAudioBufferList)); 

     //Creates a CMBlockBuffer containing a copy of the data from the 
     //AudioBufferList. 
     status = CMSampleBufferSetDataBufferFromAudioBufferList(copyBuffer, kCFAllocatorDefault , kCFAllocatorDefault, 0, &receivedAudioBufferList); 
     if (status != noErr) { 
     NSLog(@"Error in CMSampleBufferSetDataBufferFromAudioBufferList"); 
     CFRelease(blockBuffer); 
     return; 
     } 

コードレベルのサポートの答え:

このコードはOKになります(あなたには、いくつかの追加のエラーチェックを追加したいと思うでしょうが)。 AVCaptureAudioDataOutputデリゲートcaptureOutput:didOutputSampleBuffer:fromConnection:メソッドを実装したアプリケーションで、オーディオをキャプチャして録音する方法をテストしました。このディープコピーコードを使用するときに取得したキャプチャされたオーディオは、提供されたサンプルバッファ(ディープコピーなし)を直接使用するときと同じように見えます。

Appleデベロッパーテクニカルサポート

+0

これは素晴らしいですが、ビデオサンプルで同様の問題が発生しています(サンプルバッファを保持する必要があります)。あなたはそれを実装するためのヒントを持っていますか? –

関連する問題