原文:AVFoundation Programming Guide
写在前面
简单翻译一下
AVFoundation
的相关文档,本人英语水平一般,有不对的欢迎大家留言指正。
要管理从相机或麦克风等设备的捕获,您需要使用对象以表示输入和输出,并使用AVCaptureSession的实例来协调它们之间的数据流。你最少需要:
- 一个AVCaptureDevice对象来表示输入设备,如相机或麦克风
- 一个AVCaptureInput的具体子类的实例,用于配置输入设备端口
- 一个AVCaptureOutput的具体子类的实例来管理是输出到一个视频文件还是静态图片
- 一个AVCaptureSession实例来协调从输入设备到输出设备的数据流
为了给用户展示相机正在录制的内容,你可以使用一个AVCaptureVideoPreviewLayer实例。
你可以配置多个输入和输出设备,并使用一个会话(session)来协调,如图4-1。
对于许多应用,这就是你所需要的。然而,对于某些操作(例如,你想要监视音频通道中的功率电平)则需要考虑如何表示输入设备的各种端口以及这些端口如何连接到输出。
捕获会话中的输入和输出之间的连接由AVCaptureConnection对象表示。捕获输入(AVCaptureInput的实例)具有一个或多个输入端口(AVCaptureInputPort的实例)。捕获输出(AVCaptureOutput的实例)可以接受来自一个或多个源的数据(例如,AVCaptureMovieFileOutput对象接受视频和音频数据)。
当您向会话添加输入或输出时,会话将形成所有兼容捕获输入端口和捕获输出之间的连接,如图4-2所示。捕获输入和捕捉输出之间的连接由AVCaptureConnection对象表示。
你可以使用一个捕获连接来启用或禁用一个给定的输入或输出的数据流。您还可以使用连接来监视音频通道中的平均和峰值功率。
注意: 媒体捕获不支持在iOS设备上同时捕获前置和后置摄像头。
使用一个捕获会话来协调数据流
一个AVCaptureSession对象是用于管理数据捕获的中心协调对象。您使用它来协调从AV输入设备到输出的数据流。您将捕获设备和输出添加到会话,然后通过发送会话startRunning 消息来启动数据流,并通过发送stopRunning消息来停止数据流。
AVCaptureSession *session = [[AVCaptureSession alloc] init];
// Add inputs and outputs.
[session startRunning];
配置会话
您可以在会话中使用预设来指定所需的图像质量和分辨率。预设是一些配置常数;在某些情况下,实际配置是由设备决定的:
标识 | 分辨率 | 注释 |
---|---|---|
AVCaptureSessionPresetHigh | High | 最高的录制质量,每个设备有所不同 |
AVCaptureSessionPresetMedium | Medium | 适用于wifi分享,实际值可能会变 |
AVCaptureSessionPresetLow | Low | 适用于3G分享,实际值可能会变 |
AVCaptureSessionPreset640x480 | 640x480 | VGA. |
AVCaptureSessionPreset1280x720 | 1280x720 | 720p HD. |
AVCaptureSessionPresetPhoto | Photo | 完整的照片分辨率,不支持视频输出。 |
如果你想要给媒体帧设置一个特定大小的配置,你应该在设置前检查它是否支持,如下:
if ([session canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
session.sessionPreset = AVCaptureSessionPreset1280x720;
} else {
// Handle the failure.
}
如果您需要给会话设置一个比预设可能更精细的参数,或者您想对正在运行的会话进行更改,则可以使用beginConfiguration和commitConfiguration方法来包围您的更改。 beginConfiguration
和commitConfiguration
方法确保设备更改在一个组内发生。调用beginConfiguration
之后,您可以添加或删除输出,更改会话的预设属性,或配置各个捕获输入或输出属性。在调用commitConfiguration
之前,实际上没有进行任何更改,这些配置会在一起被应用。
[session beginConfiguration];
// Remove an existing capture device.
// Add a new capture device.
// Reset the preset.
[session commitConfiguration];
监视捕获会话状态
捕获会话会发出通知,你可以监听它们,例如,当它启动或停止运行或中断时。您可以注册接收 AVCaptureSessionRuntimeErrorNotification来处理错误。您还可以查询会话的running属性,以确定其是否正在运行,以及interrupted属性以确定是否中断。此外,running
和interrupted
的属性都符合KVC,并且通知会发布在主线程上。
An AVCaptureDevice对象表示输入设备
一个AVCaptureDevice对象是一个向AVCaptureSession对象提供输入数据(如音频或视频)的物理捕获设备的抽象。每个输入设备有一个对象,例如两个视频输入: 一个用于前置摄像头,一个用于后置摄像头,和一个用于麦克风的音频输入。您可以使用AVCaptureDevice的类方法devices和devicesWithMediaType:找出当前可用的捕获设备。并且,如有必要,您可以找到iPhone,iPad或iPod提供的功能(请参阅Device Capture Settings)。可用设备的列表可能会改变。当前的输入设备可能变得不可用(如果它们被另一个应用程序使用),并且新的输入设备可能变得可用(如果它们被另一个应用程序放弃)。您应该注册接收AVCaptureDeviceWasConnectedNotification 和AVCaptureDeviceWasDisconnectedNotification 通知,以便在可用设备列表更改时提醒。您可以使用捕获输入将输入设备添加到捕获会话(请参阅Use Capture Inputs to Add a Capture Device to a Session)。
设备特性
您可以查询设备的不同的特征。您还可以测试它是否提供特定的媒体类型或支持给定捕获会话的预设,分别使用方法hasMediaType:和supportsAVCaptureSessionPreset:。要向用户提供信息,您可以找出捕获设备的位置(无论是在正在测试的设备的正面或背面)及其本地化名称。如果要提供捕获设备列表以允许用户选择一个捕获设备,这可能很有用。
图4-3显示了后置(AVCaptureDevicePositionBack) 和前置(AVCaptureDevicePositionFront)摄像机的位置。
注意:媒体捕获不支持同时捕获iOS设备上的前置和后置摄像头。
下面的代码展示了获取所有的可用设备并且输出它们的名字,如果是视频设备,输出它们的位置
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");
}
}
}
你还可以获取设备的model ID和unique ID。
设备捕获设置
不同的设备具有不同的功能;例如,一些可能支持不同的焦点或闪光模式;有一些可能会支持在一个兴趣点上聚焦。
以下代码片段显示了如何查找具有手电筒模式并支持给定捕获会话预置的视频输入设备:
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
NSMutableArray *torchDevices = [[NSMutableArray alloc] init];
for (AVCaptureDevice *device in devices) {
if ([device hasTorch] && [device supportsAVCaptureSessionPreset:AVCaptureSessionPreset640x480]) {
[torchDevices addObject:device];
}
}
如果您发现多个符合条件的设备,您可以让用户选择要使用的设备。要向用户显示设备的描述,可以使用其localizedName属性。
您以类似的方式使用各种不同的功能。有一些常数来指定特定模式,您可以询问设备是否支持特定模式。在某些情况下,您可以监听属性以便在功能正在更改时获得通知。在所有情况下,您应该在更改特定功能的模式之前锁定设备,如Configuring a Device中所述。
注意:兴趣点聚焦和兴趣点曝光是相互排斥的,聚焦模式和曝光模式也是相互排斥的。
聚焦模式
有三种聚焦模式:
- AVCaptureFocusModeLocked: 焦点位置是固定的。当您想允许用户组合场景然后锁定焦点时,这很有用。
- AVCaptureFocusModeAutoFocus: 相机执行一个扫描焦点,然后还原到锁定。这适合于您想要选择要聚焦的特定项目,然后将焦点保持在该项目上的情况,即使它不是场景的中心。
- AVCaptureFocusModeContinuousAutoFocus: 相机会根据需要不断自动对焦。
您使用isFocusModeSupported:方法来确定设备是否支持给定的对焦模式,然后使用focusMode属性设置模式。
另外,设备可能支持兴趣焦点。 您可以使用focusPointOfInterest来测试。 如果支持,您可以使用focusPointOfInterest设置焦点。 您传递一个CGPoint,其中{0,0}表示图片区域的左上角,而{1,1}表示右下角在横向模式(Home键在右侧),即使设备处于纵向模式时也是。
您可以使用adjustingFocus属性来确定设备当前是否正在进行对焦。 您可以使用键值观察观察属性,以在设备启动和停止对焦时获得通知。
如果更改对焦模式设置,可以按如下方式将其返回到默认配置:
if ([currentDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
CGPoint autofocusPoint = CGPointMake(0.5f, 0.5f);
[currentDevice setFocusPointOfInterest:autofocusPoint];
[currentDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
}
曝光模式
有两种曝光模式:
- AVCaptureExposureModeContinuousAutoExposure: 设备根据需要自动调整曝光级别。
- AVCaptureExposureModeLocked: 曝光级别固定在当前级别.
您使用isExposureModeSupported:方法来确定设备是否支持给定的曝光模式,然后使用exposureMode属性设置模式。
另外,设备可能支持兴趣点曝光。 您可以使用exposurePointOfInterestSupported测试。 如果支持,您可以使用exposurePointOfInterest设置曝光点。 您传递一个CGPoint,其中{0,0}表示图片区域的左上角,而{1,1}表示右下角在横向模式(Home键在右侧),即使设备处于纵向模式时也是。
您可以使用adjustingExposure属性来确定设备是否正在更改其曝光设置。 您可以使用键值观察监听属性,以在设备启动或停止改变曝光模式时获得通知。
如果更改曝光设置,可以按如下方式将其返回到默认配置:
if ([currentDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
CGPoint exposurePoint = CGPointMake(0.5f, 0.5f);
[currentDevice setExposurePointOfInterest:exposurePoint];
[currentDevice setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
}
闪光模式
有三种闪光模式:
- AVCaptureFlashModeOff: 闪光灯关闭.
- AVCaptureFlashModeOn: 闪关灯永远打开.
- AVCaptureFlashModeAuto: 闪光灯将根据环境光线条件自动触发.
您使用hasFlash来确定设备是否有闪光灯。 如果该方法返回YES,则使用isFlashModeSupported:方法,传递所需的模式以确定设备是否支持给定的闪光模式,然后使用flashMode属性设置模式。
手电筒模式
在手电筒模式下,闪光灯以低功率持续照明来进行视频拍摄。有三种手电筒模式:
- AVCaptureTorchModeOff: 手电筒关闭.
- AVCaptureTorchModeOn: 手电筒打开.
- AVCaptureTorchModeAuto: 手电筒在需要的时候自动打开和关闭.
您使用hasTorch来确定设备是否有闪关灯。 您使用isTorchModeSupported:方法来确定设备是否支持给定的闪关灯模式,然后使用torchMode属性设置模式。
对于带手电筒的设备,手电筒只有在设备与正在运行的捕获会话关联时才会打开。
视频稳定
视频稳定是否可用于操作视频取决于特定的设备硬件。 即使如此,并不是所有的源格式和视频分辨率都得到支持。
启用电影视频稳定也可能在视频捕获管道中加入额外的延迟。 要检测视频稳定是否正在使用,请使用videoStabilizationEnabled属性。 enablesVideoStabilizationWhenAvailable属性允许应用程序自动启用视频稳定(如果相机支持)。 默认情况下,由于上述限制,自动稳定功能被禁用。
白平衡
有两种白平衡模式:
- AVCaptureWhiteBalanceModeLocked: 白平衡模式是固定的.
- AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance: 相机会根据需要连续调节白平衡.
您使用isWhiteBalanceModeSupported:方法来确定设备是否支持给定的白平衡模式,然后使用whiteBalanceMode属性设置模式。
您可以使用adjustingWhiteBalance属性来确定设备当前是否正在更改其白平衡设置。 您可以使用键值观察观察属性,以在设备启动或停止更改其白平衡设置时获得通知。
设置设备方向
您可以在AVCaptureConnection
上设置所需的方向,以指定如何在AVCaptureOutput(AVCaptureMovieFileOutput,AVCaptureStillImageOutput和AVCaptureVideoDataOutput)
中为图形定向。
使用AVCaptureConnectionsupportsVideoOrientation
属性来确定设备是否支持更改视频的方向,以及videoOrientation
属性来指定在输出端口中的图像的方向。 List 4-1显示了如何将AVCaptureConnection
的方向设置为AVCaptureVideoOrientationLandscapeLeft
:
List 4-1设置捕获连接的方向
AVCaptureConnection *captureConnection = <#A capture connection#>;
if ([captureConnection isVideoOrientationSupported]) {
AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationLandscapeLeft;
[captureConnection setVideoOrientation:orientation];
}
配置设备
要在设备上设置捕获属性,必须先使用lockForConfiguration:获取设备的锁定。 这样可以避免与其他应用程序中的设置不兼容的更改。 以下代码片段说明了如何通过首先确定是否支持该模式,然后尝试锁定设备以进行重新配置来改变设备上的聚焦模式。 仅当获得锁定时才改变对焦模式,然后立即释放锁定。
if ([device isFocusModeSupported:AVCaptureFocusModeLocked]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
device.focusMode = AVCaptureFocusModeLocked;
[device unlockForConfiguration];
} else {
// Respond to the failure as appropriate.
只有当您需要可设置的设备属性保持不变时,才应该保持设备锁定。 不必要地持有设备锁会降低共享设备的其他应用程序的捕获效果。
在设备之间切换
有时您可能希望允许用户在输入设备之间切换,例如从使用前置摄像头切换到后置摄像头。 为了避免停顿或卡顿,您可以在运行时重新配置会话,但是您应该使用beginConfiguration和commitConfiguration来包含您的配置更改:
AVCaptureSession *session = <#A capture session#>;
[session beginConfiguration];
[session removeInput:frontFacingCameraDeviceInput];
[session addInput:backFacingCameraDeviceInput];
[session commitConfiguration];
当最外面的配置被调用时,所有的更改都在一起被改变。 这确保了一个平稳过渡。
使用捕获输入将捕获设备添加到会话
要将捕获设备添加到捕获会话中,可以使用AVCaptureDeviceInput(抽象类AVCaptureInput的具体子类)的实例。 捕获设备输入管理设备的端口。
NSError *error;
AVCaptureDeviceInput *input =
[AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) {
// Handle the error appropriately.
}
您可以使用addInput:向会话添加输入。 您可以使用canAddInput:检查捕获输入是否与现有会话兼容。
AVCaptureSession *captureSession = <#Get a capture session#>;
AVCaptureDeviceInput *captureDeviceInput = <#Get a capture device input#>;
if ([captureSession canAddInput:captureDeviceInput]) {
[captureSession addInput:captureDeviceInput];
} else {
// Handle the failure.
}
有关如何重新配置正在运行的会话的详细信息,请参阅Configuring a Session 。
AVCaptureInput
会提供一个或多个媒体数据流。 例如,输入设备可以提供音频和视频数据。 输入提供的每个媒体流由AVCaptureInputPort对象表示。 捕获会话使用AVCaptureConnection
对象来定义一组AVCaptureInputPort
对象与单个AVCaptureOutput
之间的映射。
使用捕获输出从会话获取输出
要从捕获会话获取输出,您需要添加一个或多个输出。 输出是AVCaptureOutput的具体子类的一个实例。 你可以使用:
- AVCaptureMovieFileOutput 输出到电影文件
- AVCaptureVideoDataOutput 如果要在捕获的视频中处理帧,例如,创建自己的自定义视图层
- AVCaptureAudioDataOutput 如果要处理正在捕获的音频数据
- AVCaptureStillImageOutput 如果要捕获带有附带元数据的静态图像
您可以使用addOutput:将输出添加到捕获会话。 您可以使用canAddOutput:检查捕获输出是否与现有会话兼容。 您可以在会话运行时根据需要添加和删除输出。
AVCaptureSession *captureSession = <#Get a capture session#>;
AVCaptureMovieFileOutput *movieOutput = <#Create and configure a movie output#>;
if ([captureSession canAddOutput:movieOutput]) {
[captureSession addOutput:movieOutput];
} else {
// Handle the failure.
}
保存到电影文件
可以使用AVCaptureMovieFileOutput对象将电影数据保存到文件。(AVCaptureMovieFileOutput
是AVCaptureFileOutput的具体子类,它定义了大部分的基本行为。)您可以配置影片文件输出的各个方面,例如录制的最长持续时间或文件的最大尺寸。 如果剩余的磁盘空间少于一定数量,您也可以禁止录制。
AVCaptureMovieFileOutput *aMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
CMTime maxDuration = <#Create a CMTime to represent the maximum duration#>;
aMovieFileOutput.maxRecordedDuration = maxDuration;
aMovieFileOutput.minFreeDiskSpaceLimit = <#An appropriate minimum given the quality of the movie format and the duration#>;
输出的分辨率和比特率取决于捕获会话的sessionPreset。 视频编码通常是H.264,音频编码通常是AAC。 实际值因设备而异。
开始录制
您可以使用startRecordingToOutputFileURL:recordingDelegate:来录制QuickTime影片。您需要提供一个基于文件的URL和代理。 URL不能标识现有文件,因为电影文件输出不会覆盖现有资源。 您还必须具有写入指定位置的权限。 代理必须符合AVCaptureFileOutputRecordingDelegate协议,并且必须实现captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:方法。
AVCaptureMovieFileOutput *aMovieFileOutput = <#Get a movie file output#>;
NSURL *fileURL = <#A file URL that identifies the output location#>;
[aMovieFileOutput startRecordingToOutputFileURL:fileURL recordingDelegate:<#The delegate#>];
在实现captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:时,代理可能会将生成的电影写入Camera Roll相册。 它还应该检查可能发生的任何错误。
确保文件成功写入
要确定文件是否成功保存,在实现captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:时,您不仅要检查错误,还检查错误的用户信息字典(error’s user info dictionary)中的AVErrorRecordingSuccessfullyFinishedKey的值:
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput
didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
fromConnections:(NSArray *)connections
error:(NSError *)error {
BOOL recordedSuccessfully = YES;
if ([error code] != noErr) {
// A problem occurred: Find out if the recording was successful.
id value = [[error userInfo] objectForKey:AVErrorRecordingSuccessfullyFinishedKey];
if (value) {
recordedSuccessfully = [value boolValue];
}
}
// Continue as appropriate...
您应该检查AVErrorRecordingSuccessfullyFinishedKeykey
的值,因为该文件可能已成功保存,即使返回了一个错误。 错误可能表示您的录制达到了一个约束 - 例如,AVErrorMaximumDurationReached或者AVErrorMaximumFileSizeReached。 其他导致录制结束的原因是:
- 磁盘已满 AVErrorDiskFull
- 录制设备已断开 AVErrorDeviceWasDisconnected
- 会话中断(例如,接到电话)AVErrorSessionWasInterrupted
添加元数据到文件
你也可以随时设置影片文件的元数据,即使在录制时。 对于在记录开始时信息不可用的情况(如位置信息一样)下这是很有用的。 文件输出的元数据由AVMetadataItem对象的数组表示; 您可以使用其可变子类AVMutableMetadataItem的实例来创建您自己的元数据。
AVCaptureMovieFileOutput *aMovieFileOutput = <#Get a movie file output#>;
NSArray *existingMetadataArray = aMovieFileOutput.metadata;
NSMutableArray *newMetadataArray = nil;
if (existingMetadataArray) {
newMetadataArray = [existingMetadataArray mutableCopy];
} else {
newMetadataArray = [[NSMutableArray alloc] init];
}
AVMutableMetadataItem *item = [[AVMutableMetadataItem alloc] init];
item.keySpace = AVMetadataKeySpaceCommon;
item.key = AVMetadataCommonKeyLocation;
CLLocation *location - <#The location to set#>;
item.value = [NSString stringWithFormat:@"%+08.4lf%+09.4lf/" location.coordinate.latitude, location.coordinate.longitude];
[newMetadataArray addObject:item];
aMovieFileOutput.metadata = newMetadataArray;
处理视频帧
AVCaptureVideoDataOutput对象使用委托来公开视频帧。您可以使用setSampleBufferDelegate:queue:设置委托。除了设置委托,您还可以指定一个在其上调用委托方法的串行队列。您必须使用串行队列来确保视频帧以适当的顺序传递给代理。您可以使用队列来修改传递的优先级和处理视频帧。有关示例实现,请参阅SquareCam。
这些视频帧在委托方法captureOutput:didOutputSampleBuffer:fromConnection:中,作为CMSampleBufferRef的实例(请参阅 Representations of Media)。默认情况下,缓冲区以相机最有效的格式发出。您可以使用videoSettings属性来指定自定义输出格式。视频设置属性是一个字典;目前唯一支持的键是kCVPixelBufferPixelFormatTypeKey。推荐的像素格式由availableVideoCVPixelFormatTypes属性返回,由availableVideoCodecTypes属性返回支持的值。 Core Graphics
和OpenGL
都能与BGRA
格式配合使用:
AVCaptureVideoDataOutput *videoDataOutput = [AVCaptureVideoDataOutput new];
NSDictionary *newSettings = @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) };
videoDataOutput.videoSettings = newSettings;
// discard if the data output queue is blocked (as we process the still image
[videoDataOutput setAlwaysDiscardsLateVideoFrames:YES];)
// create a serial dispatch queue used for the sample buffer delegate as well as when a still image is captured
// a serial dispatch queue must be used to guarantee that video frames will be delivered in order
// see the header doc for setSampleBufferDelegate:queue: for more information
videoDataOutputQueue = dispatch_queue_create("VideoDataOutputQueue", DISPATCH_QUEUE_SERIAL);
[videoDataOutput setSampleBufferDelegate:self queue:videoDataOutputQueue];
AVCaptureSession *captureSession = <#The Capture Session#>;
if ( [captureSession canAddOutput:videoDataOutput] )
[captureSession addOutput:videoDataOutput];
处理视频的性能注意事项
您应该将会话输出设置为应用程序的最低实际分辨率。将输出设置为更高的分辨率会浪费处理周期,并且耗电。
您必须确保实现方法captureOutput:didOutputSampleBuffer:fromConnection:能够在分配给帧的时间量内处理样本缓冲区。如果时间太长,并且您持有了视频帧,AV Foundation会停止传输视频帧,不仅是你的代理,h还包括其他的输出(如预览图层)。
您可以使用捕获视频数据输出的minFrameDuration属性来确保您有足够的时间来处理帧,而不是以比其他情况更低的帧速率。您还应该确保将alwaysDiscardsLateVideoFrames属性设置为YES(默认值)。这样可以确保任何延迟的视频帧都被丢弃,而不是交给您进行处理。或者,如果不在乎录制时的延迟,并且您希望获得所有的数据,您可以将属性值设置为NO。这并不意味着帧不会被丢弃(也就是说,帧可能会被丢弃),但是它们可能不会被过早或者频繁地被丢弃。
捕捉静态图像
如果要捕获静态图像,可以使用AVCaptureStillImageOutput输出。 图像的分辨率取决于会话的预设以及设备。
像素和编码格式
不同的设备支持不同的图像格式。 您可以分别使用availableImageDataCVPixelFormatTypes和availableImageDataCodecTypes找到设备支持的像素和编码类型。 每个方法返回设备的支持的值得一个数组。 您设置outputSettings字典以指定所需的图像格式,例如:
AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = @{ AVVideoCodecKey : AVVideoCodecJPEG};
[stillImageOutput setOutputSettings:outputSettings];
如果要捕获JPEG图像,则通常不应指定您自己的压缩格式。 相反,您应该让静态图像输出为您做压缩,因为它的压缩是硬件加速的。 如果您需要图像的数据表示,您可以使用jpegStillImageNSDataRepresentation:获取NSData对象而不重新压缩数据,即使您修改了图像的元数据。
捕获图像
当您要捕获图像时,您可以给输出发送一个captureStillImageAsynchronouslyFromConnection:completionHandler:消息。 第一个参数是要用于捕获的连接。 您需要查找正在收集视频的输入端口的连接:
AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in stillImageOutput.connections) {
for (AVCaptureInputPort *port in [connection inputPorts]) {
if ([[port mediaType] isEqual:AVMediaTypeVideo] ) {
videoConnection = connection;
break;
}
}
if (videoConnection) { break; }
}
captureStillImageAsynchronouslyFromConnection:completionHandler:
的第二个参数是一个包含两个参数的block
:一个包含图像数据的CMSampleBuffer和一个错误。 样本缓冲区本身可以包含诸如EXIF字典的元数据作为附件。 您可以根据需要修改附件,但请注意 Pixel and Encoding Formats中讨论的JPEG图像的优化。
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:
^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
CFDictionaryRef exifAttachments =
CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
if (exifAttachments) {
// Do something with the attachments.
}
// Continue as appropriate.
}];
给用户展示正在录制的内容
您可以向用户展示相机(使用预览图)或麦克风(通过监听音频通道)正在录制的内容。
视频预览
您可以使用AVCaptureVideoPreviewLayer对象向用户展示正在录制的内容。 AVCaptureVideoPreviewLayer
是CALayer
的子类(请参阅Core Animation Programming Guide),您不需要任何输出来显示预览。
使用AVCaptureVideoDataOutput类为客户端应用程序提供了在呈现给用户之前访问视频像素的能力。
与捕获输出不同,视频预览层持有了与其相关联的会话的强引用。 这是为了确保在预览层尝试显示视频时会话不会被释放。下面是初始化预览图层的方法:
AVCaptureSession *captureSession = <#Get a capture session#>;
CALayer *viewLayer = <#Get a layer from the view in which you want to present the preview#>;
AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:captureSession];
[viewLayer addSublayer:captureVideoPreviewLayer];
通常,预览图层与其他CALayer对象相同(参见Core Animation Programming Guide)。 您可以像使用其他图层一样缩放图像、执行转换、旋转等操作。 一个区别是,您可能需要设置图层的orientation属性来指定如何旋转来自相机的图像。 另外,您可以通过查询supportsVideoMirroring属性来测试设备是否支持视频镜像。 您可以根据需要设置videoMirrored属性,但是当将automaticallyAdjustsVideoMirroring属性设置为YES(默认值)时,将根据会话的配置自动设置镜像值。
视频重力模式
预览图层支持三种重力模式,可以使用videoGravity设置:
- AVLayerVideoGravityResizeAspect 保留宽高比,在视频未填满的屏幕区域显示黑条。
- AVLayerVideoGravityResizeAspectFill 保留宽高比,但填满屏幕区域,必要时裁剪视频。
- AVLayerVideoGravityResize 这简单地拉伸视频以填充屏幕区域,即使拉伸了图像。
在预览中使用“点击聚焦”
在预览图层中实现点击对焦时,您需要注意。 您必须考虑图层的预览方向和重力,以及预览展示的可能性。 请参阅示例项目AVCam-iOS: Using AVFoundation to Capture Images and Movies。
显示音频音量
要监视拍摄连接中音频通道中的平均和峰值级别,可以使用AVCaptureAudioChannel对象。 音频音量不支持键值观察,所以您必须按您想要的频率轮询更新用户界面(例如,每秒10次)。
AVCaptureAudioDataOutput *audioDataOutput = <#Get the audio data output#>;
NSArray *connections = audioDataOutput.connections;
if ([connections count] > 0) {
// There should be only one connection to an AVCaptureAudioDataOutput.
AVCaptureConnection *connection = [connections objectAtIndex:0];
NSArray *audioChannels = connection.audioChannels;
for (AVCaptureAudioChannel *channel in audioChannels) {
float avg = channel.averagePowerLevel;
float peak = channel.peakHoldLevel;
// Update the level meter user interface.
}
}
整合:将视频帧捕获为UIImage对象
这个简短的代码示例说明了如何捕获视频并将获得的帧转换为UIImage对象。:
- 创建AVCaptureSession对象以协调从AV输入到输出的数据流。
- 找到您想要的输入类型的AVCaptureDevice对象
- 为设备创建AVCaptureDeviceInput对象
- 创建AVCaptureVideoDataOutput对象以产生视频帧
- 为AVCaptureVideoDataOutput对象实现代理方法以处理视频帧
- 实现一个函数来将委托接收的
CMSampleBuffer
转换成UIImage对象
注意:为了专注于最相关的代码,本示例省略了完整应用程序的几个方面,包括内存管理。 要使用AVFoundation,您需要有足够的Cocoa编程经验,以推断出丢失的部分。
创建并配置捕获会话
您可以使用AVCaptureSession对象来协调从AV输入到输出的数据流。 创建会话,并将其配置为生成中等分辨率的视频帧。
AVCaptureSession *session = [[AVCaptureSession alloc] init];
session.sessionPreset = AVCaptureSessionPresetMedium;
创建和配置设备以及设备输入
捕获设备由AVCaptureDevice对象表示; 该类提供了检索所需输入类型的对象的方法。 一个设备具有一个或多个端口,可以使用AVCaptureInput对象进行配置。 通常,你可以使用默认的配置。
查找视频捕获设备,然后使用设备创建设备输入并将其添加到会话中。 如果找不到适当的设备,则deviceInputWithDevice:error:方法将通过引用返回错误。
AVCaptureDevice *device =
[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error = nil;
AVCaptureDeviceInput *input =
[AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) {
// Handle the error appropriately.
}
[session addInput:input];
创建和配置视频数据输出
你可以使用AVCaptureVideoDataOutput对象来处理被捕获的视频中的未压缩帧。 您通常会配置输出的几个方面。 例如,对于视频,您可以使用videoSettings属性指定像素格式,并通过设置minFrameDuration属性来限制帧率。
创建并配置视频数据的输出并将其添加到会话中; 通过把minFrameDuration
属性设置为1/15秒将帧率设置为15 fps:
AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init];
[session addOutput:output];
output.videoSettings =
@{ (NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) };
output.minFrameDuration = CMTimeMake(1, 15);
数据输出对象使用委托来处理视频帧。 代理必须实现AVCaptureVideoDataOutputSampleBufferDelegate协议。 当你设置数据输出的委托时,还必须提供一个队列,将在该队列上调用回调函数。
dispatch_queue_t queue = dispatch_queue_create("MyQueue", NULL);
[output setSampleBufferDelegate:self queue:queue];
dispatch_release(queue);
你使用队列来修改发送和处理视频帧的优先级。
实现Sample Buffer代理方法
在委托类中,实现captureOutput:didOutputSampleBuffer:fromConnection:
方法,它会在样本缓冲区被写入时调用。 视频数据输出对象将帧作为CMSampleBuffer类型传输,因此您需要将CMSampleBuffer类型转换为UIImage对象。 可以参考 Converting CMSampleBuffer to a UIImage Object。
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection {
UIImage *image = imageFromSampleBuffer(sampleBuffer);
// Add your code here that uses the image.
}
记住调用代理方法会在setSampleBufferDelegate:queue:中指定的队列上调用; 如果要更新用户界面,则必须在主线程上调用相关的代码。
开始以及结束录制
配置捕获会话后,您应确保有录制的权限。
NSString *mediaType = AVMediaTypeVideo;
[AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL granted) {
if (granted)
{
//Granted access to mediaType
[self setDeviceAuthorized:YES];
}
else
{
//Not granted access to mediaType
dispatch_async(dispatch_get_main_queue(), ^{
[[[UIAlertView alloc] initWithTitle:@"AVCam!"
message:@"AVCam doesn't have permission to use Camera, please change privacy settings"
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
[self setDeviceAuthorized:NO];
});
}
}];
如果配置好了会话,并且用户已经允许访问摄像机(如果需要,麦克风),就可以发送startRunning消息开始录制。
重要提示:startRunning
方法是一个阻塞调用,可能需要一些时间,因此您应该在串行队列上执行会话设置,以使主队列不被阻塞。 请参阅AVCam-iOS: Using AVFoundation to Capture Images and Movies。
[session startRunning];
结束录制可以发送stopRunning消息。
高帧率视频采集
iOS 7.0在所选硬件上引入了对高帧率视频捕获的支持(也称为慢动作视频)。AVFoundation框架支持高帧率内容。
您可以使用AVCaptureDeviceFormat类来确定设备的捕获功能。此类具有返回支持的媒体类型、帧率、视场、最大缩放系数、是否支持视频稳定等的方法。
捕获支持720p(1280 x 720像素)分辨率以每秒60帧(fps)的帧率,包括视频稳定和可放置的P帧(H264编码视频的功能,使在较慢和较旧的硬件上也能让视频播放顺畅。 )
播放增强了对慢速和快速播放音频的支持,从而可以以更慢或更快的速度播放音频。
编辑完全支持在可变组件中的缩放编辑。
导出提供两个选项,支持60 fps视频。可用的帧率,慢速或快速运动,或者将影片转换为任意较慢的帧速率,例如每秒30帧。
SloPoke
(没找到=。=)示例代码演示了AVFoundation支持快速视频捕获,确定硬件是否支持高帧速率视频捕获,使用各种速率和时间间距算法的播放以及编辑(包括为部分组件设置时间刻度)。
回放
AVPlayer
的实例通过setRate:
方法自动管理大部分播放速度。该值用作播放速度的乘数。值为1.0会导致正常播放,0.5以半速播放,5.0会使播放比正常快五倍,依此类推。
AVPlayerItem
对象支持audioTimePitchAlgorithm属性。该属性允许您指定使用 Time Pitch Algorithm Settings
常数以各种帧速率播放动画时的音频播放方式。
下表展示了支持的时间间距算法(Time pitch algorithm),质量(Quality),算法是否使音频捕捉到特定帧速率(Snaps to specific frame rate),以及每个算法支持的帧速率范围(Rate range)。
Time pitch algorithm | Quality | Snaps to specific frame rate | Rate range |
---|---|---|---|
AVAudioTimePitchAlgorithmLowQualityZeroLatency | 质量低,适合快进,倒带或低质量的声音 | YES | 0.5, 0.666667, 0.8, 1.0, 1.25, 1.5, 2.0 rates. |
AVAudioTimePitchAlgorithmTimeDomain | 质量适中,计算量少,适合声音。 | NO | 0.5–2x rates. |
AVAudioTimePitchAlgorithmSpectral | 最高质量,计算量大,保留原始项目的间距。 | NO | 1/32–32 rates. |
AVAudioTimePitchAlgorithmVarispeed | 高品质播放,无间距校正。 | NO | 1/32–32 rates. |
编辑
编辑时,您可以使用AVMutableComposition
类来创建时间编辑。
- 使用composition类方法创建一个新的AVMutableComposition实例。
- 使用insertTimeRange:ofAsset:atTime:error:插入视频资源。
- 使用scaleTimeRange:toDuration:设置组件的时间大小
导出
导出60 fps视频时使用AVAssetExportPresetPassthrough类导出资源。可以使用两种技术导出内容:
- 使用AVAssetExportPresetPassthrough预设来避免重新编码影片。媒体将媒体部分标记为60 fps,部分放慢或部分加快。
- 使用恒定的帧速率导出以实现最大播放兼容性。将视频组件的frameDuration属性设置为30 fps。您还可以通过使用导出会话的audioTimePitchAlgorithm属性来设置时间间隔。
录制
您可以使用AVCaptureMovieFileOutput类捕获高帧率视频,该类自动支持高帧率录制。它将自动选择正确的H264间距和比特率。
要进行自定义录制,您必须使用AVAssetWriter类,这需要一些额外的设置。
assetWriterInput.expectsMediaDataInRealTime=YES;
此设置确保捕获可以跟上输入的数据。