Android P Camera2的粗略整理

本文详细梳理了Android P中Camera2的架构,包括Camera APP、Camera Framework API、libCameraService和HAL层。介绍了上层APP如何通过CameraManager打开相机设备,创建CameraCaptureSession进行预览、拍照和录像。同时,探讨了CameraService与Framework API的AIDL通讯,以及HIDL在HAL层的角色。
摘要由CSDN通过智能技术生成

Android Camera目前的整体架构很清晰的,主要分为以下几块:

  • Camera APP 

代码在packages/apps/Camera2/目录下

是Camera的上层应用端,自己写Camera App的话,可以借鉴此部分代码

  • Camera Framework API

这部分是提供给上层APP调用的API

代码在frameworks/base/core/java/android/hardware/camera2/目录下

  • libCameraService

代码在frameworks/av/services/camera/libcameraservice目录下

Camera服务层代码,负责连接上层的Framework API和底层的HAL

  • HAL层

代码在/hardware/interfaces/camera/目录下

 

上层APP的简单调用流程

首先,对于一般人而已,Camera APP是直接用他们交互的部分。

Camera APP通过对Framework API的调用,完成整个Camera的功能。

其中CameraManager是一切开始的地方。

它可以通过获取系统服务的方式被拿到。

CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);

APP层通过调用它的openCamera方法来打开对于ID的相机设备。

public void openCamera (String cameraId, 
                CameraDevice.StateCallback callback, 
                Handler handler)

openCamera方法需要传入一个CameraDevice.StateCallback的回调接口。

如果打开成功,接口中的onOpened(CameraDevice camera)方将将被调用。

这样,就拿到了一个很关键的对象——CameraDevice

第三个参数Handler是用来指定线程的,可以为空,为空时就是当前的调用线程,但一般来说都会为其创建一个后台线程。

如下,先新建一个HandlerThread,再用Handler的Looper创建后台Handler。

HandlerThread mBackgroundThread = new HandlerThread("CameraBackground");
mBackgroundThread.start();
Handler mBackgroundHandler = new Handler(mBackgroundThread.getLooper());

 

在成功拿到CameraDevice之后,就可以通过它来创建对于的Session,Camera2的预览、拍照、录像等操作都是通过CameraCaptureSession完成的。

创建Session:

public abstract void createCaptureSession(@NonNull List<Surface> outputs,
            @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler)
            throws CameraAccessException;

CameraCaptureSession.StateCallback是一个回调,创建成功的话会调用onConfigured方法。

public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession)

onConfigured方法会把创建好的CameraCaptureSession 给带回来。

这个套路和之前创建CameraDevice是一模一样的。

 

通过CameraCaptureSession预览:下面这种带有Repeating的请求,可以无止境的捕获图像,很适合用来预览。

public abstract int setRepeatingRequest(@NonNull CaptureRequest request,
            @Nullable CaptureCallback listener, @Nullable Handler handler)
            throws CameraAccessException;

 

这里的CaptureRequest很关键,决定了这次请求的类型,通过CaptureRequest.Builder来创建,而CameraDevice提供了创建CaptureRequest.Builder的方法。

public abstract CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType)
            throws CameraAccessException;

这里需要传入一个模板类型作为参数。@RequestTemplate这个注解对这个模板类型做了输入限制。所以通过这个注解也可以知道能输入什么值。

     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"TEMPLATE_"}, value =
         {TEMPLATE_PREVIEW,
          TEMPLATE_STILL_CAPTURE,
          TEMPLATE_RECORD,
          TEMPLATE_VIDEO_SNAPSHOT,
          TEMPLATE_ZERO_SHUTTER_LAG,
          TEMPLATE_MANUAL})
     public @interface RequestTemplate {};

见名知意,预览的话,肯定是传入TEMPLATE_PREVIEW了。

 mPreviewRequestBuilder  = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

这里要提一个下,CaptureRequest.Builder需要一个显示的载体——Surface,可以通过addTarget添加。

这里添加的目标Surface必须是创建CameraCaptureSession是传入的List<Surface> outputs的子集

public void addTarget(@NonNull Surface outputTarget)

 

在调用完setRepeatingRequest后预览就开始了。

PS:预览的Surface一般用SurfaceView或者TextureView。早期都是用SurfaceView,目前TextureView用的多点,它更像是一般的View,像TextView那样能被缩放、平移,也能加上动画;但是TextureView只能在开启了硬件加速的Window中使用,并且消费的内存要比SurfaceView多;

 

通过CameraCaptureSession拍照

public abstract int capture(@NonNull CaptureRequest request,
            @Nullable CaptureCallback listener, @Nullable Handler handler)
            throws CameraAccessException;

和预览的套路是一样的,主要分两步:

1. 创建CaptureRequest.Builder

2. 添加目标Surface

final CaptureRequest.Builder captureBuilder =
                    mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(mImageReader.getSurface());

不同是的,Builder参数改为了TEMPLATE_STILL_CAPTURE(静态捕获),添加的目标Surface也变成了ImageReader的Surface。

拍照的时候,通过ImageReader来保持图像是一个很不错的选择。

    mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
                        ImageFormat.JPEG, /*maxImages*/2);
    mImageReader.setOnImageAvailableListener(
                        mOnImageAvailableListener, mBackgroundHandler);


    private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
            = new ImageReader.OnImageAvailableListener() {

        @Override
        public void onImageAvailable(ImageReader reader) {
            mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile));
        }

    };


    private static class ImageSaver implements Runnable {

        /**
         * The JPEG image
         */
        private final Image mImage;
        /**
         * The file we save the image into.
         */
        private final File mFile;

        ImageSaver(Image image, File file) {
            mImage = image;
            mFile = file;
        }

        @Override
        public void run() {
            ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            FileOutputStream output = null;
            try {
                output = new FileOutputStream(mFile);
                output.write(bytes);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                mImage.close();
                if (null != output) {
                    try {
                        output.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }

 

当然,在开始拍照前,最好停止一下之前的预览。

mCaptureSession.stopRepeating();
mCaptureSession.abortCaptures();
mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);

完成拍照后,再开启预览。

mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback,
                    mBackgroundHandler);
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值