【iOS】AVFoundationを使用してLive Photos撮影機能を実装する
AVFoundationのCapture APIを使用して、Live Photos撮影機能を実装する方法について書きます。以下の記事を先にお読みいただくと、本記事の内容をスムーズに理解することができます。
本記事をお読みいただくことで、以下の内容を学ぶことができます。
- 静止画データと動画データを同時に出力するキャプチャセッションの設定方法
- Live Photosの撮影を開始する方法
- デリゲートメソッドを介した静止画データと動画データの取得方法
- Live Photosデータを写真アルバムに追加する方法
本記事の内容は、「詳解 AVFoundation Capture」から一部の説明を抜粋した内容になっています。
アプリがカメラやマイク、写真アルバムへアクセスするために必要なアクセス権を取得する方法については、本記事では割愛しています。「詳解 AVFoundation Capture」の「第2章 アクセス権」または、以下のドキュメントをご参照ください。Live Photosとは
Live Photosとは、静止画データと撮影前後1.5秒を含む3秒のショート動画を、同時に記録することができるアセットタイプです。iOSにプリインストールされている写真アプリでは、Live Photosデータを選択すると写真が表示され、表示中の写真を長押しすると、写真と一緒に記録した3秒の動画を再生することができます。
キャプチャセッションの設定
静止画データに対して3秒の動画データを関連づけて、一つのアセットデータとして写真アルバムに追加することで、写真アルバム内でLive Photosデータとして扱うことができます。(Live Photosというデータ自体を撮影しているわけではありません) つまり、写真アルバムにLive Photosデータを追加するためには、静止画データと3秒の動画データを出力するキャプチャセッションの設定を行う必要があります。
入力設定
静止画データと音声付きの動画データを取得するためには、カメラからの入力を表すAVCaptureDeviceInput
オブジェクトに加え、マイクからの入力を表すAVCaptureDeviceInput
オブジェクトをAVCaptureSession
オブジェクトに追加します。以下のソースコードでは、マイクからの入力を表すAVCaptureDeviceInput
オブジェクトを、AVCaptureSession
オブジェクトに追加しています。
出力設定
AVCapturePhotoOutput
は、静止画データを出力するためのワークフローを提供するクラスです。
AVCaptureOutput
を継承しており、AVCaptureSession
オブジェクトに追加することで、AVCapturePhotoCaptureDelegate
プロトコルで定義されているデリゲートメソッドを介して、撮影した静止画データと3秒の動画データを取得することができます。
isLivePhotoCaptureEnabled
は、Live Photos撮影用のキャプチャパイプラインを、構築するかどうかを表すBool 値が代入されているプロパティです。
true
を代入することで、静止画データと3 秒の動画データを出力するためのキャプチャパイプラインが自動で構築されます。そして、Live Photosデータに対応する撮影フォーマットの静止画データ・動画データを出力するために、AVCaptureSession
オブジェクトのsessionPreset
プロパティに、AVCaptureSession.Preset.photo
オブジェクトを代入します。以下のソースコードでは、iPhone 背面の広角レンズカメラが記録した静止画データと、動画データを出力するキャプチャセッションの設定を行っています。
Live Photos撮影の開始
AVCapturePhotoOutput
オブジェクトのcapturePhoto(with:delegate:)
メソッドで、Live Photos撮影を開始することができます。
with
引数には、AVCapturePhotoSettings
オブジェクトを指定します。AVCapturePhotoSettings
は撮影時のフラッシュライトや、ファイル形式の変更など、静止画データ撮影オプション設定を行うことができるクラスです。Live Photos撮影を行う時は、AVCapturePhotoSettings
オブジェクトのlivePhotoMovieFileURL
プロパティに、撮影した動画データを保存するドキュメントフォルダパスを表すURL
オブジェクトを指定します。以下のソースコードでは、UUID
をファイル名とするドキュメントフォルダパスを表すURL
オブジェクトを、livePhotoMovieFileURL
プロパティに指定しています。
delegate
引数には、AVCapturePhotoCaptureDelegate
プロトコルに準拠するオブジェクトを指定します。AVCapturePhotoCaptureDelegate
プロトコルで定義されているデリゲートメソッドについては後述しますが、上記のソースコードでは、self
を指定しています。AVCapturePhotoCaptureDelegate
プロトコルに準拠したオブジェクトに、Live Photos撮影の開始から完了するまでに呼び出されるデリゲートメソッドを定義します。
プロトコルへの準拠
capturePhoto(with:delegate:)
メソッドのdelegate
引数に、Live Photos撮影の開始から完了するまでに呼び出されるデリゲートメソッドの委譲先を指定しました。今回は、移譲先にself
を指定したので、capturePhoto(with:delegate:)
メソッドを実行するクラス自身にデリゲートメソッドを定義します。デリゲートメソッドを定義するために、self
をAVCapturePhotoCaptureDelegate
プロトコルに準拠させます。
撮影の開始
photoOutput(_:willBeginCaptureFor:)
は、AVCapturePhotoCaptureDelegate
プロトコルで定義されているデリゲートメソッドです。
Live Photos撮影を開始する直前に呼び出されます。iOSにプリインストールされているカメラアプリで、Live Photosを撮影する時のように、撮影を開始したタイミングで画面上部に「LIVE」テキストを表示する時は、このデリゲートメソッドが呼び出されたタイミングで、「LIVE」テキストを画面に表示する処理を行います。
静止画データの取得
photoOutput(_:didFinishProcessingPhoto:error:)
は、AVCapturePhotoCaptureDelegate
プロトコルで定義されているデリゲートメソッドです。
静止画データが出力される時に呼び出されます。以下のソースコードでは、fileDataRepresentation()
メソッドで、撮影した静止画データを表すData
オブジェクトを取得し、compressedData
変数に代入しています。
静止画データを表すData
オブジェクトは、別のデリゲートメソッド内でLive Photosデータを写真アルバムへ追加する処理を行う時に使用するため、変数で保持しておきます。
動画データ撮影の完了
photoOutput(_:didFinishRecordingLivePhotoMovieForEventualFileAt:resolvedSettings:)
は、AVCapturePhotoCaptureDelegate
プロトコルで定義されているデリゲートメソッドです。
Live Photosデータを生成する時に使用する動画データ撮影が完了した時に呼び出されます。iOSにプリインストールされているカメラアプリで、LivePhotosを撮影する時のように、撮影を完了したタイミングで画面上部に表示されている「LIVE」テキストを非表示にする時は、このデリゲートメソッドが呼び出されたタイミングで、「LIVE」テキストを非表示にする処理を行います。
動画データの取得
photoOutput(_:didFinishProcessingLivePhotoToMovieFileAt:duration:photoDisplayTime:resolvedSettings:error:)
は、AVCapturePhotoCaptureDelegate
プロトコルで定義されているデリゲートメソッドです。
Live Photosデータを生成する時に使用する動画データが出力される時に呼び出されます。outputFileURL
引数には、Live Photosデータ用動画データが書き込まれているドキュメントフォルダパスを表すURL
オブジェクトが代入されています。outputFileURL
引数に代入されているURL
オブジェクトは、別のデリゲートメソッド内で、Live Photosデータを写真アルバムへ追加する処理を行う時に使用するため、変数で保持しておきます。
Live Photosデータを写真アルバムへ追加
photoOutput(_:didFinishCaptureFor:error:)
は、AVCapturePhotoCaptureDelegate
プロトコルで定義されているデリゲートメソッドです。
撮影に関連する全ての処理が完了した時に呼び出されます。Live Photosデータを写真アルバムへ追加する処理は、photoOutput(_:didFinishCaptureFor:error:)
メソッド内で行います。PhotoKitフレームワークのAPIを使用して、撮影した静止画データに対して動画データを関連づけて、
Live Photosデータとして写真アルバムに追加する処理を行います。写真アルバムに変更を加える時は、PHPhotoLibrary
のperformChanges(_:)
メソッドを実行します。
performChanges(_:)
は、第一引数に指定した写真アルバムに変更を加える処理を、PhotoKit にリクエストするメソッドです。
第一引数には、写真アルバムを操作する処理をクロージャで記述します。写真アルバムに対する変更は、PHAssetCreationRequest
オブジェクトのメソッドで行いますPHAssetCreationRequest
は、写真アルバムに対する変更をPhotoKit にリクエストすることができるメソッドを持つクラスです。
PHAssetCreationRequest
オブジェクトは、forAsset()
メソッドで取得することができます。
第一引数に指定したクロージャ内で実行したPHAssetCreationRequest
オブジェクトのメソッドの処理は、写真アルバムに変更を加える一つのタスクとしてPhotoKit に通知されます。つまり、第一引数に指定したクロージャ内で、静止画データと動画データを一つのアセットデータとして写真アルバムに追加する処理を行うことで、両データを一つのアセットデータ(=Live Photosデータ) として、写真アルバムに追加することができます。まず、PHAssetCreationRequest
オブジェクトのaddResource(with:data:options:)
メソッドを実行します。
addResource(with:data:options:)
は、data
引数に指定したData
オブジェクトのアセットデータを、写真アルバムに追加することをPhotoKitにリクエストするメソッドです。
with
引数には、写真アルバムに追加するアセットデータのタイプを、PHAssetResourceType
オブジェクトで指定します。今回は、静止画データを写真アルバムに追加するので、上記のソースコードでは、with
引数に.photo
を指定しています。data
引数には、写真アルバムに追加するアセットデータを、Data
オブジェクトで指定します。上記のソースコードでは、撮影した静止画データが代入されているcompressedData
変数を指定しています。
次に、動画データを写真アルバムに追加することをリクエストするメソッドを実行します。静止画データに動画データを関連づけて、Live Photosデータとして写真アルバムに追加する時は、with
引数に.pairedVideo
を指定します。.pairedVideo
を指定することで、fileURL
引数に指定したURL
オブジェクトに格納されているドキュメントフォルダパス上の動画データを、事前に実行したaddResource(with:data:options:)
メソッドで、写真アルバムに追加することをリクエストした静止画データに関連づけることを、PhotoKit にリクエストすることができます。
以下のソースコードでは、photoOutput(_:didFinishCaptureFor:error:)
メソッド内で静止画データに動画データを関連づけて、Live Photosデータとして写真アルバムに追加する処理を行っています。
参考資料
・詳解 AVFoundation Capture
・Capturing and Saving Live Photos
Discussion