利用 AVCaptureSession和AVCaptureMovieFileOutput得到一个视频文件或照片
- 流程
1. 创建捕捉会话
2. 设置视频的输入
3. 设置音频的输入
4. 配置输出
5. 添加视频预览层
6. 开始采集数据,这个时候还没有写入数据,用户点击录制后就可以开始写入数据
- 先导入 AVFoundation
#import <AVFoundation/AVFoundation.h>
- 摄像机权限
-(void)authorizationForVideo
{
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
switch (status) {
case AVAuthorizationStatusNotDetermined: // 未授权
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {
NSLog(@"相机授权成功");
} else {
NSLog(@"相机授权失败");
}
}];
break;
case AVAuthorizationStatusRestricted:
NSLog(@"AVAuthorizationStatusRestricted");
break;
case AVAuthorizationStatusDenied:
NSLog(@"AVAuthorizationStatusDenied");
break;
case AVAuthorizationStatusAuthorized:
NSLog(@"AVAuthorizationStatusAuthorized");
break;
default:
NSLog(@"Video Unknow");
break;
}
}
- 检查麦克风权限
-(void)authorizationForAudio
{
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
switch (status) {
case AVAuthorizationStatusNotDetermined:
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
if (granted) {
NSLog(@"麦克风授权成功");
} else {
NSLog(@"麦克风授权失败");
}
}];
break;
case AVAuthorizationStatusRestricted:
NSLog(@"AVAuthorizationStatusRestricted");
break;
case AVAuthorizationStatusDenied:
NSLog(@"AVAuthorizationStatusDenied");
break;
case AVAuthorizationStatusAuthorized:
NSLog(@"AVAuthorizationStatusAuthorized");
break;
default:
NSLog(@"Audio Unknow");
break;
}
}
1. 创建捕捉会话
-(void)configCaptureSession
{
self.session = [[AVCaptureSession alloc] init];
if ([self.session canSetSessionPreset:AVCaptureSessionPresetHigh]) { // 设置分辨率
[self.session setSessionPreset:AVCaptureSessionPresetHigh];
}
}
2. 设置视频的输入
-(void)configVideoInput
{
NSError *error;
// 添加视频捕捉设备
// AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 默认获取后置摄像头
AVCaptureDevice *captureDevice = [self getCameraPosition:AVCaptureDevicePositionBack];
// 将捕捉设备转化为 AVCaptureDeviceInput
// 不能直接使用AVCaptureDevice,必须将AVCaptureDevice转化为AVCaptureDeviceInput
self.videoInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
// 将捕捉设备添加给会话
if (self.videoInput && [self.session canAddInput:self.videoInput]) {
[self.session addInput:self.videoInput];
}
}
- 移除视频输入设备
-(void)removeVideoDeviceInput
{
if (self.videoInput) [self.session removeInput:self.videoInput];
self.videoInput = nil;
}
- 获取摄像头
-(AVCaptureDevice *)getCameraPosition:(AVCaptureDevicePosition)position
{
/*
AVCaptureDeviceTypeBuiltInWideAngleCamera 广角(默认设备,28mm左右焦段)
AVCaptureDeviceTypeBuiltInTelephotoCamera 长焦(默认设备的2x或3x,只能使用AVCaptureDeviceDiscoverySession获取)
AVCaptureDeviceTypeBuiltInUltraWideCamera 超广角(默认设备的0.5x,只能使用AVCaptureDeviceDiscoverySession获取)
AVCaptureDeviceTypeBuiltInDualCamera (一个广角一个长焦(iPhone7P,iPhoneX),可以自动切换摄像头,只能使用AVCaptureDeviceDiscoverySession获取)
AVCaptureDeviceTypeBuiltInDualWideCamera (一个超广一个广角(iPhone12 iPhone13),可以自动切换摄像头,只能使用AVCaptureDeviceDiscoverySession获取)
AVCaptureDeviceTypeBuiltInTripleCamera (超广,广角,长焦三摄像头,iPhone11ProMax iPhone12ProMax iPhone13ProMax,可以自动切换摄像头,只能使用AVCaptureDeviceDiscoverySession获取)
AVCaptureDeviceTypeBuiltInTrueDepthCamera (红外和摄像头, iPhone12ProMax iPhone13ProMax )
*/
NSArray *deviceTypes;
if (position == AVCaptureDevicePositionBack) {
deviceTypes = @[
AVCaptureDeviceTypeBuiltInDualCamera,
AVCaptureDeviceTypeBuiltInDualWideCamera,
AVCaptureDeviceTypeBuiltInTripleCamera
];
} else {
deviceTypes = @[AVCaptureDeviceTypeBuiltInWideAngleCamera];
}
AVCaptureDeviceDiscoverySession *deviceSession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:deviceTypes mediaType:AVMediaTypeVideo position:position];
if (deviceSession.devices.count) return deviceSession.devices.firstObject;
if (position == AVCaptureDevicePositionBack) { // 非多摄手机
deviceTypes = @[AVCaptureDeviceTypeBuiltInWideAngleCamera];
AVCaptureDeviceDiscoverySession *deviceSession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:deviceTypes mediaType:AVMediaTypeVideo position:position];
if (deviceSession.devices.count) return deviceSession.devices.firstObject;
}
return nil;
}
3. 设置音频的输入
-(void)configAudioInput
{
NSError *error;
// 添加音频捕捉设备 ,如果只是拍摄静态图片,可以不用设置
AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
self.audioInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
if (self.audioInput && [self.session canAddInput:self.audioInput]) {
[self.session addInput:self.audioInput];
}
}
- 移除音频输入设备
-(void)removeAudioDeviceInput
{
if (self.audioInput) [self.session removeInput:self.audioInput];
self.audioInput = nil;
}
4. 配置输出
-(void)configMoiveFileOutput
{
self.moiveOutput = [[AVCaptureMovieFileOutput alloc] init];
if ([self.session canAddOutput:self.moiveOutput]) {
[self.session addOutput:self.moiveOutput];
}
}
- 移除视频输出
-(void)removeMoiveOutput
{
if (self.moiveOutput) [self.session removeOutput:self.moiveOutput];
self.moiveOutput = nil;
}
5. 预览层
-(void)configPreviewLayer
{
UIView *mainPreView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 50.0, [UIScreen mainScreen].bounds.size.width, 400.0)];
mainPreView.backgroundColor = [UIColor grayColor];
[self.view addSubview:mainPreView];
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] init];
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.previewLayer.session = self.session;
self.previewLayer.frame = CGRectMake(0.0, 0.0, [UIScreen mainScreen].bounds.size.width, 400.0);
[mainPreView.layer addSublayer:self.previewLayer];
}
6. 开始会话 一定要是异步
-(void)startSessionAction
{
// 检查是否运行状态
if (![self.session isRunning]) {
__weak typeof (self) weakSelf = self;
dispatch_async(self.captureSession, ^{
[weakSelf.session startRunning];
});
}
}
- 结束会话
-(void)stopSessionAction
{
if ([self.session isRunning]) {
__weak typeof (self) weakSelf = self;
dispatch_async(self.captureSession, ^{
[weakSelf.session stopRunning];
});
}
}
开始录制视频配置相关
// 1. 创建捕捉会话
[self configCaptureSession];
// 2. 设置视频的输入
[self configVideoInput];
// 3. 设置音频的输入
[self configAudioInput];
// 4. 输出源设置
[self configMoiveFileOutput];
// 5. 预览层
[self configPreviewLayer];
- 开始录像
-(void)startVideoAction
{
NSString *documentDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *videoDirectoryPath = [documentDirectory stringByAppendingPathComponent:@"/Video"];
NSString *videoTemPath = [videoDirectoryPath stringByAppendingPathComponent:@"temp_video.mov"];
NSURL *url = [NSURL fileURLWithPath:videoTemPath];
[self.moiveOutput startRecordingToOutputFileURL:url recordingDelegate:self];
}
- 结束录像
-(void)stopVideoAction
{
[self.moiveOutput stopRecording];
}
- AVCaptureFileOutputRecordingDelegate
- (void)captureOutput:(AVCaptureFileOutput *)output didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray<AVCaptureConnection *> *)connections error:(nullable NSError *)error
{
NSLog(@"error -%@",error.localizedDescription);
}
结束录像后音频文件就保存在沙盒目录下的Video的temp_video.mov
拍照相关
- 拍照不需要音频因此不需要配置音频输入设备
// 1. 创建捕捉会话
[self configCaptureSession];
// 2. 设置视频的输入
[self configVideoInput];
// 4. 输出源设置
[self configPhotoOutput];
// 5. 预览层
[self configPreviewLayer];
- 静态图片输出配置
-(void)configPhotoOutput
{
self.photoOutput = [[AVCapturePhotoOutput alloc] init];
NSDictionary *settings = @{AVVideoCodecKey:AVVideoCodecTypeJPEG};
AVCapturePhotoSettings *photoSettings = [AVCapturePhotoSettings photoSettingsWithFormat:settings];
[self.photoOutput setPhotoSettingsForSceneMonitoring:photoSettings];
if (self.photoOutput && [self.session canAddOutput:self.photoOutput]) {
[self.session addOutput:self.photoOutput];
}
}
// 移除静态图片输出
-(void)removePhotoOutput
{
if (self.photoOutput) [self.session removeOutput:self.photoOutput];
self.photoOutput = nil;
}
- 点击拍照
-(void)takePhotoAction
{
// 每次拍照一定要重新设置 AVCapturePhotoSettings , 不能复用
NSDictionary *settings = @{AVVideoCodecKey:AVVideoCodecTypeJPEG};
AVCapturePhotoSettings *photoSettings = [AVCapturePhotoSettings photoSettingsWithFormat:settings];
[self.photoOutput capturePhotoWithSettings:photoSettings delegate:self];
}
- AVCapturePhotoCaptureDelegate 保存到相册
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(nullable NSError *)error
{
NSLog(@"didFinishProcessingPhoto");
if (error != nil) {
NSLog(@"拍照出错 - %@",error.localizedDescription);
return;
}
NSData *imageData = photo.fileDataRepresentation;
UIImage *image = [UIImage imageWithData:imageData];
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
[PHAssetChangeRequest creationRequestForAssetFromImage:image];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
if (success == NO) {
NSLog(@"保存图片出错 - %@",error.localizedDescription);
} else {
NSLog(@"成功保存图片");
}
}];
}