この記事は何か?
iPhoneのカメラからリアルタイムに映像データを得るために使用するAVFoudationについて調べました。
AVFoundationフレームワーク
主に、4つのグループに分けて捉えることができます。
-
セッション
AVCaptureSessionクラス -
デバイス
AVCaptureDeviceクラス -
入力
AVCaptureInputクラス、AVCaptureDeviceInputクラス -
出力
AVCaptureOutputクラス、AVCaptureVideoDataOutputクラス
AVCaptureSessionがデータを整理し、セッションの開始または停止の指示を受けます。
セッションには、ひとつ以上の入力を追加します。AVCaptureDeviceInputは、カメラやマイクのためのラッパーです。
セッションからデータを受け取るには、出力も追加する必要があります。
入力と出力が追加されると、セッションは互換性のある入力と出力を自動的に接続します。
AVCaptureSessionでは、各セッションに1つのカメラだけが許容されます。なお、iOS 13で導入されたAVCaptureMultiCamSessionでは、複数の入出力を扱うことが可能になりました。
AVCaptureSessionクラス
class AVCaptureSession : NSObject
キャプチャ動作を管理し、入力デバイスからキャプチャ出力へのデータの流れを調整(コーディネート)するオブジェクト。
リアルタイムにキャプチャをするには、AVCaptureSession型のインスタンスに、適切な入力と出力を追加します。
// キャプチャセッションを作成して、標準のオーティオデバイスを設定する
let captureSession = AVCaptureSession()
guard let audioDevice = AVCaptureDevice.default(for: .audio) else { return }
do {
// オーディオデバイスをラップして、「キャプチャデバイスの入力」にする
// 可能であれば、その入力をセッションに追加する
let audioInput = try AVCaptureDeviceInput(device: audioDevice)
if captureSession.canAddInput(audioInput) {
captureSession.addInput(audioInput)
}
} catch {
// オーディオデバイスを「キャプチャデバイスの入力」にできなかった場合のエラー処理
}
入力から出力へのデータの流れを開始するにはstartRunning()メソッドを呼び出します。また、停止するにはstopRunning()メソッドを呼び出します。
startRunning()メソッドは、いくらか時間のかかる恐れがあるブロッキング呼び出しなので、メインキューがブロックされないように並列キューでセッションのセットアップを実行する必要があります。こうすることで、 UIの応答性を維持できます。
実装例についてはAVCam: Building a Camera Appを参照してください。
出力の品質レベル、ビットレート、または他の設定をカスタマイズするには、sessionPresetプロパティを使用します。ほとんどの一般的なキャプチャ設定はセッションプリセットを通して利用可能ですが、いくつかの特殊なオプション(高フレームレートなど)は、AVCaptureDeviceインスタンスにキャプチャフォーマットを直接設定する必要があります。
addInput(_:)メソッド
func addInput(_ input: AVCaptureInput)
「取得した入力」をセッションに追加します。
このメソッドを使用してセッションに入力を追加できるのは、canAddInput(_:)メソッドがtrueを返した場合のみです。canAddInput(_:)メソッドを呼び出した結果がfalseの場合、このメソッドは例外をスローします。
このメソッドは、セッションの実行中に呼び出すことができます。
addOutput(_:)メソッド
func addOutput(_ output: AVCaptureOutput)
このメソッドを使用してセッションに出力を追加できるのは、canAddOutput(_:)メソッドがtrueを返した場合のみです。canAddOutput(_:)メソッドを呼び出した結果がfalseの場合、このメソッドは例外をスローします。
このメソッドは、セッションの実行中に呼び出すことができます。
AVCaptureDeviceクラス
class AVCaptureDevice : NSObject
キャプチャセッションに「オーディオやビデオなどの入力」を提供し、ハードウェア固有のキャプチャ機能向けにコントロールを提供するデバイスです。
AVCaptureDeviceオブジェクトは、物理的なキャプチャデバイスと「そのデバイスに関連付けられたプロパティ」をモデル化したものです。キャプチャデバイスを使用して、基礎となるハードウェアのプロパティを設定します。また、キャプチャデバイスはAVCaptureSessionオブジェクトに「オーディオやビデオなどの入力データ」を提供します。
AVCaptureDeviceクラスのメソッドを使用して、利用可能なデバイスを列挙し、それらの機能を呼び出し、デバイスが出入りするときに通知を受けることができます。キャプチャデバイスのプロパティ(フォーカスモード、露出モードなど)を設定しようとする前に、最初にlockForConfiguration()メソッドを使用してデバイスのロックを取得する必要があります。また、設定しようとしている新しいモードがそのデバイスで有効であることを確認するために、デバイスの機能を問い合わせる必要があります。その後、 unlockForConfiguration()メソッドを使用してプロパティを設定し、ロックを解除します。設定可能なデバイス・プロパティをすべて変更せずに残したい場合は、ロックを保持することができます。しかし、デバイスのロックを不必要に保持すると、デバイスを共有している他のアプリケーションでキャプチャの品質が低下する可能性があり、お勧めできません。
キャプチャ設定のほとんどの一般的な構成は、AVCaptureSessionオブジェクトとその利用可能なプリセットを介して利用できます。しかし、iOSデバイスでは、いくつかの特殊なオプション(高フレームレートなど)は、AVCaptureDeviceインスタンスにキャプチャ形式を直接設定する必要があります。
iOSでは、キャプチャデバイスのactiveFormatプロパティを直接設定すると、セッションのプリセットがinputPriorityに変更されます。この変更を行うと、セッションのトポロジー(配置した入力、出力のこと)を変更した後にstartRunning()メソッドかcommitConfiguration()メソッドを呼び出すと、セッションは自動的にキャプチャフォーマットを設定しなくなります。
macOSでは、変更を行った後もキャプチャセッションは自動的にキャプチャフォーマットを構成することができます。
lockForConfiguration()メソッド
デバイスのハードウェア設定に対する排他的アクセスを要求します。
func lockForConfiguration() throws
このメソッドはtry付きで呼び出し、do構文のcatch節でエラーを処理します。デバイスのハードウェア関連の設定を構成しようとする前に、このメソッドを呼び出す必要があります。
デバイスのプロパティを変更したくない場合は、(ロックを解除する代わりに)ロックを維持できます。ただし、不必要にデバイスをロックすると、デバイスを共有している他のアプリで行うキャプチャの品質が低下する恐れがあります。
iOSでは、キャプチャデバイスのactiveFormatプロパティを直接設定すると、セッションのプリセットがinputPriorityに変更されます。この変更を行った場合、セッションのトポロジーを変更した後(つまり、キャプチャの入力と出力を追加、削除、再配置した後)にstartRunning()メソッドかcommitConfiguration()メソッドを呼び出しても、セッションは自動的にキャプチャフォーマットを設定しなくなります。
AVCaptureInputクラス
キャプチャセッションに入力データを提供するオブジェクトの抽象的なスーパークラスです。
class AVCaptureInput : NSObject
AVCaptureInputオブジェクトをセッションに関連付けるには、セッションでaddInput(_:)メソッドを呼び出します。
AVCaptureInputオブジェクトは、生成できるデータストリームごとに1つ以上のポート (AVCaptureInput.Portのインスタンス) を持っています。例えば、1つのビデオデータストリームを提示する AVCaptureDeviceオブジェクトにはポートが1つあります。
AVCaptureDeviceInputクラス
class AVCaptureDeviceInput : AVCaptureInput
デバイスからセッションにメディアを提供するキャプチャ入力です。
AVCaptureDeviceInputは、AVCaptureDeviceオブジェクトからデータをキャプチャするために使用する AVCaptureInputの具体的なサブクラスです。
AVCaptureOutputクラス
class AVCaptureOutput : NSObject
キャプチャセッションで記録したメディアを出力するオブジェクトの抽象的なスーパークラスです。
AVCaptureOutputは、AVCaptureSessionオブジェクトの出力先を記述する抽象的な基底クラスです。
AVCaptureOutputは、ファイルやビデオプレビューなどのキャプチャ出力先をセッション(AVCaptureSessionのインスタンス)に接続するための抽象的なインターフェイスを提供します。キャプチャ出力はAVCaptureInputから受け取るメディアのストリームごとにAVCaptureConnectionオブジェクトで表される「いくつかの接続」を持つことができます。最初にキャプチャ出力が作成されたとき、接続はひとつもありません。セッションに出力を追加すると、セッションは「そのセッションの入力」から出力にメディアデータをマップする接続を作成します。
AVCaptureOutputインスタンスをセッションに追加するには、セッションのaddOutput(_:)メソッドを使用します。
AVCaptureVideoDataOutputクラス
class AVCaptureVideoDataOutput : AVCaptureOutput
ビデオを記録して、ビデオフレームを処理するためのアクセスを提供するキャプチャ出力です。
この出力を使用して、キャプチャしたビデオから圧縮または非圧縮のフレームを処理します。
フレームにはcaptureOutput(_:didOutput:from:)デリゲートメソッドを使用してアクセスできます。
alwaysDiscardsLateVideoFramesプロパティ
var alwaysDiscardsLateVideoFrames: Bool { get set }
ビデオフレームに遅延があった場合に、フレームを削除するかどうかを示します。
このプロパティがtrueの場合、「既存のフレーム」を扱うディスパッチキューがcaptureOutput(_:didOutput:from:)デリゲートメソッドでブロックしている間に、出力は「キャプチャしたフレーム」を直ちに破棄します。
falseにすると、出力は「新しいフレーム」を破棄する前に「古いフレーム」を処理する猶予をデリゲートに与えますが、アプリケーションのメモリ使用量は結果として大幅に増加する恐れがあります。
デフォルトはtrueです。
videoSettingsプロパティ
var videoSettings: [String : Any]! { get set }
出力の圧縮設定です。
この辞書には,videoSettingsで定義された圧縮設定キー,またはCVPixelBufferで定義されたピクセルバッファ属性キーの値が含まれます。現時点では、kCVPixelBufferPixelFormatTypeKeyキーのみがサポートされています。
サポートされているビデオピクセルフォーマット(kCVPixelBufferPixelFormatTypeKey)の値を取得するには、availableVideoPixelFormatTypesを参照してください。また、サポートされているビデオコーデックフォーマット(AVVideoCodecKey)の値を取得するには、availableVideoCodecTypesを参照してください。
デバイスのネイティブフォーマットでサンプルを受信するには、このプロパティを空の辞書に設定します。
setSampleBufferDelegate(_:queue:)
func setSampleBufferDelegate(_ sampleBufferDelegate: AVCaptureVideoDataOutputSampleBufferDelegate?,
queue sampleBufferCallbackQueue: DispatchQueue?)
サンプルバッファのデリゲートの設定、コールバックが呼び出されるべきキューを設定します。
キャプチャされたビデオの新しいサンプルバッファは、captureOutput(_:didOutput:from:)メソッドを使用してサンプルバッファのデリゲート先に送信されます。すべてのデリゲートメソッドは、指定されたディスパッチキューで呼び出されます。
新しいフレームがキャプチャされたときにキューがブロックされている場合、それらのフレームは、常にdiscardsLateVideoFramesプロパティの値に基づいて自動的にドロップされます。これにより、同じキューで既存のフレームを処理する際に、処理が入ってくるフレームのレートに追いつけない場合に発生するであろうメモリ使用量の増加を管理する必要がなくなります。