※以下はobjc.io, Issue #23 Video, April 2015 By Adriaan Stellingwerffの日本語訳です。
Capturing Video on iOS
イシュー#23ビデオ、2015年4月
Adriaan Stellingwerff
新しいiPhoneが公開されるたび、プロセスの性能とカメラのハードウェアが向上され、iPhoneを利用したビデオキャプチャーは、ますます興味深くなっている。小ぶりで、軽量で、目立たず、プロのビデオカメラとの品質の差もなくなりつつあり、ある意味でiPhoneは真の選択肢である。この記事では、ビデオキャプチャーパイプラインを設定し、ハードウェアを最大限に活用するための様々なオプションについて説明している。様々なパイプラインの実装とサンプルアプリは Github上で利用可能である。
##UIImagePickerController
アプリでビデオをキャプチャーする最も簡単な方法は、UIImagePickerControllerを使用することだ。これは、ビデオキャプチャーパイプラインとカメラのUIをラッピングしたビューコントローラーである。カメラをインスタンス化する前に、デバイスでビデオ録画のサポート有無を確認する:
if ([UIImagePickerController
isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
NSArray *availableMediaTypes = [UIImagePickerController
availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
if ([availableMediaTypes containsObject:(NSString *)kUTTypeMovie]) {
// Video recording is supported.
}
}
次に、UIImagePickerControllerオブジェクトを作成し、録画後の処理(例えば、動画のカメラロール保存)とカメラの使用停止に、応答するデリゲートを定義する:
UIImagePickerController *camera = [UIImagePickerController new];
camera.sourceType = UIImagePickerControllerSourceTypeCamera;
camera.mediaTypes = @[(NSString *)kUTTypeMovie];
camera.delegate = self;
ビデオカメラのすべての機能を使用するためのコードはこれだけだ。
##カメラ構成
UIImagePickerControllerいくつかの追加の構成オプションを提供している。
カメラは、cameraDeviceプロパティを設定により、選択される。UIImagePickerControllerカメラデバイスの列挙型である。デフォルトは、UIImagePickerControllerCameraDeviceRearに設定され、UIImagePickerControllerCameraDeviceFrontに設定することも可能だ。必ず、最初に設定カメラが実際に使用できるかどうかを確認する。:
UIImagePickerController *camera = …
if ([UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]) {
[camera setCameraDevice:UIImagePickerControllerCameraDeviceFront];
}
ビデオ品質プロパティは、録画したビデオの品質を決定する。ビットレートやビデオの解像度の両方に影響があるエンコーディングプリセットを設定することができる。6プリセットある:
enum {
UIImagePickerControllerQualityTypeHigh = 0,
UIImagePickerControllerQualityTypeMedium = 1, // default value
UIImagePickerControllerQualityTypeLow = 2,
UIImagePickerControllerQualityType640x480 = 3,
UIImagePickerControllerQualityTypeIFrame1280x720 = 4,
UIImagePickerControllerQualityTypeIFrame960x540 = 5
};
typedef NSUInteger UIImagePickerControllerQualityType;
最初の3つは相対プリセット(低、中、高)である。相手プリセットエンコードの設定は、デバイスによってそれぞれ異なり、高の設定の場合は選択したカメラで最も高い品質を提供する。他の3つは、解像度固有のプリセット(640×480 VGA、960x540のiframeおよび1280×720のiFrame)である。
##Custom UI
前述したように、UIImagePickerControllerは、すぐ使用できるカメラUIを持っている。しかし、デフォルトのコントロールを表示せず、カメラプレビュー上にカスタマイズコントロールを提供するなど、カメラのカスタマイズの可能である。
UIView *cameraOverlay = …
picker.showsCameraControls = NO;
picker.cameraOverlayView = cameraOverlay;
次に、オーバーレイ上にあるコントロールをUIImagePickerController制御メソッドに繋ぐ必要がある。(例えばstartVideoCaptureとstopVideoCapture)
#AVFoundation
ビデオキャプチャーをより細かく制御したい場合、UIImagePickerControllerより、AVFoundationを使用する必要がある。
ビデオキャプチャーのメインのAVFoundationクラスはAVCaptureSessionである。オーディオとビデオの入力と出力間のデータの流れを調整する。
キャプチャーセッションを使用するには、キャプチャーセッションをインスタンス化し、入力と出力を追加する。接続された入力から接続された出力に、データを流す。
AVCaptureSession *captureSession = [AVCaptureSession new];
AVCaptureDeviceInput *cameraDeviceInput = …
AVCaptureDeviceInput *micDeviceInput = …
AVCaptureMovieFileOutput *movieFileOutput = …
if ([captureSession canAddInput:cameraDeviceInput]) {
[captureSession addInput:cameraDeviceInput];
}
if ([captureSession canAddInput:micDeviceInput]) {
[captureSession addInput:micDeviceInput];
}
if ([captureSession canAddOutput:movieFileOutput]) {
[captureSession addOutput:movieFileOutput];
}
[captureSession startRunning];
つまり、ディスパッチキュー関連のコードは、上記の記載から省略されている。キャプチャセッションへのすべての呼び出しがブロッキングされているため、バックグラウンドシリアルキューで、処理することをお勧めする。
キャプチャーセッションは、さらに、出力の品質レベルを示すsessionPresetで構成されている。 11種類のプリセットがある。
NSString *const AVCaptureSessionPresetPhoto;
NSString *const AVCaptureSessionPresetHigh;
NSString *const AVCaptureSessionPresetMedium;
NSString *const AVCaptureSessionPresetLow;
NSString *const AVCaptureSessionPreset352x288;
NSString *const AVCaptureSessionPreset640x480;
NSString *const AVCaptureSessionPreset1280x720;
NSString *const AVCaptureSessionPreset1920x1080;
NSString *const AVCaptureSessionPresetiFrame960x540;
NSString *const AVCaptureSessionPresetiFrame1280x720;
NSString *const AVCaptureSessionPresetInputPriority;
最初のものは、ハイレソリューション写真出力のものである。残りのものは、キャプチャーセッションで使用可能な追加プリセットが存在することを除き、UIImagePickerControllerQualityTypeオプションに非常に似ている。
最後の(AVCaptureSessionPresetInputPriority)キャプチャーセッションは、オーディオとビデオの出力設定を制御しないことを示している。代わりに、接続されたキャプチャーデバイスのactiveFormatは、キャプチャーセッションの出力の品質レベルを決定する。次のセクションでは、より詳細にデバイスとデバイスフォーマットを見ていこう。
##Inputs
AVCaptureSessionの入力は、AVCaptureDeviceInputを通じ、キャプチャセッションに接続された1つ、または複数のAVCaptureDeviceオブジェクトである。
利用可能なキャプチャデバイスを見つけるため、[AVCaptureDeviceデバイス]を使用することができる。 iPhone6は、以下の通りだ。
(
“<AVCaptureFigVideoDevice: 0x136514db0 [Back Camera][com.apple.avfoundation.avcapturedevice.built-in_video:0]>”,
“<AVCaptureFigVideoDevice: 0x13660be80 [Front Camera][com.apple.avfoundation.avcapturedevice.built-in_video:1]>”,
“<AVCaptureFigAudioDevice: 0x174265e80 [iPhone Microphone][com.apple.avfoundation.avcapturedevice.built-in_audio:0]>”
)
##Video Input
ビデオ入力を設定するには、希望するカメラ装置とAVCaptureDeviceInputオブジェクトを作成し、キャプチャーセッションに追加する。
AVCaptureSession *captureSession = …
AVCaptureDevice *cameraDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error;
AVCaptureDeviceInput *cameraDeviceInput = [ [AVCaptureDeviceInput alloc] initWithDevice:cameraDevice error:&error];
if ([captureSession canAddInput:input]) {
[captureSession addInput:cameraDeviceInput];
}
以前のセクションで、キャプチャーセッションプリセットを使用したなら、それで十分である。もしそうではない場合、例えば、高フレームレートでキャプチャーするなどの場合、あるデバイスのフォーマットを設定する必要がある。ビデオキャプチャーデバイスは、特定の性質及び機能など様々なデバイスフォーマットを有している。以下はiPhone6のバックフェイシングカメラ(22利用可能なフォーマット)による例である。
● Format = ピクセルフォーマット
● FPS = サポートされているフレームレート範囲
● HRSI = 高解像度画像の大きさ
● FOV = 視野
● VIS = ビデオ安定化サポートのフォーマット
● Max Zoom = 最大ビデオズーム倍率
● Upscales = デジタルアップスケーリングが締結されたズーム倍率
● AF = オートフォーカスシステム(1=コントラスト検出 2 =フェーズ検出)
● ISO = サポートされているISO範囲
● SS = サポートされている露出時間範囲
● HDR = ビデオHDRをサポート
上記の形式から、毎秒240フレームを記録することの確認ができ、第一または第二のフォーマットを必要とする。希望のピクセルフォーマットに依存し、1920×1080の解像度でキャプチャーする場合、毎秒その240フレームは使用できない。
特定のデバイスのフォーマットを設定するには、まずlockForConfigurationを呼び出す:デバイスの構成プロパティへの排他的アクセスを取得する。そして、単にsetActiveFormatを使用し、キャプチャーデバイス上のキャプチャー形式を設定: また、自動的にAVCaptureSessionPresetInputPriorityへのキャプチャセッションの設定する。
希望のデバイスのフォーマットを設定すると、デバイスのフォーマットの制限内で、キャプチャーデバイス上の設定を行うことができる。
ビデオキャプチャーのためのフォーカス、露出、ホワイトバランスは、イシュー#21「iOSカメラのキャプチャー」に記載された画像キャプチャーの管理方法と同様である。他にも、いくつかのビデオ固有の設定オプションがある。
フレームレートは、フレームレートの逆数である捕捉装置のactiveVideoMinFrameDurationとactiveVideoMaxFrameDurationプロパティを使用してフレームレートを設定する。フレームレートを設定するには、まず、希望のフレームレートがデバイスフォーマットでサポートされていることを確認する。そして、設定するためのキャプチャデバイスをロックする。固定のフレームレートを確保するために、同じ値の最小と最大のフレームレートを設定する。
NSError *error;
CMTime frameDuration = CMTimeMake(1, 60);
NSArray *supportedFrameRateRanges = [device.activeFormat videoSupportedFrameRateRanges];
BOOL frameRateSupported = NO;
for (AVFrameRateRange *range in supportedFrameRateRanges) {
if (CMTIME_COMPARE_INLINE(frameDuration, >=, range.minFrameDuration) &&
CMTIME_COMPARE_INLINE(frameDuration, <=, range.maxFrameDuration)) {
frameRateSupported = YES;
}
}
if (frameRateSupported && [device lockForConfiguration:&error]) {
[device setActiveVideoMaxFrameDuration:frameDuration];
[device setActiveVideoMinFrameDuration:frameDuration];
[device unlockForConfiguration];
}
ビデオ安定化は、最初のiOS6とiPhone4Sに導入された。シネマティックビデオ安定化と言われた。それにより、ビデオ安定化APIが変更された。
(クラスレファレンスには、まだ反映されていないため、ヘッダファイルを確認してください)
安定化は、キャプチャデバイスで構成されず、AVCaptureConnectionで構成される。
安定化モードは、全てのデバイスフォーマットでサポートされないため、特定の安定化モードの利用可能性は、適用される前にチェックする必要がある。
AVCaptureDevice *device = ...;
AVCaptureConnection *connection = ...;
AVCaptureVideoStabilizationMode stabilizationMode = AVCaptureVideoStabilizationModeCinematic;
if ([device.activeFormat isVideoStabilizationModeSupported:stabilizationMode]) {
[connection setPreferredVideoStabilizationMode:stabilizationMode];
}
iPhone6で導入されたもう一つの新機能は、ビデオHDRである(ハイダイナミックレンジ)。高ダイナミックレンジビデオは、単一の高ダイナミックレンジ写真にEV値が異なる静止画のブラケットを融合する伝統的な方法とは対照的である。これは、センサーに内蔵されている。HDRを設定するには、2つの方法がある。キャプチャーデバイスvideoHDREnabledプロパティを直接に有効、無効にする。または、automaticallyAdjustsVideoHDREnabledプロパティを使用し、システムに残すことである。
##Audio Input
iPhone6は、3つのマイクを持っていることを考えると少し妙であるが、以前は、キャプチャーデバイスのリストは、1オーディオデバイスだけであった。それらは、ときに、パフォーマンスを最適化するため、同時に使用され、一つのデバイスとして扱われる。iPhone5以降、動画を記録する場合、フロントとバックのマイクは指向性のノイズ低減を提供するため、同時に使用される。
多くの場合、デフォルトのマイク構成は、オプションを必要とする。
バックマイクは自動的に、リアーフェイシングカメラ(フロントマイクはノイズを軽減する) で使用される。また、フロントマイクは、フロントフェイシングカメラに使用される。
だけど、それぞれのマイクロにアクセスし、設定することができる。例えば、後ろ向きのカメラでシーンをキャプチャーしながら、ユーザーが正面向きのマイクを使用し、ライブ解説を記録できる。それはAVAudioSessionで行われる。オーディオを再ルーティングできるようにするため、オーディオセッションは、最初にサポートしているカテゴリーに設定する必要がある。その後、オーディオセッションの入力ポート使用したいマイクを見つけるため、ポートのデータ·ソースを検索する必要がある。
// Configure the audio session
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[audioSession setActive:YES error:nil];
// Find the desired input port
NSArray* inputs = [audioSession availableInputs];
AVAudioSessionPortDescription *builtInMic = nil;
for (AVAudioSessionPortDescription* port in inputs) {
if ([port.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {
builtInMic = port;
break;
}
}
// Find the desired microphone
for (AVAudioSessionDataSourceDescription* source in builtInMic.dataSources) {
if( [source.orientation isEqual:AVAudioSessionOrientationFront] ) {
[builtInMic setPreferredDataSource:source error:nil];
[audioSession setPreferredInput:builtInMic error:&error];
break;
}
}
追加で、非デフォルトのマイク設定を構成する。オーディオゲイン、サンプル·レートのような他のオーディオ設定を構成するために、AVAudioSessionを使用することができる。
##Permissions
カメラやマイクにアクセスする際、ユーザーの許可が必要であることを忘れてはいけない。
iOSは、オーディオまたはビデオでAVCaptureDeviceInputを初めて使用する際、許可が必要である。
そして、必要な権限が付与されていない場合、ユーザーに警告する必要がある。ユーザーの許可を与えていないときにビデオとオーディオを録音しようとすると黒いフレームと無音になる。
##Outputs
入力が設定の次は、キャプチャーセッションの出力を伝えよう。
##AVCaptureMovieFileOutput
ファイルにビデオを書き込むための最も簡単なオプションは、AVCaptureMovieFileOutputオブジェクトである。キャプチャーセッションへの出力として追加すると、最小の構成でQuickTimeファイルにオーディオとビデオの書き込みができるようになる。:
AVCaptureMovieFileOutput *movieFileOutput = [AVCaptureMovieFileOutput new];
if([captureSession canAddOutput:movieFileOutput]){
[captureSession addOutput:movieFileOutput];
}
// Start recording
NSURL *outputURL = …
[movieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self];
記録デリゲートは、録音が開始と停止の実際のコールバックを受信するために必要である。記録が停止すると、出力は通常、まだいくつかのデータをファイルに書き込むために、デリゲートが呼ばれる。
AVCaptureMovieFileOutputオブジェクトは、あるファイル·サイズになったり、ある一定の時間を記録したり、デバイスのディスクスペースが最小限になった際、停止させるなどのいくつのオプションがある。例えば、上記を必要とする場合、ファイルに書き込む際には、様々な方法がある。オーディオやビデオのサンプルを処理するためのカスタムオーディオとビデオの圧縮を設定する。
##AVCaptureDataOutput and AVAssetWriter
キャプチャーセッションからのビデオ、及びオーディオ出力をより詳細に制御のため、先程のセクションで説明したAVCaptureMovieFileOutputの代わりに、AVCaptureVideoDataOutputオブジェクトと AVCaptureAudioDataOutputオブジェクトの使用も可能である。
これらの出力は、個々に、ビデオおよびオーディオサンプルバッファをキャプチャーし、そのデリゲートに渡す。デリゲートは、サンプルバッファーを処理する(例えば、ビデオにフィルタを追加する)、または変更がないまま通す。次に、サンプルバッファーは、AVAssetWriterオブジェクトを使用し、ファイルに書き込むことができる:
出力URLやファイルフォーマットを定義し、サンプルバッファを受信する1つまたは複数の入力を追加することにより、assetライターを構成する。キャプチャーセッションと同様、バックグラウンドシリアルキューに、assetライターの呼び出しをディスパッチすることをお勧めする。
NSURL *url = …;
AVAssetWriter *assetWriter = [AVAssetWriter assetWriterWithURL:url fileType:AVFileTypeMPEG4 error:nil];
AVAssetWriterInput *videoInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:nil];
videoInput.expectsMediaDataInRealTime = YES;
AVAssetWriterInput *audioInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:nil];
audioInput.expectsMediaDataInRealTime = YES;
if ([assetWriter canAddInput:videoInput]) {
[assetWriter addInput:videoInput];
}
if ([assetWriter canAddInput:audioInput]) {
[assetWriter addInput:audioInput];
}
上記のコードサンプルでは、assetライター入力の出力設定にnilを渡す。これは、添付のサンプルが再エンコードされないことを意味する。サンプルを再エンコードしたければ、特定の出力設定で辞書を提供する必要がある。音声出力設定のためのキーは、ここで定義されており、ビデオ出力の設定のためのキーは、ここで定義されている。
より簡単に、両方AVCaptureVideoDataOutputクラスとAVCaptureAudioDataOutputクラスは、recommendedVideoSettingsForAssetWriterWithOutputFileType: とrecommendedAudioSettingsForAssetWriterWithOutputFileType:と呼ばれるメソッドを持っている。キーのディクショナリーとバリュウを生成する。独自の出力設定を定義する簡単な方法は、ディクショナリーを構築し、プロパティを調整する。例えば、ビデオの品質を改善するために、ビデオビットレートを増加させる。
他の方法として、出力設定辞書を設定するには、AVOutputSettingsAssistantクラスを使用することができるが、経験によれば、上記の方法を使用することが望ましい。
それらの提供する出力設定は、ビデオビットレートのようなもののためのより現実的である。更に、出力アシスタントは、他のいくつかの欠点を有していると思われ、例えば、ビデオのフレームレートを変更する場合には、ビデオビットレートを変更しない。
##Live Preview
ビデオキャプチャーにAVFoundationを使用する場合、カスタムユーザインタフェースを提供しなければならない。カメラインタフェースより重要なコンポーネントは、ライブプレビューである。AVCaptureVideoPreviewLayerオブジェクトがカメラビューにサブレイヤとして追加を介してこれを最も簡単に実装されている。
AVCaptureSession *captureSession = ...;
AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
UIView *cameraView = ...;
previewLayer.frame = cameraView.bounds;
[cameraView.layer addSublayer:previewLayer];
より多くの制御が必要な場合、例えば、ライブプレビューにフィルタを適用するには、代わりに問題#21「iOSのカメラのキャプチャ」で説明したように、キャプチャセッションにAVCaptureVideoDataOutputオブジェクトを追加し、OpenGLを使用して画面上にフレームを表示する必要がある。
#Summary
簡単UIImagePickerControllerから、AVCaptureSessionとAVAssetWriterのより複雑な組み合わせに - iOSの上のビデオキャプチャのためのパイプラインを設定するには、多数の異なる方法がある。プロジェクトの正しいオプションは、要望のビデオ品質と圧縮するか、アプリのユーザーに公開したいカメラのコントロール等の要件に依存するのである。
1.Technical Note: New AV Foundation Camera Features for the iPhone 6 and iPhone 6 Plus
2.[Technical Q&A: AVAudioSession - Microphone Selection](Technical Q&A: AVAudioSession - Microphone Selection ↩)