最近在做一个趣味相机的小项目,分享一下自己在学习的过程中的收获。
首先,我们需要知道的是,自定义相机也是和系统相机一样,需要调用手机的硬件,所以相机的工作流程大致为:
1、获取设备
2、输入设备
3、输出数据
4、在预览层展示数据
所以我们需要先导入框架
#import<AVFoundation/AVFoundation.h>
然后声明几个必要的对象
@property (nonatomic, strong) AVCaptureDevice *device; //获取设备
@property (nonatomic, strong) AVCaptureDeviceInput* videoInput; //输入设备
@property (nonatomic, strong) AVCaptureSession* session; //AVCaptureSession对象来执行输入设备和输出设备之间的数据传递
@property (nonatomic, strong) AVCaptureStillImageOutput* stillImageOutput; //输出数据
@property (nonatomic, strong) AVCaptureVideoPreviewLayer* previewLayer; //预览层显示图像
@property (nonatomic, strong) UIImageView *previewImageView;//显示拍下的照片
@property (nonatomic, strong) UIImage *previewImage;
初始化
- (void)viewDidLoad{
[super viewDidLoad];
//这里可以就在viewDidload中初始化了
self.device = [self cameraWithPosition:AVCaptureDevicePositionFront];
//更改这个设置的时候必须先锁定设备,修改完后再解锁,否则崩溃
[device lockForConfiguration:nil];
//设置闪光灯为自动
[device unlockForConfiguration];
self.videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:self.device error:nil];//此处也可以做一个错误判断
self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
//输出设置。AVVedioCodecJPEG 输出jpeg格式图片
NSDictionary *outPutSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey, nil];
[self.stillImageOutput setOutputSettings:outPutSettings];
self.session = [[AVCaptureSession alloc] init];
self.session.sessionPreset = AVCaptureSessionPreset640x480;
// 拿到的图像的大小可以自行设定
// AVCaptureSessionPreset320x240
// AVCaptureSessionPreset352x288
// AVCaptureSessionPreset640x480
// AVCaptureSessionPreset960x540
// AVCaptureSessionPreset1280x720
// AVCaptureSessionPreset1920x1080
// AVCaptureSessionPreset3840x2160
// 默认为全屏
//输入输出设备结合
if ([self.session canAddInput:self.videoInput]) {
[self.session addInput:self.videoInput];
}
if ([self.session canAddOutput:self.stillImageOutput]) {
[self.session addOutput:self.stillImageOutput];
}
//初始化预览层
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
[self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
self.previewLayer.frame = CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
self.view.layer.masksToBounds = YES;
[self.view.layer addSublayer:self.previewLayer];
}
之后在viewWillAppear,viewDidDisappear方法里开启和关闭session
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:YES];
if (self.session) {
[self.session startRunning];
}
}
- (void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:YES];
if (self.session) {
[self.session stopRunning];
}
}
至此初始化工作基本完成,已经可以在程序中显示出设备捕捉到的画面,接下来需要一个获取设备方向的方法,再配置图片输出的时候需要使用
- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position{
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for ( AVCaptureDevice *device in devices )
if ( device.position == position ){
return device;
}
return nil;
}
拍照按钮方法
- (void)action_TakeShot:(UIButton *)sender{
NSLog(@"------------- 拍照");
AVCaptureConnection *connection = [self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
if (!connection) {
NSLog(@"拍照失败!");
return;
}
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if (imageDataSampleBuffer == nil) {
return ;
}
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
self.previewImage = [UIImage imageWithData:imageData];
[self.session stopRunning];
[self.view addSubview:self.previewImageView];
}];
}
到此相机的基本功能就已实现,再此基础上再添加保存照片、闪光灯、前后置摄像头切换、触屏对焦等功能
闪光灯
- (void)flashButtonClick:(UIButton *)sender {
NSLog(@"------------- 闪光灯");
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//修改前必须先锁定
[device lockForConfiguration:nil];
//必须判定是否有闪光灯,否则如果没有闪光灯会崩溃
if ([device hasFlash]) {
if (device.flashMode == AVCaptureFlashModeOff) {
device.flashMode = AVCaptureFlashModeOn;
[sender setTitle:@"flashOn"];
} else if (device.flashMode == AVCaptureFlashModeOn) {
device.flashMode = AVCaptureFlashModeAuto;
[sender setTitle:@"flashAuto"];
} else if (device.flashMode == AVCaptureFlashModeAuto) {
device.flashMode = AVCaptureFlashModeOff;
[sender setTitle:@"flashOff"];
}
} else {
NSLog(@"设备不支持闪光灯");
}
[device unlockForConfiguration];
}
device; //获取设备
videoInput; //输入设备
session; //AVCaptureSession对象来执行输入设备和输出设备之间的数据传递
stillImageOutput; //输出数据
previewLayer; //预览层显示图像
previewImageView;//显示拍下的照片
previewImage;
切换前后置摄像头
- (void)changeCamera:(UIButton *)sender{
NSUInteger cameraCount = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count];
if (cameraCount > 1) {
NSError *error;
//给摄像头的切换添加翻转动画
CATransition *animation = [CATransition animation];
animation.duration = .5f;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.type = @"oglFlip";
AVCaptureDevice *newCamera = nil;
AVCaptureDeviceInput *newInput = nil;
//拿到另外一个摄像头位置
AVCaptureDevicePosition position = [[_input device] position];
if (position == AVCaptureDevicePositionFront){
newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
animation.subtype = kCATransitionFromLeft;//动画翻转方向
}
else {
newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
animation.subtype = kCATransitionFromRight;//动画翻转方向
}
//生成新的输入
newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];
[self.previewLayer addAnimation:animation forKey:nil];
if (newInput != nil) {
[self.session beginConfiguration];
[self.session removeInput:self.input];
if ([self.session canAddInput:newInput]) {
[self.session addInput:newInput];
self.input = newInput;
} else {
[self.session addInput:self.input];
}
[self.session commitConfiguration];
} else if (error) {
NSLog(@"toggle carema failed, error = %@", error);
}
}
}
备注:
参考文档:
[https://github.com/nanshanyi/photographDemo ]
[https://github.com/RockyAo/RACustomCamera ]