AVFounction学习笔记之--媒体捕捉

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wj610671226/article/details/83097883

AVFounction学习笔记之–媒体捕捉

  • 基础知识

首先先介绍AVFounction捕捉相关的类。如下图所示
1、AVCaptureSession 捕捉会话核心类
2、AVCaptureDevice 捕捉设备,最常用的是音频和视频
3、AVCaptureVideoPreviewLayer 视频捕捉的预览图层
4、AVCaptureConnection 捕捉连接的类
5、AVCaptureDeviceInput 捕捉连接的类
6、AVCaptureOutput捕捉的输出。包括:
AVCaptureStillImageOutput(静态图片)
AVCaptureMovieFileOutput(视频)
AVCaptureAudioFileOutput(音频)
AVCapturePhotoOutput(照片)
AVCaptureMetadataOutput(元数据)
AVCaptureAudioDataOutput(音频流)
AVCaptureVideoDataOutput(视频流)

在这里插入图片描述

  • 最简单的捕捉示例

核心步骤:
1、创建会话层
2、添加输入设备
3、添加输出设备
4、创建预览图层
5、启动会话

// 1、创建会话层
let session = AVCaptureSession()
let cameraDevice = AVCaptureDevice.default(for: AVMediaType.video)
do {
    // 2、添加输入设备
    let cameraDeviceInput = try AVCaptureDeviceInput(device: cameraDevice!)
    if session.canAddInput(cameraDeviceInput) {
        session.addInput(cameraDeviceInput)
    }
    // 3、添加输出设备
    let imageOutput = AVCapturePhotoOutput()
    if session.canAddOutput(imageOutput) {
        session.addOutput(imageOutput)
    }
    // 4、创建预览图层
    let layer = AVCaptureVideoPreviewLayer(session: session)
    layer.frame = view.bounds
    view.layer.addSublayer(layer)
    
    // 5、启动会话
    session.startRunning()
} catch let error {
    print("创建捕捉视频设备失败 error = \(error)")
}

Demo例子讲解常用功能

下面利用一些代码片段来讲解一些常用API功能的使用。

  • 构建基础会话
- (BOOL)setupSession:(NSError **)error {
    // 初始化session
    self.captureSession = [[AVCaptureSession alloc] init];
    self.captureSession.sessionPreset = AVCaptureSessionPresetHigh;
    
    // 设置视频设备
    AVCaptureDevice * videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    AVCaptureDeviceInput * videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:error];
    if (videoInput) {
        if ([self.captureSession canAddInput:videoInput]) {
            [self.captureSession addInput:videoInput];
            self.activeVideoInput = videoInput;
        }
    } else {
        return NO;
    }
    
    // 设置音频设备
    AVCaptureDevice * audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
    AVCaptureDeviceInput * audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:error];
    if (audioInput) {
        if ([self.captureSession canAddInput:audioInput]) {
            [self.captureSession addInput:audioInput];
        }
    } else {
        return NO;
    }
    
    // 设置image output
    self.imageOutput = [[AVCaptureStillImageOutput alloc] init];
    self.imageOutput.outputSettings = @{AVVideoCodecKey: AVVideoCodecJPEG};
    if ([self.captureSession canAddOutput:self.imageOutput]) {
        [self.captureSession addOutput:self.imageOutput];
    }
    
    // 设置movie output
    self.movieOutput = [[AVCaptureMovieFileOutput alloc] init];
    if ([self.captureSession canAddOutput:self.movieOutput]) {
        [self.captureSession addOutput:self.movieOutput];
    }
    self.videoQueue = dispatch_queue_create("com.videoQueue", NULL);
    return YES;
}

  • 摄像头操作
- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position {
    // 获取有效的摄像头参数
    NSArray * devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    for (AVCaptureDevice * device in devices) {
        if (device.position == position) {
            return device;
        }
    }
    return nil;
}

- (AVCaptureDevice *)activeCamera {
    // 返回激活的捕捉设备输入的属性
    return self.activeVideoInput.device;
}

- (AVCaptureDevice *)inactiveCamera {
    AVCaptureDevice * device = nil;
    // 获取当前摄像头的反向摄像头
    if (self.cameraCount > 1) {
        if ([self activeCamera].position == AVCaptureDevicePositionBack) {
            device = [self cameraWithPosition:AVCaptureDevicePositionFront];
        } else {
            device = [self cameraWithPosition:AVCaptureDevicePositionBack];
        }
    }
    return device;
}

- (BOOL)canSwitchCameras {
    // 判断是否有2个摄像头以上
    return self.cameraCount > 1;
}

- (NSUInteger)cameraCount {
    // 获取摄像头个数
    return [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count];
}
  • 切换摄像头
- (BOOL)switchCameras {
    // 判断是否可以切换摄像头
    if (![self canSwitchCameras]) {
        return NO;
    }
    
    NSError * error;
    // 获取未激活的摄像头,并创建一个新的输入
    AVCaptureDevice * videoDevoce = [self inactiveCamera];
    AVCaptureDeviceInput * videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevoce error:&error];
    
    if (videoInput) {
        // 移除旧的输入,添加新的输入
        [self.captureSession beginConfiguration];
        [self.captureSession removeInput:self.activeVideoInput];
        if ([self.captureSession canAddInput:videoInput]) {
            [self.captureSession addInput:videoInput];
            self.activeVideoInput = videoInput;
        } else {
            [self.captureSession addInput:self.activeVideoInput];
        }
        [self.captureSession commitConfiguration];
    } else {
        NSLog(@"error = %@", error.localizedDescription);
        return NO;
    }
    return YES;
}
  • 摄像头对焦的实现
- (BOOL)cameraSupportsTapToFocus {
    // 判断设备是否支持对焦的功能
    return [[self activeCamera] isFocusPointOfInterestSupported];
}

- (void)focusAtPoint:(CGPoint)point {
    AVCaptureDevice * device = [self activeCamera];
    // 判断是否支持对焦并自动对焦
    if (device.isFocusPointOfInterestSupported && [device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
        NSError * error;
        if ([device lockForConfiguration:&error]) {
            // 自动对焦点
            device.focusPointOfInterest = point;
            device.focusMode = AVCaptureFocusModeAutoFocus;
            [device unlockForConfiguration];
        } else {
            NSLog(@"error = %@", error.localizedDescription);
        }
    }
}
  • 摄像头曝光
// 点击曝光
- (BOOL)cameraSupportsTapToExpose {
    // 判断激活设备是否支持曝光
    return [[self activeCamera] isExposurePointOfInterestSupported];
}

static const NSString *THCameraAdjustingExposureContext;

- (void)exposeAtPoint:(CGPoint)point {
    AVCaptureDevice * device = [self activeCamera];
    AVCaptureExposureMode exposureMode = AVCaptureExposureModeContinuousAutoExposure;
    if (device.isExposurePointOfInterestSupported && [device isExposureModeSupported:exposureMode]) {
        NSError * error;
        // 锁定设备配置,设置exposurePointOfInterest、exposureMode的值
        if ([device lockForConfiguration:&error]) {
            device.exposurePointOfInterest = point;
            device.exposureMode = exposureMode;
            // 判断设备是否支持自动曝光模式
            if ([device isExposureModeSupported:AVCaptureExposureModeLocked]) {
                [device addObserver:self forKeyPath:@"adjustingExposure" options:NSKeyValueObservingOptionNew context:&THCameraAdjustingExposureContext];
            }
            
            [device unlockForConfiguration];
        } else {
            NSLog(@"error = %@", error.localizedDescription);
        }
    }
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    if (context == &THCameraAdjustingExposureContext) {
        AVCaptureDevice * device = (AVCaptureDevice *)object;
        //  判断设备是否不再调整曝光等级
        if (!device.isAdjustingExposure && [device isExposureModeSupported:AVCaptureExposureModeLocked]) {
            // 移除通知
            [object removeObserver:self forKeyPath:@"adjustingExposure" context:&THCameraAdjustingExposureContext];
            
            dispatch_async(dispatch_get_main_queue(), ^{
                NSError * error;
                if ([device lockForConfiguration:&error]) {
                    device.exposureMode = AVCaptureExposureModeLocked;
                    [device unlockForConfiguration];
                } else {
                    NSLog(@"error = %@", error.localizedDescription);
                }
            });
        }
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

// 重新设置对焦和曝光
- (void)resetFocusAndExposureModes {
    AVCaptureDevice * device = [self activeCamera];
    AVCaptureFocusMode focusMode = AVCaptureFocusModeContinuousAutoFocus;
    BOOL canResetFocus = [device isFocusPointOfInterestSupported] && [device isFocusModeSupported:focusMode];
    AVCaptureExposureMode exposureMode = AVCaptureExposureModeContinuousAutoExposure;
    BOOL canResetExposure = [device isExposurePointOfInterestSupported] && [device isExposureModeSupported:exposureMode];
    
    CGPoint centerPoint = CGPointMake(0.5, 0.5);
    NSError * error;
    if ([device lockForConfiguration:&error]) {
        if (canResetFocus) {
            device.focusMode = focusMode;
            device.focusPointOfInterest = centerPoint;
        }
        
        if (canResetExposure) {
            device.exposureMode = exposureMode;
            device.exposurePointOfInterest = centerPoint;
        }
        
        [device unlockForConfiguration];
    } else {
        NSLog(@"error = %@", error.localizedDescription);
    }
}
  • 闪光灯和手电筒模式
// 调整闪光灯和手电筒模式
- (BOOL)cameraHasFlash {
    return [[self activeCamera] hasFlash];
}

- (AVCaptureFlashMode)flashMode {
    return [[self activeCamera] flashMode];
}

- (void)setFlashMode:(AVCaptureFlashMode)flashMode {
    AVCaptureDevice * device = [self activeCamera];
    if ([device isFlashModeSupported:flashMode]) {
        NSError * error;
        if ([device lockForConfiguration:&error]) {
            device.flashMode = flashMode;
            [device unlockForConfiguration];
        } else {
            NSLog(@"error = %@", error.localizedDescription);
        }
    }
}

- (BOOL)cameraHasTorch {
    return [[self activeCamera] hasTorch];
}

- (AVCaptureTorchMode)torchMode {
    return [[self activeCamera] torchMode];
}

- (void)setTorchMode:(AVCaptureTorchMode)torchMode {
    AVCaptureDevice * device = [self activeCamera];
    if ([device isTorchModeSupported:torchMode]) {
        NSError * error;
        if ([device lockForConfiguration:&error]) {
            device.torchMode = torchMode;
            [device unlockForConfiguration];
        } else {
            NSLog(@"error = %@", error.localizedDescription);
        }
    }
}

  • 拍照并写入照片库
- (void)captureStillImage {
    AVCaptureConnection * connection = [self.imageOutput connectionWithMediaType:AVMediaTypeVideo];
    
    // 调整方向
    if (connection.isVideoOrientationSupported) {
        connection.videoOrientation = [self currentVideoOrientation];
    }
    
    [self.imageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:^(CMSampleBufferRef  _Nullable imageDataSampleBuffer, NSError * _Nullable error) {
        if (imageDataSampleBuffer != NULL) {
            // 获取图片信息
            NSData * imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
            UIImage * image = [[UIImage alloc] initWithData:imageData];
            NSLog(@"image = %@", image);
            [self writeImageToAssetsLibrary:image];
        } else {
            NSLog(@"捕捉静态图片 sampleBuffer error = %@", error.localizedDescription);
        }
    }];
}

- (AVCaptureVideoOrientation)currentVideoOrientation {
    AVCaptureVideoOrientation orientation;
    // 根据设备方向切换AVCaptureVideoOrientation方向,注意左右是相反的
    switch ([UIDevice currentDevice].orientation) {
        case UIDeviceOrientationPortrait:
            orientation = AVCaptureVideoOrientationPortrait;
            break;
        case UIDeviceOrientationLandscapeRight:
            orientation = AVCaptureVideoOrientationLandscapeLeft;
            break;
        case UIDeviceOrientationPortraitUpsideDown:
            orientation = AVCaptureVideoOrientationPortraitUpsideDown;
            break;
        default:
            orientation = AVCaptureVideoOrientationLandscapeRight;
            break;
    }
    return orientation;
}


// 将图片写入照片库
- (void)writeImageToAssetsLibrary:(UIImage *)image {
    // Listing 6.13
    ALAssetsLibrary * library = [[ALAssetsLibrary alloc] init];
    [library writeImageToSavedPhotosAlbum:image.CGImage orientation:(NSInteger)image.imageOrientation completionBlock:^(NSURL *assetURL, NSError *error) {
        if (!error) {
            NSLog(@"写入照片成功"),
        } else {
            NSLog(@"writeImageToAssetsLibrary error = %@", error.localizedDescription);
        }
    }];
}
  • 录制视频并写入照片库
- (BOOL)isRecording {
    // 是否录制中
    return self.movieOutput.isRecording;
}

- (void)startRecording {
    if (![self isRecording]) {
        // 获取当前连接信息
        AVCaptureConnection * videoConnection = [self.movieOutput connectionWithMediaType:AVMediaTypeVideo];
        
        // 方向处理
        if ([videoConnection isVideoOrientationSupported]) {
            videoConnection.videoOrientation = self.currentVideoOrientation;
        }
        
        // 设置录制视频稳定
        if ([videoConnection isVideoOrientationSupported]) {
            videoConnection.enablesVideoStabilizationWhenAvailable = YES;
        }
        
        // 降低对焦速度,平滑对焦
        AVCaptureDevice * device = [self activeCamera];
        if (device.isSmoothAutoFocusSupported) {
            NSError * error;
            if ([device lockForConfiguration:&error]) {
                device.smoothAutoFocusEnabled = YES;
                [device unlockForConfiguration];
            } else {
                NSLog(@"error = %@", error.localizedDescription);
            }
        }
        
        // 获取视频存储地址
        self.outputURL = [self uniqueURL];
        // 设置代理开始录制
        [self.movieOutput startRecordingToOutputFileURL:self.outputURL recordingDelegate:self];
    }
}

- (CMTime)recordedDuration {
    return self.movieOutput.recordedDuration;
}

- (NSURL *)uniqueURL {
    // 生成一个存放视频的地址
    NSFileManager * fileManager = [NSFileManager defaultManager];
    NSString * dirPath = [fileManager temporaryDirectoryWithTemplateString:@"kamera.XXXXXX"];
    if (dirPath) {
        NSString * filePath = [dirPath stringByAppendingPathComponent:@"kamera_movie.mov"];
        return [NSURL fileURLWithPath:filePath];
    }
    return nil;
}

- (void)stopRecording {
    if ([self isRecording]) {
        [self.movieOutput stopRecording];
    }
}

#pragma mark - AVCaptureFileOutputRecordingDelegate
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput
didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
      fromConnections:(NSArray *)connections
                error:(NSError *)error {
    
    if (error) {
        [self.delegate mediaCaptureFailedWithError:error];
    } else {
        // 写入视频
        [self writeVideoToAssetsLibrary:[self.outputURL copy]];
    }
    self.outputURL = nil;
}

- (void)writeVideoToAssetsLibrary:(NSURL *)videoURL {
    ALAssetsLibrary * library = [[ALAssetsLibrary alloc] init];
    // 检查视频是否可以写入
    if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:videoURL]) {
        [library writeVideoAtPathToSavedPhotosAlbum:videoURL completionBlock:^(NSURL *assetURL, NSError *error) {
            if (error) {
                NSLog(@"error = %@", error.localizedDescription);
            } else {
                [self generateThumbnailForVideoAtURL:videoURL];
            }
        }];
    }
}

// 获取视频第一个时间点的缩略图
- (void)generateThumbnailForVideoAtURL:(NSURL *)videoURL {
    dispatch_async(self.videoQueue, ^{
        AVAsset * asset = [AVAsset assetWithURL:videoURL];
        AVAssetImageGenerator * imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
        imageGenerator.maximumSize = CGSizeMake(100, 0);
        // 捕捉获取缩略图方向
        imageGenerator.appliesPreferredTrackTransform = YES;
        
        CGImageRef imageRef = [imageGenerator copyCGImageAtTime:kCMTimeZero actualTime:NULL error:nil];
        UIImage *image = [UIImage imageWithCGImage:imageRef];
        CGImageRelease(imageRef);
        
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"回到主线层,其他操作");
        });
    });
}

猜你喜欢

转载自blog.csdn.net/wj610671226/article/details/83097883