HDR 视频捕获

注意: 本页涉及 Camera2 软件包。除非您的应用需要 Camera2 的特定低层级功能,否则我们建议使用 CameraX。CameraX 和 Camera2 都支持 Android 5.0 (API 级别 21) 及更高版本。

Camera2 API 支持高动态范围 (HDR) 视频捕获,使您能够使用相机预览和录制 HDR 视频内容。与标准动态范围 (SDR) 相比,HDR 提供更宽的色彩范围,并增加了亮度分量的动态范围(从当前的 100 cd/m2 提高到 1000s cd/m2)。这使得视频质量更接近真实生活,色彩更丰富,高光更明亮,阴影更深邃。

看看 HDR 视频如何以更鲜艳的细节捕捉日落。

图 1. SDR(顶部)与 HDR(底部)视频质量对比。

设备前提条件

并非所有 Android 设备都支持 HDR 视频捕获。在您的应用中捕获 HDR 视频之前,请确定您的设备是否满足以下前提条件:

  • 目标 API 级别为 Android 13 (API 级别 33)。
  • 具有 10 位或更高能力的相机传感器。有关 HDR 支持的更多信息,请参阅检查 HDR 支持

由于并非所有设备都满足这些前提条件,您可以在设置应用中的 HDR 视频捕获时添加单独的代码路径。这使您的应用可以在不兼容的设备上回退到 SDR。此外,考虑为 SDR 添加一个 UI 选项。用户可以根据其视频录制需求在 SDR 和 HDR 之间切换。

HDR 捕获架构

下图显示了 HDR 捕获架构的主要组件。

HDR capture architecture diagram.
图 2. HDR 捕获架构图。

当相机设备捕获 HDR 帧时,Camera2 框架会分配一个缓冲区来存储处理过的相机传感器输出。如果 HDR 配置文件要求,它还会附加相应的 HDR 元数据。然后,Camera2 框架会将填充的缓冲区排队到 CaptureRequest 中引用的输出 Surface,例如显示器或视频编码器,如图所示。

检查 HDR 支持

在您的应用中捕获 HDR 视频之前,请确定设备是否支持您想要的 HDR 配置文件。

使用 CameraManagergetCameraCharacteristics() 方法获取 CameraCharacteristics 实例,您可以使用该实例查询设备的 HDR 功能。

以下步骤检查设备是否支持 HLG10。HLG10 是设备制造商必须在具有 10 位输出的相机上支持的基准 HDR 标准。

  1. 首先,检查设备是否支持 10 位配置文件(HLG10 的位深)

    Kotlin

    private fun isTenBitProfileSupported(cameraId: String): Boolean {
      val cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId)
      val availableCapabilities = cameraCharacteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
      for (capability in availableCapabilities!!) {
          if (capability == CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT) {
              return true
          }
      }
      return false
    }
  2. 接下来,检查设备是否支持 HLG10(或另一个受支持的配置文件

    Kotlin

    @RequiresApi(api = 33)
    private fun isHLGSupported(cameraId: String): Boolean {
    if (isTenBitProfileSupported(cameraId)) {
      Val cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId)
      val availableProfiles = cameraCharacteristics
      .get(CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES)!!
      .getSupportedProfiles()
    
      // Checks for the desired profile, in this case HLG10
      return availableProfiles.contains(DynamicRangeProfiles.HLG10)
    }
    return false;
    }

如果设备支持 HDR,isHLGSupported() 始终返回 true。有关更多信息,请参阅 CameraCharacteristics 参考文档。

设置 HDR 捕获

确保您的设备支持 HDR 后,设置您的应用以从相机捕获原始 HDR 视频流。使用 setDynamicRangeProfile() 为流的 OutputConfiguration 提供设备支持的 HDR 配置文件,然后该配置文件在创建时传递给 CameraCaptureSession。请参阅支持的 HDR 配置文件列表

在以下代码示例中,setupSessionDynamicRangeProfile() 首先检查设备是否运行 Android 13。然后,它使用设备支持的 HDR 配置文件作为 OutputConfiguration 设置 CameraCaptureSession

Kotlin

  /**
  * Creates a [CameraCaptureSession] with a dynamic range profile.
  */
  private fun setupSessionWithDynamicRangeProfile(
          dynamicRange: Long,
          device: CameraDevice,
          targets: List,
          handler: Handler? = null,
          stateCallback: CameraCaptureSession.StateCallback
  ): Boolean {
      if (android.os.Build.VERSION.SDK_INT >=
              android.os.Build.VERSION_CODES.TIRAMISU) {
          val outputConfigs = mutableListOf()
              for (target in targets) {
                  val outputConfig = OutputConfiguration(target)
                  //sets the dynamic range profile, for example DynamicRangeProfiles.HLG10
                  outputConfig.setDynamicRangeProfile(dynamicRange)
                  outputConfigs.add(outputConfig)
              }

          device.createCaptureSessionByOutputConfigurations(
                  outputConfigs, stateCallback, handler)
          return true
      } else {
          device.createCaptureSession(targets, stateCallback, handler)
          return false
      }
  }

}

当您的相机应用初始化相机时,它会发送一个重复的 CaptureRequest 来预览录制:

Kotlin

session.setRepeatingRequest(previewRequest, null, cameraHandler)

并开始视频录制:

Kotlin

// Start recording repeating requests, which stops the ongoing preview
//  repeating requests without having to explicitly call
//  `session.stopRepeating`
session.setRepeatingRequest(recordRequest,
        object : CameraCaptureSession.CaptureCallback() {
    override fun onCaptureCompleted(session: CameraCaptureSession,
            request: CaptureRequest, result: TotalCaptureResult) {
        if (currentlyRecording) {
            encoder.frameAvailable()
        }
    }
}, cameraHandler)

编码 HDR 相机流

要编码 HDR 相机流并将其写入磁盘文件,请使用 MediaCodec

首先,获取 OutputSurface,它映射到存储原始视频数据的缓冲区。对于 MediaCodec,使用 createInputSurface()

要初始化 MediaCodec,应用必须创建一个 MediaFormat,并指定编解码器配置文件、颜色空间、颜色范围和传输函数:

Kotlin

val mimeType = when {
    dynamicRange == DynamicRangeProfiles.STANDARD -> MediaFormat.MIMETYPE_VIDEO_AVC
    dynamicRange < DynamicRangeProfiles.PUBLIC_MAX ->
            MediaFormat.MIMETYPE_VIDEO_HEVC
    else -> throw IllegalArgumentException("Unknown dynamic range format")
}

val codecProfile = when {
    dynamicRange == DynamicRangeProfiles.HLG10 ->
            MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10
    dynamicRange == DynamicRangeProfiles.HDR10 ->
            MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10
    dynamicRange == DynamicRangeProfiles.HDR10_PLUS ->
            MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10Plus
    else -> -1
}
// Failing to correctly set color transfer causes quality issues
// for example, washout and color clipping
val transferFunction = when (codecProfile) {
    MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10 ->
            MediaFormat.COLOR_TRANSFER_HLG
    MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10 ->
            MediaFormat.COLOR_TRANSFER_ST2084
    MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10Plus ->
            MediaFormat.COLOR_TRANSFER_ST2084
    else -> MediaFormat.COLOR_TRANSFER_SDR_VIDEO
}

val format = MediaFormat.createVideoFormat(mimeType, width, height)

// Set some properties.  Failing to specify some of these can cause the MediaCodec
// configure() call to throw an exception.
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
        MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface)
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate)
format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate)
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL)

if (codecProfile != -1) {
    format.setInteger(MediaFormat.KEY_PROFILE, codecProfile)
    format.setInteger(MediaFormat.KEY_COLOR_STANDARD,
            MediaFormat.COLOR_STANDARD_BT2020)
    format.setInteger(MediaFormat.KEY_COLOR_RANGE, MediaFormat.COLOR_RANGE_LIMITED)
    format.setInteger(MediaFormat.KEY_COLOR_TRANSFER, transferFunction)
    format.setFeatureEnabled(MediaCodecInfo.CodecCapabilities.FEATURE_HdrEditing,
            true)
}

mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)

有关实现的更多详细信息,请参阅 Camera2Video 示例应用的 EncoderWrapper.kt

HDR 格式

从 Android 13 开始,具有 10 位输出功能的相机设备必须支持 HLG10 进行 HDR 捕获和播放。此外,设备制造商可以使用HDR 捕获架构启用他们选择的任何 HDR 格式。

下表总结了可用的 HDR 格式及其 HDR 视频捕获能力。

格式 传输函数 (TF) 元数据 编解码器 位深
HLG10 HLG HEVC 10 位
HDR10 PQ 静态 HEVC 10 位
HDR10+ PQ 动态 HEVC 10 位
Dolby Vision 8.4 HLG 动态 HEVC 10 位

资源

有关具有 HDR 视频捕获功能的正常工作的应用,请参阅 GitHub 上的 Camera2Video 示例