【Camera2】Android Camera2 综述

一、摘要

本篇文章为Camera2的综述;主要涉及到如下几个部分

  • Part1: Camera2参考资料
  • Part2:Camera2发展历史
  • Part3:Camera2框架和使用流程

本篇文章可结合Camera1综述文章一起参看。为了描述的完整性,本篇文章和Camera1综述文章有部分内容重叠。

在Part2中,会详细介绍Camera1Camera2的差异,以及Camera2的发展历史

在Part3中,会从框架维度和代码维度详细的介绍Camera2相关的使用流程。但只涉及到使用步骤,详细完善的代码开发或讲解参看Camera2源码分析Camera2开源项目分析

相关的Camera2系列文章可参考:Android Camera系列文章进度表

二、参考资料

三、发展历史

首先要明确如下2个概念:

  • HAL
  • API

在不同的HALAPI中,我们可以发现Camera1Camera2的具体差异。

3.1 HAL:Hardware Abstraction Layer。

这里引用Camera1综述文章里的描述:

HAL 位于相机驱动程序和更高级别的 Android 框架之间,它定义您必须实现的接口,以便应用可以正确地操作相机硬件。

HAL 可定义一个标准接口以供硬件供应商实现,可让Android忽略较低级别的驱动程序实现。HAL实现通常会内置在共享库模块(.so)中。

可见HAL主要由 相机供应商所提供和更新;相关的版本历史动态只需要了解即可,不需要深究,这里也只是简要进行概括。如需要更详细的了解,可参考:

HAL版本 说明
1.0 1.Android 相机 HAL (Android 4.0) [camera.h];2.支持 android.hardware.Camera API
2.0 1.初始版本的扩展功能 HAL(Android 4.2) [camera2.h];2.足以实现现有的 android.hardware.Camera API。
3.0 1.扩展功能 HAL 的首次修订;2.重要版本更改,重新设计了输入请求和流队列接口等。
3.1 1.扩展功能 HAL 的小修订;2.configure_streams 将使用方使用情况标志传递给 HAL,刷新调用以尽可能快地丢弃所有传输中的请求/缓冲区。
3.2 1.扩展功能 HAL 的小修订;2.弃用了 get_metadata_vendor_tag_opsregister_stream_buffers;重新制定双向和输入流规范;更改输入缓冲区返回路径。
3.3 1.扩展功能 HAL 的小修订;2.OPAQUE 和 YUV 重新处理 API 更新;对深度输出缓冲区的基本支持;添加一些字段。
3.4 1.对受支持的元数据添加了少许内容并对 data_space 支持进行了更改。
3.5 1.Android10.0;ICameraDevice,ICameraDeviceSession,ICameraDeviceCallback更新。
Android 8.0 1.引入了 Treble;2.供应商相机 HAL 实现必须为绑定式
Android 9.0 1.HAL 3.3添加一些键和元数据标记;2.HAL 3.4 更新ICameraDeviceSession和ICameraDeviceCallback。
Android 10.0 1.HAL 3.4添加图片格式、元数据标志、新功能、数据流配置等;

3.2 Camera1和Camera2差异

Camera1Camera2分别对应着,即相机API1相机API2

3.2.1 相机API1

Android 5.0 已弃用 Camera API1,新平台重点开发 Camera API2Camera API1 会逐渐被淘汰。但淘汰期限将会很长,且在一段时间内新 Android 版本会继续支持 Camera API1 应用。这也是为什么Camera1框架可在后续Android SDK中作为应用兜底框架的原因。

3.2.2 相机API2

  • Camera API2 框架为应用提供更接近底层的相机控件,包括高效的零复制连拍/视频流以及曝光、增益、白平衡增益、颜色转换、去噪、锐化等方面的每帧控件
  • Android 5.0 及更高版本提供相机 API2;即在Android5.0以上才支持Camera2;但 Android 5.0 及更高版本的设备可能并不支持所有相机 API2 功能。
  • Camera2分为如下支持级别
Camera2支持级别 说明
LEGACY 所用功能与API1 大致相同。即底层将相机 API2 调用转换为相机 API1 调用
LIMITED 部分相机 API2 功能,且相机 HAL 3.2 或更高版本。
FULL 支持 API2 所有主要功能,且相机 HAL 3.2 或更高版本。
LEVEL_3 支持 YUV 重新处理和 RAW 图片拍摄,以及其他输出流配置。
EXTERNAL 类似于 LIMITED 设备,此级别用于外部相机。

考虑到兼容性成本,在实际使用Camera2的功能时,并不会直接在Android5.0支持Camera2。而是在HAL3.2更高版本对应的Android SDK里支持使用Camera2。那到底在Android SDK几里开始支持Camera2呢,将在【3.3】中进行揭晓。

3.2.3 相机API版本历史

这里只做简要总结,详细可参考
相机API版本历史

API版本 说明
1.0 实现HAL1.0 即Camera1
2.0 实现HAL2.0以上,即Camera2
2.1 添加了从相机 HAL 模块到框架的异步回调支持
2.2 添加了模块供应商标记支持
2.3 添加了对打开旧版相机 HAL 设备的支持。
2.4 API更改;1.手电筒模式支持;2.外部相机(例如 USB 热插拔相机)支持;3.相机仲裁提示;4.模块初始化方法。

3.3 HAL1和HAL3

3.3.1 HAL1

HAL1对应着Camera1,目前已经弃用。HAL1的架构如下:
在这里插入图片描述
在这里,官方文档明确提出了。

注意:由于 Camera HAL1 已弃用,建议在搭载 Android 9 或更高版本的设备上使用 Camera HAL3。

因此通常来看,在9.0以上支持Camera2比较好。而且在Camera2的支持级别里,也标记着FULLLevel3级别建议使用HAL3.2以上。

3.3.2 HAL3

【3.2】里阐述了Camera1Camera2的功能差异,这里贴上HAL3的架构图,详细理论知识阐述可参看文章:
【Android Camera】Android Camera架构设计详解
在这里插入图片描述

四、框架和使用流程

主要分为2个维度,框架维度代码层面维度

4.1 框架维度

包括如下几个部分

  • Camera2相机模型
  • Camera HAL3核心操作模型
  • HAL操作摘要
  • Camera传感器
  • Android Camera API步骤

4.1.1 Camera2相机模型

在这里插入图片描述
如上图包含如下3个层面:

4.1.1.1 Camera-Using Application

模块 说明
CameraManager.AvailabilityCallback 应用层通过该回调监听Camera 设备状态
CameraCharacteristics Camera设备信息和相关设置类
Output Stream Destinations 输出流配置:可配置预览、拍照、录制等场景
CallBack 一些回调方法如:CameraCaptureSession.CaptureCallBack

4.1.1.2 Camera2 API

模块 说明
CameraManager 应用层通过该类调链接设备、获取摄像头信息
CameraDevice 提供创建绘画和提供应用层创建帧捕获
CameraCaptureSession 具体的帧捕获转化为输出流信息
Configured Outputs 输出流队列

4.1.1.3 Camera Device Hardware

硬件层面:包含镜头控制,Camera Sensor,闪光灯设置,后处理,控制操作。

4.1.2 Camera HAL3核心操作模型

在相机模型里核心的操作为把帧捕获请求输出到对应的输出流上。如下:
在这里插入图片描述
API 将相机子系统塑造为一个管道,将传入的帧捕获请求(CaptureRequest)转化为帧。请求包含有关帧的捕获和处理的所有配置信息,其中包括分辨率和像素格式手动传感器镜头和闪光灯控件3A 操作模式RAW 到 YUV 处理控件统计信息生成等等。

  1. 构造CaptureRequest
  2. 单次Capture
  3. request
  4. Output1:configured output surfaces[预览,拍照等]
  5. Output2:onCaptureComplete -> CaptureResult

核心处理流程如上图和上5个步骤。这里只是单次的request操作。Camera2也提供了重复的Request操作流程。请继续阅读。

4.1.3 HAL操作摘要

在这里插入图片描述
如上图,包含如下几个流程:

  1. 输入和构造CaptureRequest
  2. Camera HAL经过配置、帧捕获和后处理
  3. 输出到输出流配置里。

在图片里可以看到CaptureRequest分为2种:

  • 单次Capture,直接输出
  • 重复Capture,重新由4转移到6

几点说明

  1. 捕获的异步请求来自于框架。
  2. HAL 设备必须按顺序处理请求。对于每个请求,均生成输出结果元数据以及一个或多个输出图像缓冲区。
  3. 请求和结果以及后续请求引用的信息流遵守先进先出规则。
  4. 指定请求的所有输出的时间戳必须完全相同,以便框架可以根据需要将它们匹配在一起。
  5. 所有捕获配置和状态(不包括 3A 例程)都包含在请求和结果中。

4.1.4 Camera传感器

本小节具体介绍Camera硬件层面即Camera Sensor
在这里插入图片描述
如上图为:图像信号处理器(ISP,也称为相机传感器)包含了如下几个模块:

  1. 3A算法;
  2. 输出流分辨率和大小以及格式的处理
  3. 图像处理:Hot Pixel Correction -> Demosaic -> Noise Reduction -> ShadingCorrection -> Geometric Correction -> Color Correction -> Tone Curve Adjustment -> Edge Enhancement。

整个ISP处理流程的简介看参看如下2篇文章:

【Android Camera】1.Camera理论知识和基本原理

这里总结为如下2张图:
在这里插入图片描述

在这里插入图片描述

4.1.5 Android Camera API步骤

  1. 监听和枚举相机设备。
  2. 打开设备并连接监听器。
  3. 配置目标使用情形的输出(如静态捕获、录制等)。
  4. 为目标使用情形创建请求。
  5. 捕获/重复请求和连拍。
  6. 接收结果元数据和图片数据。
  7. 切换使用情形时,返回到第 3 步。

4.2 代码维度

这里以【4.1.5里的流程进行阐述】

4.2.1 监听和枚举相机设备。

4.2.1.1监听相机设备

可通过CameraManager的如下方法:

   public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback,
            @Nullable Handler handler) {
    
    
        CameraManagerGlobal.get().registerAvailabilityCallback(callback,
                CameraDeviceImpl.checkAndWrapHandler(handler));
    }

AvailabilityCallback包含了如下3个方法:

 public void onCameraAvailable(@NonNull String cameraId) {
    
    
        }

 public void onCameraUnavailable(@NonNull String cameraId) {
    
    
        }

   public void onCameraAccessPrioritiesChanged() {
    
    
        }

4.2.1.2 枚举相机设备

现在相机设备都有2个及以上相机设备:可通过如下方法枚举相机设备

val cameraIdList = cameraManager.cameraIdList // may be empty
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
val cameraLensFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
val cameraCapabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
val cameraCompatible = cameraCapabilities?.contains(
        CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) ?: false

如下方法提供获取前置或后置的cameraId

fun getFirstCameraIdFacing(cameraManager: CameraManager,
                           facing: Int = CameraMetadata.LENS_FACING_BACK): String? {
    
    
    // Get list of all compatible cameras
    val cameraIds = cameraManager.cameraIdList.filter {
    
    
        val characteristics = cameraManager.getCameraCharacteristics(it)
        val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
        capabilities?.contains(
                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) ?: false
    }

    // Iterate over the list of cameras and return the first one matching desired
    // lens-facing configuration
    cameraIds.forEach {
    
    
        val characteristics = cameraManager.getCameraCharacteristics(it)
        if (characteristics.get(CameraCharacteristics.LENS_FACING) == facing) {
    
    
            return it
        }
    }
    // If no camera matched desired orientation, return the first one from the list
    return cameraIds.firstOrNull()
}

其他相关方法:

fun filterCompatibleCameras(cameraIds: Array<String>,
                            cameraManager: CameraManager): List<String> {
    
    
    return cameraIds.filter {
    
    
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)?.contains(
                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) ?: false
    }
}

fun filterCameraIdsFacing(cameraIds: List<String>, cameraManager: CameraManager,
                          facing: Int): List<String> {
    
    
    return cameraIds.filter {
    
    
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.LENS_FACING) == facing
    }
}

fun getNextCameraId(cameraManager: CameraManager, currCameraId: String? = null): String? {
    
    
    // Get all front, back and external cameras in 3 separate lists
    val cameraIds = filterCompatibleCameras(cameraManager.cameraIdList, cameraManager)
    val backCameras = filterCameraIdsFacing(
            cameraIds, cameraManager, CameraMetadata.LENS_FACING_BACK)
    val frontCameras = filterCameraIdsFacing(
            cameraIds, cameraManager, CameraMetadata.LENS_FACING_FRONT)
    val externalCameras = filterCameraIdsFacing(
            cameraIds, cameraManager, CameraMetadata.LENS_FACING_EXTERNAL)

    // The recommended order of iteration is: all external, first back, first front
    val allCameras = (externalCameras + listOf(
            backCameras.firstOrNull(), frontCameras.firstOrNull())).filterNotNull()

    // Get the index of the currently selected camera in the list
    val cameraIndex = allCameras.indexOf(currCameraId)

    // The selected camera may not be on the list, for example it could be an
    // external camera that has been removed by the user
    return if (cameraIndex == -1) {
    
    
        // Return the first camera from the list
        allCameras.getOrNull(0)
    } else {
    
    
        // Return the next camera from the list, wrap around if necessary
        allCameras.getOrNull((cameraIndex + 1) % allCameras.size)
    }
}

4.2.2 打开设备并连接监听器。

通过如下方法:

    public void openCamera(@NonNull String cameraId,
            @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
            throws CameraAccessException {
    
    

        openCameraForUid(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler),
                USE_CALLING_UID);
    }

参数说明

  • cameraId -> 【4.2.1】
  • CameraDevice.StateCallback如下:
    public static abstract class StateCallback {
    
    
        public abstract void onOpened(@NonNull CameraDevice camera); // Must implement

        public void onClosed(@NonNull CameraDevice camera) {
    
    
            // Default empty implementation
        }

        public abstract void onDisconnected(@NonNull CameraDevice camera); // Must implement

        public abstract void onError(@NonNull CameraDevice camera,
                @ErrorCode int error); // Must implement
    }

4.2.3 配置目标使用情形的输出(如静态捕获、录制等)和为目标使用情形创建请求。。

// Retrieve the target surfaces, which could be coming from a number of places:
// 1. SurfaceView, if you want to display the image directly to the user
// 2. ImageReader, if you want to read each frame or perform frame-by-frame analysis
// 3. OpenGL Texture or TextureView, although discouraged for maintainability reasons
// 4. RenderScript.Allocation, if you want to do parallel processing
val surfaceView = findViewById<SurfaceView>(...)
val imageReader = ImageReader.newInstance(...)

// Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated()
val previewSurface = surfaceView.holder.surface
val imReaderSurface = imageReader.surface
val targets = listOf(previewSurface, imReaderSurface)

// Create a capture session using the predefined targets; this also involves defining the
// session state callback to be notified of when the session is ready
cameraDevice.createCaptureSession(targets, object: CameraCaptureSession.StateCallback() {
    
    
  override fun onConfigured(session: CameraCaptureSession) {
    
    
    // Do something with `session`
  }
  // Omitting for brevity...
  override fun onConfigureFailed(session: CameraCaptureSession) = Unit
}, null)  // null can be replaced with a Handler, falls back to current thread's Looper

4.2.4 捕获/重复请求和连拍以及切换单次和多次捕获。

val session: CameraCaptureSession = ...  // from CameraCaptureSession.StateCallback

// Create the repeating request and dispatch it
val repeatingRequest = session.device.createCaptureRequest(
        CameraDevice.TEMPLATE_PREVIEW)
repeatingRequest.addTarget(previewSurface)
session.setRepeatingRequest(repeatingRequest.build(), null, null)

// Some time later...

// Create the single request and dispatch it
// NOTE: This may disrupt the ongoing repeating request momentarily
val singleRequest = session.device.createCaptureRequest(
        CameraDevice.TEMPLATE_STILL_CAPTURE)
singleRequest.addTarget(imReaderSurface)
session.capture(singleRequest.build(), null, null)

单次和多次如下区别:
在这里插入图片描述

4.2.5 接收结果元数据和图片数据。

    public abstract int setRepeatingRequest(@NonNull CaptureRequest request,
            @Nullable CaptureCallback listener, @Nullable Handler handler)
            throws CameraAccessException;
  • CaptureRequest -> 【4.2.4】
  • CaptureCallback listener 如下:
 public static abstract class CaptureCallback {
    
    
        public static final int NO_FRAMES_CAPTURED = -1;

        public void onCaptureStarted(@NonNull CameraCaptureSession session,
                @NonNull CaptureRequest request, long timestamp, long frameNumber) {
    
    
            // default empty implementation
        }
        public void onCapturePartial(CameraCaptureSession session,
                CaptureRequest request, CaptureResult result) {
    
    
            // default empty implementation
        }
        public void onCaptureProgressed(@NonNull CameraCaptureSession session,
                @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
    
    
            // default empty implementation
        }
        public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
    
    
            // default empty implementation
        }
        public void onCaptureFailed(@NonNull CameraCaptureSession session,
                @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
    
    
            // default empty implementation
        }
        public void onCaptureSequenceCompleted(@NonNull CameraCaptureSession session,
                int sequenceId, long frameNumber) {
    
    
            // default empty implementation
        }
        public void onCaptureSequenceAborted(@NonNull CameraCaptureSession session,
                int sequenceId) {
    
    
            // default empty implementation
        }
        public void onCaptureBufferLost(@NonNull CameraCaptureSession session,
                @NonNull CaptureRequest request, @NonNull Surface target, long frameNumber) {
    
    
            // default empty implementation
        }
    }

END

如继续阅读相关文章可参看:Android Camera系列文章进度表也欢迎大家收藏Android Camera系列文章一起共同学习和进步。

如有疑问或勘误请评论或者通过如下邮箱联系我
[email protected]

猜你喜欢

转载自blog.csdn.net/Scott_S/article/details/122143560