2017-05-14 3 views
1

Ich verwende einen AVCaptureSession, um Video- und Audioeingang zu verwenden und ein H.264-Video mit AVAssetWriter zu codieren.Beschädigte Videoaufnahme von Audio und Video mit AVAssetWriter

Wenn ich das Audio nicht schreibe, wird das Video wie erwartet codiert. Aber wenn ich den Ton schreibe, bekomme ich ein korruptes Video.

Wenn ich die Audio inspizieren CMSampleBuffer zum AVAssetWriter versorgt wird es diese Informationen zeigt:

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)} 

Da es LPCM-Audio liefert, I die AVAssetWriterInput mit dieser Einstellung für die Schall konfiguriert haben (I haben beide eine versuchte und zwei Kanäle):

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) 

Wenn ich die lpcm Einstellung oben verwende, kann ich das Video mit keiner Anwendung öffnen. Ich habe versucht mit kAudioFormatMPEG4AAC und kAudioFormatAppleLossless und ich bekomme immer noch eine beschädigte Video, aber ich bin in der Lage, das Video mit QuickTime Player 8 (nicht QuickTime Player 7) zu sehen, aber es ist verwirrt über die Dauer des Videos und kein Ton wird gespielt.

Wenn die Aufnahme abgeschlossen Ich rufe:

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

Dies ist, wie die AVCaptureSession konfiguriert wird:

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() 
} 

Die AVCaptureVideoDataOutput Delegatmethode:

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 ...// 
} 

Welche Anrufe:

func audioSampleUpdated(sampleBuffer: CMSampleBuffer) { 
    if (isRecording) { 
     while !assetWriterAudioInput.isReadyForMoreMediaData {} 
     if (!assetWriterAudioInput.append(sampleBuffer)) { 
      print("Unable to write to audio input"); 
     } 
    } 
} 

Wenn ich den oben genannten Anruf assetWriterAudioInput.append() deaktiviere, ist das Video nicht beschädigt, aber natürlich habe ich keine Audio-Codierung. Wie kann ich sowohl die Video- als auch die Audiocodierung nutzen?

Antwort

1

Ich habe es herausgefunden. Ich habe die Quellzeit assetWriter.startSession auf 0 gesetzt und dann die Startzeit von der aktuellen CACurrentMediaTime() zum Schreiben der Pixeldaten subtrahiert.

Ich änderte die assetWriter.startSession Quellzeit auf die CACurrentMediaTime() und subtrahieren Sie nicht die aktuelle Zeit beim Schreiben des Videoframes.

Old Start Session-Code:

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

New Code, das funktioniert:

let presentationStartTime = CMTimeMakeWithSeconds(CACurrentMediaTime(), 240) 

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