五、捕捉媒体

先上一个媒体捕捉类关系图。

五、捕捉媒体_第1张图片
媒体捕捉类.png

捕捉会话
AVCaptureSession:AV Foundation捕捉栈的核心类是 AVCaptureSession。会话用于连接输入和输出,管理从输入端得到的数据流,输出到输出设备。

捕捉设备
AVCaptureDevice: 为摄像头、麦克风等物理设备定义接口。最常用的一个方法就是

+ (nullable AVCaptureDevice *)defaultDeviceWithMediaType:(AVMediaType)mediaType;

根据给定的媒体类型返回一个系统指定的默认设备。如参数设置AVMediaTypeVideo会返回后置摄像头,默认为后置摄像头。

捕捉设备输入
在使用AVCaptureDevice前,需要将其添加到session的输入。

    AVCaptureSession *session = [[AVCaptureSession alloc] init];
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];  
    if ([session canAddInput:input]) {
        [session addInput:input];
    }

输出
AV Foundation的输出核心类为AVCaptureOutput,这是一个抽象基类。我们使用子类来做输出,如AVCaptureMovieFileOutput视频、AVCaptureAudioDataOutput获取捕捉到的音频数据等。

    _output = [[AVCaptureMovieFileOutput alloc] init];
    if ([_session canAddOutput:_output]) {
        [_session addOutput:_output];
    }

预览
AVCaptureVideoPreviewLayer类似AVPlayerLayer,属于Core Animation中CALayer子类。可以实时预览捕捉到的视频数据。

    _layer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
    _layer.frame = self.view.bounds;
    [self.view.layer addSublayer:_layer];
     //启动捕捉
    [self.session startRunning];

切换摄像头
可以通过device.position获取摄像头位置,返回一个AVCaptureDevicePosition枚举值,表示是前置还是后置摄像头。

typedef NS_ENUM(NSInteger, AVCaptureDevicePosition) {
    AVCaptureDevicePositionUnspecified = 0,
    AVCaptureDevicePositionBack        = 1, //后置摄像头
    AVCaptureDevicePositionFront       = 2,//前置摄像头
} 

在切换摄像头之前需要动态配置session。

    [self.session beginConfiguration];
    //删除原来的输入
    [self.session removeInput:_input];
    //重新构建输入
    _input = [self switchCameraWithPosition:_device.position];
    //添加新的输入
    if ([self.session canAddInput:_input]) {
        [self.session addInput:_input];
    }
    
    //切换完成后提交
    [self.session commitConfiguration];
//切换
- (AVCaptureDeviceInput *)switchCameraWithPosition:(AVCaptureDevicePosition)position {
    
    if (![[self class] canSwitchCamera]) {
        return self.input;
    }
    
    AVCaptureDevice *device = [[self class] cameraWithPosition:position];
    
    NSError *error;
    AVCaptureDeviceInput *input  = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
    _device = device;
    return input;
}
//切换相机
+ (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position {
    if (position == AVCaptureDevicePositionBack) {
        position = AVCaptureDevicePositionFront;
    } else if (position == AVCaptureDevicePositionFront) {
        position = AVCaptureDevicePositionBack;
    } else {
        position = AVCaptureDevicePositionBack;
    }
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    AVCaptureDevice *newDevice;
    for (AVCaptureDevice *device in devices) {
        if (device.position == position) {
            newDevice = device;
            break;
        }
    }
    return newDevice;
}

+ (BOOL)canSwitchCamera {
    return [[AVCaptureDevice  devicesWithMediaType:AVMediaTypeVideo] count] > 1;
}

获取AVCaptureDevice
在iOS 10 以前,我们可以通过[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]获取设备集合,一般就是前置后置摄像头。

localizedName:Back Camera
modelID:com.apple.avfoundation.avcapturedevice.built-in_video:0
uniqueID:com.apple.avfoundation.avcapturedevice.built-in_video:0

localizedName:Front Camera
modelID:com.apple.avfoundation.avcapturedevice.built-in_video:1
uniqueID:com.apple.avfoundation.avcapturedevice.built-in_video:1

在iOS 10 之后这个方法被废弃,需要使用AVCaptureDeviceDiscoverySession获取。

AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
NSArray *devices = discoverySession.devices;

deviceTypesAVCaptureDeviceType类型的集合;
mediaType:媒体类型;
AVCaptureDevicePosition:相机位置

简单介绍一下deviceTypes,如下

//内建麦克风
AVCaptureDeviceTypeBuiltInMicrophone 
//广角相机 满足大部分需求
AVCaptureDeviceTypeBuiltInWideAngleCamera
/**
  长焦相机,根据官方文档,这里的长焦相机与摄影上的长焦并不是一个概念,
  仅仅是用来区分设备上广角和窄角相机的一个类型
  只能通过AVCaptureDeviceDiscoverySession获取
*/
AVCaptureDeviceTypeBuiltInTelephotoCamera
/**
  双摄像头,结合广角和长焦相机,可根据实际场景自动切换相机,
  曝光时间、光圈等参数被锁定,但是在切换相机时这些参数仍可能发生变化
  仅支持双摄,如人像模式等
*/
AVCaptureDeviceTypeBuiltInDualCamera
/**
   仅iPhone X及以上设备支持,红外前置深感相机
*/
AVCaptureDeviceTypeBuiltInTrueDepthCamera

配置摄像头

配置摄像头的焦距、曝光、白平衡等参数。在修改参数前需要判断设备是否支持调整该参数,否则若设备不支持,会引起崩溃。以调整焦距为例:

   if ([self.device isFocusModeSupported: AVCaptureFocusModeAutoFocus]) {
       //锁定设备配置
       NSError *error;
       if ([self.device lockForConfiguration:&error]) {
           //设置自动对焦
           [self.device setFocusMode:AVCaptureFocusModeAutoFocus];
           //解锁
           [self.device unlockForConfiguration];
       }
   }

拍照
在iOS 10 之前静态图片的获取通过AVCaptureStillImageOutput,在iOS 10 之后使用AVCapturePhotoOutput获取。

//self.output:AVCapturePhotoOutput
[self.output capturePhotoWithSettings:[AVCapturePhotoSettings photoSettings] delegate:self];  
//在代理方法中获取图片
//在iOS 11及以上有效
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(NSError *)error {
    if (@available(iOS 11.0, *)) {
        NSData *data = [photo fileDataRepresentation];
        UIImage *image = [UIImage imageWithData:data];
        UIImageWriteToSavedPhotosAlbum(image, self, nil, nil);
    }
}
//在iOS 10 到iOS 11有效
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhotoSampleBuffer:(CMSampleBufferRef)photoSampleBuffer previewPhotoSampleBuffer:(CMSampleBufferRef)previewPhotoSampleBuffer resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings bracketSettings:(AVCaptureBracketedStillImageSettings *)bracketSettings error:(NSError *)error {
    NSData *data = [AVCapturePhotoOutput JPEGPhotoDataRepresentationForJPEGSampleBuffer:photoSampleBuffer previewPhotoSampleBuffer:previewPhotoSampleBuffer];
    UIImage *image = [UIImage imageWithData:data];
    UIImageWriteToSavedPhotosAlbum(image, self, nil, nil);
}

你可能感兴趣的:(五、捕捉媒体)