相机扩展程序

设备制造商可以通过 OEM 供应商库提供的相机扩展接口向第三方开发者提供焦外成像、夜间模式和 HDR 等扩展。开发者可以使用 Camera2 Extensions APICameraX Extensions API 访问在 OEM 供应商库中实现的扩展。

如需查看支持的扩展列表(对于 Camera2 和 CameraX 来说是一样的),请参阅 CameraX Extensions API。 如果您想添加扩展,请使用问题跟踪器提交 bug。

本文介绍了如何在设备上实现和启用 OEM 供应商库。

架构

下图显示了相机扩展程序接口(即 extensions-interface)的架构: 架构

图 1. 相机扩展程序架构图

如图所示,为了支持相机扩展,您需要实现 OEM 供应商库提供的 extensions-interface。您的 OEM 供应商库启用了两个 API:CameraX Extensions APICamera2 Extensions API,它们分别供 CameraX 应用和 Camera2 应用访问供应商扩展。

实现 OEM 供应商库

如需实现 OEM 供应商库,请将 camera-extensions-stub 文件复制到系统库项目中。这些文件用于定义相机扩展程序接口。

camera-extensions-stub 文件可分为以下几类:

基本接口文件(请勿修改)

  • PreviewExtenderImpl.java
  • ImageCaptureExtenderImpl.java
  • ExtenderStateListener.java
  • ProcessorImpl.java
  • PreviewImageProcessorImpl.java
  • CaptureProcessorImpl.java
  • CaptureStageImpl.java
  • RequestUpdateProcessorImpl.java
  • ProcessResultImpl.java
  • advanced/AdvancedExtenderImpl.java
  • advanced/Camera2OutputConfigImpl.java
  • advanced/Camera2SessionConfigImpl.java
  • advanced/ImageProcessorImpl.java
  • advanced/ImageReaderOutputConfigImpl.java
  • advanced/ImageReferenceImpl.java
  • advanced/MultiResolutionImageReaderOutputConfigImpl.java
  • advanced/OutputSurfaceImpl.java
  • advanced/RequestProcessorImpl.java
  • advanced/SessionProcessorImpl.java
  • advanced/SurfaceOutputConfigImpl.java

强制性实现(请添加您的实现)

  • ExtensionVersionImpl.java
  • InitializerImpl.java

焦外成像扩展器类(如果支持焦外成像扩展程序,请予以实现)

  • BokehImageCaptureExtenderImpl.java
  • BokehPreviewExtenderImpl.java
  • advanced/BokehAdvancedExtenderImpl.java

夜间模式扩展器类(如果支持夜间模式扩展程序,请予以实现)

  • NightImageCaptureExtenderImpl.java
  • NightPreviewExtenderImpl.java
  • advanced/NightAdvancedExtenderImpl.java

自动扩展器类(如果支持自动扩展程序,请予以实现)

  • AutoImageCaptureExtenderImpl.java
  • AutoPreviewExtenderImpl.java
  • advanced/AutoAdvancedExtenderImpl.java

HDR 扩展器类(如果支持 HDR 扩展程序,请予以实现)

  • HdrImageCaptureExtenderImpl.java
  • HdrPreviewExtenderImpl.java
  • advanced/HdrAdvancedExtenderImpl.java

脸部照片修复扩展器类(如果支持脸部照片修复扩展程序,请予以实现)

  • BeautyImageCaptureExtenderImpl.java
  • BeautyPreviewExtenderImpl.java
  • advanced/BeautyAdvancedExtenderImpl.java

实用程序(可选,可以删除)

  • advanced/Camera2OutputConfigImplBuilder.java
  • advanced/Camera2SessionConfigImplBuilder.java

您不需要实现所有扩展程序。如果您不实现某个扩展程序,请将 isExtensionAvailable() 设置为返回 false,或移除相应的扩展器类。Camera2 Extensions API 和 CameraX Extensions API 会向应用报告该扩展程序不可用。

接下来我们了解一下 Camera2 Extensions API 和 CameraX Extensions API 如何与供应商库进行交互,以启用扩展。下图以夜间模式扩展程序为例,显示了端到端流程:

主流程

图 2. 夜间模式扩展程序实现

  1. 版本验证

    Camera2/X 会调用 ExtensionVersionImpl.checkApiVersion(),以确保 OEM 实现的 extensions-interface 版本与 Camera2/X 支持的版本兼容。

  2. 供应商库初始化

    InitializerImpl 具有一个 init() 方法,用于初始化供应商库。Camera2/X 会先完成该初始化,然后再访问扩展器类。

  3. 实例化扩展器类

    为扩展程序实例化扩展器类。扩展器分为两种类型:基本扩展器和高级扩展器。您必须为所有扩展程序实现一种扩展器类型。如需了解详情,请参阅基本扩展器与高级扩展器

    Camera2/X 会实例化扩展器类并与之交互,以检索信息并启用扩展程序。对于给定扩展程序,Camera2/X 可以将扩展器类实例化多次。因此,请勿在构造函数或 init() 调用中执行繁重的初始化任务,而是仅在相机会话即将启动时进行,例如在基本扩展器中调用 onInit() 时或在高级扩展器中调用 initSession() 时。

    对于夜间模式扩展程序,Camera2/X 会针对基本扩展器类型实例化以下扩展器类:

    • NightImageCaptureExtenderImpl.java
    • NightPreviewExtenderImpl.java

    并会针对高级扩展器类型实例化以下扩展器类:

    • NightAdvancedExtenderImpl.java
  4. 检查扩展程序的可用性

    在启用扩展程序之前,isExtensionAvailable() 会通过扩展器实例检查扩展程序对于指定的相机 ID 是否可用。

  5. 使用相机信息初始化扩展器

    Camera2/X 会针对扩展器实例调用 init(),并向其传递相机 ID 和 CameraCharacteristics

  6. 查询信息

    调用扩展器类,以从扩展器检索支持的分辨率、预计静态拍摄延迟时间以及拍摄请求密钥等信息,为启用扩展程序做好准备。

  7. 在扩展器上启用扩展程序

    扩展器类提供启用该类所需的所有接口。它提供一种将 OEM 实现接入 Camera2 管道的机制,例如注入拍摄请求参数或启用后期处理程序。

    对于高级扩展器类型,Camera2/X 会与 SessionProcessorImpl 进行交互,以启用扩展程序。Camera2/X 通过在扩展器上调用 createSessionProcessor() 来检索 SessionProcessorImpl 实例。

以下部分更详细地介绍了扩展程序的流程。

版本验证

在运行时从设备加载 OEM 供应商库时,Camera2/X 会验证该库是否与 extensions-interface 版本兼容。extensions-interface 使用语义版本控制或 MAJOR.MINOR.PATCH(例如 1.1.0 或 1.2.0),但在版本验证期间,它仅会使用主要版本和次要版本。

为了验证版本,Camera2/X 会使用支持的 extensions-interface 版本调用 ExtensionVersionImpl.checkApiVersion()。然后,Camera2/X 会根据 OEM 库报告的版本来确定相应扩展程序能否启用以及应调用哪些功能。

主要版本兼容性

如果 Camera2/X 和供应商库的“extension-interface”的主要版本不同,系统会将其视为不兼容,并停用相应扩展程序。

向后兼容性

只要主要版本相同,Camera2/X 即可确保向后兼容使用之前的 extensions-interface 版本构建的 OEM 供应商库。例如,如果 Camera2/X 支持 extensions-interface 1.3.0,则实现 1.0.0、1.1.0 和 1.2.0 的 OEM 供应商库仍然兼容。这也意味着,您实现特定版本的供应商库后,可确保该库向后兼容支持即将推出的 extension-interface 版本的 Camera2/X。

向前兼容性

是否与实现较新的 extensions-interface 版本的供应商库向前兼容取决于您(即 OEM)。如果您需要某些功能来实现扩展程序,则可能需要支持自特定版本以来的扩展程序。在这种情况下,您可以在 Camera2/X 库版本符合相关要求时,返回支持的 extensions-interface 版本。如果 Camera2/X 版本不受支持,您可以返回不兼容的版本(例如 99.0.0),以停用扩展程序。

供应商库初始化

验证 OEM 库实现的 extensions-interface 版本后,Camera2/X 会启动初始化流程。InitializerImpl.init() 方法会向 OEM 库发出信号,表明应用正在尝试使用扩展程序。

在 OEM 供应商库调用 OnExtensionsInitializedCallback.onSuccess() 以通知初始化完成之前,Camera2/X 不会对 OEM 库进行任何其他调用(版本检查除外)。

extensions-interface 1.1.0 开始,您必须实现 InitializerImpl。如果 OEM 供应商库实现 extensions-interface 1.0.0,Camera2/X 会跳过库初始化步骤。

基本扩展器与高级扩展器

extensions-interface 实现分为两种类型:基本扩展器和高级扩展器。高级扩展器从 extensions-interface 1.2.0 开始受支持。

针对在相机 HAL 中处理图片或使用能够处理 YUV 数据流的后期处理程序的扩展程序,实现基本扩展器。

针对需要自定义 Camera2 数据流配置和根据需要发送拍摄请求的扩展程序,实现高级扩展器。

如需进行比较,请参阅下表:

基本扩展器 高级扩展器
数据流配置 固定
预览:PRIVATEYUV_420_888(如果有处理器)
静态拍摄:JPEGYUV_420_888(如果有处理器)
可由 OEM 自定义。
发送拍摄请求 只有 Camera2/X 可以发送拍摄请求。您可以为这些请求设置参数。为图片拍摄提供处理器时,Camera2/X 可以发送多个拍摄请求,并将所有图片和拍摄结果发送给处理器。 系统为您提供了一个 RequestProcessorImpl 实例,可用于执行 Camera2 拍摄请求并获取结果和图片。

Camera2/X 会针对 SessionProcessorImpl 调用 startRepeatingstartCapture,以分别指示 OEM 发起重复预览请求或启动静态拍摄序列。

相机管道中的钩子
  • onPresetSession 用于提供会话参数。
  • onEnableSession 用于在 CameraCaptureSession 配置完成后立即发送单个请求。
  • onDisableSession 用于在 CameraCaptureSession 关闭之前发送单个请求。
  • initSession 用于初始化并返回自定义的 Camera2 会话配置,以创建拍摄会话。
  • 系统会在 CameraCaptureSession 配置完成后立即调用 onCaptureSessionStart
  • 系统会在 CameraCaptureSession 关闭之前调用 onCaptureSessionEnd
适用情形 在相机 HAL 或处理 YUV 图片的处理器中实现的扩展。
  • 对于扩展,采用基于 Camera2 的实现。
  • 需要自定义数据流配置,例如 RAW 数据流。
  • 需要交互式拍摄序列。
支持的 API 版本 Camera2 扩展程序:Android 13 或更高版本
CameraX 扩展程序:camera-extensions 1.1.0 或更高版本
Camera2 扩展:Android 12L 或更高版本
CameraX 扩展:camera-extensions 1.2.0-alpha03 或更高版本

应用流程

下表显示了 3 种类型的应用流程及其对应的 Camera Extensions API 调用。尽管 Camera2/X 提供这些 API,但您必须正确实现供应商库才能支持这些流程,我们将在后续部分对此进行详细介绍。

Camera2 扩展程序 CameraX 扩展程序
查询扩展程序的可用性 CameraExtensionCharacteristics .getSupportedExtensions ExtensionsManager. isExtensionAvailable
查询信息 CameraExtensionCharacteristics. getExtensionSupportedSizes CameraExtensionCharacteristics. getEstimatedCaptureLatencyRangeMillis CameraExtensionCharacteristics. getAvailableCaptureRequestKeys CameraExtensionCharacteristics. getAvailableCaptureResultKeys ExtensionsManager. getEstimatedCaptureLatencyRange

CameraX 会处理库中的其余信息。

在启用扩展程序的情况下预览和静态拍摄 CameraDevice. createExtensionSession

cameraExtensionsSession. setRepeatingRequest

cameraExtensionsSession. capture

val cameraSelector = ExtensionsManager. getExtensionEnabledCameraSelector

bindToLifecycle(lifecycleOwner, cameraSelector, preview, ...)

基本扩展器

基本扩展器接口在相机管道中的多个位置提供钩子。每种扩展程序类型都有相应的扩展器类,OEM 需要实现这些类。

下表列出了 OEM 需要为每个扩展程序实现的扩展器类:

需要实现的扩展器类
夜间模式 NightPreviewExtenderImpl.java

NightImageCaptureExtenderImpl.java

HDR HdrPreviewExtenderImpl.java

HdrImageCaptureExtenderImpl.java

自动 AutoPreviewExtenderImpl.java

AutoImageCaptureExtenderImpl.java

焦外成像 BokehPreviewExtenderImpl.java

BokehImageCaptureExtenderImpl.java

脸部照片修复 BeautyPreviewExtenderImpl.java

BeautyImageCaptureExtenderImpl.java

在以下示例中,我们将 PreviewExtenderImplImageCaptureExtenderImpl 用作占位符。请用您要实现的实际文件的名称替换它们。

基本扩展器具有以下功能:

  • 在配置 CameraCaptureSession (onPresetSession) 时注入会话参数。
  • 将拍摄会话开始和关闭事件通知您,并发送单个包含返回的参数(onEnableSessiononDisableSession)的请求,以通知 HAL。
  • 为请求(PreviewExtenderImpl.getCaptureStageImageCaptureExtenderImpl.getCaptureStages)注入拍摄参数。
  • 针对预览和静态拍摄添加能够处理 YUV_420_888 数据流的处理器。

我们不妨了解一下 Camera2/X 如何调用 extensions-interface 来实现上述 3 个应用流程。

应用流程 1:检查扩展程序的可用性

BasicExtenderAppFlow1

图 3. 基本扩展器中的应用流程 1

在此流程中,Camera2/X 会直接调用 PreviewExtenderImplImageCaptureExtenderImplisExtensionAvailable() 方法,而不会调用 init()。两个扩展器类都必须返回 true 才能启用扩展程序。

应用在启用特定类型的扩展之前,通常会先执行此步骤来检查它是否受给定相机 ID 支持。 这是因为,某些扩展程序仅受特定相机 ID 支持。

应用流程 2:查询相关信息

BasicExtenderAppFlow2

图 4. 基本扩展器中的应用流程 2

在确定扩展程序是否可用后,应用应在启用它之前,先查询以下信息。

  • 静态拍摄延迟时间范围ImageCaptureExtenderImpl.getEstimatedCaptureLatencyRange 会返回应用的拍摄延迟时间范围,以评估当前情形是否适合启用扩展。

  • 支持的预览和拍摄 surface 尺寸ImageCaptureExtenderImpl.getSupportedResolutionsPreviewExtenderImpl.getSupportedResolutions 会针对 surface 格式和尺寸返回支持的图片格式和尺寸列表。

  • 支持的请求密钥和结果密钥:Camera2/X 会调用以下方法,从您的实现中检索支持的拍摄请求密钥和结果密钥:

    • ImageCaptureExtenderImpl.getAvailableCaptureRequestKeys
    • ImageCaptureExtenderImpl.getAvailableCapturetResultKeys

在查询更多信息之前,Camera2/X 始终会先对这些扩展器类调用 init()

应用流程 3:在启用扩展的情况下预览/静态拍摄(HAL 实现)

BasicExtenderAppFlow3

图 5. 基本扩展器中的应用流程 3

上图显示了在没有任何处理器的情况下使用某种扩展启用预览和静态拍摄的主要流程。这意味着相机 HAL 可处理扩展程序。

在此流程中,Camera2/X 会先后调用 init()onInit,后者会通知您系统将使用指定的扩展程序启动相机会话。您可以在 onInit() 调用中执行繁重的初始化任务。

配置 CameraCaptureSession 时,Camera2/X 会调用 onPresetSession 来获取会话参数。拍摄会话配置成功后,Camera2/X 会调用 onEnableSession,并返回一个包含拍摄参数的 CaptureStageImpl 实例。Camera2/X 会立即发送单个包含这些拍摄参数的请求,以通知 HAL。同样,在拍摄会话关闭之前,Camera2/X 会调用 onDisableSession,然后发送单个包含返回的拍摄参数的请求。

由 Camera2/X 触发的重复请求包含 PreviewExtenderImpl.getCaptureStage() 返回的请求参数。此外,静态拍摄请求包含 ImageCaptureExtenderImpl.getCaptureStages() 返回的参数。

最后,Camera2/X 会在相机会话完成后调用 onDeInit()。您可以在 onDeinit() 中释放资源。

预览处理器

除了相机 HAL 之外,您还可以在处理器中实现扩展程序。

实现 PreviewExtenderImpl.getProcessorType 以指定处理器类型,如下所述:

  • PROCESSOR_TYPE_NONE:无处理器。图片在相机 HAL 中进行处理。

  • PROCESSOR_TYPE_REQUEST_UPDATE_ONLY:该处理器类型允许您根据最新的 TotalCaptureResult 更新包含新的拍摄请求参数的重复请求。

    PreviewExtenderImpl.getProcessor 必须返回处理 TotalCaptureResult 实例的 RequestUpdateProcessorImpl 实例,并返回 CaptureStageImpl 实例,以更新重复请求。PreviewExtenderImpl.getCaptureStage() 还应反映处理的结果并返回最新的 CaptureStageImpl

  • PROCESSOR_TYPE_IMAGE_PROCESSOR:该处理器类型允许您实现处理器来处理 YUV_420_888 图片并将输出写入 PRIVATE surface。

    您需要在 PreviewExtenderImpl.getProcessor 中实现并返回一个 PreviewImageProcessorImpl 实例。该处理器负责处理 YUV_420_888 输入图片。它应将输出写入 PRIVATE 格式的预览。对于预览,Camera2/X 使用 YUV_420_888 surface(而不是 PRIVATE)来配置 CameraCaptureSession

    请参阅以下流程图:

PreviewProcessor

图 6. 采用 PreviewImageProcessorImpl 的预览流程

PreviewImageProcessorImpl 接口会扩展 ProcessImpl,并且有 3 个重要的方法:

  • onOutputSurface(Surface surface, int imageFormat) 用于设置处理器的输出 surface。对于 PreviewImageProcessorImpl 来说,imageFormat 是一种像素格式,例如 PixelFormat.RGBA_8888

  • onResolutionUpdate(Size size) 用于设置输入图片的大小。

  • onImageFormatUpdate(int imageFormat) 用于设置输入图片的图片格式。目前,图片格式只能为 YUV_420_888

图片拍摄处理器

对于静态拍摄,您可以通过使用 ImageCaptureExtenderImpl.getCaptureProcessor 返回 CaptureProcessorImpl 实例来实现处理器。该处理器负责处理拍摄的一系列 YUV_420_888 图片和 TotalCaptureResult 实例,并将输出写入 YUV_420_888 surface。

您可以放心地假设预览会在发送静态拍摄请求之前启用和运行。

请查看下图中的流程:

CaptureProcessor

图 7. 采用 CaptureProcessorImpl 的静态拍摄流程

  1. 对于静态拍摄,Camera2/X 使用 YUV_420_888 格式的 surface 来配置拍摄会话。Camera2/X 通过执行以下操作来准备 CaptureProcessorImpl

    • 使用 YUV_420_888 调用 CaptureProcessorImpl.onImageFormatUpdate()
    • 使用输入图片大小调用 CaptureProcessorImpl.onResolutionUpdate()
    • 使用输出 YUV_420_888 surface 调用 CaptureProcessorImpl.onOutputSurface()
  2. ImageCaptureExtenderImpl.getCaptureStages 会返回 CaptureStageImpl 列表,其中每个元素都会映射到一个包含 Camera2/X 发送的拍摄参数的 CaptureRequest 实例。例如,如果它返回包含 3 个 CaptureStageImpl 实例的列表,则 Camera2/X 会使用 captureBurst API 发送 3 个包含相应拍摄参数的拍摄请求。

  3. 系统会将收到的图片和 TotalCaptureResult 实例绑定到一起,并发送给 CaptureProcessorImpl 进行处理。

  4. CaptureProcessorImpl 会将结果图片(YUV_420_888 格式)写入 onOutputSurface() 调用指定的输出 surface。Camera2/X 会根据需要将其转换为 JPEG 图片。

支持拍摄请求密钥和结果

除了相机预览和拍摄之外,应用还可以设置缩放、闪光灯参数,或触发点按对焦。这些参数可能与您的扩展程序实现不兼容。

以下方法已添加到 extensions-interface 1.3.0,可让您公开自己的实现支持的参数:

  • ImageCaptureExtenderImpl.getAvailableCaptureRequestKeys() 用于返回您的实现支持的拍摄请求键。
  • ImageCaptureExtenderImpl.getAvailableCaptureResultKeys() 用于返回拍摄结果中包含的拍摄结果密钥。

如果由相机 HAL 处理扩展程序,那么 Camera2/X 会在 CameraCaptureSession.CaptureCallback 中检索拍摄结果。但是,如果实现了处理器,Camera2/X 则会在 ProcessResultImpl 中检索拍摄结果,并将其传递给 PreviewImageProcessorImplCaptureProcessorImpl 中的 process() 方法。您负责通过 ProcessResultImpl 向 Camera2/X 报告拍摄结果。

下面的 CaptureProcessorImpl 接口的定义就是一个示例。在 extensions-interface 1.3.0 或更高版本中,系统会调用第二个 process() 调用:

Interface CaptureProcessorImpl extends ProcessorImpl {
    // invoked when extensions-interface version < 1.3.0
    void process(Map<Integer, Pair<Image, TotalCaptureResult>> results);
    // invoked when extensions-interface version >= 1.3.0
    void process(Map<Integer, Pair<Image, TotalCaptureResult>> results,
            ProcessResultImpl resultCallback, Executor executor);
}

对于“缩放”“点按对焦”“闪光灯”和“曝光补偿”等常见的相机操作,我们建议针对拍摄请求和拍摄结果支持以下密钥:

  • 缩放
    • CaptureRequest#CONTROL_ZOOM_RATIO
    • CaptureRequest#SCALER_CROP_REGION
  • 点按对焦
    • CaptureRequest#CONTROL_AF_MODE
    • CaptureRequest#CONTROL_AF_TRIGGER
    • CaptureRequest#CONTROL_AF_REGIONS
    • CaptureRequest#CONTROL_AE_REGIONS
    • CaptureRequest#CONTROL_AWB_REGIONS
  • 闪光灯
    • CaptureRequest#CONTROL_AE_MODE
    • CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
    • CaptureRequest#FLASH_MODE
  • 曝光补偿
    • CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION

对于实现 1.2.0 或更低版本的基本扩展器,CameraX Extensions API 明确支持上述所有密钥。对于 extensions-interface 1.3.0,CameraX 和 Camera2 均会接受返回的列表,并仅支持其中包含的密钥。例如,如果您决定在 1.3.0 实现中仅返回 CaptureRequest#CONTROL_ZOOM_RATIOCaptureRequest#SCALER_CROP_REGION,这意味着应用仅支持“缩放”操作,不允许“点按对焦”“闪光灯”和“曝光补偿”操作。

高级扩展器

高级扩展器是一种基于 Camera2 API 的供应商实现。extensions-interface 1.2.0 中添加了这种扩展器。根据设备制造商的不同,扩展程序可能会在应用层实现,具体取决于以下因素:

  • 自定义数据流配置:配置 RAW 数据流等自定义数据流,或针对不同的物理相机 ID 提供多个数据流。

  • 能够发送 Camera2 请求:支持复杂的交互逻辑,即可以根据之前请求的结果发送包含参数的拍摄请求。

高级扩展器提供一个封装容器,或者说中间层,以便您可以自定义数据流配置,并按需发送拍摄请求。

要实现的文件

如需改用高级扩展器实现,ExtensionVersionImpl 中的 isAdvancedExtenderImplemented() 方法必须返回 true。对于每种扩展程序类型,OEM 都必须实现相应的扩展器类。高级扩展器实现文件位于 advanced 软件包中。

需要实现的扩展器类
夜间模式 advanced/NightAdvancedExtenderImpl.java
HDR advanced/HdrAdvancedExtenderImpl.java
自动 advanced/AutoAdvancedExtenderImpl.java
焦外成像 advanced/BokehAdvancedExtenderImpl.java
脸部照片修复 advanced/BeautyAdvancedExtenderImpl.java

在以下示例中,我们将 AdvancedExtenderImpl 用作占位符。请用您要实现的扩展的扩展器文件名称替换它。

我们来了解一下 Camera2/X 如何调用 extensions-interface 来实现 3 个应用流程。

应用流程 1:检查扩展程序的可用性

AdvancedAppFlow1

图 8. 高级扩展器中的应用流程 1

首先,应用会检查给定扩展程序是否受支持。

应用流程 2:查询相关信息

AdvancedAppFlow2

图 9. 高级扩展器中的应用流程 2

调用 AdvancedExtenderImpl.init() 后,应用可以在 AdvancedExtenderImpl 上查询以下信息:

  • 预计静态拍摄延迟时间AdvancedExtenderImpl.getEstimatedCaptureLatencyRange() 会返回应用的拍摄延迟时间范围,以评估当前情形是否适合启用扩展。

  • 支持的预览和静态拍摄分辨率

    • AdvancedExtenderImpl.getSupportedPreviewOutputResolutions() 会针对预览 surface 格式和尺寸返回支持的图片格式与尺寸的映射列表。OEM 必须至少支持 PRIVATE 格式。

    • AdvancedExtenderImpl.getSupportedCaptureOutputResolutions() 会针对静态拍摄 surface 返回支持的格式和尺寸。OEM 必须同时支持 JPEGYUV_420_888 格式输出。

    • AdvancedExtenderImpl.getSupportedYuvAnalysisResolutions() 会针对用于图片分析的额外 YUV_420_888 数据流返回支持的尺寸。如果图片分析 YUV surface 不受支持,getSupportedYuvAnalysisResolutions() 应返回 null 或空列表。

  • 可用的拍摄请求密钥/结果(在 extensions-interface 1.3.0 中添加):Camera2/X 会调用以下方法,从您的实现中检索支持的拍摄请求密钥和结果密钥:

    • AdvancedExtenderImpl.getAvailableCaptureRequestKeys
    • AdvancedExtenderImpl.getAvailableCaptureResultKeys

如需了解详情,请参阅支持拍摄请求密钥和结果

应用流程 3:在启用扩展程序的情况下预览/静态拍摄

AdvancedAppFlow3

图 10. 高级扩展器中的应用流程 3

上图显示了针对高级扩展器类型启动预览和静态拍摄的主要流程。我们来了解一下每个步骤。

  1. SessionProcessorImpl 实例

    核心高级扩展器实现位于 SessionProcessorImpl 中,它负责提供自定义会话配置和发送拍摄请求,以发起预览和静态拍摄请求。系统会调用 AdvancedExtenderImpl.createSessionProcessor() 以返回 SessionProcessorImpl 实例。

  2. initSession

    SessionProcessorImpl.initSession() 会为扩展程序初始化会话。您可以通过它分配资源并返回会话配置,以准备 CameraCaptureSession

    对于输入参数,Camera2/X 为预览、静态拍摄和可选的 YUV 图片分析指定了输出 surface 配置。此输出 surface 配置 (OutputSurfaceImpl) 包含由 AdvancedExtenderImpl 中的以下方法检索的 surface、尺寸和图片格式:

    • getSupportedPreviewOutputResolutions()
    • getSupportedCaptureOutputResolutions()
    • getSupportedYuvAnalysisResolutions()

    您必须返回一个 Camera2SessionConfigImpl 实例,其中包含 Camera2OutputConfigImpl 实例列表以及用于配置 CameraCaptureSession 的会话参数。您负责将正确的相机图片输出到由 Camera2/X 传入的输出 surface。以下是用于启用输出的一些选项:

    • 在相机 HAL 中处理:您可以直接通过 SurfaceOutputConfigImpl 实现向 CameraCaptureSession 添加输出 surface。这会将提供的输出 surface 配置到相机管道,并允许相机 HAL 处理图片。
    • 处理中间 ImageReader surface(RAW、YUV 等):通过 ImageReaderOutputConfigImpl 实例向 CameraCaptureSession 添加中间 ImageReader surface。

      您需要处理中间图片,并将结果图片写入输出 surface。

    • 使用 Camera2 surface 共享:将任一 Camera2OutputConfigImpl 实例添加到另一 Camera2OutputConfigImpl 实例的 getSurfaceSharingOutputConfigs() 方法,以通过其他 surface 使用 surface 共享。surface 格式和尺寸必须相同。

    包括 SurfaceOutputConfigImplImageReaderOutputConfigImpl 在内的所有 Camera2OutputConfigImpl 都必须具有唯一 ID (getId()),该 ID 用于指定目标 surface,并从 ImageReaderOutputConfigImpl 检索图片。

  3. onCaptureSessionStartRequestProcessorImpl

    CameraCaptureSession 启动且相机框架调用 onConfigured() 时,Camera2/X 会使用 Camera2 请求封装容器 RequestProcessImpl 调用 SessionProcessorImpl.onCaptureSessionStart()。Camera2/X 实现了 RequestProcessImpl,使您能够执行拍摄请求,以及检索图片(如果使用了 ImageReaderOutputConfigImpl)。

    在执行请求方面,RequestProcessImpl API 与 Camera2 CameraCaptureSession API 类似。区别如下:

    • 目标 surface 由 Camera2OutputConfigImpl 实例的 ID 指定。
    • 检索 ImageReader 的图片的功能。

    您可以使用指定的 Camera2OutputConfigImpl ID 调用 RequestProcessorImpl.setImageProcessor() 来注册 ImageProcessorImpl 实例,以接收图片。

    RequestProcessImpl 实例在 Camera2/X 调用 SessionProcessorImpl.onCaptureSessionEnd() 后变为无效。

  4. 启动预览和拍照

    在高级扩展器实现中,您可以通过 RequestProcessorImpl 接口发送拍摄请求。Camera2/X 分别通过调用 SessionProcessorImpl#startRepeatingSessionProcessorImpl#startCapture,通知您发起重复预览请求或启动静态拍摄序列。您应该发送拍摄请求,以满足这些预览和静态拍摄请求。

    Camera2/X 还通过 SessionProcessorImpl#setParameters 设置拍摄请求参数。您必须为重复请求和单个请求设置这些请求参数(如果参数受支持)。

    您必须至少支持 CaptureRequest.JPEG_ORIENTATIONCaptureRequest.JPEG_QUALITYextensions-interface 1.3.0 支持请求密钥和结果密钥,这些密钥通过以下方法提供:

    • AdvancedExtenderImpl.getAvailableCaptureRequestKeys()
    • AdvancedExtenderImpl.getAvailableCaptureResultKeys()

    当开发者在 getAvailableCaptureRequestKeys 列表中设置密钥时,您必须启用这些参数,并确保拍摄结果包含 getAvailableCaptureResultKeys 列表中的密钥。

  5. startTrigger

    系统会调用 SessionProcessorImpl.startTrigger() 以启动触发器,例如 CaptureRequest.CONTROL_AF_TRIGGERCaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER。您可以忽略未在 AdvancedExtenderImpl.getAvailableCaptureRequestKeys() 中通告的任何拍摄请求密钥。

    startTrigger()extensions-interface 1.3.0 开始受支持。它使应用能够通过扩展程序实现“点按对焦”和“闪光灯”。

  6. 清理

    结束拍摄会话时,系统会先调用 SessionProcessorImpl.onCaptureSessionEnd(),然后再关闭 CameraCaptureSession。拍摄会话关闭后,deInitSession() 会执行清理任务。

支持预览、静态拍摄和图片分析

您应该针对预览和静态拍摄用例应用扩展。 但是,如果延迟时间过长,无法顺利显示预览,您可以仅针对静态拍摄应用扩展。

对于基本扩展器类型,无论是否针对预览启用扩展,您都必须为给定扩展实现 ImageCaptureExtenderImplPreviewExtenderImpl。通常,应用还会使用 YUV 数据流来分析图片内容,例如查找二维码或文本。为了更好地支持此用例,您应支持预览、静态拍摄以及 YUV_420_888 数据流的数据流组合,以便配置 CameraCaptureSession。这意味着,如果您实现处理器,则必须支持 3 个 YUV_420_888 数据流的数据流组合。

对于高级扩展器,Camera2/X 会将 3 个输出 surface 传递给 SessionProcessorImpl.initSession() 调用。这些输出 surface 分别用于预览、静态拍摄和图片分析。您必须确保预览和静态拍摄输出 surface 显示有效的输出。但是,对于图片分析输出 surface,请确保其仅在非 null 时正常工作。如果您的实现不支持图片分析数据流,您可以在 AdvancedExtenderImpl.getSupportedYuvAnalysisResolutions() 中返回空列表。这可以确保 SessionProcessorImpl.initSession() 中的图片分析输出 surface 始终为 null。

支持视频拍摄

当前的相机扩展程序架构仅支持预览和静态拍摄用例。我们不支持在 MediaCodecMediaRecorder surface 上启用扩展来录制视频。不过,应用可以录制预览输出。

我们正在积极研究如何支持 MediaCodecMediaRecorder surface。

特定于扩展程序的元数据

对于 Android 14 及更高版本,特定于扩展程序的元数据可让相机扩展程序客户端设置并接收特定于扩展程序的拍摄请求设置和结果。具体而言,相机扩展程序客户端可以使用 EXTENSION_STRENGTH 拍摄请求参数控制扩展程序强度,并使用 EXTENSION_CURRENT_TYPE 拍摄结果指明已启用的扩展程序类型。

拍摄请求

EXTENSION_STRENGTH 拍摄请求参数用于控制扩展程序后期处理效果的强度。如果客户端未明确设置此参数,则相应的捕获结果会包含默认强度值。对于这些扩展程序类型,此参数的应用方式如下所示:

  • BOKEH:控制模糊处理程度。
  • HDRNIGHT:控制图片融合程度和最终图片的亮度。
  • FACE_RETOUCH:控制美颜和美肤的程度。

EXTENSION_STRENGTH 参数的支持范围介于 0100 之间,其中 0 表示不经过扩展程序处理或简单透传,100 表示处理效果达到了扩展程序的最大强度。

如需添加对 EXTENSION_STRENGTH 的支持,请使用扩展程序库接口 1.3.0 版中引入的供应商特定参数 API。如需了解详情,请参阅 getAvailableCaptureRequestKeys()

拍摄结果

EXTENSION_CURRENT_TYPE 拍摄结果让扩展程序实现能够通知客户端有效的扩展程序类型。

由于使用 AUTO 类型的扩展程序会根据场景条件在 HDRNIGHT 等扩展程序类型之间动态切换,因此相机扩展程序应用可以使用 EXTENSION_CURRENT_TYPE 显示 AUTO 扩展程序所选的当前扩展程序的相关信息。

实时预估静态拍摄延迟时间

对于 Android 14 及更高版本,相机扩展程序客户端可以根据场景和环境条件,使用 getRealtimeStillCaptureLatency() 查询实时静态拍摄延迟时间估算值。相较于静态 getEstimatedCaptureLatencyRangeMillis() 方法,此方法可提供更准确的估算值。根据估算的延迟时间,应用可以决定是跳过扩展程序处理,还是向用户显示一条消息,指出长时间运行的操作。

CameraExtensionSession.StillCaptureLatency latency;

latency = extensionSession.getRealtimeStillCaptureLatency();

// The capture latency from ExtensionCaptureCallback#onCaptureStarted() until ExtensionCaptureCallback#onCaptureProcessStarted().

latency.getCaptureLatency();

// The processing latency from  ExtensionCaptureCallback#onCaptureProcessStarted() until  the processed frame returns to the client.

latency.getProcessingLatency();

如需支持实时预估静态拍摄延迟时间,请实现以下各项:

拍摄处理进度回调

对于 Android 14 及更高版本,相机扩展程序客户端可以接收长时间运行的静态拍摄处理操作进度的回调。应用可以向用户显示当前进度,以改善整体用户体验。

应用可以使用以下代码集成此功能:

import android.hardware.camera2.CameraExtensionSession.
ExtensionCaptureCallback;

{
…
  class AppCallbackImpl extends ExtensionCaptureCallback {
…
    @Override
    public void onCaptureProcessProgressed(
      @NonNull CameraExtensionSession session,
      @NonNull CaptureRequest request,
      @IntRange(from = 0, to = 100) int progress) {
      // Update app UI with current progress
    }
  }
…
}

如需支持拍摄处理进度回调,您的扩展程序供应商实现必须使用当前进度值调用以下回调:

postview 静态拍摄

对于 Android 14 及更高版本,相机扩展程序可以使用 setPostviewOutputConfiguration 提供 postview(预览图片)。为了提升用户体验,当扩展程序的处理延迟时间增加时,应用会将 postview 图片显示为占位符,并在最终图片可用时替换该图片。应用可以使用以下参考代码配置和提出 postview 拍摄请求:

{
…
if (!CameraExtensionCharacteristics.isPostviewAvailable()) {
    continue;
}
…
ExtensionSessionConfiguration extensionConfiguration = new
        ExtensionSessionConfiguration(
                CameraExtensionCharacteristics.EXTENSION_NIGHT,
                outputConfig,
                backgroundExecutor,
                extensionSessionStateCallback
    );

extensionConfiguration.setPostviewOutputConfiguration(
    postviewImageOutput);
…
CaptureRequest.Builder captureRequestBuilder =
    cameraDevice.createCaptureRequest(
        CameraDevice.TEMPLATE_STILL_CAPTURE);
captureRequestBuilder.addTarget(stillImageReader.getSurface());
captureRequestBuilder.addTarget(postviewImageSurface);

CaptureRequest captureRequest = captureRequestBuilder.build();
…
}

如需支持 postview 静态拍摄,您的供应商实现必须实现以下各项:

支持 SurfaceView 输出

对于 Android 14 及更高版本,相机扩展程序客户端可以通过为重复请求的预览输出注册 SurfaceView 实例,来使用功耗和性能经过优化的预览渲染路径。

如需支持 SurfaceView 输出,您的供应商扩展程序实现必须能够流式传输预览并将预览输出到 SurfaceView 实例。如需验证此功能是否受支持,请运行 SurfaceViewExtensionPreviewTest.java CTS 模块。

特定于供应商的会话类型

借助该功能,供应商扩展程序实现可以选择要在内部相机拍摄会话中设置的特定于供应商的会话类型,而不是默认值。

该功能完全在框架和供应商堆栈内运行,对客户端/公开 API 没有任何影响。

如需选择特定于供应商的会话类型,请为您的扩展程序库实现以下各项: * 对于基本扩展程序:ExtenderStateListener.onSessionType() * 对于高级扩展程序:Camera2SessionConfigImpl.getSessionType()

扩展程序接口版本记录

下表显示了相机扩展程序接口的版本记录。您应始终使用最新版本实现供应商库。

版本 添加的功能
1.0.0
  • 版本验证
    • ExtensionVersionImpl
  • 基本扩展器
    • PreviewExtenderImpl
    • ImageCaptureExtenderImpl
    • Processor
      • PreviewImageProcessorImpl
      • CaptureProcessorImpl
      • RequestUpdateProcessorImpl
1.1.0
  • 库初始化
    • InitializerImpl
  • 公开支持的分辨率
    • PreviewExtenderImpl.getSupportedResolutions
    • ImageCaptureExtenderImpl.getSupportedResolutions
1.2.0
  • 高级扩展器
    • AdvancedExtenderImpl
    • SessionProcessorImpl
  • 获取预计的拍摄延迟时间
    • ImageCaptureExtenderImpl.getEstimatedCaptureLatencyRange
1.3.0
  • 公开支持的拍摄请求密钥/结果密钥
    • ImageCaptureExtenderImpl.getAvailableCaptureRequestKeysgetAvailableCaptureResultKeys
    • AdvancedExtenderImpl.getAvailableCaptureRequestKeysgetAvailableCaptureResultKeys
    • 接受 PreviewImageProcessorImplCaptureProcessorImpl 中的 ProcessResultImpl 的新 process() 调用
    • 支持触发器类型请求
      • AdvancedExtenderImpl.startTrigger
1.4.0
  • 特定于扩展程序的元数据
  • 动态预估静态拍摄延迟时间
  • 拍摄处理进度回调
  • postview 静态拍摄
  • 支持 SurfaceView 输出
  • 特定于供应商的会话类型

参考实现

frameworks/ex 中提供了以下参考 OEM 供应商库实现。

  • advancedSample:高级扩展器的基本实现。

  • sample:基本扩展器的基本实现。

  • service_based_sample:此实现演示了如何在 Service 中托管相机扩展。此实现包含以下组件:

    • oem_library:适用于 Camera2 和 CameraX Extensions API 且实现 Extensions-Interface 的 Camera Extensions OEM 库。这充当将调用从 Extensions-Interface 转发到服务的直通通道。此库还提供了 AIDL 文件和封装容器类,用于与服务进行通信。

      高级扩展器默认处于启用状态。如需启用基本扩展器,请将 ExtensionsVersionImpl#isAdvancedExtenderImplemented 更改为返回 false

    • extensions_service:扩展服务的实现示例。请在此处添加您的实现代码。在服务中实现的接口与 Extensions-Interface 类似。例如,实现 IAdvancedExtenderImpl.Stub 会执行与 AdvancedExtenderImpl 相同的操作。为了使 ImageTotalCaptureResult Parcelable,需要 ImageWrapperTotalCaptureResultWrapper

在设备上设置供应商库

OEM 供应商库未内置在应用中,而是在运行时由 Camera2/X 从设备加载。在 CameraX 中,<uses-library> 标记声明 androidx.camera.extensions.impl 库(在 camera-extensions 库的 AndroidManifest.xml 文件中定义)是 CameraX 的依赖项,必须在运行时加载。在 Camera2 中,框架会加载一个扩展程序服务,该服务还声明 <uses-library> 在运行时加载同一 androidx.camera.extensions.impl 库。

这样,使用扩展程序的第三方应用就可以自动加载 OEM 供应商库。OEM 库被标记为可选,因此应用可以在没有该库的设备上运行。 只要设备制造商将 OEM 库放置在设备上,以使应用能够发现它,Camera2/X 就会在应用尝试使用相机扩展时自动处理此行为。

如需在设备上设置 OEM 库,请执行以下操作:

  1. 添加 <uses-library> 标记所需的权限文件,格式如:/etc/permissions/ANY_FILENAME.xml。示例权限文件:/etc/permissions/camera_extensions.xml。此目录中的文件可将 <uses-library> 中指定的库映射到设备上的实际文件路径。
  2. 使用以下示例在该文件中添加必要的信息。

    • name 必须为 androidx.camera.extensions.impl,因为这是 CameraX 搜索的库。
    • file 是包含扩展程序实现的文件的绝对路径(例如 /system/framework/androidx.camera.extensions.impl.jar)。
    <?xml version="1.0" encoding="utf-8"?>
    <permissions>
        <library name="androidx.camera.extensions.impl"
                 file="OEM_IMPLEMENTED_JAR" />
    </permissions>
    

在 Android 12 或更高版本中,支持 CameraX 扩展程序的设备必须将 ro.camerax.extensions.enabled 属性设置为 true,这样才能查询设备是否支持扩展程序。为此,请在设备 makefile 中添加以下行:

PRODUCT_VENDOR_PROPERTIES += \
    ro.camerax.extensions.enabled=true \

验证

如需在开发阶段测试 OEM 供应商库的实现,请使用 androidx-main/camera/integration-tests/extensionstestapp/ 中的示例应用,该应用可通过各种供应商扩展程序运行。

完成实现后,请使用相机扩展验证工具运行自动和手动测试,以验证供应商库是否已正确实现。

扩展取景模式与相机扩展

对于焦外成像扩展,您除了可以使用相机扩展公开它,还可以使用扩展场景模式(通过 CONTROL_EXTENDED_SCENE_MODE 键启用)公开它。 如需了解实现详情,请参阅相机焦外成像

与 Camera2 应用的相机扩展相比,扩展取景模式的限制更少。例如,您可以在支持灵活组合数据流和捕获请求参数的常规 CameraCaptureSession 实例中启用扩展取景模式。相比之下,相机扩展仅支持一组固定的数据流类型,并且对拍摄请求参数的支持有限。

扩展取景模式的缺点是,只能在相机 HAL 中实现该模式,这意味着它必须经过验证,可供应用开发者可用的所有正交控件使用。

我们建议您同时使用扩展取景模式和相机扩展来公开焦外成像,因为应用可能更倾向于使用特定的 API 来启用焦外成像。 我们建议您先使用扩展取景模式,因为这是应用启用焦外成像扩展的最灵活方式。然后,您可以根据扩展取景模式实现相机扩展接口。如果难以在相机 HAL 中实现焦外成像(例如,由于需要使用应用层中运行的后处理器来处理图片),我们建议您使用相机扩展接口实现焦外成像扩展。

常见问题解答 (FAQ)

API 级别是否有限制?

有,这取决于 OEM 供应商库实现所需的 Android API 功能集。例如,ExtenderStateListener.onPresetSession() 使用 SessionConfiguration.setSessionParameters() 调用来设置基准标记集。此调用仅适用于 API 级别 28 以及更高级别。如需详细了解特定接口方法,请参阅 API 参考文档