2017-08-26 12 views
10

私はSwift iOSアプリケーションを30秒遅れでビデオを録画し、同じ画面で再生することを検討しています。AVFoundationを使用してキャプチャしたビデオを数秒で同時に録画して再生する方法は?

私はofficial exampleを使ってビデオを録画しています。次に、画面上の別のビューでAVPlayerを使用してself.movieFileOutput?.outputFileURLを再生するボタンを追加しました。それは私が望むものに近いですが、明らかにディスクに書き込まれたファイルの終わりに来たら再生を停止し、次のバッファリングされたチャンクが書き込まれたときには進行しません。

30秒ごとにビデオ録画を停止し、各ファイルのURLを保存して再生することができますが、ビデオのキャプチャと再生に中断が発生する可能性があります。

ビデオ録画を止めることができないようにするには、どうすればいいですか?

私はAVFoundationのドキュメントで指摘された同様の質問とすべての答えを見ました。私はAVFoundationを記録時に予測可能なビデオチャンクをメモリからディスクに書き込む方法を見つけることができませんでした。

+0

[ビデオとビデオを同時に再生する](https://stackoverflow.com/questions/7707427/record-video-and-play-video-at-the-same-time)の可能な複製 –

答えて

4

30秒分のビデオを録画してから、AVQueuePlayerにエンキューしてシームレスに再生することで、必要な機能を実現できます。ビデオチャンクを記録することはMacOSでAVCaptureFileOutputで非常に簡単になりますが、悲しいことに、iOSの上であなたはコマ落ちすることなく、新しいチャンクを作成することはできませんので、あなたはwordier、低いレベルAVAssetWriter APIを使用する必要があります。

import UIKit 
import AVFoundation 

// TODO: delete old videos 
// TODO: audio 

class ViewController: UIViewController { 
    // capture 
    let captureSession = AVCaptureSession() 

    // playback 
    let player = AVQueuePlayer() 
    var playerLayer: AVPlayerLayer! = nil 

    // output. sadly not AVCaptureMovieFileOutput 
    var assetWriter: AVAssetWriter! = nil 
    var assetWriterInput: AVAssetWriterInput! = nil 

    var chunkNumber = 0 
    var chunkStartTime: CMTime! = nil 
    var chunkOutputURL: URL! = nil 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     playerLayer = AVPlayerLayer(player: player) 
     view.layer.addSublayer(playerLayer) 

     // inputs 
     let videoCaptureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) 
     let videoInput = try! AVCaptureDeviceInput(device: videoCaptureDevice) 
     captureSession.addInput(videoInput) 

     // outputs 
     // iOS AVCaptureFileOutput/AVCaptureMovieFileOutput still don't support dynamically 
     // switching files (?) so we have to re-implement with AVAssetWriter 
     let videoOutput = AVCaptureVideoDataOutput() 
     // TODO: probably something else 
     videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main) 
     captureSession.addOutput(videoOutput) 

     captureSession.startRunning() 
    } 

    override func viewDidLayoutSubviews() { 
     super.viewDidLayoutSubviews() 
     playerLayer.frame = view.layer.bounds 
    } 

    func createWriterInput(for presentationTimeStamp: CMTime) { 
     let fileManager = FileManager.default 
     chunkOutputURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("chunk\(chunkNumber).mov") 
     try? fileManager.removeItem(at: chunkOutputURL) 

     assetWriter = try! AVAssetWriter(outputURL: chunkOutputURL, fileType: AVFileTypeQuickTimeMovie) 
     // TODO: get dimensions from image CMSampleBufferGetImageBuffer(sampleBuffer) 
     let outputSettings: [String: Any] = [AVVideoCodecKey:AVVideoCodecH264, AVVideoWidthKey: 1920, AVVideoHeightKey: 1080] 
     assetWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: outputSettings) 
     assetWriterInput.expectsMediaDataInRealTime = true 
     assetWriter.add(assetWriterInput) 

     chunkNumber += 1 
     chunkStartTime = presentationTimeStamp 

     assetWriter.startWriting() 
     assetWriter.startSession(atSourceTime: chunkStartTime) 
    } 
} 

extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate { 
    func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) { 
     let presentationTimeStamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) 

     if assetWriter == nil { 
      createWriterInput(for: presentationTimeStamp) 
     } else { 
      let chunkDuration = CMTimeGetSeconds(CMTimeSubtract(presentationTimeStamp, chunkStartTime)) 

      if chunkDuration > 30 { 
       assetWriter.endSession(atSourceTime: presentationTimeStamp) 

       // make a copy, as finishWriting is asynchronous 
       let newChunkURL = chunkOutputURL! 
       let chunkAssetWriter = assetWriter! 

       chunkAssetWriter.finishWriting { 
        print("finishWriting says: \(chunkAssetWriter.status.rawValue, chunkAssetWriter.error)") 
        print("queuing \(newChunkURL)") 
        self.player.insert(AVPlayerItem(url: newChunkURL), after: nil) 
        self.player.play() 
       } 
       createWriterInput(for: presentationTimeStamp) 
      } 
     } 

     if !assetWriterInput.append(sampleBuffer) { 
      print("append says NO: \(assetWriter.status.rawValue, assetWriter.error)") 
     } 
    } 
} 

P.S.をあなたが30秒前に何をしていたのかを知ることは非常に興味があります。あなたは何を正確に作っていますか?

+1

ありがとうあなたは返事をして、すぐに試してみるつもりです。私はいろいろな種類のスポーツの人々が彼らのトリックをすぐに見直すのを手助けするために、さまざまなアプローチを試しています。 – Maklaus

関連する問題