说明:camera子系统 系列文章针对Android10.0系统,主要针对 camera API2 + HAL3 框架进行解读。

1 预览流程简要解读

@1 由 CameraManager.openCamera 方法,打开摄像头。该方法传入三个参数:

  • String CameraId:传入要打开的摄像头的 Id
  • CameraDevice.stateCallback:即 CameraDevice.stateCallback 的实例
  • Handler:代表执行callback的handler,如果程序希望直接在当前线程中执行callback,则可以将handler参数设为null

关键代码如下所示:

//camera process,step1 打开camera
mCameraManager.openCamera(mCameraId, mStateCallback, mCameraHandler);

@2 实例化CameraDevice.statCallback,并重写 onOpened 方法,该方法在摄像 头完成打开动作后调用,以下动作均在 onOpened 方法中完成,onOpened方法,会返回当前使用的CameraDevice。关键代码如下所示:

//camera process,step2 mStateCallback 实例化private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {@Overridepublic void onOpened(CameraDevice camera) {//重写onOpened方法,最为关键mCameraOpenCloseLock.release();mCameraDevice = camera;startCaptureSession();}@Overridepublic void onDisconnected(CameraDevice camera) {//...}@Overridepublic void onError(CameraDevice camera, int error) {//...}};

@3 调用 CameraDevice.CreateCapture(int templateType),创建一个 CaptureRequest.Builder,templateType来区分是拍照还是预览,当预览时,我们传入CameraDevice.TEMPLATE_PREVIEW参数,该方法回返回一个对于返回的对象,我们声明一个 CaptureRequest.Builder类型的mCaptureRequestBuilder变量来接收。关键代码如下所示:

//camera process,step3 创建一个 CaptureRequest.Builder,templateType来区分是拍照还是预览
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

@4 调用mCaptureRequestBuilder.addTarget()方法,将显示预览用的surface的实例传入,即将显示预览用的 surface 的实例,作为一个显示层添加到该 请求的目标列表中。关键代码如下所示:

//camera process,step4 将显示预览用的surface的实例传入,即将显示预览用的 surface 的实例,作为一个显示层添加到该 请求的目标列表中
mPreviewRequestBuilder.addTarget(mImageReader.getSurface());

@5 调用 CameraDevice.CreateCaptureSession()方法,该方法传入三个参数:

  • List<Surface>:可用于捕获目标图像数据的 Surface 集合,此处传入用于显示预览图像的 Surface 即可,即 Arrays.asList(surface)
  • CameraCaptureSession.stateCallback:该回调的实例
  • Handler:一个句柄,代表执行 callback 的 handler,如果程序希望在当前线程执行callback,可以将 Handler 参数设为 null。
//camera process,step5 将显示预览用的surface的实例传入,即将显示预览用的surface的实例,作为一个显示层添加到该请求的目标列表中
surfaceList = Arrays.asList(surface, mImageReader.getSurface());

@6 调用 CameraDevice.CreateCaptureSession()方法。该方法传入三个参数:

  • List<Surface>:可用于捕获目标图像数据的Surface集合,此处传入用于显示预览图像的 Surface 即可,即 Arrays.asList(surface) CameraCaptureSession.
  • stateCallback:该回调的实例
  • Handler:代表执行callback的handler,如果程序希望在当前线程执行callback,可以将 Handler 参数设为 null

关键代码整合在8中,因为 6 7 8 代码紧密相关。

@7 实例化 CameraCaptureSession.stateCallback,并重写 onConfigured 方法,该方法在摄像头设备完成自身配置的时候调用,该方法会从底层传回一个 CameraCaptureSession,该 session 可以开始处理捕获请求。

关键代码整合在8中,因为 6 7 8 代码紧密相关。

@8 调用 CameraCaptureSession.setRepeatingRequest()方法创建预览,此方法 接收三个参数:

  • CaptureReqeust:无限重复的请求,此处传入上一步中中创建的captureRequest对象,即mCaptureRequest
  • CameraCaptureSession.CaptureCallback:callback的回调,不过预览中该回调中不用进行任何处理
  • Handler:代表执行callback的handler,如果程序希望直接在 当前线程中执行callback,则可以将handler参数设为 null

关键代码如下所示:

//camera process,step6 & 7
// 6 执行createCaptureSession方法
// 7 参数中实例化 CameraCaptureSession.stateCallback,并重写 onConfigured 方法
mCameraDevice.createCaptureSession(surfaceList, new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(CameraCaptureSession session) {mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);mCaptureSession = session;//camera process,step8 用 CameraCaptureSession.setRepeatingRequest()方法创建预览mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, null);}@Overridepublic void onConfigureFailed(CameraCaptureSession session) {//...}@Overridepublic void onClosed(CameraCaptureSession session) {//...}
}, mCameraHandler);

2 camera预览流程代码完整解读

2.1 java源码部分(草稿)

Camera流程相关代码如下所示:

class CameraCoreManager {private static final String TAG = "CameraDemo";private Context mContext;private CameraManager mCameraManager;private String mCameraId;private HandlerThread mCameraThread;private Handler mCameraHandler;private Handler mImageHandler;private ImageReader mImageReader;private CameraDevice mCameraDevice;private CameraCharacteristics mCameraCharacteristics;//Max preview width&height that is guaranteed by Camera2 APIprivate static final int MAX_PREVIEW_WIDTH = 1920;private static final int MAX_PREVIEW_HEIGHT = 1080;//A Semaphore to prevent the app from exiting before closing the camera.private Semaphore mCameraOpenCloseLock = new Semaphore(1);private Size mPreviewSize = new Size(1920, 1080);private CaptureRequest.Builder mPreviewRequestBuilder;private CameraCaptureSession mCaptureSession;private int mFacing = CameraCharacteristics.LENS_FACING_BACK;private Choreographer.FrameCallback mFrameCallback;private SurfaceTexture mSurfaceTexture;private ImageReader.OnImageAvailableListener mImageAvailableListener = new ImageReader.OnImageAvailableListener() {@Overridepublic void onImageAvailable(ImageReader reader) {Image image = reader.acquireNextImage();Log.d(TAG, "##### onFrame: " + image.getPlanes());image.close();}};//camera process,step2 mStateCallback 实例化private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {@Overridepublic void onOpened(CameraDevice camera) {//重写onOpened方法,最为关键mCameraOpenCloseLock.release();mCameraDevice = camera;startCaptureSession();}@Overridepublic void onDisconnected(CameraDevice camera) {mCameraOpenCloseLock.release();camera.close();mCameraDevice = null;}@Overridepublic void onError(CameraDevice camera, int error) {Log.e("DEBUG", "onError: " + error);mCameraOpenCloseLock.release();camera.close();mCameraDevice = null;Log.e("DEBUG", "onError:  restart camera");stopPreview();startPreview();}};public CameraCoreManager(Context context) {mContext = context;mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);}public void startPreview() {if (!chooseCameraIdByFacing()) {Log.e(TAG, "Choose camera failed.");return;}mCameraThread = new HandlerThread("CameraThread");mCameraThread.start();mCameraHandler = new Handler(mCameraThread.getLooper());prepareImageReader();openCamera();}public void stopPreview() {closeCamera();if (mCameraThread != null) {mCameraThread.quitSafely();mCameraThread = null;}mCameraHandler = null;}private void prepareImageReader() {if (mImageReader != null) {mImageReader.close();}mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.YUV_420_888, 1);mImageReader.setOnImageAvailableListener(mImageAvailableListener, mCameraHandler);}private boolean chooseCameraIdByFacing() {try {String ids[] = mCameraManager.getCameraIdList();if (ids.length == 0) {Log.e(TAG, "No available camera.");return false;}for (String cameraId : mCameraManager.getCameraIdList()) {CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(cameraId);StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);if (map == null) {continue;}Integer level = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);if (level == null || level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {continue;}Integer internal = characteristics.get(CameraCharacteristics.LENS_FACING);if (internal == null) {continue;}if (internal == mFacing) {mCameraId = cameraId;mCameraCharacteristics = characteristics;return true;}}mCameraId = ids[1];mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);Integer level = mCameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);if (level == null || level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {return false;}Integer internal = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING);if (internal == null) {return false;}mFacing = CameraCharacteristics.LENS_FACING_BACK;} catch (CameraAccessException e) {e.printStackTrace();}return true;}@SuppressLint("MissingPermission")public void openCamera() {if (TextUtils.isEmpty(mCameraId)) {Log.e(TAG, "Open camera failed. No camera available");return;}try {if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {throw new RuntimeException("Time out waiting to lock camera opening.");}//camera process,step1 打开cameramCameraManager.openCamera(mCameraId, mStateCallback, mCameraHandler);} catch (InterruptedException | CameraAccessException e) {Log.e(TAG, e.getMessage());}}private void closeCamera() {try {mCameraOpenCloseLock.acquire();if (mCaptureSession != null) {mCaptureSession.close();mCaptureSession = null;}if (mCameraDevice != null) {mCameraDevice.close();mCameraDevice = null;}if (mImageReader != null) {mImageReader.close();mImageReader = null;}} catch (InterruptedException e) {throw new RuntimeException("Interrupted while trying to lock camera closing.", e);} finally {mCameraOpenCloseLock.release();}}private void startCaptureSession() {if (mCameraDevice == null) {return;}if ((mImageReader != null || mSurfaceTexture != null)) {try {//camera process,step3 创建一个 CaptureRequest.Builder,templateType来区分是拍照还是预览mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);//camera process,step4 将显示预览用的surface的实例传入,即将显示预览用的 surface 的实例,作为一个显示层添加到该 请求的目标列表中mPreviewRequestBuilder.addTarget(mImageReader.getSurface());List<Surface> surfaceList = Arrays.asList(mImageReader.getSurface());if (mSurfaceTexture != null) {mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());Surface surface = new Surface(mSurfaceTexture);mPreviewRequestBuilder.addTarget(mImageReader.getSurface());mPreviewRequestBuilder.addTarget(surface);//camera process,step5 将显示预览用的surface的实例传入,即将显示预览用的surface的实例,作为一个显示层添加到该请求的目标列表中surfaceList = Arrays.asList(surface, mImageReader.getSurface());}Range<Integer>[] fpsRanges = mCameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);Log.d("DEBUG", "##### fpsRange: " + Arrays.toString(fpsRanges));//camera process,step6 & 7// 6 执行createCaptureSession方法// 7 参数中实例化 CameraCaptureSession.stateCallback,并重写 onConfigured 方法mCameraDevice.createCaptureSession(surfaceList, new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(CameraCaptureSession session) {if (mCameraDevice == null)return;mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);mCaptureSession = session;try {if (mCaptureSession != null)//camera process,step8 用 CameraCaptureSession.setRepeatingRequest()方法创建预览mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, null);} catch (CameraAccessException | IllegalArgumentException | IllegalStateException | NullPointerException e) {e.printStackTrace();}}@Overridepublic void onConfigureFailed(CameraCaptureSession session) {Log.e(TAG, "Failed to configure capture session");}@Overridepublic void onClosed(CameraCaptureSession session) {if (mCaptureSession != null && mCaptureSession.equals(session)) {mCaptureSession = null;}}}, mCameraHandler);} catch (CameraAccessException e) {e.printStackTrace();Log.e(TAG, e.getMessage());} catch (IllegalStateException e) {stopPreview();startPreview();} catch (UnsupportedOperationException e) {e.printStackTrace();Log.e(TAG, e.getMessage());}}}public interface FrameCallback {void onFrame(Image data);}public Size getPreviewSize() {return mPreviewSize;}public void setPreviewSize(Size previewSize) {mPreviewSize = previewSize;}public FrameCallback getFrameCallback() {return (FrameCallback) mFrameCallback;}public void setFrameCallback(FrameCallback frameCallback) {mFrameCallback = (Choreographer.FrameCallback) frameCallback;}public void setSurfaceTexture(SurfaceTexture surfaceTexture) {mSurfaceTexture = surfaceTexture;}
}

Activity UI相关代码如下所示:

public class MainActivity extends AppCompatActivity implements View.OnClickListener{private ImageButton mTakePictureBtn;private CameraCoreManager manager;private TextureView mTextureView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_camera);manager = new CameraCoreManager(this);mTakePictureBtn = findViewById(R.id.camera_take_picture);mTakePictureBtn.setOnClickListener(this);mTextureView = findViewById(R.id.texture_view);}@Overridepublic void onResume() {super.onResume();mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {@Overridepublic void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {manager.setSurfaceTexture(surface);manager.startPreview();}@Overridepublic void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}@Overridepublic boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {return false;}@Overridepublic void onSurfaceTextureUpdated(SurfaceTexture surface) {}});}@Overridepublic void onPause() {super.onPause();}@Overrideprotected void onDestroy() {super.onDestroy();manager.stopPreview();}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.camera_take_picture://take picturebreak;}}
}

2.2 layout文件

这里涉及布局文件主要为activity_camera.xml,xml文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"
android:layout_height="match_parent"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/gray"android:gravity="center"android:orientation="horizontal"android:padding="20dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"><ImageButtonandroid:id="@+id/camera_take_picture"android:layout_width="60dp"android:layout_height="60dp"android:background="@drawable/ic_camera" />
</LinearLayout><TextureViewandroid:id="@+id/texture_view"android:layout_width="320dp"android:layout_height="180dp"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

camera图标的矢量xml文件,这里特殊记录下,文件内容为:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<pathandroid:fillColor="#FFFFFFFF"android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
<pathandroid:fillColor="#FFFFFFFF"android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
</vector>

Android APP Camera2应用(02)预览流程相关推荐

  1. android 相机预览的分辨率,Android开发 Camera2开发_2_预览分辨率或拍照分辨率的计算...

    前言 不管在Camera1或者Camera2在适配不同手机/不同使用场景的情况下都需要计算摄像头里提供的分辨率列表中最合适的那一个分辨率.所以在需要大量机型适配的app,是不建议不经过计算直接自定义分 ...

  2. Android 源码 Camera2 预览流程分析一

    先上一段典型的预览代码,梳理一下相机预览流程. 从 TextureView 获取到 SurfaceTexture 将 SurfaceTexture 默认缓冲区的大小配置为相机预览的大小 新建一个 Su ...

  3. Android 源码 Camera2 预览流程分析四

    <Android 源码 Camera2 预览流程分析二>中进行了流启动,这是调用 QCamera3Channel start() 方法实现的,对应于 HAL_PIXEL_FORMAT_YC ...

  4. Android camera预览流程

    前面已经简单介绍了,在Android系统中open camera的流程,但是,它又是怎么预览.怎么配置流,如何最终操作到camera HAL的呢.接下来以android原生相机应用,android9, ...

  5. android 组件消失了,Android 12 2.2 开发者预览版发布:修复桌面小组件消失等问题...

    IT 之家 4 月 8 日消息 据外媒 XDA 论坛消息,Android 12 2.2 开发者预览版今日发布,同时包含安卓 2021 年 4 月安全更新.本次预览版主要修复了一些错误,没有带来重要功能 ...

  6. android xml 预览,解决Android studio xml界面无法预览问题

    解决Android studio xml界面无法预览问题 发布时间:2020-10-05 18:48:37 来源:脚本之家 阅读:140 作者:张雨明 如下图 修改style.xml中的 parent ...

  7. android 摄像头比例,Android摄像头是全屏预览最简单的方式.doc

    Android摄像头是全屏预览最简单的方式 Android Camera做全屏预览之最简单方法 M厂开发五部:刘 博 一.全屏预览与非全屏预览的区别 对于大多数人来说,我们看电影.玩游戏等都喜欢全屏, ...

  8. android camera 全屏,Android Camera做全屏预览之最简单方法.doc

    Android Camera做全屏预览之最简单方法 M厂开发五部:刘 博 一.全屏预览与非全屏预览的区别 对于大多数人来说,我们看电影.玩游戏等都喜欢全屏,我们之所以喜欢全屏的一个主要原因就是全屏的感 ...

  9. 视频联网云平台EasyCVR集成海康EHome协议:Ehome协议预览流程

    之前我们讲了EasyCVR视频平台集成了海康EHome协议系统配置,EasyCVR集成海康EHome私有协议内容繁杂琐碎,测试内容众多,所以我们特地开辟一个系列,如果大家有兴趣,可以翻阅以往的博客了解 ...

最新文章

  1. 一作解读:Microbiome马铃薯疮痂病与土壤微生物组关系新进展
  2. java.getRunTime.exe
  3. 打造支撑百万用户的分布式代码托管平台
  4. vue-cli中的webpack配置
  5. java电话计费系统_java 连接数据库开发的电话计费管理系统
  6. modelMapper.map的一个使用例子
  7. 谨慎使用Hibernate中的本机SQL
  8. maven报错:Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:x.x.x:test
  9. 【nginx】nginx 反向代理 指令说明
  10. SilverLight学习笔记--实际应用(一)(4):手把手建立一个Silverlight应用程序之同步数据校验1...
  11. c语言 条件 设a 3 b 4,2012年计算机等级二级C语言章节习题及答案(4)
  12. 软件类配置(七)【ubuntu16.04安装netbeans、opencv并配置开发环境。】
  13. 关于ie浏览器不能访问localhost的问题
  14. 赵小楼《天道》《遥远的救世主》深度解析(6)为什么肖亚文说丁元英是魔是鬼都可以,就是不是人?
  15. 手机rar压缩包密码忘了怎么办,rar压缩包不能复制打印、rar压缩包忘记密码怎么办?
  16. Learning Without Forgetting的pytorch实现
  17. LCD1602和12864简单的介绍
  18. 看完《奇葩说》,还有一个千亿级的市场故事可以说说
  19. iOS 7 之 Sprites
  20. 解决git cherry-pick xxx fatal bad object xxx

热门文章

  1. android 横向的日历,Andorid 日历控件库,可左右滑动,显示公历,农历,节假日等功能...
  2. Gilbert Strang 《Introduction to Linear Algebra》 chap1 Introduction to Vectors 笔记
  3. 视觉SLAM⑫----建图(未完)
  4. 谱估计matlab,利用MATLAB工具箱比较三种谱估计算法
  5. java程序的运行机制详细分析
  6. Java 运行机制及运行过程
  7. git push origin master报错,解决方法
  8. 国标GB28181安防视频平台EasyGBS配置完成之后无法播放的问题排查步骤与解决
  9. 修改配置文件解决matplotlib中文与正负号乱码问题
  10. Machine Learning in Action 读书笔记---第4章 基于概率论的分类方法:朴素贝叶斯