1.架构图
1258499-8e2cdea415352eb6.png
2.AVCaptureDevice
AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
//拿到默认视频捕捉设备,iOS默认返回后摄像头
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
- 1
AVCaptureDevice
表示提供实时输入媒体数据(如视频和音频)的物理设备。
- 2
AVCaptureDevice
的每个实例对应于一个设备,例如摄像机或麦克风。无法直接创建AVCaptureDevice
的实例。还可以使用 AVCaptureDeviceDiscoverySession
获得所有当前可用设备的数组。通过给定媒体类型,设备可以提供的一个或多个媒体流。应用程序可以使用AVCaptureDeviceDiscoverySession
来搜索匹配所需条件的设备,也可以使用 +[AVCaptureDevice defaultDeviceWithDeviceType:mediaType:position:]
来获得对匹配所需条件的默认设备的引用。
- 3
AVCaptureDevice
的实例可用于为AVCaptureSession
提供媒体数据,方法是使用该设备创建AVCaptureDeviceInput
并将其添加到捕获会话中。
3.AVCaptureDeviceInput
//将捕捉的设备封装成 AVCaptureDeviceInput
AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:error];
- 1
AVCaptureDeviceInput
是AVCaptureInput
的一个具体子类,它提供了一个用于从AVCaptureDevice
捕获媒体的接口。
- 2
AVCaptureDeviceInput
实例是AVCaptureSession
的输入源,它提供了系统设备(AVCaptureDevice
实例)的媒体数据,
4.AVCaptureSession
//初始化
self.captureSession = [[AVCaptureSession alloc] init];
self.captureSession.sessionPreset = AVCaptureSessionPresetHigh;
//添加视频输入设备
if ([self.captureSession canAddInput:videoInput])
[self.captureSession addInput:videoInput];
self.activeVideoInput = videoInput;
}
//添加音频输入设备
if ([self.captureSession canAddInput:audioInput]) {
[self.captureSession addInput:audioInput];
}
//AVCaptureVideoDataOutput实例,输出视频
self.videoOutput = [[AVCaptureVideoDataOutput alloc] init];
self.videoOutput.videoSettings = @{(id)kCVPixelBufferPixelFormatTypeKey : [NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange]};
[self.videoOutput setSampleBufferDelegate:self queue:self.videoDataOutQueue];
if ([self.captureSession canAddOutput:self.videoOutput]) {
[self.captureSession addOutput:self.videoOutput];
}
//AVCaptureAudioDataOutput实例,输出音频
self.audioOutput = [[AVCaptureAudioDataOutput alloc] init];
[self.audioOutput setSampleBufferDelegate:self queue:self.videoDataOutQueue];
if ([self.captureSession canAddOutput:self.audioOutput]) {
[self.captureSession addOutput:self.audioOutput];
}
//关于AVCaptureSession对性能有一定影响,因此要放在自建的串行队列中
- (void)startSession {
if (![self.captureSession isRunning]) {
dispatch_async(self.videoQueue, ^{
[self.captureSession startRunning];
});
}
}
- (void)stopSession {
if ([self.captureSession isRunning]) {
dispatch_async(self.videoQueue, ^{
[self.captureSession stopRunning];
});
}
}
- 1 AVCaptureSession是AVFoundation捕获类的核心
- 2 要执行实时捕获,客户机可以实例化
AVCaptureSession
并添加适当的AVCaptureInputs
,如AVCaptureDeviceInput
和Outputs
,如AVCaptureMovieFileOutput
。[AVCaptureSession startRunning]
启动从输入到输出的数据流,[AVCaptureSession stopRunning]
停止数据流。客户端可以设置sessionPreset
属性来定制输出的质量级别或比特率。
5.AVCaptureVideoPreviewLayer
//THPreviewView.h
@interface THPreviewView : UIView
@property (strong, nonatomic) AVCaptureSession *session;
@end
//THPreviewView.m
@implementation THPreviewView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setupView];
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
[self setupView];
}
return self;
}
/**
直接使用Layer初始化,并添加到UIView上时,
由于Layer隐式动画的存在,
在通过动画修改视图frame时会有影响,
通过重写+ (Class)layerClass则可以避免这种情况
*/
+ (Class)layerClass {
return [AVCaptureVideoPreviewLayer class];
}
- (AVCaptureSession*)session {
return [(AVCaptureVideoPreviewLayer*)self.layer session];
}
- (void)setSession:(AVCaptureSession *)session {
[(AVCaptureVideoPreviewLayer*)self.layer setSession:session];
}
@end
- 1 用于预览
AVCaptureSession
的可视输出的CoreAnimation Layer子类。
- 2
AVCaptureVideoPreviewLayer
实例是CALayer
的子类,因此适合作为图形界面的一部分插入到层层次结构中。创建一个AVCaptureVideoPreviewLayer
实例,使用+layerWithSession:
或-initWithSession:
预览捕获会话。使用@“videoGravity”
属性,可以影响相对于层边界如何查看内容。在某些硬件配置中,可以使用@“orientation”
和@“mirror”
来操作层的方向。
5.1 videoGravity属性
//THPreviewView.m
- (void)setupView {
[(AVCaptureVideoPreviewLayer *)self.layer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
}
- 1 定义视频如何在
AVCaptureVideoPreviewLayer
边界矩形内显示
- 2 选项有
AVLayerVideoGravityResize
, AVLayerVideoGravityResizeAspect
和AVLayerVideoGravityResizeAspectFill
。AVLayerVideoGravityResizeAspect
是默认的。见< AVFoundation / AVAnimation.h>
来描述这些选项。
6. AVCaptureVideoDataOutput/AVCaptureAudioDataOutput
初始化代码见第4部分
- 1
AVCaptureVideoDataOutput
是AVCaptureOutput
的一个具体子类,可用于处理捕获视频的未压缩或压缩帧。
- 2
AVCaptureVideoDataOutput
的实例生成适合使用其他媒体api进行处理的视频帧。应用程序可以使用captureOutput:didOutputSampleBuffer:fromConnection: delegate
方法访问帧。
7 AVCaptureStillImageOutput
//AVCaptureStillImageOutput实例,从摄像头捕捉静态图片
self.imageOutput = [[AVCaptureStillImageOutput alloc] init];
self.imageOutput.outputSettings = @{AVVideoCodecKey: AVVideoCodecJPEG};
if ([self.captureSession canAddOutput:self.imageOutput]) {
[self.captureSession addOutput:self.imageOutput];
}
//拍照
- (void)captureStillImage {
AVCaptureConnection *connection = [self.imageOutput connectionWithMediaType:AVMediaTypeVideo];
if (connection.isVideoOrientationSupported) {
//根据当前设备的横竖屏方向,给connection设置合适的方向来使图片按正确的方向展示
connection.videoOrientation = [self currentVideoOrientation];
}
id handler = ^(CMSampleBufferRef sampleBuffer, NSError *error) {
if (sampleBuffer != NULL) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:sampleBuffer];
UIImage *image = [UIImage imageWithData:imageData];
[self writeImageToAssetsLibrary:image];
}else {
NSLog(@"NUll sampleBuffer");
}
};
[self.imageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:handler];
}
- 1
AVCaptureStillImageOutput
是AVCaptureOutput
的一个具体子类,可以使用它来捕获高质量的静态图像和附带的元数据。
- 2
AVCaptureStillImageOutput
的实例可以根据需要从实时捕获源捕获高质量快照。客户端可以使用captureStillImageAsynchronouslyFromConnection:completionHandler:
方法请求当前时间的静态图像。客户端还可以配置静态图像输出,以生成特定图像格式的静态图像。
8 AVCaptureMovieFileOutput
//初始化
self.movieOutput = [[AVCaptureMovieFileOutput alloc] init];
if ([self.captureSession canAddOutput:self.movieOutput]) {
[self.captureSession addOutput:self.movieOutput];
}
//拍视频
AVCaptureConnection *videoConnection = [self.movieOutput connectionWithMediaType:AVMediaTypeVideo];
if ([videoConnection isVideoOrientationSupported]) {
videoConnection.videoOrientation = [self currentVideoOrientation];
}
if ([videoConnection isVideoStabilizationSupported]) {
videoConnection.enablesVideoStabilizationWhenAvailable = YES;
}
AVCaptureDevice *device = [self activeCamera];
if (device.isSmoothAutoFocusEnabled) {
NSError *error;
if ([device lockForConfiguration:&error]) {
device.smoothAutoFocusEnabled = YES;
[device unlockForConfiguration];
}else {
[self.delegate deviceConfigurationFailedWithError:error];
}
}
//通过[NSURL fileURLWithPath:filePath]创建沙盒中的URL
self.outputURL = [self uniqueURL];
[self.movieOutput startRecordingToOutputFileURL:self.outputURL recordingDelegate:self];
//AVCaptureFileOutputRecordingDelegate
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput
didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
fromConnections:(NSArray *)connections
error:(NSError *)error {
if (error) {
[self.delegate mediaCaptureFailedWithError:error];
}else {
//拍摄结束时根据outputFileURL将视频写入相册
[self writeVideoToAssetsLibrary:[self.outputURL copy]];
}
self.outputURL = nil;
}
- 1
AVCaptureMovieFileOutput
是AVCaptureFileOutput
的一个具体子类,它将捕获的媒体写入QuickTime电影文件。
- 2
AVCaptureMovieFileOutput
实现AVCaptureFileOutput
声明的完整文件记录接口,用于将媒体数据写入QuickTime电影文件。此外,AVCaptureMovieFileOutput
的实例允许客户端配置特定于QuickTime文件格式的选项,包括允许客户端向每个文件写入元数据集合,为每个track(轨道)
指定媒体编码选项(Mac OS X),以及指定写入电影片段的时间间隔。
相关链接
AV Foundation之视频捕捉
iOS-AVFoundation自定义相机详解