AVFoundation 官方文档

目前只翻译完一小部分,先保存一下.之后有空继续....(好长好长啊...). 翻译的比较渣,但应该可以看懂...
官方文档链接

AVFoundation

一.静态图像及视频的采集

为了管理一个设备,如相机或者麦克风.你可以弄一些对象来描绘输入设备输出设备.同时用一个AVCaptureSession的实例来协调二者之间的数据流传递.你至少需要如下实例:

  • AVCaptureDevice的实例
    • 用来表示输入设备.如相机或者麦克风
  • AVCaptureInput具体子类的实例
    • 用来配置输入设备的端口
  • AVCaptureOutput具体子类的实例
    • 用来管理是输出成一个视频文件还是静态图片
  • AVCaptureSession的实例
    • 协调输入设备和输出设备之间数据流的传递

为了向用户展示一个相机正在录制什么,你可以使用AVCaptureVideoPreviewLayer的实例.(它是CALayer的子类),你可以配置多个输入和输出,由一个单例会话来协调.(如下图1所示)

1.png

AVCaptureConnection:在一个capture session中,一个输入采集输出捕捉之间的连接由 AVCaptureConnection对象来管理.

一个采集输入(AVCaptureInput的实例)有一个或多个输入端口(AVCaptureInputPort的实例).

一个输出捕捉(AVCaptureOutput)可以接受一个或多个数据源的数据(如AVCaptureMovieFileOutput对象可以接收视频和音频数据)

当你将输入和输出添加到一个会话的时候,这个会话会把所有兼容的捕获输入端口和输出捕捉之间形成连接.二者之间的连接用AVCaptureConnection对象来表示.如图2

2.png

你可以使用一个捕获连接来启用或禁用来自给定的输入或者输出的数据流.你也可以使用这个连接来监视音频信道的平均峰值功率电平.

Note : 媒体捕获不支持在 iOS 设备上同时捕捉正面和背面的摄像头.

二.使用捕捉会话(Capture Session)来协调数据流

AVCaptureSession对象是一个中央协调的对象,你可以用它来管理数据流的采集.你可以用一个实例来协调从AV输入设备到输出设备之间的数据流.您添加你想要的采集设备和输出设备到会话.然后通过发送startRunning消息来启动数据流,发送stopRunning消息来停止数据流.

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

// Add inputs and outputs.

[session startRunning];

配置会话

你可以在会话中使用一个预设的配置参数来指定想要的图像质量和分辨率.预设的是一个常数.它在许多可能的配置中标识了一个.在某些特定情况下.实际的配置是设备特定的.

配置信息:

3.png

在我们想要设置一个视频的配置信息时,我们最好再设置之前对他做一层判断.如

if ([session canSetSessionPreset:AVCaptureSessionPreset1280*720]) {
    session.sessionPreset = AVCaptureSessionPreset1280*720;
} else {
    // Handle the failure.
}

如果想要调整配置参数.那么就在[session beginConfiguration][session commitConfiguration]方法之间来调整.

[session beginConfiguration];

// Remove an existing capture device.

// Add a new capture device.

// Reset the preset.

[session commitConfiguration];

监视会话捕捉状态

捕捉会话会发布通知,你可以观察到通知.如当它开始或停止运行,或当它被中断时,如果发生运行时错误,你可以通过注册接受到一个AVCaptureSessionRuntimeErrorNotification这样的通知.你还可以询问会话的运行属性,来查明它是否正在运行.以及它中断的属性是否中断.此外,runninginterrupted遵从KVO并且发布通知在主线程.

三.一个 AVCaptureDevice对象代表一个输入设备

一个AVCaptureDevice对象抽象出一个物理捕获设备到AVCaptureSession对象.这个物理捕获设备提供输入数据(如音频或者视频).如.两个视频的输入设备.一个是前置摄像头.一个是后置摄像头和一个音频输入的麦克风.

下面的代码示例遍历所有可用设备并打印出他们的名字.视频设备.及位置

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");
        }
    }
}

此外,你还可以找到该设备的模型ID和唯一标识

设备捕获设置

不同的设备,功能不同,如有些可能支持不同焦点或闪光模式,有的就可能集中在一个焦点上.

下面代码演示了如何找到具有手电筒模式的视频输入设备,并支持给定的捕获会话 预置.

NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
NSMutableArray *torchDevices = [[NSMutableArray alloc] init];
for(AVCaptureDevice *device in devices) {
    [if ([device hasTorch] && [device supportsAVCaptureSessionPreset:AVCaptureSessionPreset640*480]) {
        [torchDevices addObject:device];
    }]
}

对焦模式(3种)

1.AVCaptureFocusModeLocked

  • 焦点固定
  • 使用场景 : 当你想让用户组成一个场景,然后锁定焦点

2.AVCaptureFocusModeAutoFocus

  • 仅仅扫描聚焦然后恢复到锁定焦点
  • 使用场景 : 你想选择一个特定的项目,然后聚焦,并且把焦点放在这个项目上,虽然这个项目不在场景的正中

3.AVCaptureFocusModeContinuousAutoFocus

  • 相机持续自动聚焦模式

你可以使用adjustingFocus这个属性来确定设备是否是目前的焦点.当设备启动和停止聚焦时,可以使用KVO来观察属性的变化.

如果更改焦点模式的设置.则可以将他们返回到默认配置:

if([currentDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
    CGPoint autofocusPoint = CGPointMake(0.5f,0.5f);
    [currentDevice setFocusPointOfInterest:autofocusPoint];
    [currentDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
}

曝光模式(2种)

1.AVCaptureExposureModeContinuousAutoExposure

  • 该设备根据需要自动调整曝光水平

2.AVCaptureExposureModeLocked

  • 曝光水平固定在当前水平

你使用isExposureModeSupported:方法确定设备是否支持给定的曝光模式,然后用exposureMode这个属性来设置曝光模式

同样也可以使用KVO来观察设备当启动及停止更改其曝光设置时的改变状态.

如果更改了曝光设置,则可以将它们返回到默认的配置:

if([currentDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]){
    CGPoint exposurePoint = CGPointMake(0.5f,0.5f);
    [currentDevice setExposurePointOfInterest:exposurePoint];
    [currentDevice setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
}

闪光模式(3种)

1.AVCaptureFlashModeOff

  • 永久关闭闪光模式

2.AVCaptureFlashModeOn

  • 永久打开闪光模式

3.AVCaptureFlashModeAuto

  • 闪光是否开启取决于环境的光条件

你可以使用hasFlash来确定该设备是否有闪光.如果这个方法返回 YES,那么使用isFlashModeSupported:方法.传递所需的模式来确定这个设备是否支持给定的闪光模式.然后用flashMode属性来设置模式.

手电筒模式(3种)

在手电筒模式下,闪光灯是在一种低功耗照亮视频捕获情况下连续开启的.

1.AVCaptureTorchModeOff

  • 永久关闭手电筒

2.AVCaptureTorchModeOn

  • 永久打开手电筒

3.AVCaptureTorchModeAuto

  • 如果需要,自动打开和关闭

你使用hasTorch来确定设备是否有闪光灯.你用isTorchModeSupported:这个方法来确定设备是否支持给定的闪光模式,然后使用torchMode属性来设置这个模式.

视频稳定

电影视频稳定可用于连接的视频操作,这取决于特定的设备硬件. 即便如此,也不是支持所有的源格式和视频分辨率

启用电影视频稳定也可能引入额外的延迟到视频捕获管道.我们可以使用videoStabilizationEnabled这个属性来检测何时使用视频稳定. 如果相机支持,enablesVideoStabilizationWhenAvailable属性允许应用程序自动使用视频稳定,所以,由于上面的限制,在默认的情况下,自动稳定被禁用.

白平衡(2种)

1.AVCaptureWhiteBalanceModeLocked

  • 固定模式.

2.AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance

  • 相机根据需要不断调整白平衡

你可以用isWhiteBalanceModeSupported:方法来确定设备是否支持白平衡模式.然后用whiteBalanceMode属性来设置模式

同样的也可以使用KVO观察属性的改变

设置定位装置

在一个AVCaptureConnection上面你可以设置一个你想要的AVCaptureOutput输出的需求方向,是哪一个(AVCaptureMoviewFileOutput, AVCaptureStillImageOutput, AVCaptureVideoDataOutput)用来连接(connection)

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

切换装置

有时,您可能希望允许用户在输入设备之间切换,例如,从使用前向后置摄像头切换。为了避免停顿或卡顿,你可以配置会话在运行,但是你应该使用beginconfigurationcommitconfiguration支架配置变化

AVCaptureSession *session = <#A capture session#>;
[session beginConfiguration];
 
[session removeInput:frontFacingCameraDeviceInput];
[session addInput:backFacingCameraDeviceInput];
 
[session commitConfiguration];

当外面的commitconfiguration被调用时,所有的改变都是一起做的。这确保了平稳过渡

四.使用 Capture Inputs 将一个捕捉设备添加到会话中

添加一个捕获设备来捕捉一个会话.你可以使用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.
}

一个AVCaptureInput可以声明一个或多个流媒体数据.比如,输入设备可以同时提供音频和视频数据.每个流媒体的输入提供了一个AVCaptureInputPort表示.捕捉会话会使用AVCaptureConnection对象来定义一个映射在一组AVCaptureInputPort对象和一个AVCaptureOutput对象之间.

五.使用 Capture Outputs 从一个会话中获取输出

要从捕获会话中获取输出.请添加一个或多个输出.输出是AVCaptureOutput的一个具体实例.

1.AVCaptureMovieFileOutput

  • 输出一个视频文件

2.AVCaptureVideoDataOutput

  • 如果想要从被捕获的视频中处理它的帧.可以用这个.比如可以创建自定义的视图层

3.AVCaptureAudioDataOutput

  • 如果想处理正在捕获的音频数据,可以使用这个

4.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.视频编码通常都是H264.音频编码通常都是AAC.实际值要因设备而异.

开始录制

你可以使用startRecordingToOutputFileURL:recordingDelegate:开始录制一个 QuickTime 电影.你需要提供URL及delegate.这个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:这个方法里你不仅仅应该检查可能发生的错误,还要检查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这个key检查用户信息在错误信息中,因为该文件可能已成功保存,即使你有一个错误.这种错误可能表名你的一个录制参数到了最大值,就是不能再继续录制了.如AVErrorMaximumDurationReached或者AVErrorMaximumFileSizeReached.记录可能停止的其他原因:

1.AVErrorDiskFull

  • 磁盘满了

2.AVErrorDeviceWasDisconnected

  • 录制设备断开了

3.AVErrorSessionWasInterrupted

  • 会话被意外终端(如突然接个电话)

向一个文件添加 MetaData(元数据)

你可以在任何时候设置电影文件的元数据,即便你在录制.这在你录制开始,信息不可用时,作用就体现了.一个 输出文件的 MetaData 是由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:来设置代理.除了设置代理之外,还要指定一个串行队列,在该队列中调用代理方法.

必须使用串行队列来确保帧以适当的顺序传递给代理.您可以使用队列修改传送和处理视频帧的优先级

captureOutput:didOutputSampleBuffer:fromConnection:这个代理方法中,帧被作为一个CMSampleBufferRef隐式类型的实例被presented.默认情况下,buffers 在相机最有效的格式下被发出.

你可以使用videoSettings属性来自定义输出格式.视频的设置属性是一个字典.目前唯一支持的key 是KCVPixelBufferPixelFormatTypeKey.推荐的像素格式被availableVideoCVPixelFormatTypes属性给返回了.并且availableVideoCodecTypes属性返回支持的值.

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 停止提供基础帧,不仅对你的代理也对其他输出如预览层(preview layer).

你可以使用捕获视频数据输出的minframeduration属性以确保您有足够的时间来处理帧具有较低的帧速率比其他情况下的成本。你也可以确保alwaysdiscardslatevideoframes属性设置为Yes(默认)。这将确保任何迟到的视频帧下降,而不是交给你处理。或者,如果你正在录制,如果输出的帧稍微晚一些没有关系的话,你宁愿都要了,这并不意味着不会掉帧(即帧仍可能下降),但他们可能不会过早被抛弃,或仍然有效.

捕获静止图像

如果你想捕捉静止图像伴随元数据可以使用Avcapturestillimageoutput输出。图像的分辨率取决于会话的preset,以及设备

像素以及编码格式

不同的设备支持不同的图像格式。使用availableimagedatacvpixelformattypesavailableimagedatacodectypes你可以找到像素和编解码器类型通过支持的设备。每个方法返回特定设备的支持值的数组。你可以通过设置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; }
}

第二个参数是一个block块.它有两个参数.一是包含图像数据的隐式类型CMSampleBuffer.另一个是错误信息.sample buffer 它本身可能包含 metadata,如 EXIF字典.作为附件.如果你想你可以修改附件.但要注意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.
    }];

六.向用户显示正在录制的内容

您可以为用户提供相机录制的预览图层preview layer.或者麦克风(通过监听音频通道).

视频预览图层

你可以通过使用AVCaptureVideoPreviewLayer对象来给用户提供一个正在录制的预览图层.

AVCaptureVideoPreviewLayer是CALayer的一个子类.你不需要任何输出来显示预览图层.

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对象,您可以缩放图像和执行转换,旋转,等等.一个区别是,您可能需要设置层的定位属性,以指定它应该如何旋转来自相机的图像.此外,你可以通过查询supportsVideoMirroring属性来为支持的设备的视频镜像做测试.你可以设置videoMirrored属性.但是当automaticallyAdjustsVideoMirroring属性被设置为YES 的时候.镜像值会基于会话的配置来自动设置.

视频重力模式

预览层支持3种重力模式.你可以使用videoGravity:来设置

1.AVLayerVideoGravityResizeAspect

  • 保留纵横比.视频不填满屏幕.有黑色条

2.AVLayerVideoGravityResizeAspectFill

  • 保留纵横比,视频填满屏幕.在必要时裁剪视频

3.AVLayerVideoGravityResize

  • 拉伸视频填充可用屏幕区域,但是会扭曲图像
使用"点击聚焦"来预览

您需要注意当实现TAP与预览层一起集中。您必须解释图层的预览方向和重力,以及预览可能被镜像的可能性。看示例代码项目avcam iOS:利用AVFoundation捕捉到这一功能的实现图像和电影

显示音频电平

使用AVCaptureAudioChannel对象来监测平均峰值功率电平在捕获连接中的音频信道上.它不是KVO来监测的,所以如果要更新用户界面.请经常更新(如1秒钟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对象的代理来处理视频帧
  • 通过代理实现一个功能,将CMSampleBuffeer转换成一个UIImage对象

Note : 这个例子只是核心的代码.省略了几个方面.包括内存管理, 及AVFoundation的基本使用.希望你自己可以把剩余的补充完整

创建并配置捕捉会话

使用AVCaptureSession对象来协调 AV输入设备和输出设备之间数据流的传输.创建一个session.配置它以生成一个中等分辨率的视频帧.

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

创建和配置视频输出数据并将其添加到会话.封顶的帧速率是15帧,设置minframeduration属性为 1/15

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 代理方法

在代理类中,当一个 sample buffer (样本缓冲区) 被写入的时候,实现captureOutput:didOutputSampleBuffer:fromConnection:这个方法.视频数据输出对象提供CMSampleBuffer类型的对象.所以你需要从CMSampleBuffer类型转换到UIImage类型.这个操作的目的就是将CMSampleBuffer转成UIImage

- (void)captureOutput:(AVCaptureOutput *)captureOutput
         didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
         fromConnection:(AVCaptureConnection *)connection {
 
    UIImage *image = imageFromSampleBuffer(sampleBuffer);
    // Add your code here that uses the image.
 }

记住,代理方法是指定在setSampleBufferDelegate:queue:队列调用:;如果你想更新UI,你必须调用任何相关代码在主线程

开始和停止录制

在配置捕获会话之后,你要确保相机允许按照用户的意愿来录制

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这个方法调用会需要一些时间.如果在主线程调用,那么就会阻塞主线程,造成卡顿.我们要在子线程中执行这个方法.

[session startRunning];

如果要停止录制的话,那么就发送一个stopRunning的消息

[session stopRunning];

高帧频视频采集

iOS 7 引入了高帧速率的视频拍摄支持在选定的硬件设备上(也称"SloMo"视频).整个AVFoundation框架都支持高帧速率的内容.

使用AVCaptureDevieFormat类来确定一个设备的捕捉能力.这个类有如下这些方法用来返回一些支持的媒体类型. 帧速率、视野、最大缩放因子、是否支持视频稳定等.

  • 捕捉支持每秒60帧,全720p(1280 * 720像素)的分辨率.包括视频的稳定性和可删除的P帧(H264对电影的特征编码,使电影可以在又慢有老的硬件设备上都可以顺畅的播放)
  • 回放具有增加的音频支持慢速和快速播放.允许音频的时间间距可以保存在较慢或更快的速度.
  • 编辑完全支持可变编辑的缩放编辑
  • 当支持FPS 60的电影输出有两种选择时,可变帧速率,慢或块的运动,我们可以把它转成慢帧速率,如每秒30帧

录音重放

AVPlayer的实例管理大部分的回放速度.回放速度通过设置setRate:方法的值来自动设置.该值用作播放速度的乘数.值为1.0表示可以正常回放,0.5的话回放速度减半,5.0的话就是正常播放速度的5倍,等等

AVPlayerItem对象支持audioTimePitchAlgorithm属性.当电影使用不同的帧速率通过使用Time Pitch Algoruthm Settings常量正在播放,这个属性允许你指定音频.

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


4.png
  • AVAudioTimePitchAlgorithmLowQualityZeroLatency
    • 质量低,适合快进,快退或低质量语音
  • AVAudioTimePitchAlgoruthmTimeDomain
    • 质量适中,计算成本较低,适合语音
  • AVAudioTimePitchAlgorithmSpectral
    • 最高质量,最昂贵的计算,保留了原来的项目间距
  • AVAudioTimePitchAlgorithmVarispeed
    • 高品质的播放没有音高校正

编辑

当开始编辑时,你用AVMutableComposition类来建立短暂的编辑

  • composition类方法来创建一个AVMutableComposition实例
  • insertTimeRange:ofAsset:atTime:error:这个方法来插入你的视频资源
  • scaleTimeRange:toDuration:方法来设置组成部分的时间刻度.

输出

使用AVAssetExportSession类来输出 FPS 60的视频,使用两种技术可以导出内容

  • 使用AVAssetExportPresetPassthrough预先避免重新编码的电影.随着媒体的部分FPS 60,部分加速,部分放缓.它会重新为媒体定时
  • 使用一个恒定的帧速率导出最大的回放兼容性,设置视频合成的frameDuration属性为30 fps,你也可以指定时间间距通过设置输出会话的audioTimePitchAlgorithm属性.

录制

使用AVCaptureMovieFileOutput类来捕获高帧率的视频,它自动支持高帧率视频的录制,他会自动选择正确的H264的音高和比特率

为了做自定义的录制.你必须使用AVAssetWriter类,它需要一些额外的设置

assetWriterInput.expectsMediaDataInRealTime=YES;

这个设置可以确保可以与传入的数据保持一致.

翻译了一部分,还没有完全完成....

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

推荐阅读更多精彩内容