视频分享最佳实践

许多人使用其 Android 设备共享视频。由于分享应用执行的处理,接收到的视频质量通常低于原始视频质量。本文档介绍了如何优化共享视频的质量以及一些常见的视频处理缺陷,应避免这些缺陷。要优化 HDR 视频内容的共享,请参阅本页上的使用 Transformer 模块将 HDR 转码为 SDR

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

共享管道

图 1 说明了共享视频的典型流程

Sharing video pipeline 图 1. 视频共享管道。

管道包括以下步骤

  1. 捕获并编码视频,可能在捕获过程中添加效果。或者,用户可以跳过此步骤,从存储中选择从其他应用程序预先录制的视频。
  2. 编辑、过滤、润色或以其他方式处理视频。
  3. 缩放或调整视频大小,准备进行转码。
  4. 转码视频以供共享。步骤 2 中的过滤通常在此步骤中应用。

在管道中,有两个步骤可以设置参数来决定视频质量:初始录制时的编码和共享之前的转码。此外,您可能需要在最终转码步骤之前重新调整视频大小,这也会影响质量。

建议

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

参数 捕获 共享
配置文件 Y Y
分辨率 Y Y
比特率 Y Y
量化参数 (QP) (很少) Y
B 帧 N Y

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

配置文件

为了获得更好的效果,请使用特定编解码器提供的更高级配置文件。对于 AVC 编码,请选择高级配置文件和 4 级。

分辨率、裁剪和缩放

您可以在转码以供共享之前,在缩放步骤中更改捕获视频的初始分辨率,但缩放会降低视频质量。我们建议您避免缩放,并为初始编码选择可以在整个管道中使用的分辨率。还要记住,极端的裁剪会导致图像质量下降,尤其是在放大裁剪的图像时。请遵循以下指南

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

    • 如果共享编码产生 720x1280 分辨率,我们建议使用 720x1280 捕获分辨率。
    • 如果捕获和共享之间的中间步骤包括裁剪,请使用更高的捕获分辨率,例如 1080x1920,并增加捕获比特率以处理额外的像素。
  • 极端的裁剪会导致图像质量下降,尤其是在放大裁剪的图像时。

  • 避免从较低分辨率向上缩放至较高分辨率。向上缩放尝试创建不存在的细节。从一开始就保持所需的较高分辨率。

  • 如果必须向上缩放,请调整编码参数。例如,如果向上缩放的分辨率具有两倍的像素,则将比特率加倍。

分辨率和比特率是相互关联的。例如,将高分辨率视频通过最终转码为低比特率的共享管道,产生的图像质量低于从较低分辨率开始的图像质量。随着比特率降低,存在交叉点,在这些交叉点,较小的分辨率开始产生更好的结果

比特率 分辨率
5+ Mbps 1080x1920
1.5 - 5+ Mbps 720x1280
1.5 Mbps 或更低 SD 等效。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 媒体库 中使用。在早期版本的 Android 上,QP 操作仅通过使用 MediaFormat 配置中的供应商特定密钥,使用框架函数才能实现。

录制

在视频捕获期间,使用比特率控制而不是 QP 设置,因为 QP 设置并不总是可用。

我们不建议调整 10 Mbps 捕获比特率(对于 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' 将用于配置 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 帧的数量。

使用 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 参考文档。