2017-10-24 2 views
10

AVCaptureAudioDataOutputSampleBufferDelegate의 captureOutput에 의해 반환 된 CMSampleBuffer의 복사본을 만들려고합니다.오디오의 딥 복사

내가 가지고있는 문제는 오랫동안 CFArray에 유지 한 후 대리인 메서드 captureOutput:didOutputSampleBuffer:fromConnection:에서 삭제 된 것입니다.

분명히 추가 처리를 위해 들어오는 버퍼의 전체 복사본을 만들어야합니다. 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를 완전 복사합니까? 귀하의 답변에 어떤 언어 (신속한/객관적인 -c)라도 자유롭게 사용하십시오.

+0

딥 복사본이 필요합니까? 'CMSampleBufferCreateCopy'를 사용할 때 어떤 일이 발생합니까? 'CMSampleBufferCopySampleBufferForRange'는 깊은 복사본을 줄까요? 추가 처리를 위해'CMSampleBuffer'가 정말로 필요합니까? 자신 만의 처리를하는 경우에는 길이 + 포인터가 더 편리 할 수 ​​있습니다. –

+0

@RhythmicFistman 예,'CMSampleBufferCreateCopy'를 사용하고'CFArray'에 복사 된 샘플을 1 이상 유지하면,'didOutputSampleBuffer'가 호출되는 것을 막을 필요가 있습니다. 이 [질문] (https://stackoverflow.com/questions/30850676/avcaptureoutput-didoutputsamplebuffer-stops-getting-called)을 사용하면 쉽게 재현 할 수 있습니다. 나는'CMSampleBufferCopySampleBufferForRange'로 동작을 점검하고 당신을 업데이트 할 것입니다. –

+0

아, 오케이, 대기 버퍼가 대리인 콜백을 차단한다는 것이 중요한 정보입니다. 위 코드의 실행 가능한 버전에 대한 링크가 있습니까? –

답변

6

마지막으로 구현 한 작업 솔루션입니다. 이 스 니펫을 Apple Developer Technical Support에 보냈고 들어오는 샘플 버퍼를 복사하는 올바른 방법인지 확인하도록 요청했습니다. 기본 아이디어는 복사 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; 
     } 

코드 수준의 지원 답변 :

이 코드는 괜찮아 보이는 (당신이 몇 가지 추가 오류 검사를 추가 할 수 있습니다하지만). AVCaptureAudioDataOutput 대리자 captureOutput:didOutputSampleBuffer:fromConnection: 메서드를 구현하는 응용 프로그램에서 오디오를 캡처하고 녹음하는 데 성공적으로 테스트했습니다. 이 깊은 복사본 코드를 사용할 때 캡처 된 오디오는 제공된 샘플 버퍼를 직접 사용할 때와 똑같은 것으로 나타납니다 (딥 복사본이없는 경우).

Apple 개발자 기술 지원

+0

이것은 훌륭하지만 비디오 샘플과 비슷한 문제가 있습니다 (샘플 버퍼를 유지해야합니다). 그것을 구현하는 방법에 대한 조언이 있습니까? –