原文:Android Camera2拍照(一)——使用SurfaceView

Camera2 API简介

Android 从5.0(21)开始,引入了新的Camera API Camera2,原来的android.hardware.Camera被废弃(下面称为Camera1),还有一个android.graphics.Camera,这个android.graphics.Camera不是用来照相的,是用来处理图像的,可以做出3D的图像效果之类的,之前的Camera1则由android.hardware.Camera代替。Camera2支持RAW输出,可以调节曝光,对焦模式,快门等,功能比原先Camera强大。

这里引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送 Capture 请求,而摄像头会返回 CameraMetadata。这一切建立在一个叫作 CameraCaptureSession 的会话中。

  • CameraManaer 摄像头管理器,用于检测摄像头,打开系统摄像头,调用CameraManager.getCameraCharacteristics(String)可以获取指定摄像头的相关特性
  • CameraCharacteristics 摄像头的特性
  • CameraDevice 摄像头,类似android.hardware.Camera也就是Camera1的Camera
  • CameraCaptureSession 这个类控制摄像头的预览或者拍照,setRepeatingRequest()开启预览,capture()拍照,CameraCaptureSession提供了StateCallback、CaptureCallback两个接口来监听CameraCaptureSession的创建和拍照过程。
  • CameraRequest和CameraRequest.Builder,预览或者拍照时,都需要一个CameraRequest对象。CameraRequest表示一次捕获请求,用来对照片的各种参数设置,比如对焦模式、曝光模式等。CameraRequest.Builder用来生成CameraRequest对象。

Camera2的简单使用(使用SurfaceView)

主要步骤:

  1. 获得摄像头管理器CameraManager mCameraManager,mCameraManager.openCamera()来打开摄像头
  2. 指定要打开的摄像头,并创建openCamera()所需要的CameraDevice.StateCallback stateCallback
  3. 在CameraDevice.StateCallback stateCallback中调用takePreview(),这个方法中,使用CaptureRequest.Builder创建预览需要的CameraRequest,并初始化了CameraCaptureSession,最后调用了setRepeatingRequest(previewRequest, null, childHandler)进行了预览
  4. 点击拍照按钮,调用takePicture(),这个方法内,最终调用了capture(mCaptureRequest, null, childHandler)
  5. 在new ImageReader.OnImageAvailableListener(){}回调方法中,将拍照拿到的图片进行展示

  1. package org.hunter.a361camera.view;
  2.  
  3. import android.Manifest;
  4. import android.content.Context;
  5. import android.content.pm.PackageManager;
  6. import android.graphics.Bitmap;
  7. import android.graphics.BitmapFactory;
  8. import android.graphics.ImageFormat;
  9. import android.hardware.camera2.CameraAccessException;
  10. import android.hardware.camera2.CameraCaptureSession;
  11. import android.hardware.camera2.CameraCharacteristics;
  12. import android.hardware.camera2.CameraDevice;
  13. import android.hardware.camera2.CameraManager;
  14. import android.hardware.camera2.CaptureRequest;
  15. import android.media.Image;
  16. import android.media.ImageReader;
  17. import android.os.Build;
  18. import android.os.Bundle;
  19. import android.os.Handler;
  20. import android.os.HandlerThread;
  21. import android.support.annotation.Nullable;
  22. import android.support.annotation.RequiresApi;
  23. import android.support.v4.app.ActivityCompat;
  24. import android.support.v4.app.Fragment;
  25. import android.util.SparseIntArray;
  26. import android.view.LayoutInflater;
  27. import android.view.Surface;
  28. import android.view.SurfaceHolder;
  29. import android.view.SurfaceView;
  30. import android.view.View;
  31. import android.view.ViewGroup;
  32. import android.widget.ImageView;
  33. import android.widget.Toast;
  34.  
  35. import org.hunter.a361camera.R;
  36.  
  37. import java.nio.ByteBuffer;
  38. import java.util.Arrays;
  39.  
  40. import static android.os.Looper.getMainLooper;
  41.  
  42. /**
  43. * Main UI for the statistics screen.
  44. */
  45. public class CameraFragment extends Fragment {
  46. public static final int REQUEST_CAMERA_CODE = 100;
  47. public static final String PACKAGE = "package:";
  48.  
  49. private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
  50.  
  51. ///为了使照片竖直显示
  52. static {
  53. ORIENTATIONS.append(Surface.ROTATION_0, 90);
  54. ORIENTATIONS.append(Surface.ROTATION_90, 0);
  55. ORIENTATIONS.append(Surface.ROTATION_180, 270);
  56. ORIENTATIONS.append(Surface.ROTATION_270, 180);
  57. }
  58.  
  59. private SurfaceView mSurfaceView;
  60. private SurfaceHolder mSurfaceHolder;
  61. private ImageView iv_show;
  62. private ImageView mCatture;
  63. private CameraManager mCameraManager;//摄像头管理器
  64. private Handler childHandler, mainHandler;
  65. private String mCameraID;//摄像头Id 0 为后 1 为前
  66. private ImageReader mImageReader;
  67. private CameraCaptureSession mCameraCaptureSession;
  68. private CameraDevice mCameraDevice;
  69. /**
  70. * 摄像头创建监听
  71. */
  72. private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
  73. @Override
  74. public void onOpened(CameraDevice camera) {//打开摄像头
  75. mCameraDevice = camera;
  76. //开启预览
  77. takePreview();
  78. }
  79.  
  80. @Override
  81. public void onDisconnected(CameraDevice camera) {//关闭摄像头
  82. if (null != mCameraDevice) {
  83. mCameraDevice.close();
  84. CameraFragment.this.mCameraDevice = null;
  85. }
  86. }
  87.  
  88. @Override
  89. public void onError(CameraDevice camera, int error) {//发生错误
  90. Toast.makeText(getContext(), "摄像头开启失败", Toast.LENGTH_SHORT).show();
  91. }
  92. };
  93.  
  94. public static CameraFragment newInstance() {
  95. return new CameraFragment();
  96. }
  97.  
  98. @Nullable
  99. @Override
  100. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  101. Bundle savedInstanceState) {
  102. View root = inflater.inflate(R.layout.camera_frag, container, false);
  103. initVIew(root);
  104. initListener();
  105. return root;
  106. }
  107.  
  108. @Override
  109. public void onCreate(Bundle savedInstanceState) {
  110. super.onCreate(savedInstanceState);
  111. }
  112.  
  113. @Override
  114. public void onResume() {
  115. super.onResume();
  116. }
  117.  
  118. /**
  119. * 初始化
  120. */
  121. private void initVIew(View root) {
  122. iv_show = (ImageView) root.findViewById(R.id.iv_show_camera2);
  123. //mSurfaceView
  124. mSurfaceView = (SurfaceView) root.findViewById(R.id.surface_view_camera2);
  125. mCatture = (ImageView) root.findViewById(R.id.capture);
  126. mSurfaceHolder = mSurfaceView.getHolder();
  127. mSurfaceHolder.setKeepScreenOn(true);
  128. // mSurfaceView添加回调
  129. mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
  130. @Override
  131. public void surfaceCreated(SurfaceHolder holder) { //SurfaceView创建
  132. // 初始化Camera
  133. initCamera2();
  134. }
  135.  
  136. @Override
  137. public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
  138. }
  139.  
  140. @Override
  141. public void surfaceDestroyed(SurfaceHolder holder) { //SurfaceView销毁
  142. // 释放Camera资源
  143. if (null != mCameraDevice) {
  144. mCameraDevice.close();
  145. CameraFragment.this.mCameraDevice = null;
  146. }
  147. }
  148. });
  149. }
  150.  
  151. /**
  152. * 初始化Camera2
  153. */
  154. @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
  155. private void initCamera2() {
  156. HandlerThread handlerThread = new HandlerThread("Camera2");
  157. handlerThread.start();
  158. childHandler = new Handler(handlerThread.getLooper());
  159. mainHandler = new Handler(getMainLooper());
  160. mCameraID = "" + CameraCharacteristics.LENS_FACING_FRONT;//后摄像头
  161. mImageReader = ImageReader.newInstance(1080, 1920, ImageFormat.JPEG, 1);
  162. mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { //可以在这里处理拍照得到的临时照片 例如,写入本地
  163. @Override
  164. public void onImageAvailable(ImageReader reader) {
  165. //mCameraDevice.close();
  166. // 拿到拍照照片数据
  167. Image image = reader.acquireNextImage();
  168. ByteBuffer buffer = image.getPlanes()[0].getBuffer();
  169. byte[] bytes = new byte[buffer.remaining()];
  170. buffer.get(bytes);//由缓冲区存入字节数组
  171. final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
  172. if (bitmap != null) {
  173. iv_show.setImageBitmap(bitmap);
  174. }
  175. }
  176. }, mainHandler);
  177. //获取摄像头管理
  178. mCameraManager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE);
  179. try {
  180. if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
  181. //申请WRITE_EXTERNAL_STORAGE权限
  182. requestPermissions(new String[]{Manifest.permission.CAMERA},
  183. REQUEST_CAMERA_CODE);
  184. //return;
  185. } else {
  186. //打开摄像头
  187. mCameraManager.openCamera(mCameraID, stateCallback, mainHandler);
  188. }
  189. } catch (CameraAccessException e) {
  190. e.printStackTrace();
  191. }
  192. }
  193.  
  194. @Override
  195. public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
  196. super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  197. if (requestCode == REQUEST_CAMERA_CODE) {
  198. if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
  199. // Permission Granted
  200. try {
  201. mCameraManager.openCamera(mCameraID, stateCallback, mainHandler);
  202. } catch (CameraAccessException e) {
  203. e.printStackTrace();
  204. } catch (SecurityException e) {
  205. e.printStackTrace();
  206. }
  207. } else {
  208. // Permission Denied
  209. }
  210. }
  211. }
  212.  
  213. /**
  214. * 开始预览
  215. */
  216. private void takePreview() {
  217. try {
  218. // 创建预览需要的CaptureRequest.Builder
  219. final CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
  220. // 将SurfaceView的surface作为CaptureRequest.Builder的目标
  221. previewRequestBuilder.addTarget(mSurfaceHolder.getSurface());
  222. // 创建CameraCaptureSession,该对象负责管理处理预览请求和拍照请求
  223. mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceHolder.getSurface(), mImageReader.getSurface()), new CameraCaptureSession.StateCallback() // ③
  224. {
  225. @Override
  226. public void onConfigured(CameraCaptureSession cameraCaptureSession) {
  227. if (null == mCameraDevice) return;
  228. // 当摄像头已经准备好时,开始显示预览
  229. mCameraCaptureSession = cameraCaptureSession;
  230. try {
  231. // 自动对焦
  232. previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
  233. // 打开闪光灯
  234. previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
  235. // 显示预览
  236. CaptureRequest previewRequest = previewRequestBuilder.build();
  237. mCameraCaptureSession.setRepeatingRequest(previewRequest, null, childHandler);
  238. } catch (CameraAccessException e) {
  239. e.printStackTrace();
  240. }
  241. }
  242.  
  243. @Override
  244. public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
  245. Toast.makeText(getContext(), "配置失败", Toast.LENGTH_SHORT).show();
  246. }
  247. }, childHandler);
  248. } catch (CameraAccessException e) {
  249. e.printStackTrace();
  250. }
  251. }
  252.  
  253. /**
  254. * 拍照
  255. */
  256. private void takePicture() {
  257. if (mCameraDevice == null) return;
  258. // 创建拍照需要的CaptureRequest.Builder
  259. final CaptureRequest.Builder captureRequestBuilder;
  260. try {
  261. captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
  262. // 将imageReader的surface作为CaptureRequest.Builder的目标
  263. captureRequestBuilder.addTarget(mImageReader.getSurface());
  264. // 自动对焦
  265. captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
  266. // 自动曝光
  267. captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
  268. // 获取手机方向
  269. int rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();
  270. // 根据设备方向计算设置照片的方向
  271. captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
  272. //拍照
  273. CaptureRequest mCaptureRequest = captureRequestBuilder.build();
  274. mCameraCaptureSession.capture(mCaptureRequest, null, childHandler);
  275. } catch (CameraAccessException e) {
  276. e.printStackTrace();
  277. }
  278. }
  279.  
  280. private void initListener() {
  281. mCatture.setOnClickListener(new View.OnClickListener() {
  282. @Override
  283. public void onClick(View v) {
  284. takePicture();
  285. }
  286. });
  287. }
  288. }

布局文件:

  1. <?xml version="1.0" encoding="utf-8"?>
  2.  
  3. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent">
  6.  
  7. <SurfaceView
  8. android:id="@+id/surface_view_camera2"
  9. android:layout_width="match_parent"
  10. android:layout_height="match_parent"/>
  11.  
  12. <org.hunter.a361camera.widget.PorterDuffViewImageView
  13. android:id="@+id/capture"
  14. android:layout_width="90dp"
  15. android:layout_height="90dp"
  16. android:layout_alignParentBottom="true"
  17. android:layout_centerHorizontal="true"
  18. android:layout_marginBottom="20dp"
  19. android:src="@mipmap/capture"/>
  20.  
  21. <org.hunter.a361camera.widget.PorterDuffViewImageView
  22. android:id="@+id/iv_show_camera2"
  23. android:layout_width="80dp"
  24. android:layout_height="80dp"
  25. android:layout_alignParentBottom="true"
  26. android:layout_alignParentRight="true"
  27. android:layout_marginBottom="20dp"
  28. android:layout_marginRight="20dp"
  29. android:scaleType="centerCrop"/>
  30. </RelativeLayout>

源代码地址:https://github.com/gengqifu/361Camera。欢迎fork/star

Android Camera2拍照(一)——使用SurfaceView的更多相关文章

  1. Android Camera2 拍照(二)——使用TextureView

    原文:Android Camera2 拍照(二)--使用TextureView 上一篇博文简单介绍了使用Camera2 API拍摄照片,并使用SurfaceView作为预览界面.实际上,相对于Surf ...

  2. Android Camera2 拍照入门学习

    原文:Android Camera2 拍照入门学习 学习资料: 肾虚将军android camera2 详解说明 极客学院android.hardware.camera2 使用指南 Android 5 ...

  3. Android Camera2 拍照(三)——切换摄像头,延时拍摄和闪光模式

    原文:Android Camera2 拍照(三)--切换摄像头,延时拍摄和闪光模式 一.切换摄像头 在前后摄像头之间切换,首先需要关闭之前打开的摄像头,关闭preview,之后重新打开新的摄像头,重新 ...

  4. Android Camera2 拍照(四)——对焦模式

    原文:Android Camera2 拍照(四)--对焦模式 本篇将重点介绍使用Camera2 API进行手动对焦的设置,以及在手动对焦与自动对焦模式之间切换. 一.手动对焦响应事件 首先我们要实现点 ...

  5. Android Camera2 预览,拍照,人脸检测并实时展现

    https://www.jianshu.com/p/5414ba2b5508 背景     最近需要做一个人脸检测并实时预览的功能.就是边检测人脸,边在预览界面上框出来.     当然本人并不是专门做 ...

  6. android Camera2 API使用详解

    原文:android Camera2 API使用详解 由于最近需要使用相机拍照等功能,鉴于老旧的相机API问题多多,而且新的设备都是基于安卓5.0以上的,于是本人决定研究一下安卓5.0新引入的Came ...

  7. android自定义拍照

    调用系统相机,然后在自己的surfaceview上预览,拍照,不废话,直接上源码 package com.example.customecamera; import java.io.File; imp ...

  8. Android摄像头:只拍摄SurfaceView预览界面特定区域内容(矩形框)---完整(原理:底层SurfaceView+上层绘制ImageView)

    Android摄像头:只拍摄SurfaceView预览界面特定区域内容(矩形框)---完整实现(原理:底层SurfaceView+上层绘制ImageView) 分类: Android开发 Androi ...

  9. Android Camera2采集摄像头原始数据并手动预览

    Android Camera2采集摄像头原始数据并手动预览 最近研究了一下android摄像头开发相关的技术,也看了Google提供的Camera2Basic调用示例,以及网上一部分代码,但都是在Te ...

随机推荐

  1. Java 学习(22):Java MySQL 连接

    Java MySQL 连接 本章节我们为大家介绍 Java 如何使用 使用 JDBC 连接 MySQL 数据库. Java 连接 MySQL 需要驱动包,最新版下载地址为:http://dev.mys ...

  2. iOS writeTofile 和对象的序列化

    前言:做了一个图片浏览的小demo,支持随意添加.删除图片,图片放大.缩小,带矩形框的截图.随后几篇博客都会详细讲解在此过程中遇到的各种问题.这篇主要讲,在做添加.删除这个功能时,遇到的存文件的问题. ...

  3. java 替换json字符串中间的引号保留两边的引号,避免json校验失败

    一.json概要 JSON(JavaScript Object Notation, JS 对象标记)-一种轻量级的数据交换标准(相对xml),独立于编程语言.具体以逗号分隔的key:value键值对的 ...

  4. [Django] Creating an app, models and database

    To add a new app, first cd to the project. Then run: python manage.py startapp scrumboard After that ...

  5. HDU 4870 Rating 高斯消元法

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=4870 题意:用两个账号去參加一种比赛,初始状态下两个账号都是零分,每次比赛都用分数低的账号去比赛.有P的概 ...

  6. PCI的imagework已由freeview软件代替

    作者:朱金灿 来源:http://blog.csdn.net/clever101 在PCI 9.1中重要模块集成显示环境imagework还存在,但是到了PCI 10.0中imagework已经消失了 ...

  7. hadoop 3.x 启动过程中 Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).

    出现这种状况是因为当前账号没有配置ssh免密登录 进入到以下目录,查看是否生成过秘钥对,如果有的话直接ssh-copy-id 主机名 没有的话执行ssh-keygen -t rsa后再重新执行ssh- ...

  8. 数据可视化 —— 数据流图(Data Flow Diagram)

    数据流图(Data Flow Diagram):简称 DFD,它从数据传递和加工角度,以图形方式来表达系统的逻辑功能.数据在系统内部的逻辑流向和逻辑变换过程,是结构化系统分析方法的主要表达工具及用于表 ...

  9. 如何在华为云软件开发云上运行Python

    一. 华为云软件开发云与Python 1. 华为云软件开发云简介 华为云软件开发云(DevCloud)是集华为近30年研发实践,前沿研发理念,先进研发工具为一体的一站式云端DevOps平台,面向开发者 ...

  10. 一个自己犯的react错误

    在看<react小书>高阶组件一节的时候,看到如下代码 import React, { Component } from 'react' export default (WrappedCo ...