Android摄像头采集选Camera1还是Camera2?
Camera1还是Camera2?
好多开发者纠结,Android平台采集摄像头,到底是用Camera1还是Camera2?实际上,Camera1和Camera2分别对应相机API1和相机API2。Android 5.0开始,已经弃用了Camera API1,新平台重点开发Camera API2,Camera API1 会逐渐被淘汰。Camera API2 框架为应用提供更接近底层的相机控件,包括高效的零复制连拍/视频流以及曝光、增益、白平衡增益、颜色转换、去噪、锐化等方面的每帧控件。
Camera2 相对Camera1有哪些优势?
1. 更灵活的相机控制
- 细粒度参数调整:Camera2 API提供了更多的相机参数和配置选项,开发者可以更精确地控制相机的焦距、曝光时间、ISO感光度、白平衡等,以满足不同场景下的需求。
- 多摄像头支持:能够同时管理和控制多个相机设备,包括前置摄像头、后置摄像头以及其他可用的摄像头,提高了系统的灵活性和扩展性。
2. 更高的性能
- 异步操作:Camera2 API使用异步操作模式,减少了对UI线程的阻塞,提高了应用的响应速度和流畅性。
- 并发访问:支持并发访问和操作多个相机设备,提高了相机的利用率和整体性能。
3. 更好的图像处理能力
- RAW图像捕获:支持原生的RAW图像捕获和处理,开发者可以获取到相机传感器的原始数据,进行更高质量的图像处理和分析。
- 实时预览和后处理:提供实时的图像预览和后处理功能,有助于开发者实现更丰富的图像效果。
4. 更好的适配性和扩展性
- 统一的接口和架构:Camera2 API提供了更统一的接口和更清晰的架构,便于开发者进行相机功能的开发和适配。
- 功能扩展:允许开发者通过CameraCharacteristics和CaptureRequest等接口获取和设置摄像头的各种功能和参数,支持自定义功能的开发。
5. 广泛的应用场景
- 相机应用开发:Camera2 API提供了丰富的相机控制接口,可以用于开发各种相机应用,如拍照、录像、实时滤镜等。
- 视频通话和实时视频应用:支持高质量的视频捕获和处理,适用于视频通话、视频会议等实时视频应用。
- 计算机视觉和图像处理:结合OpenCV等图像处理库,可以进行实时的图像分析、特征提取、目标识别等计算机视觉任务。
如何使用Camera2进行相机操作
使用Android的Camera2 API来进行相机操作,包括预览、拍照等功能,是一个相对复杂但功能强大的过程。以下是一个基本的步骤指南,帮助你开始使用Camera2 API:
1. 添加权限
首先,你需要在AndroidManifest.xml
文件中添加必要的权限,以便应用能够访问设备的相机。至少需要添加相机权限:
<uses-permission android:name="android.permission.CAMERA"/>
如果你的应用还需要录制视频或音频,还需要添加相应的权限:
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.camera" android:required="true"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>
需要注意的是,从Android 6.0(API 级别 23)开始,需要在运行时请求这些权限,而不是仅仅在清单文件中声明。
2. 初始化CameraManager
在你的Activity或Fragment中,首先需要获取CameraManager
的实例,这个类是用于管理设备上的相机资源:
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
3. 获取相机ID
使用CameraManager
的getCameraIdList()
方法获取设备上所有可用的相机列表,并选择一个相机ID进行后续操作。通常,后置摄像头的ID是"0",前置摄像头的ID是"1",但这不是绝对的,需要根据实际情况判断:
try { String[] cameraIdList = cameraManager.getCameraIdList(); for (String cameraId : cameraIdList) { CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId); // 根据需要选择合适的相机,例如选择后置摄像头 if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK) { // 选择后置摄像头 break; } }
} catch (CameraAccessException e) { e.printStackTrace();
}
4. 打开相机
使用CameraManager
的openCamera()
方法打开选定的相机。这个方法是异步的,并且需要一个CameraDevice.StateCallback
来接收相机打开的结果:
cameraManager.openCamera(cameraId, new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice cameraDevice) { // 相机打开成功,可以进行后续操作 } @Override public void onDisconnected(@NonNull CameraDevice cameraDevice) { // 相机断开连接 } @Override public void onError(@NonNull CameraDevice cameraDevice, int error) { // 打开相机发生错误 }
}, null);
5. 创建CameraCaptureSession
一旦相机成功打开,你需要创建一个CameraCaptureSession
来进行预览、拍照等操作。这个过程也是异步的,并且需要设置Surface来接收相机数据(如TextureView或SurfaceView):
cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { // 会话创建成功,可以开始预览或拍照 } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { // 会话配置失败 }
}, null);
6. 预览和拍照
在CameraCaptureSession
配置成功后,你可以通过调用setRepeatingRequest()
方法来开始预览,并通过调用capture()
方法来拍照。这些操作都需要CaptureRequest
对象,该对象描述了捕获请求的各种参数:
CaptureRequest.Builder previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
// 设置预览的参数...
previewRequestBuilder.addTarget(surface); // 预览
cameraCaptureSession.setRepeatingRequest(previewRequestBuilder.build(), null, null); // 拍照
CaptureRequest.Builder captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
// 设置拍照的参数...
captureRequestBuilder.addTarget(imageReader.getSurface());
cameraCaptureSession.capture(captureRequestBuilder.build(), captureCallback, null);
7. 释放资源
当相机不再需要时,你应该及时释放相关资源,避免内存泄漏等问题。
如何从Camera1和Camera2获取原始数据?
我们在做Android平台RTMP推送、轻量级RTSP服务、实时录像和GB28181设备对接模块的时候,都需要用到摄像头采集,早期,我们提供了Camera1的采集demo,后面碎渣Camera2的优势越来越明显,高版本设备已成主流,目前一般建议采用Camera2的采集。
先说针对Camera1的采集和数据投递处理:
/** CameraPublishActivity.java* Author: daniusdk.com* WeChat: xinsheng120*/
@Override
public void onPreviewFrame(byte[] data, Camera camera) {frameCount++;if (frameCount % 3000 == 0) {Log.i("OnPre", "gc+");System.gc();Log.i("OnPre", "gc-");}if (data == null) {Parameters params = camera.getParameters();Size size = params.getPreviewSize();int bufferSize = (((size.width | 0x1f) + 1) * size.height * ImageFormat.getBitsPerPixel(params.getPreviewFormat())) / 8;camera.addCallbackBuffer(new byte[bufferSize]);} else {if (isRTSPPublisherRunning || isPushingRtmp || isRecording || isPushingRtsp) {if (1 == video_opt_) {libPublisher.SmartPublisherOnCaptureVideoData(publisherHandle, data, data.length, currentCameraType, currentOrigentation);} else if (3 == video_opt_) {int w = videoWidth, h = videoHeight;int y_stride = videoWidth, uv_stride = videoWidth;int y_offset = 0, uv_offset = videoWidth * videoHeight;int is_vertical_flip = 0, is_horizontal_flip = 0;int rotation_degree = 0;// 镜像只用在前置摄像头场景下if (is_mirror && FRONT == currentCameraType) {// 竖屏, (垂直翻转->顺时旋转270度)等价于(顺时旋转旋转270度->水平翻转)if (PORTRAIT == currentOrigentation)is_vertical_flip = 1;elseis_horizontal_flip = 1;}if (PORTRAIT == currentOrigentation) {if (BACK == currentCameraType)rotation_degree = 90;elserotation_degree = 270;} else if (LANDSCAPE_LEFT_HOME_KEY == currentOrigentation) {rotation_degree = 180;}int scale_w = 0, scale_h = 0, scale_filter_mode = 0;libPublisher.PostLayerImageNV21ByteArray(publisherHandle, 0, 0, 0,data, y_offset, y_stride, data, uv_offset, uv_stride, w, h,is_vertical_flip, is_horizontal_flip, scale_w, scale_h, scale_filter_mode, rotation_degree);}}camera.addCallbackBuffer(data);}
}
再说针对Camera2的数据投递处理:
/** Camera2Activity.java* Author: daniusdk.com* WeChat: xinsheng120*/
@Override
public void onCameraImageData(Image image) {Image.Plane[] planes = image.getPlanes();int w = image.getWidth(), h = image.getHeight();int y_offset = 0, u_offset = 0, v_offset = 0;int scale_w = 0, scale_h = 0, scale_filter_mode = 0;scale_filter_mode = 3;int rotation_degree = cameraImageRotationDegree_;if (rotation_degree < 0) {Log.i(TAG, "onCameraImageData rotation_degree < 0, may need to set orientation_ to 0, 90, 180 or 270");return;}for (LibPublisherWrapper i : publisher_array_)i.PostLayerImageYUV420888ByteBuffer(0, 0, 0,planes[0].getBuffer(), y_offset, planes[0].getRowStride(),planes[1].getBuffer(), u_offset, planes[1].getRowStride(),planes[2].getBuffer(), v_offset, planes[2].getRowStride(), planes[1].getPixelStride(),w, h, 0, 0,scale_w, scale_h, scale_filter_mode, rotation_degree);}
总结
Android Camera2 API控制更灵活,性能、图像处理能力优异、适配性和扩展性也好,在版本支持的前提下,一般建议采用Camera2实现摄像头采集技术诉求,以上是Camera1和Camera2技术扫盲和技术探讨,感兴趣的开发者,可以单独跟我沟通探讨。
相关文章:

Android摄像头采集选Camera1还是Camera2?
Camera1还是Camera2? 好多开发者纠结,Android平台采集摄像头,到底是用Camera1还是Camera2?实际上,Camera1和Camera2分别对应相机API1和相机API2。Android 5.0开始,已经弃用了Camera API1,新平台…...

零基础5分钟上手亚马逊云科技AWS核心云开发/云架构 - 创建高可用数据库集群
简介: 欢迎来到小李哥全新亚马逊云科技AWS云计算知识学习系列,适用于任何无云计算或者亚马逊云科技技术背景的开发者,让大家零基础5分钟通过这篇文章就能完全学会亚马逊云科技一个经典的服务开发架构方案。 我将每天介绍一个基于亚马逊云科…...
力扣315.计算右侧小于当前元素的个数
力扣315.计算右侧小于当前元素的个数 离散化 树状数组 const int N 100010;int tr[N],n;class Solution {public:vector<int> countSmaller(vector<int>& nums) {n nums.size();vector<int> tmp(nums);vector<int> res(n);memset(tr,0,sizeo…...
websocket,css动画和css-position、display、区别
一、websocket codereturn {// 用于存储 WebSocket 返回的状态数据statusList: [],},mounted() {this.setupWebSocket();this.startBlinking();},methods: {setupWebSocket() {// 创建 WebSocket 连接const socket = new WebSocket(ws://xxx.xxx:xxx/xxx);// WebSocket 连接成功…...

前端获取视频文件宽高信息和视频时长
安装 yarn add video-metadata-thumbnails | npm install video-metadata-thumbnails引入依赖包 import { getMetadata } from video-metadata-thumbnails使用 if (file.name.includes(mp4)) {if (file) {try {console.log(file)// 获取视频的元数据const metadata await …...
【区块链+医疗健康】基于区块链的药品类监管应用管理系统 | FISCO BCOS应用案例
退热类药品的购药信息及政企互动信息等各项数据的安全性、保密性、真实性,不仅影响着监管部门的科学监管、 有效监管,也影响着企业的经营安全、诚信口碑,是区域药品安全监管工作进展的直观体现。 江苏数予科技有限公司构建基于区块链的药品类…...

MySQL4多表查询 内连接
多表查询 数据准备 CREATE DATABASE db4; USE db4; -- 创建部门表 create table if not exists dept(deptno varchar(20) primary key , -- 部门号name varchar(20) -- 部门名字 );-- 创建员工表 create table if not exists emp(eid varchar(20) primary key , -- 员工编号…...
Java -数组
1.一维数组 1.1数组定义 public class Main {public static void main(String[] args) throws Exception {int[] a new int[10];float[] f new float[10];double[] d new double[10];char[] c new char[10];} } 1.2 初始化 public class Main {public static void main(S…...
.prettierrc.js 有什么用
.prettierrc.js 是 Prettier 代码格式化工具的配置文件。 1. 作用 Prettier 是一个用于统一代码风格的工具,它可以使代码更具可读性和一致性。.prettierrc.js 文件用于自定义 Prettier 的格式化规则。 通过配置 .prettierrc.js,团队中的开发者可以遵循…...
haproxy七层代理
一.haproxy的基本部署 1.RS上装nginx [rootwebserver1 ~]# dnf install nginx -y 2.再RS上写入测试信息 [rootwebserver1 ~]# echo webserver1 - 172.25.254.10 > /usr/share/nginx/html/index.html [rootwebserver1 ~]# systemctl enable --now nginx [rootwebserver…...

<数据集>柑橘缺陷识别数据集<目标检测>
数据集格式:VOCYOLO格式 图片数量:1290张 标注数量(xml文件个数):1290 标注数量(txt文件个数):1290 标注类别数:4 标注类别名称:[Orange-Green-Black-Spot, Orange-Black-Spot, Orange-Canker, Orange…...

Go开发后端和Vue3开发前端的前后端分离框架中自己手戳一个OA流程审批、工作流引擎给新时代一个漂亮便捷的工作流引擎
前言 在软件项目开发中,我们都会接触到流程审批的需要业务,我们以往用的最多就是如下图这种流程编辑引擎插件: 以上截图中的流程工具是不是大家常见的呀!感觉很丑拿不出手呀!在当前行业内卷及竞争激烈情况下ÿ…...
深入理解 toDto 与 toEntity:结合 Eladmin 框架的最佳实践
在现代软件开发中,尤其是后端开发中,数据传输对象(DTO)和实体对象的转换是一个常见且重要的操作。理解和正确实现这种转换不仅能提高代码的可维护性,还能提升应用的性能和安全性。本文将深入探讨 toDto 和 toEntity 方…...

基于区块链的供应链应用开发
区块链的供应链溯源应用开发 一 、环境准备 (1)更新镜像源 apt update(2)安装(openssl、jdk、git) apt -y install openssl default-jdk git(3)配置JAVA_HOME环境变量 echo “export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64/” >> /etc/profilesource /etc…...
获取GORM执行时的sql字符串
示例: import "log" func GetDetail(tx *gorm.DB,id int)(data any,err error){var query tx.Session(&gorm.Session{DryRun: true})err query.Where("id ?", id).First(&res).Errorif err!nil{zap.L().Error("get detail er…...

Linux系统使用Docker安装RStudio服务并实现任意浏览器远程访问
文章目录 前言1. 安装RStudio Server2. 本地访问3. Linux 安装cpolar4. 配置RStudio server公网访问地址5. 公网远程访问RStudio6. 固定RStudio公网地址 前言 RStudio Server 使你能够在 Linux 服务器上运行你所熟悉和喜爱的 RStudio IDE,并通过 Web 浏览器进行访问…...

【原创】springboot+mysql法律咨询网设计与实现
个人主页:程序猿小小杨 个人简介:从事开发多年,Java、Php、Python、前端开发均有涉猎 博客内容:Java项目实战、项目演示、技术分享 文末有作者名片,希望和大家一起共同进步,你只管努力,剩下的交…...
Vue 应用实例的关键方法与配置案例二
目录 createApp createSSRApp app.mount app.unmount app.component app.directive Vue3.X自定义全局指令 Vue2.X自定义全局指令 app.use app.mixin 非 VIP 用户能够免费下载博文资源 createApp 详见上一章节:Vue 应用实例的关键方法与配置案例一-CSDN博客 createSS…...

Java面试题--JVM大厂篇之破解 JVM 性能瓶颈:实战优化策略大全
目录 引言: 正文: 1. 常见的JVM性能问题 频繁的GC导致应用暂停 内存泄漏导致的内存不足 线程争用导致的CPU利用率过高 类加载问题导致的启动时间过长 2. 优化策略大全 2.1 代码层面的优化 2.1.1 避免不必要的对象创建 2.1.2 优化数据结构的选择 2.1.3 使用并发工具…...
Apache Curator 创建节点时,如果节点存储就会抛出异常吗?
在Apache Curator中,创建节点时,如果该节点已经存在,并且你的代码没有正确处理这种情况,那么会抛出NodeExistsException异常。这是ZooKeeper客户端库(包括Curator)的常见行为,因为它需要确保Zoo…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
k8s从入门到放弃之HPA控制器
k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率(或其他自定义指标)来调整这些对象的规模,从而帮助应用程序在负…...
es6+和css3新增的特性有哪些
一:ECMAScript 新特性(ES6) ES6 (2015) - 革命性更新 1,记住的方法,从一个方法里面用到了哪些技术 1,let /const块级作用域声明2,**默认参数**:函数参数可以设置默认值。3&#x…...

2025 后端自学UNIAPP【项目实战:旅游项目】7、景点详情页面【完结】
1、获取景点详情的请求【my_api.js】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http(/login/getWXSessionKey, {code,avatar}); };//…...

开疆智能Ethernet/IP转Modbus网关连接鸣志步进电机驱动器配置案例
在工业自动化控制系统中,常常会遇到不同品牌和通信协议的设备需要协同工作的情况。本案例中,客户现场采用了 罗克韦尔PLC,但需要控制的变频器仅支持 ModbusRTU 协议。为了实现PLC 对变频器的有效控制与监控,引入了开疆智能Etherne…...