向 Android 应用程序添加 HDR 视频功能
最新的 Android 设备具有强大的移动计算和丰富的媒体功能,其中高动态范围(HDR)的超高质量视频是最吸引人的功能之一。与标准动态范围(SDR)相比,HDR 可实现更明亮、更细致的高光和阴影以及更广泛的颜色。然而,由于 SDR 仍然是一种重要的格式,用户需要无缝体验来与其他用户的 SDR 设备以及社交媒体网站等外部系统共享 HDR 内容(例如,一些社交媒体网站支持 HDR 从标准 8 扩展)位 JPEG)。
为了支持这一点,Android 13要求任何具有 10 位 YUV 功能的 Android 设备也必须支持 SDR 并使用HLG10作为 HDR 捕获的基准。应用程序可以选择支持其他 HDR 标准,包括HDR10、HDR10+和Dolby Vision 8.4。
如果您是集成支持 HDR 的典型相机到最终用户管道的 Android 应用程序开发人员,那么您将需要更加熟悉Android API 中的Camera2 API包。API 提供对特定于设备的功能的低级访问,尽管它需要管理特定于设备的配置,但它允许您处理复杂的用例。
让我们仔细看看这意味着什么。
常用术语
在进入 Camera2 之前,了解在实现典型的相机到最终用户管道时会遇到的一些关键术语非常重要:
- 捕获:从板载摄像头传感器捕获数据 - 用于预览或记录。
- 编辑:在编解码器级别将原始数据处理为 HDR 或 SDR。此过程的一个关键阶段是色调映射,它会降低色调值,使图像适合在数字屏幕上显示。
- 编码:压缩原始数据(例如,用于存储和共享)。
- 转码:解压缩视频并重新编码(例如,转换为不同的编解码器格式、HDR 到 SDR 模式等)。在此阶段还可以进行其他更改(例如,添加水印)。
- 解码:解压缩编码视频以进行播放。
检查 HLG 支持
第一个任务是检查 HLG 支持。Android 的Camera2 API 为此提供了一个简单的接口。正如他们的文档所述:“HLG10 是设备制造商必须在 10 位相机上支持的基线 HDR 标准”,因此首先检查是否存在 10 位相机,如代码示例所示:
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)
{
//HDR available
}
}
HDR 视频捕捉
下一步是为CameraDevice设置CameraCaptureSession以从相机捕获视频。CameraCaptureSession抽象了从相机捕获图像到一个或多个目标表面的过程。
以下来自 Android HDR 视频捕获主题的代码示例显示了根据操作系统版本使用不同方法创建 CameraCaptureSession :
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
}
}
}
注意:预览流及其共享流需要低延迟配置文件,但这对于视频流来说是可选的。应用程序可以通过在调用setDynamicRangeProfile () 之前调用isExtraLatencyPresent()(传入DynamicRangeProfiles.HDR10_PLUS、DynamicRangeProfiles.HDR10和DynamicRangeProfiles.HLG)来确定任何 HDR 模式是否存在额外的前瞻延迟。
然后,会话对象可用于预览和录制。下面的代码示例展示了如何通过调用重复的 CaptureRequest来启动预览:
session.setRepeatingRequest(previewRequest,null,cameraHandler)。
注意事项:
- cameraHandler是应调用侦听器的线程处理程序(或者可以设置为null以使用当前线程)。
- 如果应用程序使用不同的 HDR 配置文件进行预览和视频,则必须使用getProfileCaptureRequestConstraints()检查有效的配置文件组合。
重复的CaptureRequest维护连续的帧流,而不必不断调用逐帧捕获请求。第一个参数是CaptureRequest,它包含执行捕获所需的信息(例如,捕获硬件、输出缓冲区、目标表面等)。
类似地,也可以使用重复请求来开始记录。以下示例显示了带有CaptureCallback的此请求,该请求可用于跟踪捕获进度(例如,开始、停止等):
session.setRepeatingRequest(recordRequest,
object : CameraCaptureSession.CaptureCallback() {
override fun onCaptureCompleted(session: CameraCaptureSession,
request: CaptureRequest, result: TotalCaptureResult) {
if (currentlyRecording) {
encoder.frameAvailable()
}
}
}, cameraHandler)
HDR10/10+ 视频编辑
使用MediaCodec类执行视频编辑。要确定设备是否支持 HDR 视频,请调用getCapabilityForType()方法,该方法返回MediaCodecInfo.CodecCapability对象。然后调用该对象的isFeatureSupported()方法并传入FEATURE_HdrEditing字符串。如果该方法返回 true,则设备支持 YUV 和 RGB 输入。在这种情况下,编码器将 RGBA1010102 变换并色调映射为可编码的 YUV P010。例如,如果用户以 HLG 格式录制 HDR 视频,他们可以缩小/旋转视频或添加徽标/贴纸并将其保存为 HDR 格式。
TransformationRequest.Builder类的experimental_setEnableHdrEditing()方法可用于构造HDR 编辑的转换。
HDR 转码为 SDR
您可能需要支持将 HDR 内容转码为 SDR,以允许在不同设备之间共享内容或将视频导出为其他格式。Snapdragon® 技术采用优化的管道,寻找减少转换过程中延迟的方法,并可以色调映射各种 HDR 格式,包括 HLG10、HDR10、HDR10+ 和杜比视界(在许可设备上)。
您可以通过实现Codec.DecoderFactory接口并使用Media API来启用转码。在这里,您为视频的 MIME 类型构造一个MediaFormat对象,并将MediaFormat.KEY_COLOR_TRANSFER_REQUEST以及MediaFormat.COLOR_TRANSFER_SDR_VIDEO标志传递到该对象的setInteger()方法。
以下代码示例显示了该接口的createForVideoDecoding()方法的实现。该实现配置了一个编解码器,如果调用者请求,该编解码器可以对原始视频帧进行色调映射以匹配请求的传输:
public Codec createForVideoDecoding(
Format, Surface outputSurface, boolean enableRequestSdrToneMapping)
throws TransformationException {
MediaFormat =
MediaFormat.createVideoFormat(
checkNotNull(format.sampleMimeType), format.width, format.height);
MediaFormatUtil.maybeSetInteger(mediaFormat, MediaFormat.KEY_ROTATION, format.rotationDegrees);
MediaFormatUtil.maybeSetInteger(
mediaFormat, MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
MediaFormatUtil.setCsdBuffers(mediaFormat, format.initializationData);
if (SDK_INT >= 29) {
// On API levels over 29, Transformer decodes as many frames as possible in one render
// cycle. This key ensures no frame dropping when the decoder's output surface is full.
mediaFormat.setInteger(MediaFormat.KEY_ALLOW_FRAME_DROP, 0);
}
if (SDK_INT >= 31 && enableRequestSdrToneMapping) {
mediaFormat.setInteger(
MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
}
@Nullable
String mediaCodecName = EncoderUtil.findCodecForFormat(mediaFormat, /* isDecoder= */ true);
if (mediaCodecName == null) {
throw createTransformationException(format);
}
return new DefaultCodec(
format, mediaFormat, mediaCodecName, /* isDecoder= */ true, outputSurface);
}
请参阅此处查看完整示例。
AndroidX Media(媒体代码示例集合)中的 Transformer示例显示了一个更复杂的示例。
结论
Camera2 是 Android 应用程序开发人员添加 HDR 支持的良好起点。借助它,您可以在运行时查询设备功能,并提供可选的代码路径,以便在受支持的设备(例如由Snapdragon 移动平台提供支持的设备)上充分利用 HDR 。最重要的是,这些基础今天就可供您使用!
请务必查看我们新的Android on Snapdragon 页面,以获取此学习资源以及适用于您的 Android 开发的其他 API 资源。