当前位置: 移动技术网 > IT编程>移动开发>Android > Android Camera2 实现预览功能

Android Camera2 实现预览功能

2019年08月01日  | 移动技术网IT编程  | 我要评论

北京印刷公司,永年佛光寺,鄂尔多斯新闻网

1. 概述

最近在做一些关于人脸识别的项目,需要用到 android 相机的预览功能。网上查阅相关资料后,发现 android 5.0 及以后的版本中,原有的 camera api 已经被 camera2 api 所取代。

全新的 camera2 在 camera 的基础上进行了改造,大幅提升了 android 系统的拍照功能。它通过以下几个类与方法来实现相机预览时的工作过程:

•cameramanager :摄像头管理器,主要用于检测系统摄像头、打开系统摄像头等;
•cameradevice : 用于描述系统摄像头,可用于关闭相机、创建相机会话、发送拍照请求等;
•cameracharacteristics :用于描述摄像头所支持的各种特性;
•cameracapturesession :当程序需要预览、拍照时,都需要先通过 cameracapturesession 来实现。该会话通过调用方法 setrepeatingrequest() 实现预览;
•camerarequest :代表一次捕获请求,用于描述捕获图片的各种参数设置;
•camerarequest.builder :负责生成 camerarequest 对象。

2. 相机预览

下面通过源码来讲解如何使用 camera2 来实现相机的预览功能。

2.1 相机权限设置

<uses-permission android:name="android.permission.camera" />

2.2 app 布局

•activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<framelayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/container"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="#000"
 tools:context=".mainactivity">
</framelayout>
•fragment_camera.xml
<?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:context=".camerafragment">
 <com.lightweh.camera2preview.autofittextureview
 android:id="@+id/textureview"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_centervertical="true"
 android:layout_centerhorizontal="true" />
</relativelayout>

2.3 相机自定义view

public class autofittextureview extends textureview {
 private int mratiowidth = 0;
 private int mratioheight = 0;
 public autofittextureview(context context) {
 this(context, null);
 }
 public autofittextureview(context context, attributeset attrs) {
 this(context, attrs, 0);
 }
 public autofittextureview(context context, attributeset attrs, int defstyle) {
 super(context, attrs, defstyle);
 }
 public void setaspectratio(int width, int height) {
 if (width < 0 || height < 0) {
 throw new illegalargumentexception("size cannot be negative.");
 }
 mratiowidth = width;
 mratioheight = height;
 requestlayout();
 }
 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
 super.onmeasure(widthmeasurespec, heightmeasurespec);
 int width = measurespec.getsize(widthmeasurespec);
 int height = measurespec.getsize(heightmeasurespec);
 if (0 == mratiowidth || 0 == mratioheight) {
 setmeasureddimension(width, height);
 } else {
 if (width < height * mratiowidth / mratioheight) {
 setmeasureddimension(width, width * mratioheight / mratiowidth);
 } else {
 setmeasureddimension(height * mratiowidth / mratioheight, height);
 }
 }
 }
}

2.4 动态申请相机权限

public class mainactivity extends appcompatactivity {
 private static final int request_permission = 1;
 @override
 protected void oncreate(bundle savedinstancestate) {
 super.oncreate(savedinstancestate);
 setcontentview(r.layout.activity_main);
 if (haspermission()) {
 if (null == savedinstancestate) {
 setfragment();
 }
 } else {
 requestpermission();
 }
 }
 @override
 public void onrequestpermissionsresult(int requestcode, string permissions[], int[] grantresults) {
 if (requestcode == request_permission) {
 if (grantresults.length == 1 && grantresults[0] == packagemanager.permission_granted) {
 setfragment();
 } else {
 requestpermission();
 }
 } else {
 super.onrequestpermissionsresult(requestcode, permissions, grantresults);
 }
 }
 // 权限判断,当系统版本大于23时,才有必要判断是否获取权限
 private boolean haspermission() {
 if (build.version.sdk_int >= build.version_codes.m) {
 return checkselfpermission(manifest.permission.camera) == packagemanager.permission_granted;
 } else {
 return true;
 }
 }
 // 请求相机权限
 private void requestpermission() {
 if (build.version.sdk_int >= build.version_codes.m) {
 if (shouldshowrequestpermissionrationale(manifest.permission.camera)) {
 toast.maketext(mainactivity.this, "camera permission are required for this demo", toast.length_long).show();
 }
 requestpermissions(new string[]{manifest.permission.camera}, request_permission);
 }
 }
 // 启动相机fragment
 private void setfragment() {
 getsupportfragmentmanager()
 .begintransaction()
 .replace(r.id.container, camerafragment.newinstance())
 .commitnowallowingstateloss();
 }
}

2.5 开启相机预览

首先,在onresume()中,我们需要开启一个 handlerthread,然后利用该线程的 looper 对象构建一个 handler 用于相机回调。

@override
public void onresume() {
 super.onresume();
 startbackgroundthread();

 // when the screen is turned off and turned back on, the surfacetexture is 
 // already available, and "onsurfacetextureavailable" will not be called. in 
 // that case, we can open a camera and start preview from here (otherwise, we 
 // wait until the surface is ready in the surfacetexturelistener).
 if (mtextureview.isavailable()) {
 opencamera(mtextureview.getwidth(), mtextureview.getheight());
 } else {
 mtextureview.setsurfacetexturelistener(msurfacetexturelistener);
 }
}
private void startbackgroundthread() {
 mbackgroundthread = new handlerthread("camerabackground");
 mbackgroundthread.start();
 mbackgroundhandler = new handler(mbackgroundthread.getlooper());
}

同时,在 onpause() 中有对应的 handlerthread 关闭方法。

当屏幕关闭后重新开启,surfacetexture 已经就绪,此时不会触发 onsurfacetextureavailable 回调。因此,我们判断 mtextureview 如果可用,则直接打开相机,否则等待 surfacetexture 回调就绪后再开启相机。

private void opencamera(int width, int height) {
 if (contextcompat.checkselfpermission(getactivity(), manifest.permission.camera)
 != packagemanager.permission_granted) {
 return;
 }
 setupcameraoutputs(width, height);
 configuretransform(width, height);
 activity activity = getactivity();
 cameramanager manager = (cameramanager) activity.getsystemservice(context.camera_service);
 try {
 if (!mcameraopencloselock.tryacquire(2500, timeunit.milliseconds)) {
 throw new runtimeexception("time out waiting to lock camera opening.");
 }
 manager.opencamera(mcameraid, mstatecallback, mbackgroundhandler);
 } catch (cameraaccessexception e) {
 e.printstacktrace();
 } catch (interruptedexception e) {
 throw new runtimeexception("interrupted while trying to lock camera opening.", e);
 }
}

开启相机时,我们首先判断是否具备相机权限,然后调用 setupcameraoutputs 函数对相机参数进行设置(包括指定摄像头、相机预览方向以及预览尺寸的设定等),接下来调用 configuretransform 函数对预览图片的大小和方向进行调整,最后获取 cameramanager 对象开启相机。因为相机有可能会被其他进程同时访问,所以在开启相机时需要加锁。

private final cameradevice.statecallback mstatecallback = new cameradevice.statecallback() {
 @override
 public void onopened(@nonnull cameradevice cameradevice) {
 mcameraopencloselock.release();
 mcameradevice = cameradevice;
 createcamerapreviewsession();
 }
 @override
 public void ondisconnected(@nonnull cameradevice cameradevice) {
 mcameraopencloselock.release();
 cameradevice.close();
 mcameradevice = null;
 }
 @override
 public void onerror(@nonnull cameradevice cameradevice, int error) {
 mcameraopencloselock.release();
 cameradevice.close();
 mcameradevice = null;
 activity activity = getactivity();
 if (null != activity) {
 activity.finish();
 }
 }
};

相机开启时还会指定相机的状态变化回调函数 mstatecallback,如果相机成功开启,则开始创建相机预览会话。

private void createcamerapreviewsession() {
 try {
 // 获取 texture 实例
 surfacetexture texture = mtextureview.getsurfacetexture();
 assert texture != null;
 // 设置 textureview 缓冲区大小
 texture.setdefaultbuffersize(mpreviewsize.getwidth(), mpreviewsize.getheight());
 // 获取 surface 显示预览数据
 surface surface = new surface(texture);
 // 构建适合相机预览的请求
 mpreviewrequestbuilder = mcameradevice.createcapturerequest(cameradevice.template_preview);
 // 设置 surface 作为预览数据的显示界面
 mpreviewrequestbuilder.addtarget(surface);
 // 创建相机捕获会话用于预览
 mcameradevice.createcapturesession(arrays.aslist(surface),
 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);
  mpreviewrequest = mpreviewrequestbuilder.build();
  // 设置反复捕获数据的请求,预览界面一直显示画面
  mcapturesession.setrepeatingrequest(mpreviewrequest,
   null, mbackgroundhandler);
  } catch (cameraaccessexception e) {
  e.printstacktrace();
  }
  }
  @override
  public void onconfigurefailed(
  @nonnull cameracapturesession cameracapturesession) {
  showtoast("failed");
  }
 }, null
 );
 } catch (cameraaccessexception e) {
 e.printstacktrace();
 }
}

以上便是 camera2 api 实现相机预览的主要过程。

3. demo 源码

github:camera2preview

4. 参考

https://github.com/googlesamples/android-camera2basic  

总结

以上所述是小编给大家介绍的android camera2 实现预览功能,希望对大家有所帮助

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网