【swift】AVFoundationを使って、録音、動画の撮影を行う!

はじめに

最近、CoreMLという「学習済み機械学習モデル」を簡単にiOSに実装できるという技術に興味を持っております。
中でもVisionAPIを使用すれば、画像、動画から人間の骨格や特定の動作を検出するといったことの再現が可能になります。

私たちはスマホのカメラアプリを使って写真や動画を撮影することがありますが、CoreMLの技術を搭載することで今まで見えなかったものも見えるようになると考えています(すごい!)。

CoreMLは音声や文章、画像などを解析するAPIを持っているのですが、前提としてこれらのAPIには「インプットとなる情報」が渡されています。

ですので、この「インプットとなる情報」を収集するロジックについても、最低限の実装方法は理解する必要があると考えております。


今回の記事では「AVFoundation」を使用して、そのインプットとなる情報(画像、動画)を取得する方法について概念をまとめます。

AVFoundationについて

参考

本記事では、以下のドキュメントを参考にしております。
developer.apple.com

概念

本ライブラリを使用すると、
iPhoneのカメラやマイクを起動して「音声」や「画像」といった「入力データ」として取得したデータを、必要に応じて加工したり保存する(アウトプット)ことが可能になります。

このAVFoudationのポイントは、「インプット」と「アウトプット」、そしてそれらを管理する「セッション」になります。
以下画像はCameras and Media Captureから引用したものですが、まずはこの図について理解していく必要があります。

https://docs-assets.developer.apple.com/published/058e665a6c/4ecf0924-ea2b-4faa-aea8-7bfc0b3fe419.png

セッションの構成要素

上記画像にも記載してあますが、AVFoundationを使用するために必要な要素は以下三つになります。
・AVCaptureInput
・AVCaptureOutput
・AVCaptureSession

「AVCaptureInput」、「AVCaptureOutput」の実態はただのabstractクラスでして、インプット、アウトプットに必要な変数・メソッドが定義されています。
そして、このabstractクラスを実装している「インプット」、「アウトプット」クラスはいろんな形で用意されています。

例えば、「AVCaptureInput」を継承する「AVCaptureDevice」クラスは、「カメラ」と「マイク」を起動して、音声つきの動画の撮影が可能となります。
→詳細は後述しますが、AVCaptureInputは「Port」変数を所持していて、この「Port」に対してインプットの情報を格納していきます。

次に、アウトプットとして「AVCaptureOutput」を継承したクラスを用いて、インプットで取得したデータをどう出力するか?設定します。
「AVCaptureMovieFileOutput」では音声つきの動画を「.mov」ファイルとして保存しますが、「AVCaptureAudioFileOutput」では「音声」のみを指定した拡張子に変換して保存します。


最後に「AVCaptureSession」、これは上記のインプット、アウトプットに関する「セッション」を制御していく役目を担っていて、上記で説明したようなインプットとアウトプットの紐付けや、動画撮影などの「開始・終了タイミング」を制御をします。


まとめです。

AVFoundationでは、「AVCaptureInput」によって動画や音声を記録することができますが、それを最終的にどう扱いたいか目的に応じて「AVCaptureOutput」を選択する必要がある。
また、AVCaptureSessionでは「AVCaptureInput」と「AVCaptureOutput」に対するセッション管理などを担っている。

インプットに関わる「ポート」の説明

概念

先ほど「ポート」について軽く触れましたが、ポートは「どの端子でどういったデータを取得するか?」を定義するもの、と考えれば良いのかなと思います。

以下コードは、「AVCaptureInput」を継承したクラス(AVCaptureDevice)に対して、ポートを定義する方法を示します。

// ポートの定義
guard let captureDevice = AVCaptureDevice.default(.builtInDualCamera, for: AVMediaType.video, position: .front) else {
        return
}
// AVCaptureDeviceInputクラスの定義(ポートを設定している)
guard  let videoInput = try? AVCaptureDeviceInput(device: captureDevice) else {
    return
}
// portsメソッド(AVCaptureInputクラスのabstractメソッド)でも、ポート設定が可能
videoInput.ports(for: AVMediaType.video, sourceDeviceType: AVCaptureDevice.DeviceType.builtInDualCamera, sourceDevicePosition: AVCaptureDevice.Position.back)

上記のコードで何となくイメージが掴めると思いますが、「AVCaptureInput」に対して一つポートを設定しています。

AVMediaType.video 動画で撮影することの定義
AVCaptureDevice.DeviceType.builtInTripleCamera トリプルカメラを使用することの定義
AVCaptureDevice.Position.back 背面カメラを使用することの定義


ちなみに、「AVCaptureInput」は、一つ以上の「ポート」の設定が可能です。
「音声付きで動画を撮影したい」というケースには、「カメラ用のポート」と「マイク用のポート」の二つを用意してあげないといけない、ということになりますね。


※余談ですが、Portのドキュメントのページには「Data Stream」という単語があり、直訳すると「データの流れ」ということでなかなかイメージしずらかったです。
ですので、私は「Data Stream = "どの端子でどういったデータを取得するか?"を定義するもの」と変換して理解しました。

Apple Developer Documentation

最後に

さて、今回はざっくりとAVFoundationについて説明しましたが、今現在適当なサンプルコードも作成しているところです。
実際にコードを書いてみて、気づきがあれば本記事をアップデート行こうと思います。


ここまで見ていただきありがとうございました!🙏