一:簡(jiǎn)介
截圖1.png
AVCaptureDevice錄制視頻過程.png
AVCaptureStillImageOutput 輸出圖片
AVCapturePhotoOutput 照片輸出流
AVCaptureSession 把輸入輸出結(jié)合在一起烫饼,并開始啟動(dòng)捕獲設(shè)備(攝像頭)
媒體(音稚晚、視頻)捕獲會(huì)話,負(fù)責(zé)把捕獲的音視頻數(shù)據(jù)輸出到輸出設(shè)備中况增。一個(gè)AVCaptureSession可以有多個(gè)輸入輸出
AVCaptureDevice 捕獲設(shè)備短绸,通常是前置攝像頭肤寝,后置攝像頭像街,麥克風(fēng)(音頻輸入)
AVCaptureDeviceInput 代表輸入設(shè)備黎棠,他使用AVCaptureDevice 來初始化
AVCaptureOutput 輸出數(shù)據(jù)管理對(duì)象,用于接收各類輸出數(shù)據(jù)镰绎,通常使用對(duì)應(yīng)的子類AVCaptureAudioDataOutput脓斩、
AVCaptureStillImageOutput、AVCaptureVideoDataOutput畴栖、AVCaptureFileOutput随静,該對(duì)象將會(huì)被添加到
AVCaptureSession中管理。
注意:
1吗讶,前面幾個(gè)對(duì)象的輸出數(shù)據(jù)都是NSData類型燎猛,而AVCaptureFileOutput
代表數(shù)據(jù)以文件形式輸出叼丑,類似的,AVCcaptureFileOutput
也不會(huì)直接創(chuàng)建使用扛门,通常會(huì)使用其子類:AVCaptureAudioFileOutput
、AVCaptureMovieFileOutput
纵寝。
2论寨,當(dāng)把一個(gè)輸入或者輸出添加到AVCaptureSession
之后AVCaptureSession
就會(huì)在所有相符的輸入、輸出設(shè)備之間建立連接(AVCaptionConnection
):
AVCaptureVideoPreviewLayer:相機(jī)拍攝預(yù)覽圖層爽茴,是CALayer的子類葬凳,使用該對(duì)象可以實(shí)時(shí)查看拍照或視頻錄制效果,創(chuàng)建該對(duì)象需要指定對(duì)應(yīng)的AVCaptureSession對(duì)象室奏。
AVCaptureDevicePositionBack 后置攝像頭
AVCaptureDevicePositionFront 前置攝像頭
AVCaptureMetadataOutput 當(dāng)啟動(dòng)攝像頭開始捕獲輸入
AVCaptureDevicePosition 攝像頭位置
閃光燈和白平衡可以在生成相機(jī)時(shí)候設(shè)置
曝光要根據(jù)對(duì)焦點(diǎn)的光線狀況而決定,所以和對(duì)焦一塊寫
point為點(diǎn)擊的位置
AVCaptureFlashMode 閃光燈
AVCaptureFocusMode 對(duì)焦
AVCaptureExposureMode 曝光
AVCaptureWhiteBalanceMode 白平衡
一定要先設(shè)置位置火焰,再設(shè)置對(duì)焦模式
拿到的圖像的大小可以自行設(shè)定
AVCaptureSessionPreset320x240
AVCaptureSessionPreset352x288
AVCaptureSessionPreset640x480
AVCaptureSessionPreset960x540
AVCaptureSessionPreset1280x720
AVCaptureSessionPreset1920x1080
AVCaptureSessionPreset3840x2160
二:拍照和錄制視頻的一般步驟
使用AVFoundation
拍照和錄制視頻的一般步驟如下:
1.創(chuàng)建
AVCaptureSession
對(duì)象。
2.使用AVCaptureDevice
的靜態(tài)方法獲得需要使用的設(shè)備胧沫,例如拍照和錄像
3.就需要獲得攝像頭設(shè)備昌简,錄音就要獲得麥克風(fēng)設(shè)備。
4.利用輸入設(shè)備AVCaptureDevice
初始化AVCaptureDeviceInput
對(duì)象绒怨。
5.初始化輸出數(shù)據(jù)管理對(duì)象纯赎,如果要拍照就初始化
6.AVCaptureStillImageOutput
對(duì)象;如果拍攝視頻就初始化AVCaptureMovieFileOutput
對(duì)象南蹂。
7.將數(shù)據(jù)輸入對(duì)象AVCaptureDeviceInpu
t犬金、數(shù)據(jù)輸出對(duì)象AVCaptureOutput
添加到媒體會(huì)話管理對(duì)象AVCaptureSession
中。
8.創(chuàng)建視頻預(yù)覽圖層AVCaptureVideoPreviewLayer
并指定媒體會(huì)話六剥,添加圖層到顯示容器中晚顷,調(diào)用AVCaptureSession
的startRuning
方法開始捕獲。
9.將捕獲的音頻或視頻數(shù)據(jù)輸出到指定文件疗疟。
*/
iOS相機(jī)的基本開發(fā)
目錄
一扔嵌、設(shè)置會(huì)話
1、初始化會(huì)話樞紐
2改衩、創(chuàng)建視頻輸入
3澎羞、添加到會(huì)話中
4、創(chuàng)建音頻輸入
5锅锨、添加音頻輸入
6叽赊、設(shè)置靜態(tài)圖片輸出
7、設(shè)置錄像輸出
二必搞、開啟會(huì)話
1必指、開啟會(huì)話
2、攝像頭處理
2.1 獲取指定位置攝像頭
2.2 獲取當(dāng)前活躍的攝像頭
2.3 獲取當(dāng)前可用攝像頭數(shù)和未使用的攝像頭
2.4 切換攝像頭
3恕洲、對(duì)焦處理
3.1 是否支持對(duì)焦
3.2 設(shè)置對(duì)焦點(diǎn)
4塔橡、曝光處理
4.1 是否支持曝光
4.2 設(shè)置曝光
4.3 重置對(duì)焦曝光
5梅割、閃光燈 & 手電筒
5.1 判斷是否有閃光燈
5.2 閃光燈模式
5.3 設(shè)置閃光燈模式
5.4 是否支持手電筒
5.5 手電筒模式
5.6 設(shè)置手電筒模式
三、拍攝
1葛家、拍攝靜態(tài)圖片
2户辞、寫入媒體庫
3、捕捉視頻
3.1 判斷是否錄制中
3.2 開始錄制
3.3 結(jié)束錄制
3.4 錄制結(jié)束回調(diào)
3.5 將視頻寫入媒體庫
3.6 制作視頻縮略圖
ps: 這里可以看到錄制的視頻包癞谒。
image.png
四底燎、具體demo
關(guān)聯(lián)的攝像頭頁面
image.png
對(duì)應(yīng)控制器的代碼:
#import "HBViewController2.h"
#import <AVFoundation/AVFoundation.h>
#define kScreenBounds [UIScreen mainScreen].bounds
#define kScreenWidth kScreenBounds.size.width*1.0
#define kScreenHeight kScreenBounds.size.height*1.0
@interface HBViewController2 ()<AVCaptureMetadataOutputObjectsDelegate,UIAlertViewDelegate>
//捕獲設(shè)備,通常是前置攝像頭弹砚,后置攝像頭双仍,麥克風(fēng)(音頻輸入)
@property(nonatomic)AVCaptureDevice *device;
//AVCaptureDeviceInput 代表輸入設(shè)備,他使用AVCaptureDevice 來初始化
@property(nonatomic)AVCaptureDeviceInput *input;
//當(dāng)啟動(dòng)攝像頭開始捕獲輸入
@property(nonatomic)AVCaptureMetadataOutput *output;
@property (nonatomic)AVCaptureStillImageOutput *ImageOutPut;
//session:由他把輸入輸出結(jié)合在一起桌吃,并開始啟動(dòng)捕獲設(shè)備(攝像頭)
@property(nonatomic)AVCaptureSession *session;
//圖像預(yù)覽層朱沃,實(shí)時(shí)顯示捕獲的圖像
@property(nonatomic)AVCaptureVideoPreviewLayer *previewLayer;
@property (weak, nonatomic) IBOutlet UIButton *PhotoButton;
@property (weak, nonatomic) IBOutlet UIButton *flashButton;
@property (weak, nonatomic) IBOutlet UIButton *cancleButton;
@property (weak, nonatomic) IBOutlet UIButton *changeButton;
@property (nonatomic)UIImageView *imageView;
@property (nonatomic)UIView *greenView;
@property (nonatomic)BOOL isflashOn;
@property (nonatomic)UIImage *image;
@property (nonatomic)BOOL canCa;
@property (weak, nonatomic) IBOutlet UIView *xyView;
@end
@implementation HBViewController2
- (void)viewDidLoad {
[super viewDidLoad];
_canCa = [self canUserCamear];
if (_canCa) {
[self customCamera];
}
}
- (void)customCamera{
#pragma mark -- 相機(jī) 牌照相關(guān)
//AVCaptureDevice捕獲設(shè)備,通常是前置攝像頭茅诱,后置攝像頭逗物,麥克風(fēng)(音頻輸入) 使用AVMediaTypeVideo 指明self.device代表視頻,默認(rèn)使用后置攝像頭進(jìn)行初始化
self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//AVCaptureDeviceInput 代表輸入設(shè)備瑟俭,他使用AVCaptureDevice 來初始化
self.input = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:nil];
//當(dāng)啟動(dòng)攝像頭開始捕獲輸入
self.output = [[AVCaptureMetadataOutput alloc]init];
self.ImageOutPut = [[AVCaptureStillImageOutput alloc] init];
//session:由他把輸入輸出結(jié)合在一起敬察,并開始啟動(dòng)捕獲設(shè)備(攝像頭) 生成會(huì)話,用來結(jié)合輸入輸出
self.session = [[AVCaptureSession alloc]init];
if ([self.session canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
self.session.sessionPreset = AVCaptureSessionPreset1280x720;
}
if ([self.session canAddInput:self.input]) {
[self.session addInput:self.input];
}
if ([self.session canAddOutput:self.ImageOutPut]) {
[self.session addOutput:self.ImageOutPut];
}
//使用self.session尔当,初始化預(yù)覽層莲祸,self.session負(fù)責(zé)驅(qū)動(dòng)input進(jìn)行信息的采集,layer負(fù)責(zé)把圖像渲染顯示 圖像預(yù)覽層椭迎,實(shí)時(shí)顯示捕獲的圖像
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.session];
self.previewLayer.frame = CGRectMake(0, 0, kScreenWidth-20*2, 240);
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
//layer 加到這里 就是把攝像頭捕捉的視頻畫面 給到這個(gè)view去顯示
[self.xyView.layer addSublayer:self.previewLayer];
//開始啟動(dòng)
[self.session startRunning];
if ([_device lockForConfiguration:nil]) {
if ([_device isFlashModeSupported:AVCaptureFlashModeAuto]) {
[_device setFlashMode:AVCaptureFlashModeAuto];
}
//自動(dòng)白平衡
if ([_device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeAutoWhiteBalance]) {
[_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeAutoWhiteBalance];
}
[_device unlockForConfiguration];
}
#pragma mark -- 四個(gè)按鈕 和 綠色框
self.view.backgroundColor = [UIColor systemGroupedBackgroundColor];
self.greenView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 60, 60)];
self.greenView .layer.borderWidth = 1.0;
self.greenView .layer.borderColor =[UIColor greenColor].CGColor;
self.greenView .backgroundColor = [UIColor clearColor];
[self.xyView addSubview: self.greenView ];
self.greenView .hidden = YES;
[self.view bringSubviewToFront: self.xyView];
[self.view bringSubviewToFront:self.cancleButton];
[self.view bringSubviewToFront:self.PhotoButton];
[self.view bringSubviewToFront:self.changeButton];
[self.view bringSubviewToFront:self.flashButton];
[self.view bringSubviewToFront: self.greenView ];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapGesture:)];
[self.xyView addGestureRecognizer:tapGesture];
}
#pragma mark - 按鈕點(diǎn)擊事件
- (IBAction)click:(UIButton *)sender {
switch (sender.tag) {
case 0:
{
//拍照
AVCaptureConnection * videoConnection = [self.ImageOutPut connectionWithMediaType:AVMediaTypeVideo];
if (!videoConnection) {
NSLog(@"take photo failed!");
return;
}
[self.ImageOutPut captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if (imageDataSampleBuffer == NULL) {
return;
}
NSData * imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
self.image = [UIImage imageWithData:imageData];
[self.session stopRunning];
[self saveImageToPhotoAlbum:self.image];
self.imageView = [[UIImageView alloc]initWithFrame:self.previewLayer.frame];
[self.xyView insertSubview:_imageView belowSubview:_PhotoButton];
self.imageView.layer.masksToBounds = YES;
self.imageView.image = _image;
NSLog(@"image size = %@",NSStringFromCGSize(self.image.size));
}];
}
break;
case 1:
{
//取消
[self.imageView removeFromSuperview];
[self.session stopRunning];
[self.navigationController popViewControllerAnimated:YES];
}
break;
case 2:
{
//切換
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:_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);
}
}
}
break;
case 3:
{
//閃光燈
if ([_device lockForConfiguration:nil]) {
if (_isflashOn) {
if ([_device isFlashModeSupported:AVCaptureFlashModeOff]) {
[_device setFlashMode:AVCaptureFlashModeOff];
_isflashOn = NO;
[_flashButton setTitle:@"閃光燈關(guān)" forState:UIControlStateNormal];
}
}else{
if ([_device isFlashModeSupported:AVCaptureFlashModeOn]) {
[_device setFlashMode:AVCaptureFlashModeOn];
_isflashOn = YES;
[_flashButton setTitle:@"閃光燈開" forState:UIControlStateNormal];
}
}
[_device unlockForConfiguration];
}
}
break;
default:
break;
}
}
- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position{
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for ( AVCaptureDevice *device in devices )
if ( device.position == position ) return device;
return nil;
}
#pragma mark -- 綠框 觸碰
- (void)tapGesture:(UITapGestureRecognizer*)gesture{
CGPoint point = [gesture locationInView:gesture.view];
[self focusAtPoint:point];
}
- (void)focusAtPoint:(CGPoint)point{
CGSize size = self.view.bounds.size;
CGPoint focusPoint = CGPointMake( point.y /size.height ,1-point.x/size.width );
NSError *error;
if ([self.device lockForConfiguration:&error]) {
if ([self.device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
[self.device setFocusPointOfInterest:focusPoint];
[self.device setFocusMode:AVCaptureFocusModeAutoFocus];
}
if ([self.device isExposureModeSupported:AVCaptureExposureModeAutoExpose ]) {
[self.device setExposurePointOfInterest:focusPoint];
[self.device setExposureMode:AVCaptureExposureModeAutoExpose];
}
[self.device unlockForConfiguration];
_greenView.center = point;
_greenView.hidden = NO;
[UIView animateWithDuration:0.3 animations:^{
_greenView.transform = CGAffineTransformMakeScale(1.25, 1.25);
}completion:^(BOOL finished) {
[UIView animateWithDuration:0.5 animations:^{
_greenView.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
_greenView.hidden = YES;
}];
}];
}
}
#pragma mark - 保存至相冊(cè)
- (void)saveImageToPhotoAlbum:(UIImage*)savedImage{
UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL);
}
// 回調(diào)方法
- (void)image: (UIImage *) image didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo{
NSString *msg = nil ;
if(error != NULL){
msg = @"保存圖片失敗" ;
}else{
msg = @"保存圖片成功" ;
}
UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"保存圖片結(jié)果提示" message:msg preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"點(diǎn)擊了按鈕1锐帜,進(jìn)入按鈕1的事件");
[self.imageView removeFromSuperview];
[self.session startRunning];
[self.navigationController popViewControllerAnimated:YES];
}];
UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"點(diǎn)擊了取消");
[self.imageView removeFromSuperview];
[self.session stopRunning];
[self.navigationController popViewControllerAnimated:YES];
}];
[alertController addAction:action1];
[alertController addAction:action2];
[self presentViewController:alertController animated:YES completion:nil];
}
#pragma mark - 檢查相機(jī)權(quán)限
- (BOOL)canUserCamear{
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (authStatus == AVAuthorizationStatusDenied) {
UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"請(qǐng)打開相機(jī)權(quán)限" message:@"設(shè)置-隱私-相機(jī)" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSURL * url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url options:nil completionHandler:^(BOOL success) {
}];
}
}];
UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"點(diǎn)擊了取消");
}];
[alertController addAction:action1];
[alertController addAction:action2];
[self presentViewController:alertController animated:YES completion:nil];
return NO;
}
else{
return YES;
}
return YES;
}
@end