#author("2016-06-20T13:11:12+00:00","default:nitta","nitta")
#author("2016-06-23T00:25:40+00:00","default:nitta","nitta")
[[iOS/AVFoundation]]

*AVFoundation: 静止画像やビデオ画像の撮影 [#r846c8c9]

デバイス(カメラやマイクなど)からデータをキャプチャ処理するには、
AVCaputeSession を用いる。

-セッション(入力から出力へのデータの流れ)を管理する AVCaptureSession
-入力デバイスを表す AVCaptureDevice
-入力デバイスのポートを表す AVCaptureDeviceInput
-出力ポートを扱う AVCaptureDeviceOutput
-セッションにおける入力と出力の接続を表す AVCaptureConnection
-プレビュー表示を扱う AVCaptureVideoPreviewLayer

*セッション AVCaptureSession [#me03b908]

-startRunningメソッド -- セッションの動作を開始する
-stopRunningメソッド -- セッションの動作を止める

 //[Swift]
 var session: AVCaptureSession!
 session =  AVCaptureSession()
 ...
 session.startRunning()
 ...
 session.stopRunning()

 //[Objective-C]
 AVCaptureSession *session = [[AVCaptureSession alloc] init];
 ...
 [session startRunning]
 ...
 [session stopRunning]

**セッションの設定 [#h26db6c4]
|シンボル|解像度|コメント|
|AVCaptureSessionPresetHigh|高|最高の録画品質。デバイス依存。|
|AVCaptureSessionPresetMedium|中|WiFi共有に適する。|
|AVCaptureSessionPresetLow|低い|3G共有に適する。|
|AVCaptureSessionPreset640x480|640x480|VGA|
|AVCaptureSessionPreset1280x720|1280x720|720p HD|
|AVCaptureSessionPresetPhoto|写真|写真用の最大解像度。ビデオ出力では使えない。|

-セッションのパラメータを変更する場合: 変更処理を beginConfiguration と commitConfiguration メソッドの間に記述する。
-キャプチャ・セッションの状態監視:
--running プロパティ: 実行中かどうか
--interrupted プロパティ: 中断されたか
--ランタイムエラーを監視するには AVCaptureSessionRuntimeErrorNotification を受ける。

 //[Swift]
 var session: AVCaptureSession!
 ...
 if session.canSetSesstionPreset(AVCaptureSessionPresetHight) {
     session.sessionPreset = AVCaptureSessionPresetHigh
 } else {
    ...
 }
 ...
 mySession.beginConfiguration()
 ...
 mySession.commitConfiguration()
 

 //[Objective-C]
 if ([session canSetSessionPreset: AVCaptureSessionPreset1280x720]) {
   session.sessionPreset = AVCaptureSessionPreset1280x720;
 }
 ...
 [session beginConfiguration]
 // 設定変更
 [session commitConfiguration]

*入力デバイス AVCaptureDevice [#bdd9801a]

-使用可能なキャプチャ・デバイスを調べる: device(), devicesWithMediaType()
-変更の通知は AVCaptureDeviceWasConnectedNotification, AVCaptureDeviceWasDisconnectedNotification 
-特定のメディアタイプを提供しているか調べる: hasMediaType()
-特定のキャプチャ・プリセットを提供しているか調べる: supportsAVCaptureSessionPreset()

 //[Swift] バック・カメラ・デバイスを取り出す
 var camera: AVCaptureDevice!
 let devices = AVCaptureDevice.devices()
 for device in devices {
   if (device.position == AVCaptureDevicePosition.Back) {
     camera = device as! AVCaptureDevice
   }
 }

 //[Objective-C] バック・カメラ・デバイスを取り出す
 NSArray *devices = [AVCaptureDevice devices];
 for (AVCaptureDevice* device in devices) {
   NSLog(@"Device name: %@", [device localizedName]);
   if ([device hasMediaType: AVMediaTypeVideo]) {
     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];
   }
 }

**フォーカス・モード [#k9a9f37f]

|AVCaptureFocusModeLocked| 焦点位置を固定する。|
|AVCaptureFocusModeAutoFocus| シングル・スキャン・フォーカスを実行してから焦点位置を固定する。|
|AVCaptureFocusModeContinuousAutoFocus| オート・フォーカスを継続する。|

-isFocusModeSupported(): デバイスがそのフォーカス・モードをサポートしているか調べる
-focusMode(): フォーカス・モードを設定する
-focusPointOfInterest(): 焦点を固定する。 座標系は(0,0)が左上、(1,1)が右下。
-adjustingFocus: 焦点が合っているか

**露出モード [#n6088088]
|AVCaptureExposureModeContinuousAutoExposure|露出レベルを自動調節する|
|AVCaptureExposureModeLocked|露出レベルを固定する|

-isExposureModeSupported(): その露出モードをサポートしているか調べる
-exposurePointOfInterest(): 露出を設定する。座標系は(0,0)が左上、(1,1)が右下。
-adjustingExposure: 露出が合っているか

**フラッシュ・モード [#o39d6524]
|AVCaptureFlashModeOff|フラッシュを使わない|
|AVCaptureFlashModeOn|フラッシュが常に点灯する|
|AVCaptureFlashModeAuto|状況に応じてフラッシュが点灯する|

-hasFlash(): フラッシュ機能があるか調べる
-isFlashModeSupported(): そのフラッシュ・モードをサポートしているか調べる
-flashMode: フラッシュ・モードを設定するプロパティ。

**トーチ・モード [#ec4c0df9]
フラッシュが低電力で点灯したままになる。

|AVCaptureTorchModeOff|トーチを使わない|
|AVCaptureTorchModeOn|トーチが常に点灯する|
|AVCaptureTorchModeAuto|状況に応じてトーチが点灯する|

-hasTorch(): トーチ機能があるか調べる
-isTorchModeSupported(): そのトーチ・モードをサポートしているか調べる
-torchMode: トーチ・モードを設定するプロパティ。

**デバイスの設定 [#na0a4324]

-lockForConfiguration: -- デバイスのロックを取得する。設定を変更するにはロックして行う必要がある。
-unlockForConfiguration -- デバイスのロックを解放する。

 //[Objective-C] デバイスの設定を変更する
 if ([device isFocusModeSupported: AVCaptureFocusModeLocked]) {
   NSError *error = nil;
   if ([device lockForConfiguration: &error]) {
     device.focusMode = AVCaptureFocusModeLocked;
     [device unlockForConfiguration];
   }
 }

**デバイスの切り替え [#mbed1b55]
一旦デバイスを削除してから、別のデバイスを追加する。

-removeInput: -- 入力デバイスをセッションから削除する。
-addInput: 入力デバイスをセッションに追加する。

 //[Swift]
 var session: AVCaptureSession!
 var frontFacingCameraDeviceInput: AVCaptureDeviceInput!
 var backFacingCameraDeviceInput:  AVCaptureDeviceInput!
 session.beginConfiguration()
 session.removeInput(frontFacingCameraDeviceInput)
 session.addInput(backFacingCameraDeviceInput)
 session.commitConfiguration()

 //[Objective-C]
 AVCaptureSession *session = ...;
 [session beginConfiguration]
 [session removeInput: frontFacingCameraDeviceInput];
 [session addInput: backFacingCameraDeviceInput];
 [session commitConfiguration]


*入力 AVCaptureDeviceInput [#lc3580bd]

セッションが入力として扱うのは、入力デバイスの入力ポートである。

セッションに addInput() を用いて AVCaptureDeviceInputオブジェクトを追加する。
セッションは AVCaptureConnectionオブジェクトを用いて、AVCaptureInputPortとAVCaptureOutputの対応を定義する。

 //[Swift]
 var session: AVCaptureSession!
 var input: AVCaptureDeviceInput!
 ...
 if session.canAddInput(input) {
   session.addInput(input)
 }

//[Objective-C]
 AVCaptureSession *session = [[AVCaptureSession alloc] init];
 AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice: device error: &error]
 ...
 if ([session canAddInput: input]) {
   [session addInput: input];
 }


*出力 AVCaptureOutput [#v13ad437]
セッションに出力として追加するのは AVCaptureOutput クラスのサブクラスである。

|AVCaptureVideDataOutput|ビデオをフレーム単位で処理する場合|
|AVCaptureStillImageOutput|静止画像をキャプチャする場合|
|AVCaptureMovieFileOutput|ムービーファイルに保存する場合|
|AVCaptureAudioDataOutput|音声データを処理する場合|

セッションの addOutput() メソッドを用いて出力を追加する。

 //[Swift]
 var session: AVCaptureSession!
 var output: AVCaptureMovieFileOutput!
 ...
 if session.canAddOutput(output) {
   session.addOutput(output)
 }

 //[Objective-C]
 AVCaptureSession *session = ...
 AVCaptureMovieFileOutput *output = ...
 if ([session canAddOutput: output]) {
   [session addOutput: output]
 }

**ビデオをフレーム単位で処理する場合 AVCaptureVideDataOutput [#ud0c5163]

AVCaptureVideDataOutput オブジェクトは、デリゲートを使用してビデオフレームを提供する。
-setSampleBufferDelegate: queue: --- デリゲートを設定する。キューには serial queue を指定する(フレームが順番通りに渡されるように)。
-デリゲートに渡されるクラスは AVCaptureVideoDataOutputSampleBufferDelegate プロトコルに適合している必要がある。
-デリゲートの captureOutput:didOutputSampleBuffer:fromConnection: メソッドにビデオのフレームが渡される。フレームは不透過型 CMSampleBufferref のインスタンスである。
-出力フォーマットは videoSettings プロパティ(辞書型)に指定する。現在サポートされているキーは kCVPixelBufferPixelFormatTypeKey のみ。
--推奨するピクセル形式 availableVideoCVPixelFormatTypesプロパティ
--サポートされているコーデックの値 availableVideoCodecTypesプロパティ

BGRAフォーマットは CoreGraphics でも OpenGL でも正常に動作する。(すなわちこのフォーマットを使えということか...)

 //[Swift]
 class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
   ...
   var session: AVCaptureSession!
   ...
   var output: AVCaptureVideoDataOutput!
   output = AVCaptureVideoDataOutput()
   output.videoSettings = [kCVPixelBufferPixelFormatTypeKey : Int(kCVPixelFormatType_32BGRA)]
   output.setSampleBufferDelegate(self,queue:dispatch_get_main_queue())
   output.alwaysDiscardsLateVideoFrames = true
   if (session.canAddOutput(output)) {
     session.addOutput(myVideoOutput)
   }

 //[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];
 }

デリゲートのメソッドは captureOutput:didOutputSampleBuffer である。
ビデオ・フレームの方向を整えたあとで、sampleBuffer の内容を UIImage に変換する。
変換方法は以下の通り。

 //[Swift]
 @IBOutlet weak var imageView: UIImageView!
 ...
 func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!) {
   if connection.supportsVideoOrientation {  // 画像の向きを設定する
     connection.videoOrientation = AVCaptureVideoOrientation.Portrait
   }
   dispatch_async(dispatch_get_main_queue(), {    // メイン・キューの上で実行する
     let image = self.imageFromSampleBuffer(sampleBuffer)  // サンプルバッファをUIImageに変換して
     self.imageView.image = image; // Image View に表示する。
   })
 }

***ビデオ・フレームの向きを指定する [#n046a764]
 //[Swift]
 if connection.supportsVideoOrientation {
   connection.videoOrientation = AVCaptureVideoOrientation.Portrait
   // .Portrait, .LandscapeRight, .LandscapeLeft or .PortraitUpsideDown
 }


***ビデオ・フレームをUIImageへ変換する [#eb0754cb]

ビデオ・フレームは CMSampleBufferRef 型で渡されてくる。
これを UIImage に変換する方法は次の通り。

 func imageFromSampleBuffer(sampleBuffer: CMSampleBufferRef) -> UIImage {
   let imageBuffer: CVImageBufferRef = CMSampleBufferGetImageBuffer(sampleBuffer)!
   CVPixelBufferLockBaseAddress(imageBuffer, 0)   // Lock Base Address
   let baseAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0)  // Get Original Image Information
   let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer)
   let width = CVPixelBufferGetWidth(imageBuffer)
   let height = CVPixelBufferGetHeight(imageBuffer)
   let colorSpace = CGColorSpaceCreateDeviceRGB()  // RGB ColorSpace
   let bitmapInfo = (CGBitmapInfo.ByteOrder32Little.rawValue | CGImageAlphaInfo.PremultipliedFirst.rawValue)
   let context = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, bitmapInfo)
   let imageRef = CGBitmapContextCreateImage(context) // Create Quarts image
   CVPixelBufferUnlockBaseAddress(imageBuffer, 0)    // Unlock Base Address
   let resultImage: UIImage = UIImage(CGImage: imageRef!)
   return resultImage
 }

** 静止画像をキャプチャする場合 AVCaptureStillImageOutput [#n508b4b9]

AVCaptureStillImageOutput 出力を利用する。

静止画像のフォーマットとコーデックの設定
-availableImageDataCVPixelFormatTypes --- デバイスがサポートする画像フォーマットを調べる
-availableImageDataCodecTypes --- デバイスがサポートするコーデックを調べる

 //[Swift]
 var session: AVCaptureSession!
 ...
 var output: AVCaptureStillImageOutput!
 output = AVCaptureStillImageOutput()
 session.addOutput(output)

 //[Objective-C]
 AVCaptureStilImageOutput *output = [[AVCaptureStillImageOutput alloc] init];
 NSDictionary *dic = @{ AVVideoCodecKey: AVVideoCodecJPEG };
 [output setOutputSettings: dic];

静止画像のキャプチャ

出力のメソッド
-captureStillImageAsynchronouslyFromConnection:completionHandler: --- 入力デバイスを指定して静止画像をキャプチャする。
第1引数はキャプチャに使用するコネクションであり、その入力ポートがビデオをサポートしている必要がある。
第2引数は2個の引数(CMSampleBuffer, NSError)をとるクロージャである。
クロージャの中では変数やメソッドは self 経由でアクセスする。

jpeg画像をキャプチャする場合は、圧縮フォーマットを指定せずデフォルトに任せた方がよい。
そのjpeg 画像に jpegStillImageNSDataRepresentaion: メソッドを使用すると、
再圧縮せずにNSDataオブジェクトが取得できる。

 //[Swift]
 @IBOutlet weak var imageView; UIImageView!
 ...
 var output : AVCaptureStillImageOutput!
 ...
 var connection: AVCaptureConnecton!
 connection = output.connectionWithMediaType(AVMediaTypeVideo)
 output.captureStillImageAsynchronouslyFromConnection(connection, completionHandler: {
   (imageDataBuffer,error) -> Void in
   let jpgData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataBuffer)
   let image: UIImage! = UIImage(data:jpgData) // このimageを利用すればよい
   self.imageView.image = image
 })

 //[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) {
        // アタッチメントを使う
      }
      ...
    }];


**ムービーファイルに保存する場合 AVCaptureMovieFileOutput [#d4af6861]

出力の解像度とビットレートは sessionPreset によって異なる。
通常のエンコードは、ビデオは H.264、オーディオは AAC である。

 //[Objective-C]
 AVCaptureMovieFileOutput *output = [[AVCaptureMovieFileOutput alloc] init];
 output.maxRecordedDuration = ...
 output.minFreeDiskSpaceLimit = ...

***録画の開始 [#n520f20b]
-startRecordingToOutputfileURL:recordingDelegate:メソッド --- quickTimeムービーの録画を開始する。上書きできないので既存のファイルをURLに指定してはいけない。
-デリゲートは AVCaptureFileOutputRecordingDelegateプロトコルに適合し、
captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error: メソッドを実装する。

***ファイルの書き込みの確認 [#k2eeb045]

-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 の値で判断できる
   ...
 }

***ファイルへメタデータを追加する [#d1be8841]

ファイル出力のメタデータは 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;


**音声データを処理する場合 AVCapture&65;udio&68;ataOutput [#hd4320e2]
**音声データを処理する場合 AVCaptureAudioDataOutput [#hd4320e2]
略

*ユーザに対する録画内容の表示 [#f4fb75d8]
カメラで録画している内容をユーザに提示するには、ブレビューレイヤを使用する。
マイクで録音中の音声をユーザに提示するには、オーディオチャンネルを監視する。

**ビデオのプレビュー [#n41b750e]
CALayer のサブクラスのAVCaptureVideoPreviewLayerオブジェクトを使用してプレビューを表示する。

AVCaptureVideoDataOutputを用いて、表示する前にビデオのピクセルにアクセスできる。

 //[Swift] 背景全体にプレビューを表示する
 var session: AVCaptureSession!
 ...
 let layer = AVCaptureVideoPreviewLayer(session: session)
 layer.frame = view.bounds
 layer.videoGravity = AVLayerVideoGravityResizeAspectFill
 view.layer.insertSublayer(layer,atIndex:0)

 //[Objective-C]
 AVCaptureSession *session = ...
 CALayer *layer = ...
 AVCaptureVidePreviewLayer *preview = [[AVCaptureVideoPreviewLayer alloc] initWithSession: session];
 [layer addSublayer: preview];

プレビュー・レイヤは他のレンダリング・ツリー内の他の CALayer オブジェクトと同じように動作し、他のレイヤと同じく拡大縮小・回転・平行移動などができる。
ただひとつ異なる点が、画像の回転方向を指定するためにレイヤのorientationプロパティを設定する必要がある場合があることである。
ミラーリングが可能かどうかは supportsVideoMirroring プロパティで判断でき、
にミラーリングするときは videoMirrored プロパティを設定する
(automaticallyAdjustVideoMirroringプロパティがYESならば自動的に設定される)。

videoGravityに次の値を設定できる。
|AVLayerVideoGraivtyResizeAspect|アスペクト比を維持したままできるだけ大きく表示する。黒いバーが残る場合あり。|
|AVLayerVideoGraivtyResizeAspectFill|アスペクト比を維持したままできるだけ大きく表示する(はみ出た部分はトリミング)|
|AVLayerVideoGraivtyResize|表示領域全体に表示する。アスペクト比は維持されない。|

**オーディオレベルの表示 [#dc2386d7]
オーディオ・チャネルの平均出力レベルやピーク出力レベルを監視するには、AVCaptureAudioChannelオブジェクトを使用する。
値を調べたいタイミングでポーリングする必要がある。

 //[Objective-C]
 AVCaptureAudioDataOutput *output = ...
 NSArray *connections = output.connections;
 if ([connections count] > 0) {
 if ([connections count] > 0) { // AVCaptureAudioDataOutput ではコネクションは1個だけしか持てない
   AVCaptureConnection *conn = [connections objectAtIndex: 0];
   NSArray channels = conn.audioChannels;
   for (AVCaptureAudioChannel) *chan in channels) {
     float avg = chan.averagePower;
     float peak = chan.peakHoldLevel;
     // 平均とピークの値を表示する
   }
 }


*UIImageオブジェクトとしてのビデオフレームのキャプチャ [#uc67b955]
ビデオをキャプチャし、取得したフレームをUIImageオブジェクトに変換する方法は以下の通り。

-AVCaptureSessionオブジェクトを作成して、AV入力デバイスから出力へのデータの流れを調整する。
 //[Swift]
 var session: AVCaptureSession! = AVCaptureSession()
 session.sessionPreset = AVCaptureSessionPresetHigh
 //[Objective-C]
 AVCaptureSession *session = [[AVCaptureSession alloc] init];
 session.sessionPreset = AVCaptureSessionPresetMedium; // 解像度:中
-必要な入力タイプの AVCaptureDevice オブジェクトを見つける。
 //[Swift]
 var device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
 //[Objective-C]
 AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
-デバイスのAVCaptureDeviceInputオブジェクトを生成する。
 //[Swift]
 do {
   var input = try AVCaptureDeviceInput(device)
   session.addInput(input)
   ...
 } catch let error as NSError {
   ...
 }
 //[Objective-C]
 NSError *error = nil;
 AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice: device error:&error];
 if (!input) { /* エラー処理 */ }
 [session addInput:input]
- AVCaptureDeviceDataOutputオブジェクトを生成して、ビデオフレームを生成する。~
キャプチャ中のビデオから非圧縮フレームにアクセスするには AVCaptureVideoDataOutputを使う。
 //[Swift]
 do {
   ...
   var output = AVCaptureVideoDataOutput()
   out.videoSettings = [kCVPixelBufferPixelFormatTypeKey : Int(kCVPixelFormatType_32BGRA)]
   ...
 } catch let error as NSError {
   ...
 }
 //[Objective-C]
 AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init];
 [session addOutput: output]
 output.videoSettings = @{ (NSString*) kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA) };
 output.minFrameDuration = CMTimeMake(1,15);  // 1/15 seconds
-AVCaptureVideoDataOutputオブジェクトのデリゲートを実装して、ビデオフレームを処理する。
 //[Swift]
 output.setSampleBufferDelegate(self, queue: dispatch_get_main_queue())
 output.alwaysDiscardsLateVideoFrames = true
 //[Objective-C]
 dispach_queue_t = dispatch_queue_create("MyQueue", NULL);
 [output setSampleBufferDelegate: self queue: queue];
 dispatch_release(queue);
-デリゲートから受け取った CMSampleBuffer を UIImage オブジェクトに変換する関数を実装する。~
デリゲートを処理するクラスにはサンプル・バッファが書き込まれたときに呼び出されるメソッド
captureOutput:didOutputSampleBuffer:fromConnection:
を実装する。フレームはVideDataOutputオブジェクトから CMSampleBuffer として渡されるのでUIImageに変換する。
 //[Swift]
 func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!) {
   dispatch_async(dispatch_get_main_queue(), {
     let image = self.imageFromSampleBuffer(sampleBuffer)
     ...
   })
 }
 
 //[Objective-C]
 - (void) captureOutput:(AVCaptureOutput *)captureOutput
              didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
	      fromConnection: (AVCaptureConnection *)connection {
   UIImage *image = imageFromSampleBuffer(sampleBuffer);
   ...
 }
このデリゲート・メソッドはqueue: に指定したキュー上で呼び出される。
ユーザインターフェイスを更新する場合は、関連するコードをメインスレッド上で呼び出す必要がある。

**録画の開始と停止 [#i686677c]
録画が許可されているか調べる
 //[Objective-C]
 NSString *type = AVMediaTypeVideo;
 [AVCaptureDevice requestAccessForMediaType: type completionHander:
   ^(BOOL granted) {
     if (granted) {
       [self setDeviceAuthorized: YES]
     } else {
       dispatch_sync(dispatch_get_main_queue(), ^{
         [[[UIAlertView alloc] initWithTitle: @"Caution"
	                             message: @"permission denied to use Camera"
				    delegate: self
		           cancelButtonTitle: @"OK"
			   otherButtonTitles: nil] show]
         [self setDeviceAuthorized: NO];
       });
     }
   }]

カメラセッションが設定されていて、ユーザがアクセスを承認している場合は、startRunning で録画を開始する。
startRunningメソッドはブロック型で多少時間がかかるため、
セッションの設定処理を同期キュー(synchronized queue)上で実行し、
メインキューがブロックされないようにする必要がある。

録画の停止には stopRunning メソッドを使う。


*再生 [#se47739b]
AVPayerのインスタンスは setRate: メソッドで値を設定して再生速度を変更することができる。

AVPlayerItemオブジェクトの audioTimePitchAlgorithm プロパティには、
音声再生方法(Time Pitch Algorithm Settings)を示す定数を設定する。

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS