Android Camera模块解析之拍照

最近学习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/

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flybirding10011

谢谢支持啊999

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值