#author("2016-06-19T03:26:47+00:00","default:nitta","nitta") [[iOS]] *AVFoundation [#e822a056] *静止画像やビデオ画像の撮影 [#w03e69c6] デバイス(カメラやマイクなど)からデータをキャプチャ処理するには、 -入力デバイス AVCaptureDevice -入力デバイスのポート設定 AVCaptureInput --入力ポート AVCaptureInputPort -出力を管理する AVCaptureOutput --静止画 AVCaptureStillImageOutput --ビデオと音声データを共に受け取る AVCaptureMovieFileOutput -入力から出力へデータの流れを調整する AVCaptureSession -(Option) プレビュー表示 AVCaptureVideoPreviewLayer を組み合わせて使う キャプチャ・セッションにおける入力と出力の接続 AVCaptureConnection **セッション AVCaptureSession [#we96bf3f] -startRunningメソッド -- セッションの動作を開始する -stopRunningメソッド -- セッションの動作を止める //[Objective-C] AVCaptureSession *session = [[AVCaptureSession alloc] init]; ... [session startRunning] ... [session stopRunning] ***セッションの設定 [#j95ab170] |シンボル|解像度|コメント| |AVCaptureSessionPresetHigh|高|最高の録画品質。デバイス依存。| |AVCaptureSessionPresetMedium|中|WiFi共有に適する。| |AVCaptureSessionPresetLow|低い|3G共有に適する。| |AVCaptureSessionPreset640x480|640x480|VGA| |AVCaptureSessionPreset1280x720|1280x720|720p HD| |AVCaptureSessionPresetPhoto|写真|写真用の最大解像度。ビデオ出力では使えない。| -セッションのパラメータを変更する場合: 変更処理を beginConfiguration と commitConfiguration メソッドの間に記述する。 -キャプチャ・セッションの状態監視: --running プロパティ: 実行中かどうか --interrupted: 中断されたか --ランタイムエラーの監視は AVCaptureSessionRuntimeErrorNotification を受ける。 //[Objective-C] if ([session canSetSessionPreset: AVCaptureSessionPreset1280x720]) { session.sessionPreset = AVCaptureSessionPreset1280x720; } else { ... } ... [session beginConfiguration] // 設定変更 [session commitConfiguration] **入力デバイス AVCaptureDevice [#f8ce08f3] -使用可能なキャプチャ・デバイスを調べる: device(), devicesWithMediaType() -変更の通知は AVCaptureDeviceWasConnectedNotification, AVCaptureDeviceWasDisconnectedNotification -特定のメディアタイプを提供しているか調べる: hasMediaType -特定のキャプチャ・プリセットを提供しているか調べる: supportsAVCaptureSessionPreset //[Objective-C] 入力デバイスの一覧を表示する NSArray *devices = [AVCaptureDevice devices]; for (AVCaptureDevice* device in devices) { NSLog(@"Device name: %@", [device localizedName]); if ([device hasMediaType: AVMediaTypeVide]) { if ([device position] == AVCaptureDevicePositionBack) { NSLog(@"Device position: back"); } else { NSLog(@"Device position: front"); } } } //[Objective-C]トーチ・モードを備え 640x480 のビデオ撮影が可能なデバイスを探す。結果は torchDevices 配列に入れる。 NSArray *devices = [AVCaptureDevice deviceWithMediaType: AVMediaTypeVideo]; NSMutableArray *torchDevices = [[NSMutableArray alloc] init]; for (AVCaptureDevice *device in devices) { if ([device hasTorch] && [device supportsAVCaptureSessionPreset: AVCaptureSessionPreset640x480]) { [touchDevices addObject:device]; } } ***フォーカス・モード [#wf4ad514] |AVCaptureFocusModeLocked| 焦点位置を固定する。| |AVCaptureFocusModeAutoFocus| シングル・スキャン・フォーカスを実行してから焦点位置を固定する。| |AVCaptureFocusModeContinuousAutoFocus| オート・フォーカスを継続する。| -isFocusModeSupported(): デバイスがそのフォーカス・モードをサポートしているか調べる -focusMode(): フォーカス・モードを設定する -focusPointOfInterest(): 焦点を固定する。 座標系は(0,0)が左上、(1,1)が右下。 -adjustingFocus: 焦点が合っているか ***露出モード [#g29e80af] |AVCaptureExposureModeContinuousAutoExposure|露出レベルを自動調節する| |AVCaptureExposureModeLocked|露出レベルを固定する| -isExposureModeSupported(): その露出モードをサポートしているか調べる -exposurePointOfInterest(): 露出を設定する。座標系は(0,0)が左上、(1,1)が右下。 -adjustingExposure: 露出が合っているか ***フラッシュ・モード [#hf934229] |AVCaptureFlashModeOff|フラッシュを使わない| |AVCaptureFlashModeOn|フラッシュが常に点灯する| |AVCaptureFlashModeAuto|状況に応じてフラッシュが点灯する| -hasFlash(): フラッシュ機能があるか調べる -isFlashModeSupported(): そのフラッシュ・モードをサポートしているか調べる -flashMode: フラッシュ・モードを設定するプロパティ。 ***トーチ・モード [#p7d9f12a] フラッシュが低電力で点灯したままになる。 |AVCaptureTorchModeOff|トーチを使わない| |AVCaptureTorchModeOn|トーチが常に点灯する| |AVCaptureTorchModeAuto|状況に応じてトーチが点灯する| -hasTorch(): トーチ機能があるか調べる -isTorchModeSupported(): そのトーチ・モードをサポートしているか調べる -torchMode: トーチ・モードを設定するプロパティ。 ***デバイスの設定 [#o4aff4f7] -lockForConfiguration: -- デバイスのロックを取得する。設定を変更するにはロックして行う必要がある。 -unlockForConfiguration -- デバイスのロックを解放する。 //[Objective-C] デバイスの設定を変更する if ([device isFocusModeSupported: AVCaptureFocusModeLocked]) { NSError *error = nil; if ([device lockForConfiguration: &error]) { device.focusMode = AVCaptureFocusModeLocked; [device unlockForConfiguration]; } } ***デバイスの切り替え [#me45f872] 一旦デバイスを削除してから、別のデバイスを追加する。 -removeInput: -- 入力デバイスをセッションから削除する。 -addInput: 入力デバイスをセッションに追加する。 //[Objective-C] AVCaptureSession *session = ...; [session beginConfiguration] [session removeInput: frontFacingCameraDeviceInput]; [session addInput: backFacingCameraDeviceInput]; [session commitConfiguration] **入力デバイスをセッションに追加する [#jed9a782] セッションに addInput() を用いて AVCaptureDeviceInputオブジェクトを追加する。 セッションは AVCaptureConnectionオブジェクトを用いて、AVCaptureInputPortとAVCaptureOutputの対応を定義する。 //[Objective-C] AVCaptureSession *session = [[AVCaptureSession alloc] init]; ... AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice: device error: &error] if ([session canAddInput: input]) { [session addInput: input]; } **出力をセッションに追加する [#qf8f39b3] セッションに addOutput() を用いて、AVCaptureOutputのサブクラスのオブジェクトを追加する。 |AVCaptureMovieFileOutput|ムービーファイルに出力する場合| |AVCaptureVideDataOutput|ビデオのフレームを処理する場合| |AVCaptureAudioDataOutput|音声データを処理する場合| |AVCaptureStillImageOutput|静止画像をキャプチャする場合| //[Objective-C] AVCaptureSession *session = ... AVCaptureMovieFileOutput *output = ... if ([session canAddOutput: output]) { [session addOutput: output] } ***ムービーファイルへの保存 [#x3361721] 出力の解像度とビットレートは sessionPreset によって異なる。 通常のエンコードは、ビデオは H.264、オーディオは AAC である。 //[Objective-C] AVCaptureMovieFileOutput *output = [[AVCaptureMovieFileOutput alloc] init]; output.maxRecordedDuration = ... output.minFreeDiskSpaceLimit = ... ***録画の開始 [#radb3651] -startRecordingToOutputfileURL:recordingDelegate:メソッド --- quickTimeムービーの録画を開始する。上書きできないので既存のファイルをURLに指定してはいけない。 -デリゲートは AVCaptureFileOutputRecordingDelegateプロトコルに適合し、 captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error: メソッドを実装する。 ***ファイルの書き込みの確認 [#b6ebda88] -captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:メソッドの実装では、 エラーをチェックするだけではなく、エラーのユーザ情報に含まれる AVErrorRecordingSuccessfullyFinishedKey の値も確認するべき。 エラーが発生してもファイルが正常に保存されている場合があるため。 よくあるエラーの種類 |AVErrorMaximumDurationReached|録画時間の上限に達した| |AVErrorMaximumFileSizeReached|録画がファイルの上限に達した| |AVErrorDiskFull|ディスクの空がなくなった |AVErrorDeviceWasDisconnected|録画デバイスが切断された| |AVErrorSessionWasInterrupted|セッションが中断された(電話がかかってきた場合など)| //[Objective-C] - (void) captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL: (NSURL *)outputFileURL fromConnections: (NSArray *)connections error: (NSError *) error { BOOL success = YES; if ([error code] != noErr) { id value = [[error userInfo] objectForKey: AVErrorRecordingSuccessfullyFinishedKey]; if (value) { success = [value boolValue]; } } // success の値で判断できる ... } &color(#ff0000){*CAUTION*: 次のコード例は要確認}; func captureOutput(captureOutput: AVCaptureFileOutput, didFinishRecordingToOutputFileAtURL outputFileURL: NSURL, fromConnections connections: NSArray, error error: NSError) { if (error?.code() != noError) { // 問題発生。録画が正常終了かどうかを調べる var value = error.userInfo()?.objectForKey(AVErrorRecordingSuccessfullyFinishedKey) // value?.boolValue() の値でファイルが正常に書き込まれたかどうかわかる } ***ファイルへメタデータを追加する [#y2d1075e] ファイル出力のメタデータは AVMetadataItem オブジェクトの配列である。 このサブクラスの AVMutableMetadataItem を用いて独自のメタデータが作成できる。 //[Objective-C] AVCaptureMovieFileOutput *output = ... NSArray *oldMeatadata = output.metadata; NSMutableArray *metadata = nil; if (oldMetadata) { metadata = [oldMetadata mutableCopy]; } else { metadata = [[NSMutableArray alloc] init]; } AVMutableMetadataItem *item = [[AVMutableMetadataItem alloc] init]; item.keySpace = AVMetadataKeySpaceCommon; item.key = AVMetadataCommonKeyLocation; CLLocation *loc = ... item.value = [NSString stringWithFormat:@"%+08.41f%+09.4lf/" loc.coordinate.latitude, loc.coordinate.longiture]; [metadata addObject: item]; output.metadata = metadata; **ビデオのフレーム処理 [#v026cd65] AVCaptureVideDataOutput オブジェクトは、デリゲートを使用してビデオフレームを提供する。 -setSampleBufferDelegate: queue: --- デリゲートを設定する。キューには serial queue を指定する(フレームが順番通りに渡されるように)。 -デリゲート captureOutput:didOutputSampleBuffer:fromConnection: にフレームが渡される。 フレームは不透過型 CMSampleBufferref のインスタンスである。 -出力フォーマットは videoSettings プロパティ(辞書型)に指定する。現在サポートされているキーは kCVPixelBufferPixelFormatTypeKey のみ。 --推奨するピクセル形式 availableVideoCVPixelFormatTypesプロパティ --サポートされているコーデックの値 availableVideoCodecTypesプロパティ BGRAフォーマットは CoreGraphics でも OpenGL でも正常に動作する。(すなわちこのフォーマットを使えということか...) //[Objective-C] AVCaptureVideDataOutput *output = [AVCaptureVideDataOutput new]; NSDictionary *dic = @{ (NSString *) kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) }; output.videoSettiong = dic; // 出力queueがブロックされていたら捨てる (静止画を撮影中などの場合) [output setAlwaysDiscardsLateVideoFrames: YES]; // 同期dispatch queueを作成する。サンプリング・バッファのデリゲートで使うため。 videoDataOutputQueue = dispatch_queue_create("VideoDataOutputQueue", DISPATCH_QUEUE_SERIAL); [output setSampleBufferDelegate: self queue: videoDataOutputQueue]; ... AVCaptureSession *session = ... if ([session canAddOutput: videoDataOutput]) { [session addOutput: videoDataOutput]; } ** 静止画像のキャプチャ [#t9c773c6] AVCaptureStillImageOutput 出力を利用する。 静止画像のフォーマットとコーデックの設定 -availableImageDataCVPixelFormatTypes --- デバイスがサポートする画像フォーマットを調べる -availableImageDataCodecTypes --- デバイスがサポートするコーデックを調べる //[Objective-C] AVCaptureStilImageOutput *output = [[AVCaptureStillImageOutput alloc] init]; NSDictionary *dic = @{ AVVideoCodecKey: AVVideoCodecJPEG }; [output setOutputSettings: dic]; 静止画像のキャプチャ 出力のメソッド -captureStillImageAsynchronouslyFromConnection:completionHandler: --- 入力デバイスを指定して静止画像をキャプチャする。 第1引数はキャプチャに使用するコネクションであり、その入力ポートがビデオをサポートしている必要がある。 第2引数は2個の引数(CMSampleBuffer, エラー)をとるブロック(Swiftのクロージャ)である。 //[Objective-C] 入力ポートがビデオをサポートしているコネクションを探す AVCaptureConnection *videoConnection = nil; for (AVCaptureConnection *conn in stillImageOutput.connections) { for (AVCaptureInputPort *port in [conn inputPorts]) { if ([[port mediaType] isEqual: AVMediaTypeVideo]) { videoConnection = conn; break; } } if (videoConnection) break; } ... AVCaptureStillImageOutput *output = ... [output captureStillImageAsynchronouslyFromConnection: videoConnection completionHandler: ^(CMSampleBufferRef buf, NSError *error) { CFDictionaryRef exif = CMGetAttachment(buf,kCGImagePropertyExifDictionary,NULL); if (exif) { // アタッチメントを使う } ... }]; **ユーザに対する録画内容の表示 [#a732b48d] 録画中の画像にアクセスするには、ブレビューレイヤを使用する。 録音中の音声にアクセスするには、オーディオチャンネルを監視する。 ***ビデオのプレビュー [#b6195c12] AVCaptureVidePrevieLayerオブジェクトを使用する。これは CALayer のサブクラスである。