2017-05-14 24 views
1

AVCaptureSession을 사용하여 비디오 및 오디오 입력을 사용하고 AVAssetWriter과 함께 H.264 비디오를 인코딩합니다.AVAssetWriter를 사용하여 오디오 및 비디오 캡처가 손상된 비디오

오디오를 쓰지 않으면 비디오가 예상대로 인코딩됩니다. 그러나 오디오를 쓰면 비디오가 손상됩니다. I는 CMSampleBufferAVAssetWriter 공급되는 오디오를 검사하면

가이 정보를 보여준다

invalid = NO 
dataReady = YES 
makeDataReadyCallback = 0x0 
makeDataReadyRefcon = 0x0 
formatDescription = <CMAudioFormatDescription 0x17410ba30 [0x1b3a70bb8]> { 
mediaType:'soun' 
mediaSubType:'lpcm' 
mediaSpecific: { 
    ASBD: { 
     mSampleRate: 44100.000000 
     mFormatID: 'lpcm' 
     mFormatFlags: 0xc 
     mBytesPerPacket: 2 
     mFramesPerPacket: 1 
     mBytesPerFrame: 2 
     mChannelsPerFrame: 1 
     mBitsPerChannel: 16  } 
    cookie: {(null)} 
    ACL: {(null)} 
    FormatList Array: {(null)} 
} 
extensions: {(null)} 

는 LPCM 오디오를 공급하기 때문에, I를 음이 설정과 AVAssetWriterInput 구성한을 (I 모두를 시도 및 두 개의 채널) :

var channelLayout = AudioChannelLayout() 
memset(&channelLayout, 0, MemoryLayout<AudioChannelLayout>.size); 
channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono 

let audioOutputSettings:[String: Any] = [AVFormatIDKey as String:UInt(kAudioFormatLinearPCM), 
              AVNumberOfChannelsKey as String:1, 
              AVSampleRateKey as String:44100.0, 
              AVLinearPCMIsBigEndianKey as String:false, 
              AVLinearPCMIsFloatKey as String:false, 
              AVLinearPCMBitDepthKey as String:16, 
              AVLinearPCMIsNonInterleaved as String:false, 
              AVChannelLayoutKey: NSData(bytes:&channelLayout, length:MemoryLayout<AudioChannelLayout>.size)] 

self.assetWriterAudioInput = AVAssetWriterInput(mediaType: AVMediaTypeAudio, outputSettings: audioOutputSettings) 
self.assetWriter.add(self.assetWriterAudioInput) 

위의 lpcm 설정을 사용할 때 응용 프로그램에서 비디오를 열 수 없습니다. kAudioFormatMPEG4AACkAudioFormatAppleLossless을 사용해 보았는데 여전히 비디오가 손상되었지만 QuickTime Player 8 (QuickTime Player 7 아님)을 사용하여 비디오를 볼 수는 있지만 비디오 재생 시간에 대해서는 혼란스럽고 사운드는 재생되지 않습니다. 기록

내가 부르고 완료 :

func endRecording(_ completionHandler: @escaping() ->()) { 
    isRecording = false 
    assetWriterVideoInput.markAsFinished() 
    assetWriterAudioInput.markAsFinished() 
    assetWriter.finishWriting(completionHandler: completionHandler) 
} 

이것은 AVCaptureSession가 구성되는 방식입니다 :

func setupCapture() { 

    captureSession = AVCaptureSession() 

    if (captureSession == nil) { 
     fatalError("ERROR: Couldnt create a capture session") 
    } 

    captureSession?.beginConfiguration() 
    captureSession?.sessionPreset = AVCaptureSessionPreset1280x720 

    let frontDevices = AVCaptureDevice.devices().filter{ ($0 as AnyObject).hasMediaType(AVMediaTypeVideo) && ($0 as AnyObject).position == AVCaptureDevicePosition.front } 

    if let captureDevice = frontDevices.first as? AVCaptureDevice { 
     do { 
      let videoDeviceInput: AVCaptureDeviceInput 
      do { 
       videoDeviceInput = try AVCaptureDeviceInput(device: captureDevice) 
      } 
      catch { 
       fatalError("Could not create AVCaptureDeviceInput instance with error: \(error).") 
      } 
      guard (captureSession?.canAddInput(videoDeviceInput))! else { 
       fatalError() 
      } 
      captureSession?.addInput(videoDeviceInput) 
     } 
    } 

    do { 
     let audioDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeAudio) 
     let audioDeviceInput: AVCaptureDeviceInput 
     do { 
      audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice) 
     } 
     catch { 
      fatalError("Could not create AVCaptureDeviceInput instance with error: \(error).") 
     } 
     guard (captureSession?.canAddInput(audioDeviceInput))! else { 
      fatalError() 
     } 
     captureSession?.addInput(audioDeviceInput) 
    } 

    do { 
     let dataOutput = AVCaptureVideoDataOutput() 
     dataOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String : kCVPixelFormatType_32BGRA] 
     dataOutput.alwaysDiscardsLateVideoFrames = true 
     let queue = DispatchQueue(label: "com.3DTOPO.videosamplequeue") 
     dataOutput.setSampleBufferDelegate(self, queue: queue) 
     guard (captureSession?.canAddOutput(dataOutput))! else { 
      fatalError() 
     } 
     captureSession?.addOutput(dataOutput) 

     videoConnection = dataOutput.connection(withMediaType: AVMediaTypeVideo) 
    } 

    do { 
     let audioDataOutput = AVCaptureAudioDataOutput() 
     let queue = DispatchQueue(label: "com.3DTOPO.audiosamplequeue") 
     audioDataOutput.setSampleBufferDelegate(self, queue: queue) 
     guard (captureSession?.canAddOutput(audioDataOutput))! else { 
      fatalError() 
     } 
     captureSession?.addOutput(audioDataOutput) 

     audioConnection = audioDataOutput.connection(withMediaType: AVMediaTypeAudio) 
    } 

    captureSession?.commitConfiguration() 

    // this will trigger capture on its own queue 
    captureSession?.startRunning() 
} 

AVCaptureVideoDataOutput 위임 방법 : 호출

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) { 
    // func captureOutput(captureOutput: AVCaptureOutput, sampleBuffer: CMSampleBuffer, connection:AVCaptureConnection) { 

    var error: CVReturn 

    if (connection == audioConnection) { 
     delegate?.audioSampleUpdated(sampleBuffer: sampleBuffer) 
     return 
    } 

    // ... Write video buffer ...// 
} 

:

위의 assetWriterAudioInput.append() 호출을 사용하지 않으면 비디오가 손상되지 않지만 물론 인코딩 된 오디오가 없습니다. 비디오 및 오디오 인코딩을 모두 작동 시키려면 어떻게합니까?

답변

1

나는 그것을 알아 냈다. assetWriter.startSession 소스 시간을 0으로 설정 한 다음 픽셀 데이터 쓰기를 위해 현재 CACurrentMediaTime()에서 시작 시간을 뺍니다.

assetWriter.startSession 소스 시간을 CACurrentMediaTime()으로 변경했으며 비디오 프레임을 쓸 때 현재 시간을 빼지 않았습니다.

올드 시작 세션 코드 :

assetWriter.startWriting() 
assetWriter.startSession(atSourceTime: kCMTimeZero) 

새로운 코드를 작동합니다

let presentationStartTime = CMTimeMakeWithSeconds(CACurrentMediaTime(), 240) 

assetWriter.startWriting() 
assetWriter.startSession(atSourceTime: presentationStartTime)