Camera2综述
一、摘要
本篇文章为Camera2的综述;主要涉及到如下几个部分
- Part1: Camera2参考资料
- Part2:Camera2发展历史
- Part3:Camera2框架和使用流程
本篇文章可结合Camera1综述文章一起参看。为了描述的完整性,本篇文章和Camera1综述文章有部分内容重叠。
在Part2中,会详细介绍Camera1
和Camera2
的差异,以及Camera2的发展历史
在Part3中,会从框架维度和代码维度详细的介绍Camera2相关的使用流程。但只涉及到使用步骤,详细完善的代码开发或讲解参看Camera2源码分析
和Camera2开源项目分析
。
相关的Camera2系列文章可参考:Android Camera系列文章进度表
二、参考资料
三、发展历史
首先要明确如下2个概念:
HAL
API
在不同的HAL
和API
中,我们可以发现Camera1
和Camera2
的具体差异。
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_ops 和register_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差异
Camera1
和Camera2
分别对应着,即相机API1
和相机API2
。
3.2.1 相机API1
Android 5.0 已弃用 Camera API1
,新平台重点开发 Camera API2
,Camera 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
的支持级别里,也标记着FULL
和Level3
级别建议使用HAL3.2
以上。
3.3.2 HAL3
【3.2】
里阐述了Camera1
和Camera2
的功能差异,这里贴上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 处理控件;统计信息生成等等。
- 构造
CaptureRequest
- 单次
Capture
request
- Output1:configured output surfaces[预览,拍照等]
- Output2:
onCaptureComplete
->CaptureResult
。
核心处理流程如上图和上5个步骤。这里只是单次的request操作。Camera2
也提供了重复的Request操作流程。请继续阅读。
4.1.3 HAL操作摘要
如上图,包含如下几个流程:
- 输入和构造
CaptureRequest
Camera HAL
经过配置、帧捕获和后处理- 输出到输出流配置里。
在图片里可以看到CaptureRequest
分为2种:
- 单次
Capture
,直接输出 - 重复
Capture
,重新由4转移到6
几点说明
- 捕获的异步请求来自于框架。
- HAL 设备必须按顺序处理请求。对于每个请求,均生成输出结果元数据以及一个或多个输出图像缓冲区。
- 请求和结果以及后续请求引用的信息流遵守先进先出规则。
- 指定请求的所有输出的时间戳必须完全相同,以便框架可以根据需要将它们匹配在一起。
- 所有捕获配置和状态(不包括 3A 例程)都包含在请求和结果中。
4.1.4 Camera传感器
本小节具体介绍Camera硬件层面即Camera Sensor
如上图为:图像信号处理器(ISP,也称为相机传感器)包含了如下几个模块:
- 3A算法;
- 输出流分辨率和大小以及格式的处理
- 图像处理: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步骤
- 监听和枚举相机设备。
- 打开设备并连接监听器。
- 配置目标使用情形的输出(如静态捕获、录制等)。
- 为目标使用情形创建请求。
- 捕获/重复请求和连拍。
- 接收结果元数据和图片数据。
- 切换使用情形时,返回到第 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
}
}
转载:https://blog.csdn.net/Scott_S/article/details/122143560