- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2016-06-20T07:49:02+00:00","default:nitta","nitta")
#author("2016-06-20T13:11:12+00:00","default:nitta","nitta")
[[iOS/AVFoundation]]
*AVFoundation: 静止画像やビデオ画像の撮影 [#r846c8c9]
デバイス(カメラやマイクなど)からデータをキャプチャ処理するには、
AVCaputeSession を用いる。
-セッション(入力から出力へのデータの流れ)を管理する AVCaptureSession
-入力デバイスを表す AVCaptureDevice
-入力デバイスのポートを表す AVCaptureDeviceInput
-出力ポートを扱う AVCaptureDeviceOutput
-セッションにおける入力と出力の接続を表す AVCaptureConnection
-プレビュー表示を扱う AVCaptureVideoPreviewLayer
*セッション AVCaptureSession [#me03b908]
-startRunningメソッド -- セッションの動 作を開始する
-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 を指定する(フレームが順番通りに渡されるように)。
-デリゲート captureOutput:didOutputSampleBuffer:fromConnection: にフレームが渡される。
フレームは不透過型 CMSampleBufferref のインスタンスである。
-出力フォーマットは videoSettings プロパティ(辞書型)に指定する。現在サポートされているキーは kCVPixelBufferPixelFormatTypeKey のみ。
--推奨するピクセル形式 availableVideoCVPixelFormatTypesプロパティ
--サポートされているコーデックの値 availableVideoCodecTypesプロパティ
-デリゲートに渡されるクラスは AVCaptureVideoDataOutputSampleBufferDelegate プロトコルに適合している必要がある。
-デリゲートの captureOutput:didOutputSampleBuffer:fromConnection: メソッドにビデオのフレームが渡される。フレームは不透過型 CMSampleBufferref のインスタンスである。
-出力フォーマットは videoSettings プロパティ(辞書型)に指定する。現在サポートされているキーは kCVPixelBufferPixelFormatTypeKey のみ。
--推奨するピクセル形式 availableVideoCVPixelFormatTypesプロパティ
--サポートされているコーデックの値 availableVideoCodecTypesプロパティ
BGRAフォーマットは CoreGraphics でも OpenGL でも正常に動作する。(すなわちこのフォーマットを使えということか...)
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 に変換する。
変換方法は以下の通り。
**ムービーファイルへの保存 [#u692db56]
//[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]
***録画の開始 [#n520f20b]
-startRecordingToOutputfileURL:recordingDelegate:メソッド --- quickTimeムービーの録画を開始する。上書きできないので既存のファイルをURLに指定してはいけない。
-デリゲートは AVCaptureFileOutputRecordingDelegateプロトコルに適合し、
captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error: メソッドを実装する。
**ファイルの書き込みの確認 [#k2eeb045]
***ファイルの書き込みの確認 [#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 の値で判断できる
...
}
&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() の値でファイルが正常に書き込まれたかどうかわかる
}
***ファイルへメタデータを追加する [#d1be8841]
**ファイルへメタデータを追加する [#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;
** 静止画像のキャプチャ [#efaa5640]
**音声データを処理する場合 AVCapture&65;udio&68;ataOutput [#hd4320e2]
略
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) {
// アタッチメントを使う
}
...
}];
*ユーザに対する録画内容の表示 [#f4fb75d8]
録画中の画像にアクセスするには、ブレビューレイヤを使用する。
録音中の音声にアクセスするには、オーディオチャンネルを監視する。
カメラで録画している内容をユーザに提示するには、ブレビューレイヤを使用する。
マイクで録音中の音声をユーザに提示するには、オーディオチャンネルを監視する。
**ビデオのプレビュー [#n41b750e]
CALayer のサブクラスのAVCaptureVideoPreviewLayerオブジェクトを使用してプレビューを表示する。
AVCaptureVideoDataOutputを用いて、表示する前のビデオのピクセルにアクセスできる。
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];
プレビュー・レイヤは他のレイヤと同じく拡大縮小・回転・平行移動などができる。
回転方向指定するためのレイヤのorientationプロパティを設定する必要とする場合がある点が異なる。
プレビュー・レイヤは他のレンダリング・ツリー内の他の CALayer オブジェクトと同じように動作し、他のレイヤと同じく拡大縮小・回転・平行移動などができる。
ただひとつ異なる点が、画像の回転方向を指定するためにレイヤのorientationプロパティを設定する必要がある場合があることである。
ミラーリングが可能かどうかは supportsVideoMirroring プロパティで判断でき、
にミラーリングするときは videoMirrored プロパティを設定する
(automaticallyAdjustVideoMirroringプロパティがYESならば自動的に設定される)。
|AVLayerVideoGraivtyResizeAspect|アスペクト比を維持したままできるだけ大きく表示する。黒いバーが残る場合あり。|
|AVLayerVideoGraivtyResizeAspectFill|アスペクト比を維持したままできるだけ大きく表示する(はみ出た部分はトリミング)|
|AVLayerVideoGraivtyResize|表示領域全体に表示する。アスペクト比は維持されない。|
videoGravityに次の値を設定できる。
|AVLayerVideoGraivtyResizeAspect|アスペクト比を維持したままできるだけ大きく表示する。黒いバーが残る場合あり。|
|AVLayerVideoGraivtyResizeAspectFill|アスペクト比を維持したままできるだけ大きく表示する(はみ出た部分はトリミング)|
|AVLayerVideoGraivtyResize|表示領域全体に表示する。アスペクト比は維持されない。|
**オーディオレベルの表示 [#dc2386d7]
オーディオ・チャネルの平均出力レベルやピーク出力レベルを監視するには、AVCaptureAudioChannelオブジェクトを使用する。
オーディオ・チャネルの平均出力レベルやピーク出力レベルを監視するには、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オブジェクトとしてのビデオフレームのキャプチャ [#uc67b955]
ビデオをキャプチャし、取得したフレームをUIImageオブジェクトに変換する方法は以下の通り。
-AVCaptureSessionオブジェクトを作成して、AV入力デバイスから出力へのデータの流れを調整する。
//[Swift]
var session: AVCaptureSession! = AVCaptureSession()
session.sessionPreset = AVCaptureSessionPresetHigh
//[Objective-C]
AVCaptureSession *session = [[AVCaptureSession alloc] init];
session.sessionPresent = AVCaptureSessionPresetMedium; // 解像度:中
session.sessionPreset = AVCaptureSessionPresetMedium; // 解像度:中
-必要な入力タイプの AVCaptureDevice オブジェクトを見つける。
//[Swift]
var device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
//[Objective-C]
AVCaptureDevice *devide = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
-デバイスのAVCaptureDeviceInputオブジェクトを生成する。
//[Objective-C]
//[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)を示す定数を設定する。