媒体捕捉-拍照

news/2024/7/20 20:01:04 标签: 媒体, 数码相机, iOS, 媒体捕捉

引言

在项目开发中,从媒体库中选择图片或使用相机拍摄图片是一个极为普遍的需求。通常,我们使用UIImagePickerController来实现单张图片选择或启动相机拍照。整个拍照过程由UIImagePickerController内部实现,无需我们关心细节,只需实现相应的回调以获取所需的图片。

然而,你或许好奇拍照的底层实现是什么样的,是否能够自己调用手机摄像头完成拍照功能?这正是AVFoundation发挥作用的地方。AVFoundation是一个强大的框架,提供了访问音视频的底层功能,包括相机和麦克风。通过AVFoundation,我们能够直接与设备的摄像头进行交互,实现自定义的拍照功能,为我们提供更大的灵活性和控制权。

在接下来的内容中,我们将深入探讨AVFoundation的拍照功能,了解如何通过这一框架自定义拍照过程,从而更好地满足项目的需求。

介绍

媒体捕捉主要类

首先介绍一下主要类:

AVCaptureDevice:捕捉设备。相对手机而言,它是摄像头,麦克风等物理设备定义了一个接口。

AVCaptureDeviceInput:捕捉设备的输入。捕捉设备不能直接添加到会话中,需要封装在AVCaptureDeviceInput中再进行添加。

AVCaptureSession:捕捉会话。捕获会话是整个功能的核心,有用链接输入和输出,配置捕捉环境。

AVCaptureOutput:捕捉的输出。AVCaptureOutput是一个抽象类,用于捕捉到的数据进行输出,不能直接使用,通常我们是使用它的子类比如AVCapturePhotoOutput,AVCaptureMovieFileOutput等等。

另外还有一个比较重要的类AVCaptureVideoPreviewLayer它提供了画面的预览功能。

基本使用

这里面演示一下我们使用的最小单元,也就是一个拍照功能的最核心代码:

  • 创建会话
AVCaptureSession * session = [[AVCaptureSession alloc] init];
  • 创建捕捉及输入并添加到会话
AVCaptureDevice * cameraDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError * error;
AVCaptureDeviceInput * cameraInput = [AVCaptureDeviceInput deviceInputWithDevice:cameraDevice error:&error];
if ([session canAddInput:cameraInput]) {
     [session addInput:cameraInput];
}
  • 创建输出并添加到会话
AVCapturePhotoOutput * photoOutput = [[AVCapturePhotoOutput alloc] init]; 
if ([session canAddOutput:photoOutput]) {
     [session addOutput:photoOutput];
}

上面的代码创建了一个拍摄图片最基础的框架。创建会话,将设备捕捉到的数据添加到会话,再将数据进行输出静态图片。启动会话,视频数据流就可以开始传输了。真正使用起来会比上面的示例代码复杂一点,但核心内容仍然是这几个步骤。

完整示例

这一部分内容比较多,为了更容易理解,我们将对应的功能分散到不同的类中。

PHCameraController:捕捉核心类。负责启动会话处理输入和输出。

PHPreviewView:预览图层。负责渲染预览画面。

而我们首先把注意力集中在PHCameraController上面。

捕捉核心类
配置会话

我们先来定义一个最小的功能,只声明一些拍照所需要的属性及方法。.h中对外暴漏的属性和接口如下:

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
NS_ASSUME_NONNULL_BEGIN

@interface PHCameraController : NSObject

@property(nonatomic,strong,readonly)AVCaptureSession * captureSession;

///设置会话
- (BOOL)setupSession:(NSError **)error;
///开始会话
- (void)startSession;
///停止会话
- (void)stopSession;

///拍照
- (void)capturePhoto;
@end
NS_ASSUME_NONNULL_END

我们只定义了最基本的功能,设置会话,启动会话,停止会话和拍照。

接下来我们来看一下它的.m文件中的内容。

首先是扩展中的私有属性:

#import "PHCameraController.h"
@interface PHCameraController ()<AVCapturePhotoCaptureDelegate>
///会话启动队列
@property(nonatomic,strong)dispatch_queue_t videoQueue;
///会话
@property(nonatomic,strong)AVCaptureSession * captureSession;
///图片输出
@property(nonatomic,strong)AVCapturePhotoOutput * photoOutput;

@end

在这里定义了一个自定义的队列,一个会话session和AVCaptureOutput的子类AVCapturePhotoOutput,专门用于输出静态图片。

再看一下它的接口实现,首先是配置会话相关的代码:

@implementation PHCameraController
- (BOOL)setupSession:(NSError *__autoreleasing  _Nullable *)error{
    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;
    }
    //设置图片输出
    self.photoOutput = [[AVCapturePhotoOutput alloc] init];
    NSDictionary * setDic = @{AVVideoCodecKey:AVVideoCodecTypeJPEG};
    AVCapturePhotoSettings * settings = [AVCapturePhotoSettings photoSettingsWithFormat:setDic];
    [self.photoOutput capturePhotoWithSettings:settings delegate:self];
    if ([self.captureSession canAddOutput:self.photoOutput]) {
        [self.captureSession addOutput:self.photoOutput];
    }
    self.videoQueue = dispatch_queue_create("com.panghu.VideoQueue", NULL);
    return YES;
}
@end

这和上面提到的最核心的代码实现几乎一致,创建会话添加会话输入和输出。

启动会话

再进行捕捉之前,需要先启动会话,也就是让会话处于准备捕捉静态图片的状态。

相关代码试下如下:

- (void)startSession{
    if (![self.captureSession isRunning]) {
        dispatch_async(self.videoQueue, ^{
            [self.captureSession startRunning];
        });
    }
}
开始捕捉静态图片

会话启动之后,我们就可以调用捕捉图片的方法来进行图片的捕捉:

- (void)capturePhoto{
    NSDictionary * setDic = @{AVVideoCodecKey:AVVideoCodecTypeJPEG};
    AVCapturePhotoSettings * settings = [AVCapturePhotoSettings photoSettingsWithFormat:setDic];
    self.photoSettings = settings;
    [self.photoOutput capturePhotoWithSettings:self.photoSettings delegate:self];
}

开始捕捉前,我们可以自定义捕捉静态图片的一些配置参数,比如

AVVideoCodecKey:图片类型。

AVVideoPixelAspectRatioKey:像素宽高比。

AVVideoCompressionPropertiesKey:压缩属性。

AVVideoWidthKey:宽。

AVVideoHeightKey:高。

调用拍照方法后会回调AVCapturePhotoCaptureDelegate中的代理方法:

- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(NSError *)error{
    NSData * data = photo.fileDataRepresentation;
    UIImage * image = [UIImage imageWithData:data];
}

其中image即是我们想要的静态图片。

结束会话

使用完该功能后,退出拍照功能,需要停止会话:

- (void)stopSession{
    if ([self.captureSession isRunning]) {
        dispatch_async(self.videoQueue, ^{
            [self.captureSession stopRunning];
        });
    }
}
画面预览类

我们在这里定义一个专门用作画面预览的视图PHPreviewView。

可以在对应的实例中创建一个AVCaptureVideoPreviewLayer用来渲染预览画面,也可以用另外一种更优雅的方式,通过重写LayerClass方法返回一个AVCaptureVideoPreviewLayer。

.h中的代码如下:

@interface PHPreviewView : UIView
@property(nonatomic,strong)AVCaptureSession * session;
@end

只有一个捕捉会话对象。

.m中的实现如下:

#import "PHPreviewView.h"
@implementation PHPreviewView
+ (Class)layerClass{
    return [AVCaptureVideoPreviewLayer class];
}
- (void)setSession:(AVCaptureSession *)session{
    [(AVCaptureVideoPreviewLayer*)self.layer setSession:session];
}
- (AVCaptureSession *)session{
    return [(AVCaptureVideoPreviewLayer*)self.layer session];
}
@end

通过重写session的set方法来将预览图层与捕捉会话相关联。

通过重写session的get方法来返回捕捉会话。

使用

在视图控制器ViewController中使用拍照功能。

首先声明捕捉的核心类及画面预览类:

@interface ViewController ()

//画面预览view
@property(nonatomic,strong)PHPreviewView * previewView;
///相机控制
@property(nonatomic,strong)PHCameraController * controller;

@end

添加画面预览视图:

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupView];
}

- (void)setupView{
    [self addPreviewView];
    [self configController];
}

//MARK:画面预览view
- (void)addPreviewView{
    self.previewView = [[PHPreviewView alloc] initWithFrame:self.view.bounds];
    [self.view addSubview: self.previewView];
}

//MARK:配置相机控制器
- (void)configController{
    self.controller = [[PHCameraController alloc] init];
    NSError * error = nil;
    BOOL isSuccess = [self.controller setupSession:&error];
    if (isSuccess) {
        [self.previewView setSession:self.controller.captureSession];
        [self.controller startSession];
    }
}

接下来我们只需要在屏幕上添加一个按钮然后调用拍照方法即可完成静态图片的拍摄:

//MARK:拍照或录制
- (void)capture:(UIButton *)sender{
     [self.controller capturePhoto];
}

结语

在实现自定义拍照功能时,除了深入了解AVCapturePhotoSettings等相关设置外,我们还需关注一系列前置和后续操作,以确保用户体验和功能完整性。

首先,我们必须在应用中请求摄像头和麦克风的权限,确保用户授权后才能正常使用这些设备。这是保护用户隐私的重要步骤,也是提供良好用户体验的前提。

另外,在成功捕获照片后,处理后续操作也至关重要。使用Photos框架将照片存储到相册,以确保用户可以轻松地查看和分享他们的作品。这是一个贴近用户习惯的操作,增强了应用的实用性和友好性。

在整个拍照过程中,我们还有许多机会进行细致的自定义,例如实现自动聚焦、调整曝光、切换摄像头、开启闪光灯等功能。这些细节的处理不仅提升了用户体验,也使应用更具吸引力。

在开发过程中,不断探索和尝试这些功能,根据具体项目需求进行定制,将为用户带来更为出色的拍摄体验。通过充分利用AVFoundation的强大功能,我们能够打造出更具创意和个性化的拍照应用,满足不同用户的期望和需求。


http://www.niftyadmin.cn/n/5298551.html

相关文章

hugo-theme-kiwi V0.0.2 博客主题上新了时间轴

至此佳节&#xff0c;我在此给正在屏幕前浏览本文的您和您的家人&#xff0c;恭祝元旦快乐&#xff0c;虽然&#xff0c;这声祝福是晚了&#xff0c;但却不妨碍我我由内心深处对您和您的家人的诚挚祝福&#xff01; 新的一年&#xff0c;从这一天逐渐步入我们的生活&#xff0c…

一起学Elasticsearch系列-Mapping

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 微信公众号&#xff1a;Java随想录 文章目录 Mapping 的基本概念查看索引 Mapping 字段数据类型数字类型基本数据类型Keywords 类型日期类型对象类型空间数据类型文档排名类型文本搜索类型 两种映射类型自动映…

计算机网络(第八版)期末复习(第三章数据链路层)

重要已用加粗表示&#xff0c;这些是复习内容所以并没有包括许多细节&#xff0c;仅包括重要知识点方便快速过。 信道主要类型 点对点信道&#xff08;一对一&#xff09;广播信道&#xff08;一对多&#xff09;链路&#xff08;物理链路&#xff09;&#xff1a;就是从一个节…

动力学约束下的运动规划算法——Kinodynamic RRT*算法

一、RRT * 算法回顾 为了更好的理解Kinodynamic RRT*算法&#xff0c;我们先来回顾一下RRT * 算法 RRT * 先通过Sample函数随机选取一个点Xrand&#xff0c;然后通过Near函数找到当前树上距离Xrand最近的一个点Xnear&#xff0c;再通过Steer函数&#xff0c;沿着从Xnear到Xra…

【操作系统】 文件管理

文件管理概述 文件管理的对象&#xff1a;计算机中的程序和数据。 文件管理的主要任务&#xff1a;利用文件系统把所管理的程序和数据组织成一系列文件&#xff0c;并把文件的存取、共享和保护手段提供给用户。 文件管理的主要功能包括&#xff1a;外存的分配 目录管理 存储…

00-开篇导读:学习分库分表开源框架的正确方法

1 前言 互联网高速发展带来海量的信息化数据&#xff0c;也带来更多的技术挑战。各种智能终端设备&#xff08;比如摄像头或车载设备等&#xff09;以每天千万级的数据量上报业务数据&#xff0c;电商、社交等互联网行业更不必说。这样量级的数据处理&#xff0c;已经远不是传…

不吹不黑,辩证看待开发者是否需要入坑鸿蒙

前言 自打华为2019年发布鸿蒙操作系统以来&#xff0c;网上各种声音百家争鸣。尤其是2023年发布会公布的鸿蒙4.0宣称不再支持Android&#xff0c;更激烈的讨论随之而来。 本文没有宏大的叙事&#xff0c;只有基于现实的考量。 通过本文&#xff0c;你将了解到&#xff1a; Har…

【产品设计】信息建设三驾马车:MES系统拆解

本篇文章&#xff0c;将从三个方面对MES系统进行拆解分析&#xff0c;并分析其特殊功能——文档管理。MES系统能实现多个生产信息的互联互通&#xff0c;提高生产效率。 MES系统主要实现生产业务系统管理。 ERP系统主要实现采购、销售、库存&#xff08;进销存&#xff09;、财…