【iOS】AVFoundationを使用して写真撮影機能を実装する
AVFoundationのCapture APIを使用して、写真撮影機能を実装する方法について書きます。本記事をお読みいただくことで、以下の内容を学ぶことができます。
- 静止画データを出力するキャプチャセッションの設定方法
- 静止画データ撮影を開始する方法
- デリゲートメソッドを介した静止画データの取得方法
- 静止画データを写真アルバムに追加する方法
本記事の内容は、「詳解 AVFoundation Capture」から一部の説明を抜粋した内容になっています。
アプリがカメラやマイク、写真アルバムへアクセスするために必要なアクセス権を取得する方法については、本記事では割愛しています。「詳解 AVFoundation Capture」の「第2章 アクセス権」または、以下のドキュメントをご参照ください。カメラ撮影機能のアーキテクチャ
カメラ撮影機能のアーキテクチャは、「入力」、「出力ワークフロー」、「キャプチャセッション」の三つで構成されます。入力は、カメラやマイクといったメディアデバイスが記録したデータを、キャプチャセッションに渡す役割を担います。出力ワークフローは、キャプチャセッションから受け取ったデータを、静止画データや動画データといったプログラムで扱うことができる形式のデータで出力する役割を担います。キャプチャセッションは、入力と出力ワークフローとの接続を管理する中間ハブとしての役割を担います。
キャプチャセッションに入力と出力ワークフローを追加すると、キャプチャセッション内部で自動的にこれらを接続し、データフローを確立します。データフローを確立すると、メディアデバイスが記録したデータは、入力を経由してキャプチャセッションに渡され、キャプチャセッションは受け取ったデータを出力ワークフローに渡します。出力ワークフローは受け取ったデータを、静止画データや動画データといったプログラムで扱うことができる形式のデータで出力します。
キャプチャセッションの設定
Capture API を使用してキャプチャセッションの設定を行うには、メディアデバイスの入力を表すAVCaptureDeviceInput
オブジェクトと、出力ワークフローを表すAVCaptureOutput
を継承するオブジェクトを、AVCaptureSession
オブジェクトに追加します。キャプチャセッションの設定が完了すると、メディアデバイスが記録したデータは、AVCaptureDeviceInput
オブジェクトを介してAVCaptureSession
オブジェクトに渡されます。その後、AVCaptureSession
オブジェクトは受け取ったデータを、AVCaptureOutput
オブジェクトに渡します。そして、AVCaptureOutput
オブジェクトにデータが渡されると、特定のデリゲートメソッドを介して、静止画データや動画データといったプログラムで扱うことができる形式のデータとして取得することができます。
入力設定
AVCaptureDeviceInput
オブジェクトをAVCaptureSession
オブジェクトに追加することで、キャプチャセッションの入力設定を行うことができます。AVCaptureDeviceInput
は、メディアデバイスが記録したデータを、AVCaptureSession
オブジェクトに渡すことができるクラスです。
AVCaptureDeviceInput
オブジェクトは、init(device:)
イニシャライザで取得することができます。
device
引数には、AVCaptureDevice
オブジェクトを指定するため、AVCaptureDevice
オブジェクトを取得しておく必要があります。AVCaptureDevice
オブジェクトは、AVCaptureDevice
のdefault(_:for:position:)
メソッドで取得することができます。
default(_:for:position:)
は、各引数の情報に合致したメディアデバイスを表すAVCaptureDevice
オブジェクトを返すメソッドです。第一引数には、AVCaptureDevice.DeviceType
オブジェクトを指定します。AVCaptureDevice.DeviceType
は、フレームワークがサポートするメディアデバイスの種類を表す構造体です。
.builtInWideAngleCamera
(広角レンズカメラ) や.builtInUltraWideCamera
(超広角レンズカメラ) といったメディアデバイスの種類を表します。for
引数には、AVMediaType
オブジェクトを指定します。AVMediaType
は、メデイアタイプを表す構造体です。
.video
(映像) や.audio
(音声) といったメデイアタイプを表します。position
引数には、AVCaptureDevice.Position
オブジェクトを指定します。AVCaptureDevice.Position
は、メディアデバイスの位置を表す要素を持つEnum
です。
以下のソースコードでは、iPhone 背面の広角レンズカメラが記録したデータを、AVCaptureSession
オブジェクトに渡すことができるAVCaptureDeviceInput
オブジェクトを取得しています。
取得したAVCaptureDeviceInput
オブジェクトをAVCaptureSession
オブジェクトに追加することで、キャプチャセッションの入力設定を行うことができます。AVCaptureDeviceInput
オブジェクトをAVCaptureSession
オブジェクトに追加する前に、AVCaptureSession
オブジェクトのcanAddInput(_:)
メソッドを実行します。
canAddInput(_:)
は、第一引数に指定したAVCaptureDeviceInput
オブジェクトを、AVCaptureSession
オブジェクトに追加することができるかどうかを表すBool
値を返すメソッドです。第一引数には、AVCaptureSession
オブジェクトに追加したいAVCaptureInput
オブジェクトを指定します。canAddInput(_:)
メソッドが返す値がtrue
であることを確認した後に、AVCaptureSession
オブジェクトのaddInput(_:)
メソッドを実行します。addInput(_:)
は、第一引数に指定したAVCaptureDeviceInput
オブジェクトを、AVCaptureSession
オブジェクトに追加するメソッドです。
第一引数には、AVCaptureSession
オブジェクトに追加したいAVCaptureInput
オブジェクトを指定します。
出力設定
AVCapturePhotoOutput
は、静止画データを出力するためのワークフローを提供するクラスです。
AVCaptureOutput
を継承しており、AVCaptureSession
オブジェクトに追加することで、AVCapturePhotoCaptureDelegate
プロトコルで定義されているデリゲートメソッドを介して、撮影した静止画データを取得することができます。
AVCapturePhotoOutput
オブジェクトをAVCaptureSession
オブジェクトに追加する前に、AVCaptureSession
オブジェクトのcanAddOutput(_:)
メソッドを実行します。
canAddOutput(_:)
は、第一引数に指定したAVCaptureOutput
オブジェクトを、AVCaptureSession
オブジェクトに追加することができるかどうかを表すBool
値を返すメソッドです。canAddOutput(_:)
メソッドが返す値がtrue
であることを確認した後に、AVCaptureSession
オブジェクトのaddOutput(_:)
メソッドを実行します。addOutput(_:)
は、第一引数に指定したAVCaptureOutput
オブジェクトを、AVCaptureSession
オブジェクトに追加するメソッドです。
第一引数には、AVCaptureSession
オブジェクトに追加したいAVCaptureOutput
オブジェクトを指定します。
また、AVCaptureSession
オブジェクトは、撮影フォーマットを変更することができるAVCaptureSession.Preset
型のsessionPreset
プロパティを持っています。
今回は、静止画データ撮影向けに、最大の解像度で出力することができるAVCaptureSession.Preset.photo
オブジェクトを、sessionPreset
プロパティに代入します。AVCaptureSession.Preset.photo
オブジェクトをsessionPreset
プロパティに代入する前に、AVCaptureSession
オブジェクトのcanSetSessionPreset(_:)
メソッドを実行します。
canSetSessionPreset(_:)
は、第一引数に指定したAVCaptureSession.Preset
オブジェクトを、sessionPreset
プロパティに代入できるかどうかを表すBool
値を返すメソッドです。canSetSessionPreset(_:)
メソッドが返す値がtrue
であることを確認した後に、sessionPreset
プロパティにAVCaptureSession.Preset.photo
オブジェクトを代入します。
以下のソースコードでは、iPhone 背面の広角レンズカメラが記録した静止画データを出力するキャプチャセッションの設定を行っています。
カメラ映像プレビューの表示設定
AVCaptureVideoPreviewLayer
は、カメラ映像を表示するレイヤーとしての役割を担うCALayer
を継承するクラスです。
AVCaptureVideoPreviewLayer
オブジェクトは、init(session:)
イニシャライザで取得することができます。
session
引数には、入力設定を行うAVCaptureSession
オブジェクトを指定します。AVCaptureVideoPreviewLayer
オブジェクトは、session
引数に指定したAVCaptureSession
オブジェクトが受け取るカメラ映像のビデオフレームデータを表示することができます。
UIView
オブジェクトのlayer
プロパティが持つaddSublayer(_:)
メソッドの第一引数に、AVCaptureVideoPreviewLayer
オブジェクト指定することで、addSublayer(_:)
メソッドを実行したレイヤー上にカメラ映像プレビューを表示することができます。以下のソースコードでは、SwiftUI View でカメラ映像プレビューを表示するために、UIViewControllerRepresentable
プロトコルに準拠した構造体を定義しています。
VideoPreviewLayerView
オブジェクトのイニシャライザで、previewCALayer
引数にAVCaptureVideoPreviewLayer
オブジェクトを指定し、カメラ映像プレビューを表示することができるUIViewController
を定義しています。UIViewControllerRepresentable
プロトコルに準拠することで、SwiftUI View の中でも使用することができるようになっています。以下のソースコードでは、SwiftUI View 側でVideoPreviewLayerView
を使用して、カメラ映像のプレビューを表示しています。
ここで注意すべきポイントが一つあります。AVCaptureSession
オブジェクトに変更を加える時は、変更を加える前に、AVCaptureSession
オブジェクトのbeginConfiguration()
メソッドを実行し、変更完了後に、AVCaptureSession
オブジェクトのcommitConfiguration()
メソッドを実行する必要があります。
beginConfiguration()
メソッドは、キャプチャセッションの変更開始AVCaptureSession
オブジェクトに通知し、commitConfiguration()
メソッドは、キャプチャセッションの変更完了をAVCaptureSession
オブジェクトに通知します。一つ以上のキャプチャセッションの変更処理を行った後に、commitConfiguration()
メソッドを実行することでbeginConfiguration()
メソッドを実行した後からcommitConfiguration()
メソッドを実行するまでに行われた、キャプチャセッションの変更処理を一括で行うことができます。キャプチャセッションの設定が完了した後に、AVCaptureSession
オブジェクトのstartRunning()
メソッドを実行します。
startRunning()
は、AVCaptureDevice
オブジェクトが記録したデータを、AVCaptureSession
オブジェクトを介して、AVCaptureOutput
オブジェクトに渡すデータフローを開始するメソッドです。
以下のソースコードでは、iPhone 背面の広角レンズカメラが記録した静止画データを出力するキャプチャセッションの設定を行っています。
静止画データ撮影の開始
AVCapturePhotoOutput
オブジェクトのcapturePhoto(with:delegate:)
メソッドで、静止画データ撮影を開始することができます。
with
引数には、AVCapturePhotoSettings
オブジェクトを指定します。AVCapturePhotoSettings
は撮影時のフラッシュライトや、ファイル形式の変更など、静止画データ撮影オプション設定を行うことができるクラスです。
今回は、静止画データを撮影する機能に絞って解説を進めるため、撮影オプション設定は行ないません。取得したAVCapturePhotoSettings
オブジェクトに対して変更を加えずに、with
引数に指定します。
delegate
引数には、AVCapturePhotoCaptureDelegate
プロトコルに準拠するオブジェクトを指定します。AVCapturePhotoCaptureDelegate
プロトコルで定義されているデリゲートメソッドについては後述しますが、上記のソースコードでは、self
を指定しています。AVCapturePhotoCaptureDelegate
プロトコルに準拠したオブジェクトに、静止画データ撮影の開始から完了するまでに呼び出されるデリゲートメソッドを定義します。
プロトコルへの準拠
capturePhoto(with:delegate:)
メソッドのdelegate
引数に、静止画データ撮影の開始から完了するまでに呼び出されるデリゲートメソッドの委譲先を指定しました。今回は、移譲先にself
を指定したので、capturePhoto(with:delegate:)
メソッドを実行するクラス自身にデリゲートメソッドを定義します。デリゲートメソッドを定義するために、self
をAVCapturePhotoCaptureDelegate
プロトコルに準拠させます。
静止画データの取得
photoOutput(_:didFinishProcessingPhoto:error:)
は、AVCapturePhotoCaptureDelegate
プロトコルで定義されているデリゲートメソッドです。
静止画データが出力される時に呼び出されます。出力された静止画データは、photo
引数に代入されているAVCapturePhoto
オブジェクトで取得することができます。AVCapturePhoto
は、静止画データやメタデータ情報を格納することができるクラスです。
静止画データは、AVCapturePhoto
オブジェクトのfileDataRepresentation()
メソッドで取得することができます。
fileDataRepresentation()
は、fileDataRepresentation()
を実行するAVCapturePhoto
オブジェクトから撮影データを抽出するメソッドです。fileDataRepresentation()
を実行するAVCapturePhoto
オブジェクト自身に格納されている静止画データを、Data
オブジェクトで返します。以下のソースコードでは、fileDataRepresentation()
メソッドで取得した静止画データを表すData
オブジェクトを、compressedData
変数に代入しています。
静止画データを写真アルバムへ追加
photoOutput(_:didFinishCaptureFor:error:)
は、AVCapturePhotoCaptureDelegate
プロトコルで定義されているデリゲートメソッドです。
撮影に関連する全ての処理が完了した時に呼び出されます。静止画データを写真アルバムへ追加する処理は、photoOutput(_:didFinishCaptureFor:error:)
メソッド内で行います。静止画データや動画データ(以降、アセットデータ) を写真アルバムに追加する処理は、PhotoKit フレームワークのAPI を使用します。PhotoKit は、写真アルバムを操作するためのAPIが定義されているフレームワークです。写真アルバムに変更を加える時は、PHPhotoLibrary
のperformChanges(_:)
メソッドを実行します。
performChanges(_:)
は、第一引数に指定した写真アルバムに変更を加える処理を、PhotoKit にリクエストするメソッドです。
第一引数には、写真アルバムを操作する処理をクロージャで記述します。写真アルバムに対する変更は、PHAssetCreationRequest
オブジェクトのメソッドで行います。PHAssetCreationRequest
は、写真アルバムに対する変更をPhotoKit にリクエストすることができるメソッドを持つクラス
です。
PHAssetCreationRequest
オブジェクトは、forAsset()
メソッドで取得することができます。
Data
オブジェクトのアセットデータを写真アルバムに追加する時は、PHAssetCreationRequest
オブジェクトのaddResource(with:data:options:)
メソッドを実行します。
addResource(with:data:options:)
は、data
引数に指定したData
オブジェクトのアセットデータを、写真アルバムに追加することをPhotoKit にリクエストするメソッドです。
with
引数には、写真アルバムに追加するアセットのタイプを、PHAssetResourceType
オブジェクトで指定します。PHAssetResourceType
は、アセットデータのタイプを表す要素を持つEnum
です。
今回は、静止画データを写真アルバムに追加するので、上記のソースコードでは、with
引数に.photo
を指定しています。data
引数には、写真アルバムに追加するアセットデータを、Data
オブジェクトで指定します。以下のソースコードでは、撮影した静止画データが
代入されているcompressedData
変数を指定しています。
options
引数には、PHAssetResourceCreationOptions
オブジェクトを指定します。PHAssetResourceCreationOptions
は、写真アルバムに追加するアセットデータのファイル名を指定したり、写真アルバムにアセットデータを追加する際に、元のアセットデータの挙動を制御する追加処理オプションを設定することができるクラスです。
Data
オブジェクトのアセットデータを写真アルバムに追加するだけならば、追加処理オプションは不要なので、options
引数にはnil
を指定します。以下のソースコードでは、静止画データを写真アルバムに追加しています。
次回は、本記事で解説した内容をベースに、動画撮影機能を実装する方法について解説します。
参考資料
・詳解 AVFoundation Capture
・Setting Up a Capture Session, Apple Developer Documentation
Discussion