媒体捕捉-拍照

news2025/1/2 0:04:16

引言

在项目开发中,从媒体库中选择图片或使用相机拍摄图片是一个极为普遍的需求。通常,我们使用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.coloradmin.cn/o/1350300.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

让你的隧道代理HTTP使用更加顺畅高效

在数字世界的探险中&#xff0c;隧道代理HTTP是我们穿越网络限制的重要工具。但有时候&#xff0c;我们可能会遇到连接不稳定、速度慢等问题。如何让隧道代理HTTP使用更加顺畅高效&#xff1f;下面是一些建议和技巧。 一、选择合适的代理服务器 代理服务器的地理位置、性能和…

C#/.NET/.NET Core推荐学习书籍(23年12月更新)

前言 古人云&#xff1a;“书中自有黄金屋&#xff0c;书中自有颜如玉”&#xff0c;说明了书籍的重要性。作为程序员&#xff0c;我们需要不断学习以提升自己的核心竞争力。以下是一些优秀的C#/.NET/.NET Core相关学习书籍&#xff0c;值得.NET开发者们学习和专研。书籍已分类…

查看证书有效期的命令

后面的证书是我们当时创建证书的路径 cfssl-certinfo -cert /opt/etcd/certs/etcd.pem

服务雪崩简单的介绍

定义 服务雪崩效应是一种因“服务提供者的不可用”&#xff08;原因&#xff09;导致“服务调用者不可用”&#xff08;结果&#xff09;&#xff0c;并将不可用逐渐放大的现象。如下图所示&#xff1a; 上图中, A为服务提供者, B为A的服务调用者, C和D是B的服务调用者. 当A的…

精确率(Precision,P),召回率(Recall,R)以及F1值(F1-score,F1)

狗狗识别系统的例子&#xff1a; 假设我们有两个集合&#xff1a; 实际狗狗的集合&#xff08;实际真正是狗狗的图片&#xff09;&#xff1a;A我们识别为狗狗的集合&#xff08;我们认为是狗狗的图片&#xff09;&#xff1a;B 精确率&#xff08;Precision&#xff0c;P&am…

LeetCode刷题--- 第 N 个泰波那契数

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏 力扣递归算法题 http://t.csdnimg.cn/yUl2I 【C】 ​​​​​​http://t.csdnimg.cn/6AbpV 数据结构与算法 ​​​http://t.csdnimg.cn/hKh2l 前言&#xff1a;这个专栏主要讲述动…

【十三】【动态规划】1745. 分割回文串 IV、132. 分割回文串 II、516. 最长回文子序列,三道题目深度解析

动态规划 动态规划就像是解决问题的一种策略&#xff0c;它可以帮助我们更高效地找到问题的解决方案。这个策略的核心思想就是将问题分解为一系列的小问题&#xff0c;并将每个小问题的解保存起来。这样&#xff0c;当我们需要解决原始问题的时候&#xff0c;我们就可以直接利…

半年没发过文章,这个博主竟然...

目 录 前言这半年去干了什么&#xff1f;考研求职山东电建移动 论文大创课余 未来公务员继续考研就业 结语 前言 第一次写这样的记录性的文章&#xff0c;这篇文章可能不会有太过于华丽的辞藻&#xff0c;但是它将展现我个人的真实经历和内心感受。在过去的时间里&#xff0c;我…

WEB 3D技术 three.js通过光线投射 完成几何体与外界的事件交互

本文 我们来说 光线投射 光线投射技术是用于3维空间场景中的交互事件 我们先编写代码如下 import ./style.css import * as THREE from "three"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";const scene new THRE…

Redis Cluster集群模式学习

Redis Cluster集群模式 Redis哨兵模式&#xff1a;https://blog.csdn.net/liwenyang1992/article/details/133956200 Redis Cluster集群模式示意图&#xff1a; Cluster模式是Redis3.0开始推出采用无中心结构&#xff0c;每个节点保存数据和整个集群状态&#xff0c;每个节点都…

【python】Python 3.11不支持Tix库

Tix库主要用于扩展Tkinter&#xff0c;但是Python 3.11 Tkinter已经不再支持Tix库。Tix模块提供了一些额外的部件和功能&#xff0c;但现在这些功能已经整合到了Tkinter库中。 一、如果在Python 3.11中想要使用Tix库&#xff0c;但发现它不再被内置支持&#xff0c;可以尝试以…

使用Wireshark进行网络流量分析

目录 Wireshark是什么&#xff1f; 数据包筛选 筛选指定ip 使用逻辑运算符筛选 HTTP模式过滤 端口筛选 协议筛选 包长度筛选 数据包搜索 数据流分析 数据包导出 Wireshark是什么&#xff1f; 通过Wireshark&#xff0c;我们可以捕获和分析网络数据包&#xff0c;查看…

ARM CCA机密计算硬件架构之内存管理

实施了TrustZone安全扩展的Arm A-profile处理器呈现两个物理地址空间(PAS): 非安全物理地址空间安全物理地址空间Realm管理扩展增加了两个PAS: Realm物理地址空间Root物理地址空间下图显示了这些物理地址空间以及如何在工作系统中实施这些空间: 正如表格所示,根状态能够访…

2024,启动(回顾我的2023)

零.前言 打开博客想写个年度总结&#xff0c;发现已经半年没有更新文章了&#xff0c;排名从几千掉到了几万&#xff0c;不过数据量还是不错的。 时间过得可真快&#xff0c;2023年是充满动荡的一年&#xff0c;上半年gpt横空出世&#xff0c;下半年各种翻车暴雷吃瓜吃到嘴软…

计算机毕业设计选题分享-Springboot在线问诊系统00211(赠送源码数据库)JAVA、PHP,node.js,C++、python,大屏数据可视化等

Springboot在线问诊系统 摘 要 针对医院门诊等问题&#xff0c;对在线问诊进行研究分析&#xff0c;然后开发设计出在线问诊系统以解决问题。在线问诊系统主要功能模块包括首页、轮播图管理、公告信息管理、资源管理、系统用户管理&#xff08;管理员、患者用户、医生用户&…

golang并发编程-channel

在golang 并发编程里&#xff0c;经常会听到一句话&#xff1a;不要通过共享内存进行通信&#xff0c;通过通信来共享内存。下面我们会介绍下channel, 通过源码的方式去了解channel是怎么工作的。 基本结构 流程图 代码解读 type hchan struct {qcount uint // …

【银行测试】超细支付功能测试+测试点总结分析(详全)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、支付功能怎么测…

从零开始:使用 BIND 构建和管理您的 DNS 服务器

1 前言 在这篇文章中&#xff0c;我将详细介绍如何使用 BIND&#xff08;Berkeley Internet Name Domain&#xff09;软件包中的 named 程序来配置和管理一个基本的 DNS 服务器。 从安装 BIND 开始&#xff0c;到设置 DNS 区域文件&#xff0c;再到运行和测试您的服务器&#x…

ROS学习记录:ROS系统中的激光雷达消息包的数据格式

一、在工作空间中输入source ./devel/setup.bash 二、输入roslaunch wpr_simulation wpb_simple.launch打开机器人仿真环境 三、机器人仿真环境打开成功 四、给机器人围上一圈障碍物 五、再打开一个工作空间终端 六、输入roslaunch wpr_simulation wpb_rviz.launch打开RViz 七、…

Node.js使用jemalloc内存分配器显著减少内存使用

前言 Node.js 默认使用的是 ptmalloc(glibc) 内存分配器&#xff0c;而&#xff1a; 在服务端领域「不会选择默认的 malloc」是一个常识。&#xff08; 来源 &#xff09; ptmalloc 的分配效率较低&#xff08; 来源 &#xff09;&#xff0c;对于 长时间、多核 / 多线程 运行…