- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2016-06-19T03:26:47+00:00","default:nitta","nitta")
#author("2016-06-19T06:57:49+00:00","default:nitta","nitta")
[[iOS]]
*AVFoundation [#e822a056]
*静止画像やビデオ画像の撮影 [#w03e69c6]
デバイス(カメラやマイクなど)からデータをキャプチャ処理するには、
-入力デバイス AVCaptureDevice
-入力デバイスのポート設定 AVCaptureInput
--入力ポート AVCaptureInputPort
-出力を管理する AVCaptureOutput
--静止画 AVCaptureStillImageOutput
--ビデオと音声データを共に受け取る AVCaptureMovieFileOutput
-入力から出力へデータの流れを調整する AVCaptureSession
-(Option) プレビュー表示 AVCaptureVideoPreviewLayer
を組み合わせて使う
キャプチャ・セッションにおける入力と出力の接続 AVCaptureConnection
**セッション AVCaptureSession [#we96bf3f]
-startRunningメソッド -- セッションの動作を開始する
-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*: 次のコード例は要確認};
//Swift
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 のサブクラスである。
CALayer のサブクラスのAVCaptureVidePrevieLayerオブジェクトを使用してプレビューを表示する。
AVCaptureVideoDataOutputを用いて、表示する前のビデオのピクセルにアクセスできる。
//[Objective-C]
AVCaptureSession *session = ...
CALayer *layer = ...
AVCaptureVidePreviewLayer *preview = [[AVCaptureVideoPreviewLayer alloc] initWithSession: session];
[layer addSublayer: preview];
プレビュー・レイヤは他のレイヤと同じく拡大縮小・回転・平行移動などができる。
回転方向指定するためのレイヤのorientationプロパティを設定する必要とする場合がある点が異なる。
ミラーリングが可能かどうかは supportsVideoMirroring プロパティで判断でき、
にミラーリングするときは videoMirrored プロパティを設定する
(automaticallyAdjustVideoMirroringプロパティがYESならば自動的に設定される)。
|AVLayerVideoGraivtyResizeAspect|アスペクト比を維持したままできるだけ大きく表示する。黒いバーが残る場合あり。|
|AVLayerVideoGraivtyResizeAspectFill|アスペクト比を維持したままできるだけ大きく表示する(はみ出た部分はトリミング)|
|AVLayerVideoGraivtyResize|表示領域全体に表示する。アスペクト比は維持されない。|
***オーディオレベルの表示 [#vb63134d]
オーディオ・チャネルの平均出力レベルやピーク出力レベルを監視するには、AVCaptureAudioChannelオブジェクトを使用する。
値を調べたいタイミングでポーリングする必要がある。
//[Objective-C]
AVCaptureAudioDataOutput *output = ...
NSArray *connections = output.connections;
if ([connections count] > 0) {
AVCaptureConnection *conn = [connections objectAtIndex: 0];
NSArray channels = conn.audioChannels;
for (AVCaptureAudioChannel) *chan in channels) {
float avg = chan.averagePower;
float peak = chan.peakHoldLevel;
// 平均とピークの値を表示する
}
}
**UIImageオブジェクトとしてのビデオフレームのキャプチャ [#b9a953a4]
ビデオをキャプチャし、取得したフレームをUIImageオブジェクトに変換する方法は以下の通り。
-AVCaptureSessionオブジェクトを作成して、AV入力デバイスから出力へのデータの流れを調整する。
//[Objective-C]
AVCaptureSession *session = [[AVCaptureSession alloc] init];
session.sessionPresent = AVCaptureSessionPresetMedium; // 解像度:中
-必要な入力タイプの AVCaptureDevice オブジェクトを見つける。
//[Objective-C]
AVCaptureDevice *devide = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
-デバイスのAVCaptureDeviceInputオブジェクトを生成する。
//[Objective-C]
NSError *error = nil;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice: device error:&error];
if (!input) { /* エラー処理 */ }
[session addInput:input]
- AVCaptureDeviceDataOutputオブジェクトを生成して、ビデオフレームを生成する。~
キャプチャ中のビデオから非圧縮フレームにアクセスするには AVCaptureVideoDataOutputを使う。
//[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オブジェクトのデリゲートを実装して、ビデオフレームを処理する。
//[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に変換する。
//[Objective-C]
- (void) captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection: (AVCaptureConnection *)connection {
UIImage *image = imageFromSampleBuffer(sampleBuffer);
...
}
このデリゲート・メソッドはqueue: に指定したキュー上で呼び出される。
ユーザインターフェイスを更新する場合は、関連するコードをメインスレッド上で呼び出す必要がある。
***録画の開始と停止 [#a72e5a6b]
録画が許可されているか調べる
//[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 メソッドを使う。
**再生 [#u9ad9b12]
AVPayerのインスタンスは setRate: メソッドで値を設定して再生速度を変更することができる。
AVPlayerItemオブジェクトの audioTimePitchAlgorithm プロパティには、
音声再生方法(Time Pitch Algorithm Settings)を示す定数を設定する。