许多人使用他们的 Android 设备分享视频。收到的视频质量往往不如原始视频,这是由于分享应用执行了处理。本文档介绍了如何优化分享视频的质量以及避免一些常见的视频处理陷阱。要优化 HDR 视频内容的分享,请参阅本页面上的使用 Transformer 模块将 HDR 转码为 SDR。
主要要做的是在准备分享视频时,保持分辨率恒定,并尽可能长时间地保持视频质量处于最高水平。
分享流水线
图 1 展示了典型的视频分享流程
图 1. 视频分享流水线。
该流水线包含以下步骤
- 捕获和编码视频,可能在捕获过程中添加效果。或者,用户可以跳过此步骤,直接从存储中选择一个由其他应用预先录制的视频。
- 编辑、过滤、修饰或以其他方式处理视频。
- 在转码准备阶段缩放或调整视频大小。
- 转码视频以供分享。步骤 2 中的过滤通常在此步骤中应用。
在流水线中有两个步骤可以设置决定视频质量的参数:初始录制期间的编码,以及分享之前的转码。此外,您可能需要在最终转码步骤之前重新缩放视频,这也会影响质量。
建议
表 1 列出了影响视频质量的五个主要参数,并指明了哪些步骤可以使用它们。
参数 | 捕获 | 分享 |
配置文件 | 是 | 是 |
分辨率 | 是 | 是 |
比特率 | 是 | 是 |
量化参数 (QP) | (很少) | 是 |
B 帧 | 否 | 是 |
表 1. 决定视频质量的主要参数
配置文件
为获得更好的结果,请使用特定编解码器提供的高级配置文件。对于 AVC 编码,请选择 High Profile 和 Level 4。
分辨率、裁剪和缩放
您可以在转码分享之前的缩放步骤中更改捕获视频的初始分辨率,但缩放可能会降低视频质量。我们建议您避免缩放,并在初始编码时选择一个可以在整个流水线中使用的分辨率。另请记住,过度裁剪会导致低质量图像,特别是如果您对裁剪后的图像进行放大。请遵循以下指南:
- 选择一个至少与最终分享分辨率相同或更大的分辨率。
捕获分辨率不应显著超过分享分辨率,除非所有中间步骤都设计为支持更高分辨率(例如,在初始捕获期间使用更高的比特率)。
- 如果分享编码产生 720x1280 分辨率,我们建议捕获分辨率为 720x1280。
- 如果捕获和分享之间的中间步骤包含裁剪,请使用更高的捕获分辨率,例如 1080x1920,并提高捕获比特率以处理额外像素。
过度裁剪会导致低质量图像,特别是如果对裁剪后的图像进行放大。
避免从较低分辨率放大到较高分辨率。放大尝试创建不存在的细节。从一开始就采用所需的高分辨率。
如果必须进行放大,请调整编码参数。例如,如果放大后的分辨率像素数增加一倍,则比特率也应增加一倍。
分辨率和比特率是相互关联的。例如,将高分辨率视频通过最终转码为低比特率的分享流水线,产生的图像质量低于从较低分辨率开始。随着比特率降低,存在一些交叉点,在此分辨率较小反而会产生更好的结果:
比特率 | 分辨率 |
5+ Mbps | 1080x1920 |
1.5 - 5+ Mbps | 720x1280 |
1.5 Mbps 或更低 | 标清等效。在 9:16 宽高比下,相同像素数量约为 416x736 |
表 2. 比特率与分辨率对照表
许多热门应用以 720p 或更低分辨率分享视频。数据显示,对于 1.5 到 5 Mbps 之间的目标比特率,720p 分辨率是一个合适的选择。
比特率
录制
使用更高的编码比特率可以最大程度地提高视频质量。我们建议选择与原生相机应用相匹配的比特率。对于 720x1280 分辨率,我们建议捕获比特率为 10 Mbps。
由于捕获编码是在设备上完成的,您可以使用更高的比特率来补偿大多数分享步骤转换,而负面影响很小。生成的较大文件仅用于设备上的操作。
您可以在最终转码步骤中降低比特率,如表 2 所示。
分享
比特率在分享时影响最大,因为它直接关系到将要上传的视频文件大小。视频质量、文件传输时间和云存储成本之间需要权衡。
在此阶段,编码配置文件、B 帧和 QP 边界值的选择也比捕获阶段更重要。
我们建议使用 4-5 Mbps 之间的比特率(对于 720x1280 分辨率),以确保良好的视觉质量。
量化参数 (QP)
在 Android 12 及更高版本上,QP 键已标准化,可在 MediaFormat
API 和 NDK Media 库中使用。在较早的 Android 版本上,QP 操作只能通过框架函数使用 MediaFormat
配置中的供应商特定键来使用。
录制
在视频捕获过程中,请使用比特率控制,而不是 QP 设置,因为 QP 设置并非始终可用。
对于 10Mbps 的捕获比特率(针对 720x1280),我们不建议调整 QP 设置。如果捕获比特率显著降低,例如 720x1280 的比特率低于 5 Mbps,则将 QP 设置为 40 是一个不错的折衷方案,可以在提高质量的同时避免编解码器频繁超出目标比特率。
分享
我们建议最大 QP 边界为 40,尤其是在比特率低于 4 Mbps 时。虽然这确保了编码视频的最低质量,但它可能产生比特率更高的结果。比特率的增加取决于视频的复杂性。尽管分享应用可能允许生成的视频比特率有一定的变化范围,但它可能无法容忍超出特定阈值的增加。
您可以通过使用限制较少(较高)的最大 QP 边界重新编码视频以供分享来限制比特率的增加。这为编解码器提供了更大的自由度来牺牲质量并保留视频的其他部分。您可以重新编码视频以供分享,因为这是一个转码操作;您已经捕获了想要分享的视频。
缺点是使用这些不同参数重复转码步骤会增加分享视频所需的时间。减少这种延迟的一种方法是查看部分转码的视频,以判断其是否超出了您对比特率超限的容忍范围。如果超出了,您可以停止转码并使用更合适的 QP 参数重试。
B 帧和编码配置文件
考虑仅在分享步骤期间使用 B 帧,且仅在运行 Android 10 或更高版本时使用。
应用应使用 CodecCapabilities
检查支持的编码配置文件,因为并非所有设备都支持 main 或 high profile。使用 AVC 编码器支持的最高 profile:High > Main > Baseline。为获得最安全的结果,在使用 baseline profile 时不要配置 B 帧(KEY_LATENCY
或 KEY_MAX_B_FRAMES
),因为某些编码器可能会配置失败。
以下代码段假定存在一个用于配置 AVC 编码器的 'MediaFormat format'
Android 10
API 级别 29 或更高
使用支持的最高 profile 并将 B 帧参数设置为 1
format.setInt32(KEY_PROFILE, AVCProfileHigh);
format.setInt32(KEY_MAX_B_FRAMES, 1);
在此情况下,请勿设置 KEY_LATENCY
。
Android 8、8.1 和 9
API 级别 26、27、28
使用支持的最高 profile,但禁用 B 帧的生成。这适应了这些系统版本中 MediaMuxer
的一些限制
format.setInt32(KEY_PROFILE, AVCProfileHigh);
format.setInt32(KEY_LATENCY, 1);
的 KEY_LATENCY
值禁止编解码器生成 B 帧,但仍可利用其他编解码器效率。
如果您的应用不使用 MediaMuxer
组装最终输出文件,您可以通过将 KEY_LATENCY
值设置为 2 而不是 1 来启用 B 帧。这应该允许编解码器生成 B 帧。
Android 7.1 及更早版本
API 级别 25 及更早
为获得最安全的结果,请使用 baseline profile。
format.setInt32(KEY_PROFILE, AVCProfileBaseline);
在版本 7 之前,Android AOSP 仅支持 baseline profile。但是,原始设备制造商 (OEM) 可能在某些设备上启用了 main/high profile,也许是通过使用供应商特定的 profile。
如果您的应用不使用 MediaMuxer
,当编解码器支持时,您可以使用 main 或 high profile。没有公开的格式键来控制 B 帧的数量。
使用 Transformer 模块将 HDR 转码为 SDR
从 Android 13(API 级别 33)开始,我们建议使用 Jetpack Media3 的 Transformer 模块将 HDR 内容分享给不支持 HDR 的应用、服务和设备。Transformer 模块的工作原理是将输入的 HDR 视频流进行色调映射到 SDR,并将结果保存为 MP4 文件,从而实现在不损失细节或图像亮度的情况下成功播放。
注意:在定位系统版本介于 Android 12(API 级别 32)到 Android 7.0(API 级别 24)之间的设备上,Transformer 模块的工作方式不同。如果设备支持 HDR,您的应用将播放内容而不进行色调映射。如果设备不支持 HDR,它将抛出一个错误,表明不支持 HDR 色调映射。
以下代码设置了一个 Transformer,用于将输入色调映射到 SDR,并以输入格式(例如 H.264/AVC)重新编码
Kotlin
val transformer = Transformer.Builder(context) .setTransformationRequest( TransformationRequest.Builder() .setHdrMode(TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR) .build()) .addListener(/* ... */) .build()
Java
Transformer transformer = new Transformer.Builder(context) .setTransformationRequest( new TransformationRequest.Builder() .setHdrMode(TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR) .build()) .addListener(/* ... */) .build();
要试用色调映射功能,请参阅 Transformer 演示应用。
您也可以使用 MediaCodec
设置色调映射,尽管实现更复杂。如需了解更多信息,请参阅 MediaCodec
参考文档。