🎬

【iOS】AVFoundationを使用してLive Photos撮影機能を実装する

に公開

AVFoundationのCapture APIを使用して、Live Photos撮影機能を実装する方法について書きます。以下の記事を先にお読みいただくと、本記事の内容をスムーズに理解することができます。
https://zenn.dev/naoya_maeda/articles/6f5c6bec557393
https://zenn.dev/naoya_maeda/articles/6f35ef13a5e895

本記事をお読みいただくことで、以下の内容を学ぶことができます。

  • 静止画データと動画データを同時に出力するキャプチャセッションの設定方法
  • Live Photosの撮影を開始する方法
  • デリゲートメソッドを介した静止画データと動画データの取得方法
  • Live Photosデータを写真アルバムに追加する方法

本記事の内容は、「詳解 AVFoundation Capture」から一部の説明を抜粋した内容になっています。
https://naoya-maeda.booth.pm/items/6760109
アプリがカメラやマイク、写真アルバムへアクセスするために必要なアクセス権を取得する方法については、本記事では割愛しています。「詳解 AVFoundation Capture」の「第2章 アクセス権」または、以下のドキュメントをご参照ください。
https://developer.apple.com/documentation/avfoundation/requesting-authorization-to-capture-and-save-media

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 は、静止画データを出力するためのワークフローを提供するクラスです。
https://developer.apple.com/documentation/avfoundation/avcapturemoviefileoutput

AVCaptureOutput を継承しており、AVCaptureSession オブジェクトに追加することで、AVCapturePhotoCaptureDelegate プロトコルで定義されているデリゲートメソッドを介して、撮影した静止画データと3秒の動画データを取得することができます。

isLivePhotoCaptureEnabled は、Live Photos撮影用のキャプチャパイプラインを、構築するかどうかを表すBool 値が代入されているプロパティです。
https://developer.apple.com/documentation/avfoundation/avcapturephotooutput/islivephotocaptureenabled

true を代入することで、静止画データと3 秒の動画データを出力するためのキャプチャパイプラインが自動で構築されます。そして、Live Photosデータに対応する撮影フォーマットの静止画データ・動画データを出力するために、AVCaptureSession オブジェクトのsessionPreset プロパティに、AVCaptureSession.Preset.photo オブジェクトを代入します。以下のソースコードでは、iPhone 背面の広角レンズカメラが記録した静止画データと、動画データを出力するキャプチャセッションの設定を行っています。

Live Photos撮影の開始

AVCapturePhotoOutput オブジェクトのcapturePhoto(with:delegate:) メソッドで、Live Photos撮影を開始することができます。
https://developer.apple.com/documentation/avfoundation/avcapturephotooutput/capturephoto(with:delegate:)

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:) メソッドを実行するクラス自身にデリゲートメソッドを定義します。デリゲートメソッドを定義するために、selfAVCapturePhotoCaptureDelegate プロトコルに準拠させます。

撮影の開始

photoOutput(_:willBeginCaptureFor:) は、AVCapturePhotoCaptureDelegate プロトコルで定義されているデリゲートメソッドです。
https://developer.apple.com/documentation/avfoundation/avcapturephotocapturedelegate/photooutput(_:willbegincapturefor:)

Live Photos撮影を開始する直前に呼び出されます。iOSにプリインストールされているカメラアプリで、Live Photosを撮影する時のように、撮影を開始したタイミングで画面上部に「LIVE」テキストを表示する時は、このデリゲートメソッドが呼び出されたタイミングで、「LIVE」テキストを画面に表示する処理を行います。

静止画データの取得

photoOutput(_:didFinishProcessingPhoto:error:) は、AVCapturePhotoCaptureDelegate プロトコルで定義されているデリゲートメソッドです。
https://developer.apple.com/documentation/avfoundation/avcapturephotocapturedelegate/photooutput(_:didfinishprocessingphoto:error:)

静止画データが出力される時に呼び出されます。以下のソースコードでは、fileDataRepresentation() メソッドで、撮影した静止画データを表すData オブジェクトを取得し、compressedData 変数に代入しています。

静止画データを表すData オブジェクトは、別のデリゲートメソッド内でLive Photosデータを写真アルバムへ追加する処理を行う時に使用するため、変数で保持しておきます。

動画データ撮影の完了

photoOutput(_:didFinishRecordingLivePhotoMovieForEventualFileAt:resolvedSettings:) は、AVCapturePhotoCaptureDelegate プロトコルで定義されているデリゲートメソッドです。
https://developer.apple.com/documentation/avfoundation/avcapturephotocapturedelegate/photooutput(_:didfinishrecordinglivephotomovieforeventualfileat:resolvedsettings:)

Live Photosデータを生成する時に使用する動画データ撮影が完了した時に呼び出されます。iOSにプリインストールされているカメラアプリで、LivePhotosを撮影する時のように、撮影を完了したタイミングで画面上部に表示されている「LIVE」テキストを非表示にする時は、このデリゲートメソッドが呼び出されたタイミングで、「LIVE」テキストを非表示にする処理を行います。

動画データの取得

photoOutput(_:didFinishProcessingLivePhotoToMovieFileAt:duration:photoDisplayTime:resolvedSettings:error:) は、AVCapturePhotoCaptureDelegate プロトコルで定義されているデリゲートメソッドです。
https://developer.apple.com/documentation/avfoundation/avcapturephotocapturedelegate/photooutput(_:didfinishprocessinglivephototomoviefileat:duration:photodisplaytime:resolvedsettings:error:)

Live Photosデータを生成する時に使用する動画データが出力される時に呼び出されます。outputFileURL 引数には、Live Photosデータ用動画データが書き込まれているドキュメントフォルダパスを表すURL オブジェクトが代入されています。outputFileURL 引数に代入されているURL オブジェクトは、別のデリゲートメソッド内で、Live Photosデータを写真アルバムへ追加する処理を行う時に使用するため、変数で保持しておきます。

Live Photosデータを写真アルバムへ追加

photoOutput(_:didFinishCaptureFor:error:) は、AVCapturePhotoCaptureDelegate プロトコルで定義されているデリゲートメソッドです。
https://developer.apple.com/documentation/avfoundation/avcapturephotocapturedelegate/photooutput(_:didfinishcapturefor:error:)

撮影に関連する全ての処理が完了した時に呼び出されます。Live Photosデータを写真アルバムへ追加する処理は、photoOutput(_:didFinishCaptureFor:error:) メソッド内で行います。PhotoKitフレームワークのAPIを使用して、撮影した静止画データに対して動画データを関連づけて、
Live Photosデータとして写真アルバムに追加する処理を行います。写真アルバムに変更を加える時は、PHPhotoLibraryperformChanges(_:) メソッドを実行します。
https://developer.apple.com/documentation/photos/phphotolibrary/performchanges(_:completionhandler:)

performChanges(_:) は、第一引数に指定した写真アルバムに変更を加える処理を、PhotoKit にリクエストするメソッドです。

第一引数には、写真アルバムを操作する処理をクロージャで記述します。写真アルバムに対する変更は、PHAssetCreationRequest オブジェクトのメソッドで行いますPHAssetCreationRequest は、写真アルバムに対する変更をPhotoKit にリクエストすることができるメソッドを持つクラスです。
https://developer.apple.com/documentation/Photos/PHAssetCreationRequest

PHAssetCreationRequest オブジェクトは、forAsset() メソッドで取得することができます。
https://developer.apple.com/documentation/photos/phassetcreationrequest/forasset()

第一引数に指定したクロージャ内で実行したPHAssetCreationRequest オブジェクトのメソッドの処理は、写真アルバムに変更を加える一つのタスクとしてPhotoKit に通知されます。つまり、第一引数に指定したクロージャ内で、静止画データと動画データを一つのアセットデータとして写真アルバムに追加する処理を行うことで、両データを一つのアセットデータ(=Live Photosデータ) として、写真アルバムに追加することができます。まず、PHAssetCreationRequest オブジェクトのaddResource(with:data:options:) メソッドを実行します。
https://developer.apple.com/documentation/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
https://naoya-maeda.booth.pm/items/6760109

・Capturing and Saving Live Photos
https://developer.apple.com/documentation/avfoundation/capturing-and-saving-live-photos

Discussion