#author("2016-06-19T03:26:47+00:00","default:nitta","nitta")
#author("2016-06-25T07:35:47+00:00","default:nitta","nitta")
[[iOS]]

#navi

*AVFoundation [#e822a056]

*静止画像やビデオ画像の撮影 [#w03e69c6]
- Reference 
https://developer.apple.com/reference/avfoundation/

デバイス(カメラやマイクなど)からデータをキャプチャ処理するには、
-入力デバイス 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 のサブクラスである。


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS