• Camera2相机拍照流程之预览功能梳理


    1、获取CameraManager 相机管理器

            CameraManager是通过获取系统服务方式获取CameraManager对象

    1. // 获取CameraManager 相机设备管理器
    2. mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

    2、打开摄像头并展示预览画面

            2.1 监听显示预览控件状态

            activity被显示出来(执行到Activity的onResume方法)时,判断显示相机预览的控件TextTure是否可用,可用的话则打开相机;否则注册SurfaceTextureListener监听器对象,当可用是会回调onSurfaceTextureAvailable()函数,在此函数中进行打开相机:

            1、创建activity时第一次走到OnResume()时,会先走else逻辑,因为此时TextTure还不可用,需要注册SurfaceTextureListener监听器,监听器中监听到TextTure可用时(即onSurfaceTextureAvailable)这时会首次打开摄像头;

            2、后面再进入到activity的OnResume()时,则直接打开摄像头。

    1. @Override
    2. protected void onResume() {
    3. super.onResume();
    4. if (texture.isAvailable()) {
    5. openCamera(texture.getWidth(), texture.getHeight(), mCameraId);
    6. } else {
    7. texture.setSurfaceTextureListener(textureListener);
    8. }
    9. startBackgroundThread();
    10. }
    11. /**
    12. * TextureView 生命周期响应
    13. */
    14. private final TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
    15. @Override //创建
    16. public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
    17. //当TextureView创建完成,打开指定摄像头相机
    18. openCamera(width, height, mCameraId);
    19. }
    20. @Override //尺寸改变
    21. public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
    22. }
    23. @Override //销毁
    24. public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    25. return false;
    26. }
    27. @Override //更新
    28. public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    29. }
    30. };

            2.2 打开摄像头,展示相机预览

    1. /**
    2. * 打开指定摄像头ID的相机
    3. *
    4. * @param width
    5. * @param height
    6. * @param cameraId
    7. */
    8. private void openCamera(int width, int height, int cameraId) {
    9. if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
    10. // TODO: Consider calling
    11. //return;
    12. requestPermission();
    13. }
    14. try {
    15. mSurfaceWidth = width;
    16. mSurfaceHeight = height;
    17. getCameraId(cameraId);
    18. CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(mCameraId + "");
    19. StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    20. // 获取设备方向
    21. int rotation = getWindowManager().getDefaultDisplay().getRotation();
    22. int totalRotation = sensorToDeviceRotation(characteristics, rotation);
    23. boolean swapRotation = totalRotation == 90 || totalRotation == 270;
    24. int rotatedWidth = mSurfaceWidth;
    25. int rotatedHeight = mSurfaceHeight;
    26. if (swapRotation) {
    27. rotatedWidth = mSurfaceHeight;
    28. rotatedHeight = mSurfaceWidth;
    29. }
    30. // 获取最佳的预览尺寸
    31. mPreviewSize = getPreferredPreviewSize(map.getOutputSizes(SurfaceTexture.class), rotatedWidth, rotatedHeight);
    32. if (swapRotation) {
    33. texture.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());
    34. } else {
    35. texture.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
    36. }
    37. if (mImageReader == null) {
    38. // 创建一个ImageReader对象,用于获取摄像头的图像数据,maxImages是ImageReader一次可以访问的最大图片数量
    39. mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(),
    40. ImageFormat.JPEG, 2);
    41. mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
    42. }
    43. //检查是否支持闪光灯
    44. Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
    45. mFlashSupported = available == null ? false : available;
    46. mCameraManager.openCamera(mCameraId + "", mStateCallback, null);
    47. } catch (CameraAccessException e) {
    48. e.printStackTrace();
    49. }
    50. }

    1、要打开摄像头,需要先检测app的相机权限;

    2、记录TextTure的宽高值;

    3、通过cameramanager对象获取指定摄像头的特性信息CameraCharacteristics(下文详解);

    4、通过characteristics对象获取相机支持的可用流配置;

    5、获取android手机当前摆放的方向;

    6、根据设备方向和摄像头方向,经过处理后得出最终的预览页控件的宽高大小(可能会发生旋转)。

    7、从CameraCharacteristics中选择一个最匹配TextTure大小的宽高值;

    8、然后重新设定TextTure的大小;

    9、创建ImageReader图像读取流对象,ImageReader可以让应用直接访问到相机的image 数据,并渲染到Surface上,也就是TextTure控件上;创建ImageReader需要指定预览页的宽、高和图像的格式,和最大image数。

    10、给ImageReader对象设置监听器OnImageAvailableListener; (ImageReader对象在打开摄像头显示预览这里其实不是必须的,可以在实际拍照的时候创建也可以)

    11、检查摄像头是否支持开启闪光灯;

    12、cameramanager对象调用openCamera()打开指定摄像头,并注册了状态回调对象StateCallback,因为openCamera()函数内部其实做了很多事情,是一个耗时动作,所以这里使用了回调函数异步执行。

    13、执行打开摄像头动作后,当检测到摄像头已成功打开,则回调到onOpened(),可以获取到摄像头对象CameraDevice,这个类很重要。接下来就可以在该函数中创建预览了;

    14、配置相机预览页大小,创建一个显示预览图像的缓冲区Surface。

    15、通过CameraDevice构建一个预览请求构建器对象CaptureRequest.Builder,通过这个构建器设置预览请求对象的相关信息,比如指定预览数据显示位置,即Surface;

    16、通过CameraDevice建立一个捕获会话CameraCaptureSession,有了这个会话对象后,后面的所有动作都是基于这个会话对象来操作了;创建会话时,也是注册了一个会话状态回调对象异步地执行接下来的动作;

    17、当会话成功建立后,会回调onConfigured()函数;可以在该函数中继续设置预览请求对象的属性,比如设置自动对焦,设置闪光灯模式;

    18、最后,预览请求构建器对象调用build()构建了一个捕获请求对象,并通过captureSession会话设置一个无休止的预览请求(即预览),即mCaptureSession.setRepeatingRequest(mPreviewRequest, null, mBackgroundHandler);

    通过这种无休止地发生预览请求实现了相机的预览。

    19、到这里TextTure对象中的Surface上就成功展示了相机预览画面数据了。

  • 相关阅读:
    深入URP之Shader篇16: UNITY_BRANCH和UNITY_FLATTEN
    element computed校验方式
    LeetCode:205. 同构字符串————简单
    【从测试小白到管理】这几点让我终身受益,没有人永远18岁,但永远有人18岁......
    Navicat连接openGauss数据库报错
    七、安装Centos7+8系统+超级优化
    为什么EDR需要深度防御来打击勒索软件?
    Redis Functions 介绍之一
    c 从avi 视频中提取图片
    Java项目:基于ssm智能餐厅管理系统
  • 原文地址:https://blog.csdn.net/liuqinhou/article/details/126305243