视频分享最佳实践

许多人使用 Android 设备分享视频。由于分享应用需要执行一些处理,因此接收的视频的质量通常不如原始视频。本文档介绍了如何优化共享视频的质量,以及要避免的一些常见视频处理陷阱。如需针对分享 HDR 视频内容进行优化,请参阅本页面中的使用转换器模块将 HDR 转码为 SDR

主要做法是在准备分享视频时保持稳定的分辨率,并尽可能长时间地保持视频质量。

共享流水线

图 1 展示了分享视频的典型流程:

分享视频流水线 图 1. 视频分享流水线。

流水线包括以下步骤:

  1. 拍摄视频并对其进行编码,可能会在拍摄期间添加特效。 或者,用户可以跳过此步骤,从存储空间中选择通过其他应用预先录制的视频。
  2. 编辑、过滤、修饰或以其他方式处理视频。
  3. 调整视频大小或调整视频大小,为转码做好准备。
  4. 对视频进行转码以便分享。第 2 步中的过滤通常会在此步骤中应用。

您可以在流水线中通过两个步骤设置用于确定视频质量的参数:在初始录制期间编码,以及在分享之前进行转码。此外,您可能需要在完成最终转码步骤之前重新缩放视频,这也会影响质量。

建议

表 1 显示了视频质量的五个主要参数,并指出了哪些步骤可以使用这些参数。

参数 拍摄 共享
个人资料 Y Y
分辨率 Y Y
比特率 Y Y
量化参数 (QP) (很少) Y
B 帧 Y

表 1. 决定视频画质的主要参数

个人资料

为了获得更好的结果,请使用特定编解码器提供的更高级的配置文件。对于 AVC 编码,请选择“High profile and level 4”。

分辨率、剪裁和缩放

在转码以便共享之前,您可以在缩放步骤中更改所拍摄视频的初始分辨率,但这样做可能会降低视频质量。我们建议您避免缩放,然后选择一个可以在整个流水线中使用的初始编码分辨率。另请注意,过度剪裁会导致图片质量不佳,尤其是放大剪裁后的图片时。请遵循以下准则:

  • 选择分辨率,至少与最终共享分辨率一样大。
  • 除非所有中间步骤都设计为支持较大的分辨率(例如初始拍摄期间的比特率较高),否则拍摄分辨率不应明显超过共享分辨率。

    • 如果共享编码产生的分辨率为 720x1280,我们建议使用 720x1280 的拍摄分辨率。
    • 如果拍摄与分享之间的中间步骤包括剪裁,请使用更高的拍摄分辨率(例如 1080x1920),并增加拍摄比特率以处理额外的像素。
  • 过度剪裁会导致图片质量不佳,尤其是在剪裁后的图片放大时。

  • 避免从较低分辨率提升到较高分辨率。扩展功能会尝试创建不存在的细节。从一开始就需要采用更高的分辨率。

  • 如果必须调大,请调整编码参数。例如,如果分辨率更高的像素是原来的两倍,比特率就会翻倍。

分辨率和比特率彼此相关。例如,如果通过最终转码为低比特率的共享流水线传输高分辨率视频,那么与从低分辨率开始相比,产生的图像质量较差。随着比特率降低,会出现交叉点,分辨率越小,会产生更好的结果:

比特率 分辨率
5 Mbps 以上 1080x1920
1.5 - 5 Mbps 以上 720x1280
1.5 Mbps 或更低 与标清等效。宽高比为 9:16 的相同像素数约为 416x736

表 2. 比特率与分辨率

许多热门应用以 720p 或更低的分辨率分享视频。数据表明,如果比特率介于 1.5 Mbps 到 5 Mbps 之间,则 720p 分辨率是合适的选择。

比特率

录制

使用更高的编码比特率可以最大程度地提高视频质量。我们建议您选择与原生相机应用的比特率。对于 720x1280 的分辨率,我们建议使用 10 Mbps 的捕获比特率。

由于拍摄编码是在设备上完成的,因此您可以使用更高的比特率来补偿大多数共享步骤转换,并且几乎不产生负面影响。较大的生成的文件仅用于设备上的操作。

您可以在最后的转码步骤降低比特率,如表 2 所示。

分享

比特率在共享时影响最大,因为它与将上传的视频大小直接相关。您需要在视频质量、文件传输时间和云端存储费用之间进行权衡。

在此阶段,编码配置文件、B 帧和 QP 边界值的选择也比捕获期间更重要。

我们建议采用 4-5 Mbps 的比特率(适用于 720x1280 的分辨率),以确保良好的视觉效果。

量化参数 (QP)

在 Android 12 及更高版本中,QP 键已标准化,可在 MediaFormat API 和 NDK 媒体库中使用。在较低的 Android 版本中,只能通过在 MediaFormat 配置中使用供应商专用密钥的框架函数进行 QP 操作。

录制

在视频拍摄期间,请使用比特率控制,而不是 QP 设置,它们并非始终可用。

我们不建议调整 10Mbps(针对 720x1280)捕获比特率的 QP 设置。如果捕获比特率明显较低(720x1280 低于 5 Mbps),则 QP 设置为 40 可以很好地兼顾提升质量,而且不会强制编解码器过于频繁地超过目标比特率。

分享

我们建议将 QP 上限设为 40,尤其是在比特率低于 4 Mbps 时。 虽然这样可以确保编码视频的最低质量,但会产生更高的比特率。比特率的增加取决于视频的复杂程度。虽然分享应用可以容忍所生成视频的比特率变动,但可能无法容忍超过特定阈值的比特率变动。

您可以通过限制宽松(较高)QP 边界对视频重新编码以便分享来限制比特率增加。这样一来,编解码器便可以更自由地牺牲质量并保留视频的其他部分。您可对视频重新编码以便分享,因为这属于转码操作;而且您已经拍摄了要分享的视频。

缺点是,使用这些不同参数重复执行转码步骤会增加分享视频所需的时间。缩短此延迟时间的一种方法是查看已部分转码的视频,确定该视频是否超出您的比特率限制。如果不同,您可以停止转码,然后使用更合适的 QP 参数重试。

B 帧和编码配置文件

建议仅在共享步骤中使用 B 帧,并且仅在运行 Android 10 或更高版本时使用。

应用应使用 CodecCapabilities 检查支持的编码配置文件,因为并非所有设备都支持主要配置文件或高级配置文件。使用 AVC 编码器支持的最高配置文件:高 > 主 > 基准。为了安全起见,请勿在使用基准配置文件时配置 B 帧(KEY_LATENCYKEY_MAX_B_FRAMES),因为某些编码器可能会导致配置失败。

以下代码段假定了一个 'MediaFormat format',该 ID 将用于配置 AVC 编码器

Android 10

API 29 或更高级别

使用支持的最高配置文件并将 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

使用支持级别最高的配置文件,但禁止生成 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 及更早版本

使用基准配置文件可获得最安全的结果。

format.setInt32(KEY_PROFILE, AVCProfileBaseline);

在版本 7 之前,Android AOSP 仅支持基准配置文件。不过,OEM 可能会在某些设备上启用主要/高级配置文件(也许是通过使用供应商专用配置文件)。

如果您的应用不使用 MediaMuxer,您可以使用主配置文件或高级配置文件(如果编解码器支持)。没有用于控制 B 帧数量的公共格式密钥。

使用“转换器”模块将 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 色调映射。

以下代码设置了一个转换器,它将输入色调映射到 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 参考文档。