タイムラプス撮影を自作アプリでやってみたかったのでやり方を調べてみました。
タイムラプスとは
「デジタルカメラを用いて連続したスチール画像を撮影し、それを素材として作った動画のこと。インターバル動画ともいう。一定間隔で連続撮影した静止画を、パソコンのソフト上で組み合わせることにより動画を作る。」 - コトバンクより
方針
つまり一定の間隔で映像を保存していった動画を作ればタイムラプス動画を撮れる、ということのようです。
というわけでここではビデオを毎回 4フレーム ドロップ(捨てて)しつつ映像を保存してみます。
1.AVCaptureSession を使って撮影のセットアップ
AVCaptureSession を使って撮影の準備をします。
フレームをドロップしつつ保存するため、撮影する際は Iフレームだけを吐いてもらように sessionPreset を AVCaptureSessionPresetiFrame1280x720 に指定します。
let captureSession = AVCaptureSession()
private let videoDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
private let audioDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeAudio)
func startup(){
videoDevice.activeVideoMinFrameDuration = CMTimeMake(1, 30)
do
{
let videoInput = try AVCaptureDeviceInput(device: videoDevice) as AVCaptureDeviceInput
captureSession.addInput(videoInput)
}
catch let error as NSError {
Logger.log(error.localizedDescription)
}
do
{
let audioInput = try AVCaptureDeviceInput(device: audioDevice) as AVCaptureDeviceInput
captureSession.addInput(audioInput)
}
catch let error as NSError {
Logger.log(error.localizedDescription)
}
// video output
let videoDataOutput = AVCaptureVideoDataOutput()
videoDataOutput.setSampleBufferDelegate(self, queue: recordingQueue)
videoDataOutput.alwaysDiscardsLateVideoFrames = true
videoDataOutput.videoSettings = [
kCVPixelBufferPixelFormatTypeKey : Int(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)
]
captureSession.addOutput(videoDataOutput)
captureSession.sessionPreset = AVCaptureSessionPresetiFrame1280x720
height = videoDataOutput.videoSettings["Height"] as! Int!
width = videoDataOutput.videoSettings["Width"] as! Int!
// audio output
let audioDataOutput = AVCaptureAudioDataOutput()
audioDataOutput.setSampleBufferDelegate(self, queue: recordingQueue)
captureSession.addOutput(audioDataOutput)
captureSession.startRunning()
}
2.フレームをドロップしつつファイルに保存する
重要なのは captureOutput(captureOutput: didOutputSampleBuffer fromConnection ) です。
この関数内で sampleBuffer にビデオとオーディオが渡されます。
今回はタイムラプス動画なので、
- 音声は不要なので保存しない
- ビデオが渡される回数をカウントし、5回に1回ビデオを保存する
- その際はタイムスタンプを修正しておく
let skipFrameSize = 5
private var currentSkipCount = 0
func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!){
...
// オーディオは書き込まない
if !isVideo {
return
}
// 決めた回数分フレームをドロップする
currentSkipCount += 1
guard currentSkipCount == skipFrameSize else { return }
currentSkipCount = 0
// タイムスタンプを書き換える
var buffer = sampleBuffer
if lasttimeStamp.value == 0 {
lasttimeStamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
} else {
lasttimeStamp = CMTimeAdd(lasttimeStamp, CMTimeMake(1, 30))
buffer = setTimeStamp(sampleBuffer, newTime: lasttimeStamp)
}
Logger.log("write")
// ファイルに書き出す
videoWriter?.write(buffer, isVideo: isVideo)
}
サンプルコード
動作するプロジェクト一式はこちらです。