《AVFoundation》官方文档06--Still and Video Media Capture(静态和视频媒体捕获)

要管理从相机或麦克风等设备进行捕获,您将组合对象以表示输入和输出,并使用一个实例AVCaptureSession来协调它们之间的数据流。最小的需要:

  • AVCaptureDevice表示输入设备的实例,例如相机或麦克风
  • AVCaptureInput从输入设备配置端口的具体子类的实例
  • AVCaptureOutput将输出管理到电影文件或静止图像的具体子类的实例
  • AVCaptureSession协调从输入到输出的数据流的实例
    要向用户显示相机正在录制的内容,您可以使用AVCaptureVideoPreviewLayer(的子类CALayer)的实例。您可以配置多个输入和输出,由单个会话协调,如图4-1所示
图4-1 单个会话可以配置多个输入和输出

对于许多应用,这是您需要的细节。然而,对于某些操作(例如,如果要监视音频通道中的功率电平),则需要考虑如何表示输入设备的各种端口以及这些端口如何连接到输出。

捕获会话中的捕获输入和捕获输出之间的连接由AVCaptureConnection对象表示。捕获输入(实例AVCaptureInput)具有一个或多个输入端口(实例AVCaptureInputPort)。捕获输出(实例AVCaptureOutput)可以接受来自一个或多个源的数据(例如,一个AVCaptureMovieFileOutput对象接受视频和音频数据)。

当您向会话添加输入或输出时,会话将形成所有兼容捕获输入端口和捕获输出之间的连接,如图4-2所示。捕获输入和捕捉输出之间的连接由AVCaptureConnection对象表示。

图4-2 AVCaptureConnection表示输入和输出之间的连接

您可以使用捕获连接启用或禁用来自给定输入或给定输出的数据流。您还可以使用连接来监视音频通道中的平均和峰值功率耗电水平。

注意: 媒体捕获不支持同时捕获iOS设备上的前置和后置摄像头。

Use a Capture Session to Coordinate Data Flow(使用捕获会话来协调数据流)

一个AVCaptureSession目标是用于管理数据采集中心协调对象。
您使用实例来协调从AV输入设备到输出的数据流。您将要添加到会话的捕获设备和输出,然后通过发送会话发送startRunning消息来启动数据流,并通过发送stopRunning消息来停止数据流。

AVCaptureSession * session = [[AVCaptureSession alloc] init];

//添加输入和输出。

[session startRunning];

Configuring a Session(配置会话)

符号 解析度 注释
AVCaptureSessionPresetHigh 最高录音质量 每个设备有所不同。
AVCaptureSessionPresetMedium 适合Wi-Fi共享。实际值可能会改变
AVCaptureSessionPresetLow 适合3G共享。实际值可能会改变
AVCaptureSessionPreset640x480 640×480 VGA
AVCaptureSessionPreset1280x720 1280×720 720p高清。
AVCaptureSessionPresetPhoto 照片 完全照片分辨率。视频输出不支持此功能。

如果要设置媒体帧大小的配置,您应该在设置之前检查是否支持,如下所示:

if([session canSetSessionPreset:AVCaptureSessionPreset1280x720]){
session.sessionPreset = AVCaptureSessionPreset1280x720;
}
else {
    //处理失败。
}

如果您需要调整会议参数,比预设可能更精细,或者您想对正在运行的会话进行更改,则可以使用

beginConfigurationcommitConfiguration方法围绕您的更改。该beginConfigurationcommitConfiguration方法确保设备更改作为一个组发生,最小化状态的可见性或不一致。调用后beginConfiguration,您可以添加或删除输出,更改sessionPreset属性,或配置各个捕获输入或输出属性。在您调用之前commitConfiguration,实际上并没有进行任何更改,而是在一起应用。

[session beginConfiguration];

//删除现有的捕获设备。

//添加一个新的捕获设备。

//重置预设。

[session commitConfiguration];

Monitoring Capture Session State(监控捕获会话状态)

捕获会话帖子的通知,你可以观察得到通知,例如,在启动时或停止运行,或当它被中断。AVCaptureSessionRuntimeErrorNotification
如果发生运行时错误,您可以注册接收。您还可以询问会话的running属性以确定它是否正在运行,以及它的interrupted属性来确定它是否被中断。另外,属性runninginterrupted属性都是按键值遵守的,并且通知被张贴在主线程上。

An AVCaptureDevice Object Represents an Input Device(AVCaptureDevice对象表示输入设备)

一个AVCaptureDevice对象提取一个物理捕获设备,它将输入数据(如音频或视频)提供给AVCaptureSession对象。每个输入设备有一个对象,例如两个视频输入 - 一个用于前置摄像头,一个用于背面摄像头,一个音频输入用于麦克风。

你可以找出设备目前可使用其捕获AVCaptureDevice类的方法devicesdevicesWithMediaType:。而且,如有必要,您可以找到iPhone,iPad或iPod提供的功能(请参阅设备捕获设置)。可用设备的列表可能会改变。当前输入设备可能变得不可用(如果它们被另一个应用程序使用),并且新的输入设备可能变得可用(如果它们被另一个应用程序放弃)。当可用设备列表发生变化时,您应该注册接收AVCaptureDeviceWasConnectedNotificationAVCaptureDeviceWasDisconnectedNotification 通知

您可以使用捕获输入将输入设备添加到捕获会话(请参阅使用捕获输入将捕获设备添加到会话)。

Device Characteristics(设备特性)

注意: 媒体捕获不支持同时捕获iOS设备上的前置和后置摄像头。

您可以向设备询问其不同的特征。您也可以测试它是否提供了一个特定的媒体类型或支持使用预先给定的捕获会话hasMediaType:supportsAVCaptureSessionPreset:分别。要向用户提供信息,您可以找到捕获设备的位置(无论是在正在测试的设备的正面或背面)及其本地化名称。如果要提供捕获设备列表以允许用户选择一个捕获设备,这可能很有用。
背面(AVCaptureDevicePositionBack)和前置(AVCaptureDevicePositionFront)摄像机的位置如图4-3所示。

注意: 媒体捕获不支持同时捕获iOS设备上的前置和后置摄像头。
图4-3 iOS设备正面和背面摄像头位置

以下代码示例遍历所有可用的设备并记录其名称,并将视频设备的位置记录在设备上。

NSArray * devices = [AVCaptureDevice devices];

for(AVCaptureDevice * device in devices){

NSLog(@“设备名称:%@”,[device localizedName]);

if([device hasMediaType:AVMediaTypeVideo]){

if([device position] == AVCaptureDevicePositionBack){

NSLog(@“设备位置:返回”);

}

else {

NSLog(@“设备位置:前”);

}

}

}

此外,您可以找到设备的型号及其唯一的ID。

Device Capture Settings(设备捕获设置)

不同的设备有不同的功能; 例如,有些可能支持不同的焦点或闪光模式; 有些人可能会重点关注兴趣点。以下代码片段显示了如何查找具有割炬模式并支持给定捕获会话预设的视频输入设备:

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属性。

注意: 焦点和曝光点是相互排斥的,焦点模式和曝光模式也是相互排斥的。

您以类似的方式使用各种不同的功能。有一些常数来指定一个特定的模式,你可以询问一个设备是否支持一个特定的模式。在某些情况下,您可以观察到功能正在更改时要通知的属性。在所有情况下,您应该在更改特定功能的模式之前锁定设备,如配置设备中所述

注意: 焦点和曝光点是相互排斥的,焦点模式和曝光模式也是相互排斥的。

Focus Modes(聚焦模式)

有三种焦点模式:

  • AVCaptureFocusModeLocked:焦点位置是固定的。

    当您想允许用户组合场景然后锁定焦点时,这很有用。

  • AVCaptureFocusModeAutoFocus:相机执行单次扫描对焦,然后恢复为锁定。

    这适合于您想要选择要聚焦的特定项目,然后将焦点保持在该项目上的情况,即使它不是场景的中心。

  • AVCaptureFocusModeContinuousAutoFocus:相机会根据需要连续自动对焦。
    您可以使用该isFocusModeSupported:方法来确定设备是否支持给定的对焦模式,然后使用focusMode属性设置模式。

    另外,设备可以支持关注焦点。你测试支持使用focusPointOfInterestSupported。如果支持,您可以使用焦点设置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];
    
    }
    

Exposure Modes(曝光模式)

有两种曝光模式:

  • 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];
    
    }
    

Flash Modes(Flash模式)

有三种闪光模式:

Torch Mode)

在touch模式下,闪光灯以低功率持续使能以照亮视频拍摄。有三种手电模式:

  • AVCaptureTorchModeOff火炬总是关闭。

  • AVCaptureTorchModeOn:火炬一直在开。

  • AVCaptureTorchModeAuto:手电筒根据需要自动打开和关闭。

    您可以使用它hasTorch来确定设备是否具有闪光灯。您可以使用该isTorchModeSupported:方法来确定设备是否支持给定的闪存模式,然后使用torchMode属性设置模式。

    对于带手电筒的设备,如果设备与正在运行的捕获会话相关联,则手电筒才会打开。

    Video Stabilization(视频稳定)

根据具体的设备硬件,视频稳定可用于视频操作的连接。即使如此,并不是所有的源格式和视频分辨率都得到支持。启用电影视频稳定也可能在视频捕获管道中引入额外的延迟。要检测何时使用视频稳定,请使用该videoStabilizationEnabled属性。该enablesVideoStabilizationWhenAvailable属性允许应用程序自动启用视频稳定,如果相机支持。由于上述限制,默认自动稳定被禁用。

White Balance(白平衡)

有两种白平衡模式:

Setting Device Orientation(设置设备方向)

您可以在a上设置所需的方向,AVCaptureConnection以指定如何在连接的AVCaptureOutput(AVCaptureMovieFileOutput,AVCaptureStillImageOutput和AVCaptureVideoDataOutput)中面向图像。

使用该AVCaptureConnection supportsVideoOrientation属性来确定设备是否支持更改视频的方向,以及videoOrientation指定如何在输出端口中定向映像的属性。清单4-1显示了如何将a的方向设置AVCaptureConnectionAVCaptureVideoOrientationLandscapeLeft

清单4-1 设置捕获连接的方向:

AVCaptureConnection * captureConnection = <#A capture connection#>;

if([captureConnection isVideoOrientationSupported])

{

AVCaptureVideoOrientation orientation = 
AVCaptureVideoOrientationLandscapeLeft;
[captureConnection setVideoOrientation:orientation];

}

Configuring a Device(配置设备)

要在设备上设置捕获属性,必须首先使用设备获取设备上的锁定lockForConfiguration:。这避免了可能与其他应用程序中的设置不兼容的更改。以下代码片段说明了如何通过首先确定是否支持该模式,然后尝试锁定设备以进行重新配置来更改设备上的对焦模式。仅当获得锁定时,才会更改对焦模式,然后立即释放锁定。

if([device isFocusModeSupported:AVCaptureFocusModeLocked]){
NSError * error = nil;
if([device lockForConfiguration:&error]){
    device.focusMode = AVCaptureFocusModeLocked;
    [device unlockForConfiguration];
 }
else {
    //根据需要回复失败。

只有当您需要可设置的设备属性保持不变时,才应该保持设备锁定。
不必要地握住设备锁会降低共享设备的其他应用程序的捕获质量。

Switching Between Devices(在设备之间切换)

有时您可能希望允许用户在输入设备之间切换,例如从使用前置摄像头切换到后置摄像头。为了避免停顿或口吃,您可以在运行会话时重新配置会话,但是您应该使用beginConfigurationcommitConfiguration支持配置更改:

AVCaptureSession * session = <#A capture session#>;
[session beginConfiguration];

[session removeInput:frontFacingCameraDeviceInput];
[session addInput:backFacingCameraDeviceInput];

[session commitConfiguration];

当最外面commitConfiguration被调用时,所有更改都会一起进行。这确保了平稳过渡。

Use Capture Outputs to Get Output from a Session(使用捕获输入将捕获设备添加到会话)

要将捕获设备添加到捕获会话中,您可以使用AVCaptureDeviceInput(抽象AVCaptureInput类的具体子类)的实例。捕获设备输入管理设备的端口。

NSError *error;
AVCaptureDeviceInput *input =
    [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) {
    // Handle the error appropriately.
}

您可以使用输入添加会话addInput:。如果适用,您可以检查捕获输入是否与使用的现有会话兼容canAddInput:

AVCaptureSession * captureSession = <#获取捕获会话#>;
AVCaptureDeviceInput * captureDeviceInput = <#获取捕获设备输入#>;
if([captureSession canAddInput:captureDeviceInput]){
[captureSession addInput:captureDeviceInput];
}
else {
    //处理失败。
}

有关如何重新配置​​正在运行的会话的详细信息,请参阅配置会话。

一个AVCaptureInput售货媒体数据的一个或多个流。例如,输入设备可以提供音频和视频数据。由输入提供的每个媒体流由AVCaptureInputPort对象表示。捕获会话使用AVCaptureConnection对象来定义一组AVCaptureInputPort对象和单个对象之间的映射AVCaptureOutput。

Use Capture Outputs to Get Output from a Session(使用捕获输出从会话获得输出)

要从捕获会话获取输出,您添加一个或多个输出。输出是一个具体子类的实例AVCaptureOutput。你用:

  • AVCaptureMovieFileOutput 以输出到电影文件

  • AVCaptureVideoDataOutput 如果要从捕获的视频处理帧,例如,创建自己的自定义视图层

  • AVCaptureAudioDataOutput如果要处理正在捕获的音频数据

  • AVCaptureStillImageOutput 如果要使用伴随的元数据捕获静态图像

    您可以使用输出添加到捕获会话addOutput:。您检查捕获输出是否与使用的现有会话兼容canAddOutput:。您可以在会话运行时根据需要添加和删除输出。

    AVCaptureSession * captureSession = <#获取捕获会话#>;
    
    AVCaptureMovieFileOutput * movieOutput = <#创建并配置电影输出#>;
    
    if([captureSession canAddOutput:movieOutput]){
    
    [captureSession addOutput:movieOutput];
    
     }
    
    else {
    
    //处理失败。
    
    }
    

Saving to a Movie File(保存到电影文件)

使用AVCaptureMovieFileOutput对象将影片数据保存到文件。(AVCaptureMovieFileOutput是一个具体的子类
AVCaptureFileOutput,它定义了大部分的基本行为)。您可以配置电影文件输出的各个方面,例如录制的最长持续时间或其最大文件大小。如果剩余的磁盘空间少于一定数量,您也可以禁止录制。

AVCaptureMovieFileOutput * aMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
CMTime maxDuration = <#创建一个CMTime来表示最大持续时间#>;
aMovieFileOutput.maxRecordedDuration = maxDuration;
aMovieFileOutput.minFreeDiskSpaceLimit = <#适当的最小值,给定电影格式的质量和持续时间#>;

输出的分辨率和比特率取决于捕获会话sessionPreset。视频编码通常是H.264,音频编码通常是AAC。实际值因设备而异。

Starting a Recording(开始录音)

您开始使用QuickTime影片录制startRecordingToOutputFileURL:recordingDelegate:。您需要提供基于文件的URL和代理。URL不能标识现有文件,因为影片文件输出不会覆盖现有资源。您还必须具有写入指定位置的权限。委托人必须遵守AVCaptureFileOutputRecordingDelegate协议,必须执行该captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:方法。

AVCaptureMovieFileOutput * aMovieFileOutput = <#获取电影文件输出#>;
NSURL * fileURL = <#A标识输出位置的文件URL#>;
[aMovieFileOutput startRecordingToOutputFileURL:
fileURL recordingDelegate:<#the delegate#>];

在执行中captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:,委托人可能会将生成的电影写入“相机卷”专辑。它还应该检查可能发生的任何错误。

Ensuring That the File Was Written Successfully(确保稳健写入成功)

要确定文件是否已成功保存,在执行中captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:不仅会检查错误,还会检查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...

您应该检查该AVErrorRecordingSuccessfullyFinishedKey错误的用户信息字典
中的键值,因为该文件可能已成功保存,即使您有错误。
错误可能表示您的记录约束之一达到了,例如AVErrorMaximumDurationReachedAVErrorMaximumFileSizeReached。录音可能停止的其他原因是:

Adding Metadata to a File(将元数据添加到文件)

即使在录制时也可以随时设置影片文件的元数据。这对于在记录开始时信息不可用的情况(可能与位置信息一样)是有用的。文件输出的元数据由AVMetadataItem对象数组表示; 您使用其可变子类的实例AVMutableMetadataItem,创建您自己的元数据。

AVCaptureMovieFileOutput * aMovieFileOutput = <#获取电影文件输出#>;

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 - <#要设置的位置#>;

item.value = [NSString stringWithFormat:@“%+ 08.4lf%+ 09.4lf /”

location.coordinate.latitude,
location.coordinate.longitude];

[newMetadataArray addObject:item];

aMovieFileOutput.metadata = newMetadataArray;

Processing Frames of Video(处理视频帧)

一个AVCaptureVideoDataOutput对象使用委托来销售视频帧。你设置委托使用setSampleBufferDelegate:queue:。除了设置委托之外,还可以指定一个在其上调用方法委托的串行队列。您必须使用串行队列来确保将帧以正确的顺序传递给代理。您可以使用队列修改提供和处理视频帧的优先级。有关示例实施,请参阅SquareCam

这些框架在委托方法中显示captureOutput:didOutputSampleBuffer:fromConnection:,作为CMSampleBufferRef不透明类型的实例(请参阅“介质表示”)。默认情况下,缓冲区以相机最有效的格式发出。您可以使用该videoSettings属性来指定自定义输出格式。视频设置属性是一个字典; 目前,唯一支持的关键是kCVPixelBufferPixelFormatTypeKey。推荐的像素格式由属性返回availableVideoCVPixelFormatTypes,该 availableVideoCodecTypes属性返回支持的值。Core Graphics和OpenGL都可以很好地与BGRA格式配合使用:

AVCaptureVideoDataOutput * videoDataOutput = [AVCaptureVideoDataOutput new];

NSDictionary * newSettings =

@ {(NSString *)kCVPixelBufferPixelFormatTypeKey:
@(kCVPixelFormatType_32BGRA)};

videoDataOutput.videoSettings = newSettings;

//如果数据输出队列被阻止(因为我们处理静止图像)丢弃

[videoDataOutput setAlwaysDiscardsLateVideoFrames:YES];)

//创建用于示例缓冲区委托的串行调度队列,以及捕获静态图像时

//必须使用串行调度队列来保证视频帧的顺序传送

//查看setSampleBufferDelegate的头文件:queue:了解更多信息

videoDataOutputQueue = dispatch_queue_create(“VideoDataOutputQueue”,DISPATCH_QUEUE_SERIAL);

[videoDataOutput setSampleBufferDelegate:self queue:videoDataOutputQueue];

AVCaptureSession * captureSession = <#Capture Capture#>;

if([captureSession canAddOutput:videoDataOutput])

[captureSession addOutput:videoDataOutput];

Performance Considerations for Processing Video(处理视频的性能注意事项)

您应该将会话输出设置为应用程序的最低实际分辨率。将输出设置为比必要的更高分辨率浪费处理周期,并且不必要地消耗功率。

您必须确保您的实现captureOutput:didOutputSampleBuffer:fromConnection:能够在分配给帧的时间量内处理样本缓冲区。如果需要太长时间,并且您按住视频帧,AV基金会不仅可以向您的代表提供框架,还可以传递其他输出,如预览图层。

您可以使用捕获视频数据输出的minFrameDuration属性来确保您有足够的时间来处理帧,而不是以比其他情况更低的帧速率。您也可以确保alwaysDiscardsLateVideoFrames属性设置为YES(默认)。
这可以确保任何延迟的视频帧都被丢弃,而不是传递给您进行处理。或者,如果您正在录制,如果输出fame有点晚,并且您希望获得所有这些,您可以将属性值设置为NO。这并不意味着帧不会被丢弃(也就是说,帧可能会被丢弃),但是它们可能不会像早期还是高效地丢弃。

Capturing Still Images(捕捉静态图像)

AVCaptureStillImageOutput如果要使用伴随的元数据捕获静态图像,则使用输出。图像的分辨率取决于会话的预设以及设备。

Pixel and Encoding Formats(像素和编码格式)

不同设备支持不同的图像格式。你可以找出像素和编解码类型通过使用设备支持availableImageDataCVPixelFormatTypesavailableImageDataCodecTypes分别。每个方法返回特定设备支持的值的数组。您设置outputSettings
字典以指定所需的图像格式,例如:

AVCaptureStillImageOutput * stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary * outputSettings = @ {AVVideoCodecKey:AVVideoCodecJPEG};
[stillImageOutput setOutputSettings:outputSettings];

如果要捕获JPEG图像,则通常不要指定自己的压缩格式。相反,您应该让静态图像输出为您提供压缩,因为它的压缩是硬件加速的。如果您需要图像的数据表示,即使您修改图像的元数据,也可以使用它jpegStillImageNSDataRepresentation:来获取NSData对象而不重新压缩数据。

Capturing an Image(捕捉图像)

当您要捕获图像时,会向输出发送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:是一个包含两个参数的:CMSampleBuffer包含图像数据的不透明类型和错误。示例缓冲区本身可以包含诸如EXIF字典之类的元数据作为附件。您可以根据需要修改附件,但请注意像素和编码格式中讨论的JPEG图像的优化。

[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:

^(CMSampleBufferRef imageSampleBuffer,NSError * error){

CFDictionaryRef exifAttachments =

CMGetAttachment(imageSampleBuffer,kCGImagePropertyExifDictionary,NULL);

if(exifAttachments){

//使用附件进行某些操作。

}

//根据需要继续

}];

Showing the User What’s Being Recorded(显示用户记录的内容)

您可以向用户提供相机录像内容(使用预览图层)或麦克风(通过监听音频通道)进行预览。

Video Preview(视频预览)

您可以向用户提供使用AVCaptureVideoPreviewLayer对象预录的内容。AVCaptureVideoPreviewLayer是一个子类CALayer(请参阅核心动画编程指南,您不需要任何输出来显示预览。

使用AVCaptureVideoDataOutput该类可以让客户端应用程序在向用户呈现视频像素之前访问视频像素。

与捕获输出不同,视频预览层维护与其相关联的会话的强烈引用。这是为了确保在层尝试显示视频时会话未被释放。初始化预览图层的方式反映出来:

AVCaptureSession * captureSession = <#获取捕获会话#>;
CALayer * viewLayer = <#从要显示预览的视图中获取图层#>;

AVCaptureVideoPreviewLayer * captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:captureSession];
[viewLayer addSublayer:captureVideoPreviewLayer];

通常,预览图层的行为CALayer与渲染树中的任何其他对象(请参阅核心动画编程指南)相同。您可以像任何图层一样缩放图像并执行转换,旋转等。一个区别是,您可能需要设置图层的orientation属性来指定如何旋转来自相机的图像。此外,您可以通过查询supportsVideoMirroring属性
来测试视频镜像的设备支持。您可以videoMirrored根据需要设置属性,但是当automaticallyAdjustsVideoMirroring属性设置为YES(默认值)时,将根据会话的配置自动设置镜像值。

Video Gravity Modes(视频重力模式)

预览图层支持您使用videoGravity以下设置的三种重力模式:

Using “Tap to Focus” with a Preview(使用“点击焦点”与预览)

实施点对焦与预览图层时,您需要注意。您必须考虑图层的预览方向和重力,以及预览可能被镜像的可能性。请参阅示例代码项目AVCam-iOS:使用AVFoundation捕获图像和电影以实现此功能

Showing Audio Levels(显示音频电平)

要监视捕获连接中音频通道中的平均峰值功率电平,请使用AVCaptureAudioChannel对象。音频电平不是键值观察值,所以您必须像您想要更新用户界面(例如,每秒10次)一样轮询更新的电平。

AVCaptureAudioDataOutput * audioDataOutput = <#获取音频数据输出#>;

NSArray * connections = audioDataOutput.connections;

if([connections count]> 0){

// AVCaptureAudioDataOutput应该只有一个连接。

AVCaptureConnection * connection = [connections objectAtIndex:0];

NSArray * audioChannels = connection.audioChannels;

for(AVCaptureAudioChannel * channel in audioChannels){

float avg = channel.averagePowerLevel;

float peak = channel.peakHoldLevel;

//更新电平表用户界面。

}

}

Putting It All Together: Capturing Video Frames as UIImage Objects(将其全部放在一起:将视频帧捕获为UIImage对象)

这个简短的代码示例来说明如何捕获视频并将您转换为UIImage对象的帧。它显示如何:

注意: 为了专注于最相关的代码,本示例省略了完整应用程序的几个方面,包括内存管理。要使用AV基金会,您希望有足够的经验与可可可以推断出缺少的部分。

Create and Configure a Capture Session(创建并配置捕获会话)

您可以使用AVCaptureSession对象来协调从AV输入设备到输出的数据流。创建一个会话,并配置它以产生中等分辨率的视频帧。

AVCaptureSession * session = [[AVCaptureSession alloc] init];

session.sessionPreset = AVCaptureSessionPresetMedium;

Create and Configure the Device and Device Input(创建和配置设备和设备输入)

捕获设备由AVCaptureDevice对象表示; 该类提供了检索所需输入类型的对象的方法。设备具有一个或多个端口,使用AVCaptureInput对象配置。通常,您在其默认配置中使用捕获输入。

查找视频捕获设备,然后使用设备创建设备输入并将其添加到会话中。如果无法找到适当的设备,则该deviceInputWithDevice:error:方法将通过引用返回错误。

AVCaptureDevice * device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

NSError * error = nil;

AVCaptureDeviceInput * input =

[AVCaptureDeviceInput deviceInputWithDevice:device error:&error];

if(!input){

//正确处理错误。

}

[session addInput:input];

Create and Configure the Video Data Output(创建和配置视频数据输出)

您使用一个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);

您使用队列来修改发送和处理视频帧的优先级。

Implement the Sample Buffer Delegate Method(实现Sample Buffer Delegate方法)

在委托类中,实现在captureOutput:didOutputSampleBuffer:fromConnection:写入样本缓冲区时调用的method()。视频数据输出对象将帧作为CMSampleBuffer不透明类型传递,因此您需要将CMSampleBuffer不透明类型转换为UIImage对象。此操作的功能显示在将CMSampleBuffer转换为UIImage对象

- (void)captureOutput:(AVCaptureOutput *)captureOutput
     didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
     fromConnection:(AVCaptureConnection *)connection {

UIImage * image = imageFromSampleBuffer(sampleBuffer);
//在这里添加使用图像的代码。
}

请记住,在您指定的队列中调用委托方法setSampleBufferDelegate:queue:; 如果要更新用户界面,则必须在主线程上调用相关代码。

Starting and Stopping Recording(启动和停止录制)

配置捕获会话后,应确保相机具有根据用户喜好进行记录的权限。

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方法是一个阻塞调用,可能需要一些时间,因此您应该在串行队列上执行会话设置,以使主队列不被阻止(从而保持UI响应)。请参阅AVCam-iOS:使用AVFoundation捕获图像和电影以进行规范实现。
[session startRunning];

要停止录制,您可以向会话发送stopRunning消息。

High Frame Rate Video Capture(高帧率视频采集)

iOS 7.0在所选硬件上引入了高帧率视频捕获支持(也称为“SloMo”视频)。完整的AVFoundation框架支持高帧速率内容。

您可以确定使用AVCaptureDeviceFormat该类的设备的捕获功能。此类具有返回支持的媒体类型,帧速率,视场,最大缩放系数,是否支持视频稳定等的方法。

  • Capture支持全屏720p(1280 x 720像素)分辨率,每秒60帧(fps),包括视频稳定和可放置的P帧(H264编码电影的功能,即使在较慢和较旧的硬件上也能让电影播放顺畅。 )

  • 播放增强了对慢速和快速播放的音频支持,允许以更慢或更快的速度保持音频的时间间隔。

  • 编辑完全支持可变组合中的缩放编辑。

  • 导出提供两个选项,支持60 fps的电影。可以保留可变帧速率,慢速或快速运动,或者将影片转换为任意较慢的帧速率,例如每秒30帧。

    所述SloPoke示例代码说明用于快速视频捕捉AVFoundation支撑,确定硬件是否支持高帧速率视频拍摄,使用不同的速率和时间间距的算法和编辑(包括设置时间尺度的组合物的部分)回放。

Playback(回放)

AVPlayer通过设置setRate:方法值自动管理大部分播放速度的实例。该值用作播放速度的乘数。1.0的值会导致正常播放,0.5次以半速播放,5.0播放速度比正常速度快五倍,依此类推。

该AVPlayerItem对象支持的audioTimePitchAlgorithm属性。该属性允许您指定使用Time Pitch Algorithm Settings常数以各种帧速率播放影片时如何播放音频。

下表显示支持的时间间距算法,质量,算法是否使音频捕捉到特定帧速率,以及每个算法支持的帧速率范围。

时间间距算法 质量 捕捉到特定的帧速率 速率范围
AVAudioTimePitchAlgorithmLowQualityZeroLatency 质量低,适合快进,倒带或低质量的声音。 YES 0.5,0.6666667,0.8,1.0,1.25,1.5,2.0的比率。
AVAudioTimePitchAlgorithmTimeDomain 质量适中,计算机便宜,适合声音。 NO 0.5-2倍的速率。
AVAudioTimePitchAlgorithmSpectral 最高质量,最昂贵的计算,保留原始项目的音高。 NO 1 / 32-32速率。
AVAudioTimePitchAlgorithmVarispeed 高品质播放,无间距校正。 NO 1 / 32-32速率。

Editing(编辑)

编辑时,您可以使用AVMutableComposition该类来构建时间编辑。

Export(导出)

导出60 fps视频使用AVAssetExportSession该类导出资产。可以使用两种技术导出内容:

使用恒定的帧速率导出来实现最大播放兼容性。将frameDuration视频构图的属性设置为30 fps。您还可以使用设置导出会话的audioTimePitchAlgorithm属性来指定时间间隔。

Recording(记录)

您可以使用AVCaptureMovieFileOutput该类捕获高帧率视频,这会自动支持高帧率记录。它将自动选择正确的H264音高级别和比特率。要进行自定义录音,您必须使用AVAssetWriter该类,这需要一些额外的设置。

assetWriterInput.expectsMediaDataInRealTime = YES;

此设置可确保捕获能够跟上传入的数据。
下一页
上一页

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容