文档章节

Android Camera模块解析之拍照

天王盖地虎626
 天王盖地虎626
发布于 07/21 22:49
字数 2141
阅读 32
收藏 0

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

最近学习Android的camera模块,本文先介绍一下camera2的api,然后给出android camera拍照的例子,讲解一下camera 拍照的原因知识,与大家共勉。

  • camera2 介绍
  • android camera拍照功能介绍

一、camera2 介绍



  • Camera api部分:
    frameworks/base/core/java/android/hardware/camera2
  • Camera JNI部分:
    frameworks/base/core/jni/android_hardware_Camera.cpp
    编译选项在目录下的Android.bp
    make libandroid_runtime -j1
  • Camera UI库部分:
    frameworks/av/camera/
    编译选项在目录下的Android.bp
    make libcamera_client -j1
  • Camera服务部分:
    frameworks/av/services/camera/libcameraservice/
    编译选项在目录下的Android.mk
    make libcameraservice -j1
  • Camera HAL部分:
    hardware/qcom/camera/


android.hardware.camera2开发包给开发者提供了一个操作相机的开发包,是api-21提供的,用于替代之前的Camera操作控类。该软件包将摄像机设备建模为管道,它接收捕获单个帧的输入请求,根据请求捕获单个图像,然后输出一个捕获结果元数据包,以及一组请求的输出图像缓冲区。请求按顺序处理,多个请求可以立即进行。由于相机设备是具有多个阶段的管道,因此需要在移动中处理多个捕捉请求以便在大多数Android设备上保持完全帧率。

  • TextureView可用于显示内容流。这样的内容流可以例如是视频或OpenGL场景。内容流可以来自应用程序的进程以及远程进程。
  • TextureView只能在硬件加速窗口中使用。在软件中渲染时,TextureView将不会绘制任何内容。
  • TextureView不会创建单独的窗口,但表现为常规视图。这一关键差异允许TextureView移动,转换和使用动画
  • 之后,应用程序需要构建CaptureRequest,在捕获单个图片的时候,这些request请求需要定义一些请求的参数。
  • 一旦设置了请求,就可以将其传递到活动捕获会话,以进行一次捕获或无休止地重复使用。两种方法还具有接受用作突发捕获/重复突发的请求列表的变体。重复请求的优先级低于捕获,因此在配置重复请求时通过capture()提交的请求将在当前重复(突发)捕获的任何新实例开始捕获之前捕获。
  • 处理完请求后,摄像机设备将生成一个TotalCaptureResult
    对象,该对象包含有关捕获时摄像机设备状态的信息以及使用的最终设置。如果需要舍入或解决相互矛盾的参数,这些可能与请求有所不同。相机设备还将一帧图像数据发送到请求中包括的每个输出表面。这些是相对于输出CaptureResult异步生成的,有时基本上稍晚。

根据camera2的工作示意图,画出下面的camera类关系图。下面使用camera功能的时候会详细介绍一下camera2 的api的主要功能。

二、android camera拍照功能介绍

2.1 设置camera preview预览页面

我们打开android camera一般会出现一个预览页面,通过这个预览页面,我么调用摄像头,将前方的景放在这个预览界面中,然后移动camera,预览界面会随之出现变化,实现这个预览界面的view,一般有两种选择,SurfaceView或者TextureView,上面我们也介绍了两种view之间的区别,本文我们选择TextureView,因为TextureView设置动画比较方便,我们移动或者旋转手机的时候,TextureView也应用相应的旋转,这样符合用户的体验。
可以通过设置TextureView.SurfaceTextureListener来对TextureView代表的surface监听。

    private final TextureView.SurfaceTextureListener mSurfaceTextureListener
            = new TextureView.SurfaceTextureListener() {

        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
            openCamera(width, height);
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
            configureTransform(width, height);
        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
            return true;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture texture) {
        }

    };

几个监听事件监听当前surface的状态。onSurfaceTextureAvailable表示当前的surface状态可用,onSurfaceTextureSizeChanged表示当前的surface大小正在调整。

2.2 打开相机

    private void openCamera(int width, int height) {
        if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
            requestCameraPermission();
            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);
        }
    }
  • 检查当前camera权限。
  • 设置相机当前属性和输出图片等等。
  • 设置TextureView 选装和移动的动画属性等等。
  • 使用CameraManager实例打开相机

2.3 相机打开设置回调

在执行manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);方法的时候,传入了三个参数:

  • mCameraId表示当前摄像头的标识,我们手机中有好多个摄像头,最新版的Mate20手机有3个后置摄像头和1个前置摄像,可以通过manager.getCameraIdList()来获取当前的cameraId集合。
  • StateCallback是CameraDevice.StateCallback,这是表示相机设备当前状态的回调。
    下面StateCallback的众多回调表示当前相机的状态,相机如果打开的话应该执行什么操作,相机如果断开连接的话应该执行什么操作等等。
  • 传入的Handler处理camera当前的消息。
    private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {

        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice) {
            // This method is called when the camera is opened.  We start camera preview here.
            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();
            }
        }

    };

mCameraOpenCloseLock是一个信号量,一旦相机在openCamera到真正打开这段时间,相机必须被独占,其他线程不能介入处理,不然会出现线程错乱。一旦相机呈现出下一个状态,就可以释放这个信号量了。

private Semaphore mCameraOpenCloseLock = new Semaphore(1);

2.4 创建camera预览session

    private void createCameraPreviewSession() {
        try {
            SurfaceTexture texture = mTextureView.getSurfaceTexture();
            assert texture != null;

            // We configure the size of default buffer to be the size of camera preview we want.
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());

            // This is the output Surface we need to start preview.
            Surface surface = new Surface(texture);

            // We set up a CaptureRequest.Builder with the output Surface.
            mPreviewRequestBuilder
                    = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mPreviewRequestBuilder.addTarget(surface);

            // Here, we create a CameraCaptureSession for camera preview.
            mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
                    new CameraCaptureSession.StateCallback() {

                        @Override
                        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                            // The camera is already closed
                            if (null == mCameraDevice) {
                                return;
                            }

                            // When the session is ready, we start displaying the preview.
                            mCaptureSession = cameraCaptureSession;
                            try {
                                // Auto focus should be continuous for camera preview.
                                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                                // Flash is automatically enabled when necessary.
                                setAutoFlash(mPreviewRequestBuilder);

                                // Finally, we start displaying the camera preview.
                                mPreviewRequest = mPreviewRequestBuilder.build();
                                mCaptureSession.setRepeatingRequest(mPreviewRequest,
                                        mCaptureCallback, mBackgroundHandler);
                            } catch (CameraAccessException e) {
                                e.printStackTrace();
                            }
                        }

                        @Override
                        public void onConfigureFailed(
                                @NonNull CameraCaptureSession cameraCaptureSession) {
                            showToast("Failed");
                        }
                    }, null
            );
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
  • 设置SurfaceTexture缓存大小。
  • 通过mCameraDevice.createCaptureRequest获取CaptureResult.Builder对象,这个对象要和之前的Surface绑定,表示当前的capture请求是输出到这个surface上的。
  • mCameraDevice.createCaptureSession 创建capture session,在CameraCaptureSession.StateCallback的onConfigured回调函数中,就是对当前camera移动的回调,一旦移动,会触发这个回调,然后在回调中作出相应的改变。

2.5 拍照

    private void takePicture() {
        lockFocus();
    }

    /**
     * Lock the focus as the first step for a still image capture.
     */
    private void lockFocus() {
        try {
            // This is how to tell the camera to lock focus.
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                    CameraMetadata.CONTROL_AF_TRIGGER_START);
            // Tell #mCaptureCallback to wait for the lock.
            mState = STATE_WAITING_LOCK;
            mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
                    mBackgroundHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

Capture image的过程主要在 mCaptureSession.capture函数中,下面会从这个函数着手阐释一下camera capture的原理。

2.6 关闭相机

    private void closeCamera() {
        try {
            mCameraOpenCloseLock.acquire();
            if (null != mCaptureSession) {
                mCaptureSession.close();
                mCaptureSession = null;
            }
            if (null != mCameraDevice) {
                mCameraDevice.close();
                mCameraDevice = null;
            }
            if (null != mImageReader) {
                mImageReader.close();
                mImageReader = null;
            }
        } catch (InterruptedException e) {
            throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
        } finally {
            mCameraOpenCloseLock.release();
        }
    }

主要释放camera相关的资源,手机中camera的资源具有独占性,如果在用完了不释放的话,会造成别人使用的时候camera被占用,session不关闭的,会造成严重的内存泄露。
大家也可以参考具体的源码https://github.com/googlesamples/android-Camera2Basic/

本文转载自:https://www.jianshu.com/p/bc9e96c7e95e

天王盖地虎626

天王盖地虎626

粉丝 35
博文 590
码字总数 21647
作品 0
南京
私信 提问
Android Camera模块解析之视频录制

《Android Camera架构》 《Android Camera进程间通信类总结》 《Android Camera模块解析之拍照》 《Android Camera模块解析之视频录制》 《Android Camera原理之CameraDeviceCallbacks回调模...

天王盖地虎626
06/18
115
0
Android Camera进程间通信类总结

《Android Camera架构》 《Android Camera进程间通信类总结》 《Android Camera模块解析之拍照》 《Android Camera模块解析之视频录制》 《Android Camera原理之CameraDeviceCallbacks回调模...

天王盖地虎626
07/21
36
0
Android Camera原理之setRepeatingRequest与capture模块

Camera操作过程中最重要的四个步骤: CameraManager-->openCamera ---> 打开相机 CameraDeviceImpl-->createCaptureSession ---> 创建捕获会话 CameraCaptureSession-->setRepeatingRequest......

天王盖地虎626
07/23
28
0
历时四年,给Google提交的Android Framework Bug终于被Fixed了

2014年在做一个Android终端设备开发过程中,发现了一个Android Framework层的Bug,给Google提交了issue和解决方案,和外界传言一致Google一般不太在意个人开发者提交的issue,直到2017年12月...

image_c
2018/07/12
0
0
Android Camera原理之createCaptureSession模块

《Android Camera架构》 《Android Camera进程间通信类总结》 《Android Camera模块解析之拍照》 《Android Camera模块解析之视频录制》 《Android Camera原理之CameraDeviceCallbacks回调模...

天王盖地虎626
06/10
32
0

没有更多内容

加载失败,请刷新页面

加载更多

thinkphp6 视图变量输出

thinkphp6 视图变量输出 视图默认把变量输出为字符串类型,有些时候需要保证原来的类型,比如html字符串 raw参数,禁止转换 {$content.content|raw} 支持多个参数,比如解码php内置的htmlspe...

DrChenXX
6分钟前
0
0
【搞定Jvm面试】 Java 内存区域揭秘附常见面试题解析

本文已经收录自笔者开源的 JavaGuide: https://github.com/Snailclimb (【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识)如果觉得不错的还,不妨去点个Star,鼓励一下!...

SnailClimb
16分钟前
1
0
jQuery如何根据数据属性值查找元素?

我有以下场景: var el = 'li'; 页面上有5个<li> ,每个都有一个data-slide=number属性(数字分别为1,2,3,4,5) 。 我现在需要找到当前活动的幻灯片编号,该编号映射到var current = $('ul...

javail
27分钟前
2
0
LaTeX自定义宏包、类文件的默认搜索路径设置方法

  对于自定义的LaTeX宏包与类,在调用时可以通过在命令\documentclass{}与\usepackage{}命令中指定完整路径或者相对路径,这样确实可以调用,但是编译时总是有烦人的警告信息,让人不爽。还...

陆巍_
30分钟前
2
0
计算机电子书 2018 BiliDrive 备份

下载方式 根据你的操作系统下载不同的 BiliDrive 二进制。 执行: bilidrive download <link> 链接 <!--more--> 文档 链接 Webpack 中文指南.epub (409.01 KB) bdrive://ce58b7b58292296a61......

ApacheCN_飞龙
50分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部