2015-11-20 9 views
5

Chcę nagrać serię klipów, które po odtworzeniu razem za pomocą odtwarzacza wideo lub ffmpeg -f concat odtwarzają w sposób pozorny.Segmenty ciągłe AVAssetWriter

W obu scenariuszach teraz dostaję bardzo zauważalne zakłócenie dźwięku w punkcie łączenia każdego segmentu.

Moja obecna strategia polega na utrzymaniu 2 AssetWriter wystąpień. W każdym punkcie odcięcia uruchamiam nowego pisarza, czekam, aż będzie gotowy, a następnie zacznę podawać próbki. Kiedy próbki wideo i audio są wykonywane w określonym momencie, zamykam ostatniego program piszący.

Jak mogę to zmienić, aby uzyskać ciągłe nagrywanie klipu? Jaki jest podstawowy problem?

import Foundation 
import UIKit 
import AVFoundation 

class StreamController: UIViewController, AVCaptureAudioDataOutputSampleBufferDelegate, AVCaptureVideoDataOutputSampleBufferDelegate { 
    @IBOutlet weak var previewView: UIView! 

    var closingVideoInput: AVAssetWriterInput? 
    var closingAudioInput: AVAssetWriterInput? 
    var closingAssetWriter: AVAssetWriter? 

    var currentVideoInput: AVAssetWriterInput? 
    var currentAudioInput: AVAssetWriterInput? 
    var currentAssetWriter: AVAssetWriter? 

    var nextVideoInput: AVAssetWriterInput? 
    var nextAudioInput: AVAssetWriterInput? 
    var nextAssetWriter: AVAssetWriter? 

    var previewLayer: AVCaptureVideoPreviewLayer? 
    var videoHelper: VideoHelper? 

    var startTime: NSTimeInterval = 0 
    override func viewDidLoad() { 
     super.viewDidLoad() 
     startTime = NSDate().timeIntervalSince1970 
     createSegmentWriter() 
     videoHelper = VideoHelper() 
     videoHelper!.delegate = self 
     videoHelper!.startSession() 
     NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "createSegmentWriter", userInfo: nil, repeats: true) 
    } 

    func createSegmentWriter() { 
     print("Creating segment writer at t=\(NSDate().timeIntervalSince1970 - self.startTime)") 
     nextAssetWriter = try! AVAssetWriter(URL: NSURL(fileURLWithPath: OutputFileNameHelper.instance.pathForOutput()), fileType: AVFileTypeMPEG4) 
     nextAssetWriter!.shouldOptimizeForNetworkUse = true 

     let videoSettings: [String:AnyObject] = [AVVideoCodecKey: AVVideoCodecH264, AVVideoWidthKey: 960, AVVideoHeightKey: 540] 
     nextVideoInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoSettings) 
     nextVideoInput!.expectsMediaDataInRealTime = true 
     nextAssetWriter?.addInput(nextVideoInput!) 

     let audioSettings: [String:AnyObject] = [ 
       AVFormatIDKey: NSNumber(unsignedInt: kAudioFormatMPEG4AAC), 
       AVSampleRateKey: 44100.0, 
       AVNumberOfChannelsKey: 2, 
     ] 
     nextAudioInput = AVAssetWriterInput(mediaType: AVMediaTypeAudio, outputSettings: audioSettings) 
     nextAudioInput!.expectsMediaDataInRealTime = true 
     nextAssetWriter?.addInput(nextAudioInput!) 

     nextAssetWriter!.startWriting() 
    } 

    override func viewDidAppear(animated: Bool) { 
     super.viewDidAppear(animated) 
     previewLayer = AVCaptureVideoPreviewLayer(session: videoHelper!.captureSession) 
     previewLayer!.frame = self.previewView.bounds 
     previewLayer!.videoGravity = AVLayerVideoGravityResizeAspectFill 
     if ((previewLayer?.connection?.supportsVideoOrientation) != nil) { 
      previewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.LandscapeRight 
     } 
     self.previewView.layer.addSublayer(previewLayer!) 
    } 

    func closeWriter() { 
     if videoFinished && audioFinished { 
      let outputFile = closingAssetWriter?.outputURL.pathComponents?.last 
      closingAssetWriter?.finishWritingWithCompletionHandler() { 
       let delta = NSDate().timeIntervalSince1970 - self.startTime 
       print("segment \(outputFile) finished at t=\(delta)") 
      } 
      self.closingAudioInput = nil 
      self.closingVideoInput = nil 
      self.closingAssetWriter = nil 
      audioFinished = false 
      videoFinished = false 
     } 
    } 

    func closingVideoFinished() { 
     if closingVideoInput != nil { 
      videoFinished = true 
      closeWriter() 
     } 
    } 

    func closingAudioFinished() { 
     if closingAudioInput != nil { 
      audioFinished = true 
      closeWriter() 
     } 
    } 

    var closingTime: CMTime = kCMTimeZero 
    var audioFinished = false 
    var videoFinished = false 
    func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBufferRef, fromConnection connection: AVCaptureConnection!) { 
     let sampleTime: CMTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) 
     if let nextWriter = nextAssetWriter { 
      if nextWriter.status.rawValue != 0 { 
       print("Switching asset writers at t=\(NSDate().timeIntervalSince1970 - self.startTime)") 

       closingAssetWriter = currentAssetWriter 
       closingVideoInput = currentVideoInput 
       closingAudioInput = currentAudioInput 

       currentAssetWriter = nextAssetWriter 
       currentVideoInput = nextVideoInput 
       currentAudioInput = nextAudioInput 

       nextAssetWriter = nil 
       nextVideoInput = nil 
       nextAudioInput = nil 

       closingTime = sampleTime 
       currentAssetWriter!.startSessionAtSourceTime(sampleTime) 
      } 
     } 

     if currentAssetWriter != nil { 
      if let _ = captureOutput as? AVCaptureVideoDataOutput { 
       if (CMTimeCompare(sampleTime, closingTime) < 0) { 
        if closingVideoInput?.readyForMoreMediaData == true { 
         closingVideoInput?.appendSampleBuffer(sampleBuffer) 
        } 
       } else { 
        closingVideoFinished() 
        if currentVideoInput?.readyForMoreMediaData == true { 
         currentVideoInput?.appendSampleBuffer(sampleBuffer) 
        } 
       } 

      } else if let _ = captureOutput as? AVCaptureAudioDataOutput { 
       if (CMTimeCompare(sampleTime, closingTime) < 0) { 
        if currentAudioInput?.readyForMoreMediaData == true { 
         currentAudioInput?.appendSampleBuffer(sampleBuffer) 
        } 
       } else { 
        closingAudioFinished() 
        if currentAudioInput?.readyForMoreMediaData == true { 
         currentAudioInput?.appendSampleBuffer(sampleBuffer) 
        } 
       } 
      } 
     } 
    } 

    override func shouldAutorotate() -> Bool { 
     return true 
    } 

    override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask { 
     return [UIInterfaceOrientationMask.LandscapeRight] 
    } 
} 
+1

Wszelkie aktualizacje na temat tego, jak do tego doszło? – Andy

+0

Próbuję rozwiązać podobny problem http://stackoverflow.com/questions/43322157/split-cmsamplebufferref-containing-audio –

Odpowiedz

1

Myślę, że przyczyną jest ze względu na wideo i audio CMSampleBuffer ów reprezentujących różne przedziały czasowe. Musisz podzielić i dołączyć do audio CMSampleBuffer s, aby umożliwić ich bezproblemowe łączenie się z liniami czasowymi AVAssetWriter, co prawdopodobnie powinno być oparte na sygnaturach czasowych prezentacji wideo.

Dlaczego dźwięk powinien być zmieniany, a nie wideo? Wydaje się być asymetryczna, ale myślę, że to dlatego, że dźwięk ma wyższą częstotliwość próbkowania.

p.s. W rzeczywistości tworzenie nowych buforów próbek podzielonych wygląda na zastraszające. CMSampleBufferCreate ma tonę argumentów. CMSampleBufferCopySampleBufferForRange może być łatwiejszy i bardziej wydajny w użyciu.

+0

Może być możliwe użycie CMSampleBufferCopySampleBufferForRange do wycięcia fragmentu potrzebnej próbki z jednej strony, a także uzyskać resztę na następny zestaw AVA? Zastanawiam się, czy dana próbka sama może przeskoczyć granicę czasu, ale prawidłowo partycjonowanie próbek może wystarczyć. Wygląda na to, że bufor próbki zwykle (zawsze?) Zawiera pojedynczą klatkę wideo, ale dźwięk pobiera wiele próbek, stąd asymetria. –

+0

'CMSampleBufferCopySampleBufferForRange' wygląda obiecująco, a wideo zawsze wynosi 1 klatkę. Masz rację, nie ma powodu, dla którego próbka "mogłaby" spaść na granice ramy. Ale może AVFoundation cię przerośnie - zwisający bufor audio może "po prostu" działać, jeśli dodasz go zarówno do obecnych, jak i przyszłych pisarzy. A może powinieneś podzielić pliki na granice bufora audio lub granice próbek audio, powtarzając/wycinając klatki wideo w razie potrzeby. Zapewni to stałą długość pliku dzięki stałej częstotliwości próbkowania audio. –

+1

podział na granicy dźwięku jest prawdopodobnie prostszy. Spróbuję tego. Stała długość segmentu pliku jest faktycznie wymagana + - sekundy, ale będzie to niezła funkcja, niezależnie od tego. –