📹

AVFoundationを使用したビデオ撮影機能の実装

4 min read

AVFoundationを使用したビデオ撮影機能のミニマム実装について書きます。本記事を読むことでビデオ撮影を行う設定から、撮影したビデオの保存を行うまでの流れを理解することができます。iPhoneのカメラを立ち上げて画面にカメラの映像を表示するまでの処理はAVFoundationを使用した写真撮影機能の実装をご参照ください。サンプルコードは以下に置いています。

https://github.com/NAOYA-MAEDA-DEV/AVFoundation-Camera-App

大きく分けて5ステップの処理を記述することでビデオ撮影機能を実装することができます。

1. Info.plistの設定

今回はビデオ撮影にiPhoneのマイクを使用します。アプリからカメラやマイクといったiPhoneのデバイスにアクセスする必要がある時は、Info.plist にキーと使用目的を記載する必要があります。iPhoneのマイクを使用するために以下の項目を追加します。

  • Privacy - Microphone Usage Description

2. キャプチャセッションの再構築

AVFoundationを使用した写真撮影機能の実装の記事で写真撮影を行うために入力ソースはiPhoneのバックカメラ(AVCaptureDevice)、出力のタイプは写真(AVCapturePhotoOutput)をセットする必要があると説明しました。ビデオ撮影を行うために入力ソースはiPhoneのバックカメラ(AVCaptureDevice)に加えてiPhoneのマイク(AVCaptureDevice)を追加、出力のタイプはムービー(AVCaptureMovieFileOutput)をセットする必要があります。

CameraViewController.swift
private func chageVideoMode() {
    self.captureSession.beginConfiguration()
    
    if let audioDevice = AVCaptureDevice.default(for: .audio) {
        do {
	    // 入力ソースの生成
            let input = try AVCaptureDeviceInput(device: audioDevice)
	    // 入力ソースをキャプチャセッションにセット
            if self.captureSession.canAddInput(input) {
                self.captureSession.addInput(input)
                self.audioDeviceInput = input
            }
        } catch {
            print("Error input audio device to capture session : \(error)")
        }
    }
    
    // 出力タイプをキャプチャセッションにセット
    if self.captureSession.canAddOutput(self.movieFileOutput) {
        self.captureSession.addOutput(self.movieFileOutput)
    }
    
    // 不要になった出力タイプをキャプチャセッションから削除
    self.captureSession.removeOutput(self.photoOutput)
    
    self.captureSession.commitConfiguration()
}

3. ビデオ撮影の開始

AVCaptureMovieFileOutputstartRecordingメソッドを実行するとビデオ撮影が開始されます。startRecordingメソッドの引数にビデオを一時的に保存するパスと、ビデオの保存が完了した時にコールされるデリゲートの移譲先を指定することでビデオ撮影が開始されます。今回はselfつまりCameraViewController自身を委譲先にセットしています。

CameraViewController.swift
let tempDirectory: URL = URL(fileURLWithPath: NSTemporaryDirectory())
let fileURL: URL = tempDirectory.appendingPathComponent("sample.mov")
self.movieFileOutput.startRecording(to: fileURL, recordingDelegate: self)

4. ビデオ撮影の終了

AVCaptureMovieFileOutputstopRecordingメソッドを実行するとビデオ撮影が終了します。

CameraViewController.swift
self.movieFileOutput.stopRecording()

5. 撮影したビデオの保存

ステップ2でデリゲートの移譲先をCameraViewControllerに指定しました。撮影したビデオファイルの作成が完了すると、以下のデリゲートメソッドがコールされます。引数のurlが撮影したビデオが一時保存されているパスです。PHPhotoLibrary.shared().performChangesを使用してフォトライブラリにビデオを保存しています。

CameraViewController.swift
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
    PHPhotoLibrary.shared().performChanges({
        PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputFileURL)
    }) { _, error in
        DispatchQueue.main.async {
            self.shutterButton.isEnabled = true
            self.changeModeSegmentControl.isEnabled = true
        }

        if let error = error {
            print(error)
        }
        
        cleanup()
    }
    
    // Clean file path.
    func cleanup() {
        let path = outputFileURL.path
        if FileManager.default.fileExists(atPath: path) {
            do {
                try FileManager.default.removeItem(atPath: path)
            } catch {
                print("Error clean up: \(error)")
            }
        }
    }
}

5. おまけ

キャプチャセッションをビデオ撮影向けに変更した後に写真撮影を行いたい時は、以下の様にキャプチャセッションを再構築することで写真撮影を行うことができる様になります。

CameraViewController.swift
private func chagePhotoMode() {
    self.captureSession.beginConfiguration()

    if let input = self.audioDeviceInput {
        self.captureSession.removeInput(input)
    }

    if self.captureSession.canAddOutput(self.photoOutput) {
        self.captureSession.addOutput(self.photoOutput)
    }

    self.captureSession.removeOutput(self.movieFileOutput)
    
    self.captureSession.commitConfiguration()
}

以上になります。
最後までお読みいただきありがとうございました🎉