2s相机 android6,Android Camera2 使用总结

最近在做自定义相机相关的项目,网上查了资料都是有关android.hardware.Camera的资料,开始使用的才发现这个类已经废弃了。Android 5.0(21)之后android.hardware.Camera就被废弃了,取而代之的是全新的android.hardware.Camera2 。Android 5.0对拍照API进行了全新的设计,新增了全新设计的Camera v2 API,这些API不仅大幅提高了Android系统拍照的功能,还能支持RAW照片输出,甚至允许程序调整相机的对焦模式、曝光模式、快门等。

Camera2主要的类说明

CameraManager:摄像头管理器。这是一个全新的系统管理器,专门用于检测系统摄像头、打开系统摄像头。除此之外,调用CameraManager的getCameraCharacteristics(String)方法即可获取指定摄像头的相关特性。

CameraCharacteristics:摄像头特性。该对象通过CameraManager来获取,用于描述特定摄像头所支持的各种特性。

CameraDevice:代表系统摄像头。该类的功能类似于早期的Camera类。

CameraCaptureSession:这是一个非常重要的API,当程序需要预览、拍照时,都需要先通过该类的实例创建Session。而且不管预览还是拍照,也都是由该对象的方法进行控制的,其中控制预览的方法为setRepeatingRequest();控制拍照的方法为capture()。

CameraRequest和CameraRequest.Builder:当程序调用setRepeatingRequest()方法进行预览时,或调用capture()方法进行拍照时,都需要传入CameraRequest参数。CameraRequest代表了一次捕获请求,用于描述捕获图片的各种参数设置,比如对焦模式、曝光模式……总之,程序需要对照片所做的各种控制,都通过CameraRequest参数进行设置。CameraRequest.Builder则负责生成CameraRequest对象。

开启相机预览

开启相机请一定添加相关的相机权限,判断6.0以后添加动态权限的获取。如果相机预览出现黑屏多半就是因为没有相机权限而导致的

首页我们要设置相机相关的参数

CameraManager manager = (CameraManager) ctx.getSystemService(Context.CAMERA_SERVICE);

try {

//获取可用摄像头列表

for (String cameraId : manager.getCameraIdList()) {

//获取相机的相关参数

CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);

// 不使用前置摄像头。

Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);

if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {

continue;

}

StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

if (map == null) {

continue;

}

// 检查闪光灯是否支持。

Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);

mFlashSupported = available == null ? false : available;

mCameraId = cameraId;

Log.e(TAG," 相机可用 ");

return;

}

} catch (CameraAccessException e) {

e.printStackTrace();

} catch (NullPointerException e) {

//不支持Camera2API

}

}

通过getSystemService(Context.CAMERA_SERVICE);拿到了CameraManager 返回当前可用的相机列表,在这里你可以选择使用前置还是后置摄像头。CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);可以拿到当前相机的相关参数,在这里你可以进行相关的参数检查,例如检查闪光灯是否支持等。在这里我们拿到当前相机的cameraId后面使用。

拿到cameraId我们就可以调用CameraManager的openCamera(String cameraId, CameraDevice.StateCallback callback, Handler handler)打开相机了

CameraManager manager = (CameraManager) ctx.getSystemService(Context.CAMERA_SERVICE);

try {

//打开相机预览

manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);

} catch (CameraAccessException e) {

e.printStackTrace();

} catch (InterruptedException e) {

throw new RuntimeException("Interrupted while trying to lock camera opening.", e);

}

添加 CameraDevice.StateCallback 监听

我们需要对相机状态就行监听,以便在相机状态发生改变的时候做相应的操作。openCamera(String cameraId, CameraDevice.StateCallback callback, Handler handler)中CameraDevice.StateCallback就是对相机的状态改变的Callback

private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {

@Override

public void onOpened(@NonNull CameraDevice cameraDevice) {

mCameraDevice = cameraDevice;

//创建CameraPreviewSession

createCameraPreviewSession();

}

@Override

public void onDisconnected(@NonNull CameraDevice cameraDevice) {

cameraDevice.close();

mCameraDevice = null;

}

@Override

public void onError(@NonNull CameraDevice cameraDevice, int error) {

cameraDevice.close();

mCameraDevice = null;

}

};

创建 CameraCaptureSession

在onOpened()中我们可以拿到CameraDevice对象,在相机打开后需要创建CameraCaptureSession。

CameraCaptureSession是什么呢?由于Camera2是一套全新的API,所以它引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送 Capture 请求,而摄像头会返回 CameraMetadata。这一切建立在一个叫作 CameraCaptureSession的会话中。如下图:

1488625

2086682-e68d187e1240bfc5.png

这里我们需要预览相机的内容就需要创建CameraCaptureSession向相机发送Capture请求预览相机内容

/**

* 为相机预览创建新的CameraCaptureSession

*/

private void createCameraPreviewSession() {

try {

//设置了一个具有输出Surface的CaptureRequest.Builder。

mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

mPreviewRequestBuilder.addTarget(mSurfaceHolder.getSurface());

//创建一个CameraCaptureSession来进行相机预览。

mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceHolder.getSurface()),

new CameraCaptureSession.StateCallback() {

@Override

public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {

// 相机已经关闭

if (null == mCameraDevice) {

return;

}

// 会话准备好后,我们开始显示预览

mCaptureSession = cameraCaptureSession;

try {

// 自动对焦应

mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,

CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);

// 闪光灯

setAutoFlash(mPreviewRequestBuilder);

// 开启相机预览并添加事件

mPreviewRequest = mPreviewRequestBuilder.build();

//发送请求

mCaptureSession.setRepeatingRequest(mPreviewRequest,

null, mBackgroundHandler);

Log.e(TAG," 开启相机预览并添加事件");

} catch (CameraAccessException e) {

e.printStackTrace();

}

}

@Override

public void onConfigureFailed(

@NonNull CameraCaptureSession cameraCaptureSession) {

Log.e(TAG," onConfigureFailed 开启预览失败");

}

}, null);

} catch (CameraAccessException e) {

Log.e(TAG," CameraAccessException 开启预览失败");

e.printStackTrace();

}

}

首先我们创建了一个CaptureRequest 上面说过了我们需要跟相机通信只有通过CameraCaptureSession。而要和CameraCaptureSession通信就是发送请求。这里我们相当于在创建请求的一些参数。

createCaptureRequest(int); 也有很多参数可选。这里我们发送的是CameraDevice.TEMPLATE_PREVIEW也就是告诉相机我们只需要预览。更多参数如下

详细信息可以参考官网的API文档。

调用创建方法createCaptureSession(List outputs, CameraCaptureSession.StateCallback callback, Handler handler) 第一参数就是我们需要输出到的Surface列表,这里我们可以输出到一个SurfaceView中或者TextureView中。第二参数是对创建过程的一个回调方法,当onConfigured回调的时候说明CameraCaptureSession创建成功了。现在我们可以向CameraCaptureSession发送前面创建的好的预览相机请求了。调用mCaptureSession.setRepeatingRequest(mPreviewRequest,null, mBackgroundHandler); 这样我们就开启的相机的预览,在刚才添加的输出Surface对应的控件中我们可以看到摄像头的预览内容了。

拍照

当我们需要拍照并且得到相应的照片数据的时候和开启相机预览相同的操作,我们只需要向CameraCaptureSession发送我们创建好的请求就行,就像我们请求网络数据一样,封装好参数直接告诉CameraCaptureSession需要做什么由它去和相机建立通信并执行相应的操作。

对焦

/**

* 将焦点锁定为静态图像捕获的第一步。(对焦)

*/

private void lockFocus() {

try {

// 相机对焦

mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,

CameraMetadata.CONTROL_AF_TRIGGER_START);

// 修改状态

mState = STATE_WAITING_LOCK;

mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,

mBackgroundHandler);

} catch (CameraAccessException e) {

e.printStackTrace();

}

}

向CameraCaptureSession发送对焦请求,并且对对焦是否成功进行监听,在mCaptureCallback中对回调进行处理

/**

* 处理与JPEG捕获有关的事件

*/

private CameraCaptureSession.CaptureCallback mCaptureCallback

= new CameraCaptureSession.CaptureCallback() {

//处理

private void process(CaptureResult result) {

switch (mState) {

case STATE_PREVIEW: {

//预览状态

break;

}

case STATE_WAITING_LOCK: {

//等待对焦

Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);

if (afState == null) {

captureStillPicture();

} else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||

CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {

// CONTROL_AE_STATE can be null on some devices

Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);

if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {

mState = STATE_PICTURE_TAKEN;

//对焦完成

captureStillPicture();

} else {

runPrecaptureSequence();

}

}

break;

}

}

}

}

拍摄图片

对焦完成后我们就可以向CameraCaptureSession发送请求可以拍照了

/**

*

* 拍摄静态图片。

*/

private void captureStillPicture() {

try {

if ( null == mCameraDevice) {

return;

}

// 这是用来拍摄照片的CaptureRequest.Builder。

final CaptureRequest.Builder captureBuilder =

mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);

captureBuilder.addTarget(mImageReader.getSurface());

// 使用相同的AE和AF模式作为预览。

captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,

CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);

setAutoFlash(captureBuilder);

// 方向

int rotation = this.getWindowManager().getDefaultDisplay().getRotation();

captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));

CameraCaptureSession.CaptureCallback CaptureCallback

= new CameraCaptureSession.CaptureCallback() {

@Override

public void onCaptureCompleted(@NonNull CameraCaptureSession session,

@NonNull CaptureRequest request,

@NonNull TotalCaptureResult result) {

showToast("Saved: " + mFile);

Log.d(TAG, mFile.toString());

unlockFocus();

}

};

//停止连续取景

mCaptureSession.stopRepeating();

//捕获图片

mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);

} catch (CameraAccessException e) {

e.printStackTrace();

}

}

相信看到这里代码已经不复杂了,组装好我们的请求然后用CameraCaptureSession发送这个请求就可以了。这里需要注意的是我们怎么拿到图片数据呢? 这里要说回在创建CameraCaptureSession时参数不是有一个输出的Surface列表么,在列表中添加一个ImageReader的Surface用户获取图片数据

mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),null).....

在ImageReader中对图片获取就行监听

mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);

private final ImageReader.OnImageAvailableListener mOnImageAvailableListener

= new ImageReader.OnImageAvailableListener() {

@Override

public void onImageAvailable(ImageReader reader) {

//当图片可得到的时候获取图片并保存

mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile));

}

};

调用reader.acquireNextImage()我们就可以拿到当前的图片数据了。拍完后我们需要解锁焦点让相机回到预览状态,同样的我们发送请求就可以了

/**

* 解锁焦点

*/

private void unlockFocus() {

try {

// 重置自动对焦

mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,

CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);

setAutoFlash(mPreviewRequestBuilder);

mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,

mBackgroundHandler);

// 将相机恢复正常的预览状态。

mState = STATE_PREVIEW;

// 打开连续取景模式

mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback,

mBackgroundHandler);

} catch (CameraAccessException e) {

e.printStackTrace();

}

}

效果

1488625

GIF.gif

之前看了鸿神推送的Android 仿火萤视频桌面 神奇的LiveWallPaper 一文中提到了用相机来做壁纸也就是透明屏幕,项目地址https://github.com/songixan/Wallpaper 查看源码发现还是用的旧的CameraAPI,所以我在Demo中用Camera2API做了透明屏幕,有兴趣的可以去看下。 PS:后来在同事的小米2S(5.1.1)中测试发现出错了,初步猜测是分辨率的原因,目前正在解决中。有问题大家可以私信我 谢谢~

到此Camera2的学习就结束了,同样的如果你想用相机就行拍摄视频也是如此,用CameraCaptureSession发送相应的请求了就可以了,大家有兴趣可以做一做视频的拍摄。现在做微信的小10秒小视频拍摄也很简单了,思路很简单当用户按下拍摄按钮用CameraCaptureSession发送拍摄视频的请求,松开手指的时候相机恢复到预览状态。so easy~ 大家有兴趣可以尝试下。

作者:_小河马

链接:http://www.jianshu.com/p/73fed068a795

來源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/547464.shtml

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

相关文章

简单的小工具wordlight——让VS变量高亮起来

前段时间一直在使用matlab,今天需要使用vs2008,而用惯了matlab,习惯了其中一项选中变量高亮的设置,突然回来使用VS,感到各种不适应,顿时想到了一个词:矫情 呵呵,于是在网上找各种插…

CentOS 7上搭建Spark3.0.1+ Hadoop3.2.1分布式集群

CentOS 7上搭建Spark3.0.1 Hadoop3.2.1分布式集群 VMWare 安装CentOS 7使用Xshell连接虚拟机集群设置安装JDK 1.8SSH 免密登陆安装hadoop 3.2安装Spark 3.0.1总结VMWare 安装CentOS 7 推荐使用VMware Workstation Pro 16,下载安装即可。下载最新的CentOS 7 Minimal…

微软的SQLHelper类(含完整中文注释)

using System; using System.Data; using System.Xml; using System.Data.SqlClient; using System.Collections; namespace Book.DAL { /// <summary> /// SqlServer数据访问帮助类 /// </summary> public sealed class SqlHelper { #region 私有构造函数和方法 p…

再见安卓 你好鸿蒙,安卓,再见!你好,鸿蒙系统!

今年9月份&#xff0c;华为宣布鸿蒙OS操作系统将面向手机发布&#xff0c;今年12月份正式开放开发者Beta版本。昨天&#xff0c;华为鸿蒙OS迎来里程碑式的新进展&#xff0c;开发者Beta版本如约而至&#xff0c;正式开启线上公测招募&#xff0c;我们也可以可以一睹鸿蒙系统真容…

Kubernetes 部署 Traefik Ingress 控制器 (1.7.12)

目录[-] . 一、Ingress 介绍. 二、Traefik 介绍. 三、部署 Ingress 控制器 Traefik. 1、Traefik 两种部署方式介绍. 2、创建 Traefik 配置文件. 3、将 Traefik 配置文件挂载到 ConfigMap. 4、设置 CA 证书. 5、给节点设置 Label. 6、创建 Traefik 服务账户与角色权限. 7、创建…

Android (cocos2dx 网络访问)访问权限设置

Android开发应用程序时&#xff0c;如果应用程序需要访问网络权限,需要在 AndroidManifest.xml 中加入以下代码: 同样的如果用到其它的权限&#xff0c;也需要作出声明,部分权限列表如下: android.permission.ACCESS_CHECKIN_PROPERTIES 允许读写访问”properties”表在 checki…

获取页面所有属性并生成html6,JavaScript基础练习题(三)

一、单选题1.以下关于Javascript中事件的描述中&#xff0c;不正确的是A click——鼠标单击事件B focus——获取焦点事件C mouseover——鼠标指针移动到事件源对象上时触发的事件D change——选择字段时触发的事件正确答案: D解析:change事件为input内容发生改变时触发的事件。…

Git本地缓存问题 修改密码后git无法拉取

Git本地缓存问题 修改密码后git无法拉取 问题描述&#xff1a;使用正确的用户名和密码可以登录到Git代码仓库&#xff0c;但是在本地无法使用Git bash命令行的方式拉取代码。 问题原因&#xff1a;第一次使用Git bash方式拉取代码时&#xff0c;会根据当前的用户和密码生成一串…

Ext.Net常用方法

1、js&#xff08;Ext&#xff09;操作 Ext.Msg.alert(系统提示, 未连接血站&#xff0c;该功能暂时不能使用。); Ext.getCmp("id").getValue();Ext.getCmp("id").focus();Ext.getCmp("id").selectText(); //选中修改 if (!GridPanel1.hasSelec…

表单 单击怪异事件

<input class"big" name"gohome" type"button" id"gohome" value"回到主页" οnclick"gohome()" /> 如果上面的name、ID属性跟后面的单击事件所调用的函数名一样。则无效&#xff01;&#xff01;&#xff…

Pytorch 版YOLOV5训练自己的数据集

1、环境搭建 https://github.com/ultralytics/yolov5 2、安装需要的软件 pip install -U -r requirements.txt 3、准备数据 在data文件下建立上面三个文件&#xff08;Annotations、images与ImageSets&#xff0c;labels后续我们脚本生成&#xff09;其中Annotations存放xml…

使用SQL Server作业设置定时任务

1.开启SQL Server Agent服务 使用作业需要SQL Agent服务的支持&#xff0c;并且需要设置为自动启动&#xff0c;否则你的作业不会被执行。 以下步骤开启服务:开始-->>>运行-->>>输入"services.msc"-->>>进入服务,开启SQL Server Agent服…

ListView与GridView优化

前言 ListView是Android中最常用的控件&#xff0c;通过适配器来进行数据适配然后显示出来&#xff0c;而其性能是个很值得研究的话题。本文与你一起探讨Google I/O提供的优化Adapter方案&#xff0c;欢迎大家交流。 声明 欢迎转载&#xff0c;但请保留文章原始出处:) 博客园&…

2021安徽舒城中学高考成绩查询,2021安徽省地区高考成绩排名查询,安徽省高考各高中成绩喜报榜单...

怀宁中学芜湖2017年芜湖市高考文科头名是来自于芜湖师大附中的唐逸云&#xff0c;高考成绩653分。2017年芜湖市高考理科头名是来自于芜湖师大附中的茅志鹏&#xff0c;高考成绩676分。师大附中芜湖一中淮南淮南二中文科考生最高分631(市应届生第一名)&#xff0c;理科考生最高分…

CentOS7挂载nfs盘快速指南

A服务器做服务端&#xff1a; 1、安装nfs相关软件&#xff0c;实际上是基于rpc协议的 yum install -y nfs-utils rpcbind2、设置共享目录 mkdir -p /data/vi /etc/exports/data 192.168.139.71(rw,async,no_root_squash)#rw(来访者的权限&#xff0c;rw表示可读写&#xff0…

带毫秒的字符转换成时间(DateTime)格式的通用方法

C#自身有更好的方式&#xff0c;Net任意String格式转换为DateTime类型 原文 好久没更新日志了&#xff0c;添加个方法吧&#xff0c;本身没有什么技术可言&#xff0c;为了能方便大家&#xff0c;我稍微整理一下咯~ 带毫秒的字符转换成时间&#xff08;DateTime&#xff09…

陕西省2021年高考成绩结果查询,陕西招生考试信息网:2021年陕西高考成绩查询入口、查分系统...

【摘要】为了方便陕西高考考生能及时查询到2021年高考成绩&#xff0c;高考频道特别整理了的陕西招生考试信息网2021年陕西高考成绩查询入口、查分系统&#xff0c;考生可在成绩公布时直接点击下面的链接进行查分&#xff0c;预祝大家金榜题名&#xff01;自陕西招生考试信息网…

SpringBoot 2.3.x 分层构建 Docker 镜像实践

目录[-] . 一、什么是镜像分层. 二、SpringBoot 2.3.x 新增对分层的支持. 三、创建测试的 SpringBoot 应用. 1、Maven 中引入相关依赖和插件. 2、创建测试的 Controller 类. 3、创建 SpringBoot 启动类. 四、创建两种构建镜像的 Dockerfile 脚本. 1、普通镜像构建脚本文件 doc…

Net任意String格式转换为DateTime类型

方式一&#xff1a;Convert.ToDateTime(string) Convert.ToDateTime(string) 注意&#xff1a;string格式有要求&#xff0c;必须是yyyy-MM-dd hh:mm:ss 方式二&#xff1a;Convert.ToDateTime(string, IFormatProvider) DateTimeFormatInfo dtFormat new System.Globalizatio…