媒体捕捉-拍照
引言
在项目开发中,从媒体库中选择图片或使用相机拍摄图片是一个极为普遍的需求。通常,我们使用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的强大功能,我们能够打造出更具创意和个性化的拍照应用,满足不同用户的期望和需求。
相关文章:
媒体捕捉-拍照
引言 在项目开发中,从媒体库中选择图片或使用相机拍摄图片是一个极为普遍的需求。通常,我们使用UIImagePickerController来实现单张图片选择或启动相机拍照。整个拍照过程由UIImagePickerController内部实现,无需我们关心细节,只…...
Typora+PicGo+Gitee构建云存储图片
创建Gitee仓库 首先,打开工作台 - Gitee.com,自行注册一个账户 注册完后,新建一个仓库(记得仓库要开源) 然后创建完仓库后,鼠标移动到右上角头像位置,选择设置,并点击ÿ…...
【话题】ChatGPT等大语言模型为什么没有智能2
我们接着上一次的讨论,继续探索大模型的存在的问题。正巧CSDN最近在搞文章活动,我们来看看大模型“幻觉”。当然,本文可能有很多我自己的“幻觉”,欢迎批评指正。如果这么说的话,其实很容易得出一个小结论——大模型如…...
通过大量生物、地球、农业、气象、生态、环境科学领域中案例,一起探索如何优雅地使用大模型吧!
以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮,可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…...
slf4j+logback源码加载流程解析
slf4j绑定logback源码解析 Logger log LoggerFactory.getLogger(LogbackDemo.class);如上述代码所示,在项目中通常会这样创建一个Logger对象去打印日志。 然后点进去,会走到LoggerFactory的getILoggerFactory()方法,如下代码所示。 public …...
KVM虚拟机部署K8S重启后/etc/hosts内容丢失
前言 使用KVM开了虚拟机部署K8S,部署完成后重启,节点的pod等信息无法获取到,查看报错初步推测为域名解析失效,查看/etc/hosts后发现安装k8s时添加的内容全部消失 网上搜索一番之后发现了 如果直接修改 /etc/hosts 文件࿰…...
Redis使用场景(五)
Redis实战精讲-13小时彻底学会Redis 1.计数器 可以对 String 进行自增自减运算,从而实现计数器功能。 Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。 2.缓存 将热点数据放到内存中,设置内存的最大使用量以及淘汰策略…...
【UnityShader入门精要学习笔记】(2)GPU流水线
本系列为作者学习UnityShader入门精要而作的笔记,内容将包括: 书本中句子照抄 个人批注项目源码一堆新手会犯的错误潜在的太监断更,有始无终 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 文章目录 上节复习GPU流水线顶点着色…...
CSS免费在线字体格式转换器 CSS @font-face 生成器
今天竟意外发现的一款免费的“网页字体生成器”,功能强大又好用~ 工具地址:https://transfonter.org/ 根据你设置生成后的文件预览: 支持TTF、OTF、WOFF、WOFF2 或 SVG字体格式转换生成,每个文件最大15MB。转换完成以后还会生成一…...
Codeium在IDEA里的3个坑
转载自Codeium在IDEA里的3个坑:无法log in,downloading language server和中文乱码_downloading codeium language server...-CSDN博客文章浏览阅读1.7w次,点赞26次,收藏47次。Codeium安装IDEA插件的3个常见坑_downloading codeiu…...
C-C++ 项目构建指南:如何使用 Makefile 提高开发效率
Makefile是一个常用的自动化构建工具,它可以为开发人员提供方便的项目构建方式。在C/C项目中,Makefile可以用来编译、链接和生成可执行文件。使用Makefile的好处是可以自动执行一系列命令,从而减少手动操作的复杂性和出错的可能性。此外&…...
基于SpringBoot的图书管理系统
文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 🚀🚀🚀SpringBoot 阿博图书管理系…...
矩阵对角线遍历
Diagonal 2614. 对角线上的质数 class Solution {public int diagonalPrime(int[][] nums) {int n = nums....
【教程】Typecho Joe主题开启并修复壁纸相册不显示问题
转载请注明出处:小锋学长生活大爆炸[xfxuezhang.cn] 背景说明 Joe主题本身支持“壁纸”功能,其实就是相册。当时还在网上找了好久相册部署的开源项目,太傻了。 但是网上教程很少,一没说如何开启壁纸功能,二没说开启后为…...
MR混合现实情景实训教学系统在法律专业课堂上的应用
MR混合现实情景实训教学系统是一种将虚拟现实(VR)、增强现实(AR)相结合的先进技术。在法律教学课堂上,MR教学系统为学生模拟模拟法庭、案例分析等多种形式,让学生在实践中掌握法律知识,提高法律…...
车载 Android之 核心服务 - CarPropertyService 的VehicleHAL
前言: 本文是车载Android之核心服务-CarPropertyService的第二篇,了解一下CarPropertyService的VehicleHAL, 第一篇在车载 Android之 核心服务 - CarPropertyService 解析-CSDN博客,有兴趣的 朋友可以去看下。 本节介绍 AndroidAutomotiveOS中对于 Veh…...
年底了,准备跳槽的可以看看...
前两天跟朋友感慨,今年的铜九铁十、裁员、疫情导致好多人都没拿到offer!现在已经1月了,具体明年的金三银四只剩下两个月。 对于想跳槽的职场人来说,绝对要从现在开始做准备了。这时候,很多高薪技术岗、管理岗的缺口和市场需求也出…...
Bagging算法_随机森林Random_Forest
Bagging B a g g i n g Bagging Bagging是并行式集成学习方法最著名的代表,这个名字是由 B o o t s t r a p A G G r e g a t I N G Bootstrap AGGregatING BootstrapAGGregatING而来,顾名思义,该算法由 B o o s t s t r a p Booststrap Boos…...
物理与网络安全
物流环境安全 场地选择考虑抗震、承重、防火、防水、供电、空气调节、电磁防护、雷击及静电 场地因素: 自然灾害,社会因素(加油站、化工厂),配套条件(消防,交通,电力,…...
torch.meshgrid和np.meshgrid的区别
numpy中meshgrid: 把数组a当作一行,再根据数组b的长度扩充行。 把数组b当作一列,再根据数组a的长度扩充列。 torch中meshgrid: 把数组a当作一列,再根据数组b的长度扩充列。 把数组b当作一行,再根据数组a的…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
学习一下用鸿蒙DevEco Studio HarmonyOS5实现百度地图
在鸿蒙(HarmonyOS5)中集成百度地图,可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API,可以构建跨设备的定位、导航和地图展示功能。 1. 鸿蒙环境准备 开发工具:下载安装 De…...
[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG
TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码:HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...
消防一体化安全管控平台:构建消防“一张图”和APP统一管理
在城市的某个角落,一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延,滚滚浓烟弥漫开来,周围群众的生命财产安全受到严重威胁。就在这千钧一发之际,消防救援队伍迅速行动,而豪越科技消防一体化安全管控平台构建的消防“…...
