兼容的媒体转码

在 Android 12(API 级别 31)及更高版本中,当以 HEVC (H.265) 等格式录制的视频由不支持 HEVC 格式的应用打开时,系统可以自动将其转换为 AVC (H.264) 格式。借助此功能,视频捕获应用可以对设备上录制的视频进行更现代、存储效率更高的编码,同时又不影响与其他应用的兼容性。

对于在设备上创建的以下格式的内容,系统可以自动进行转码:

媒体格式 XML 属性 MediaFormat MIME 类型
HEVC (H.265) HEVC MediaFormat.MIMETYPE_VIDEO_HEVC
HDR10HDR10 MediaFeature.HdrType.HDR10
HDR10+ HDR10Plus MediaFeature.HdrType.HDR10_PLUS

Android 假定应用可以支持播放所有媒体格式,因此兼容的媒体转码在默认情况下处于关闭状态。

何时使用转码

转码是一项计算开销很大的操作,会在打开视频文件时大幅增加延迟。例如,时长为一分钟的 HEVC 视频文件大约需要 20 秒才能在 Pixel 3 手机上转码为 AVC。因此,只有在您要将视频文件从设备上发送出去时,才应对其进行转码。例如,在与同一应用的其他用户或不支持现代视频格式的云服务器共享视频文件时。

打开视频文件以便在设备上播放或创建缩略图时,不要进行转码。

配置转码

应用可以通过声明其媒体功能来控制其转码行为。声明这些功能的方式有两种:在代码中或在资源中。

在代码中声明功能

您可以使用构建器构造 ApplicationMediaCapabilities 对象的实例,在代码中声明媒体功能:

Kotlin

val mediaCapabilities = ApplicationMediaCapabilities.Builder()
    .addSupportedVideoMimeType(MediaFormat.MIMETYPE_VIDEO_HEVC)
    .addUnsupportedHdrType(MediaFeature.HdrType.HDR10)
    .addUnsupportedHdrType(MediaFeature.HdrType.HDR10_PLUS)
    .build()

Java

ApplicationMediaCapabilities mediaCapabilities = new ApplicationMediaCapabilities.Builder()
        .addSupportedVideoMimeType(MediaFormat.MIMETYPE_VIDEO_HEVC)
        .addUnsupportedHdrType(MediaFeature.HdrType.HDR10)
        .addUnsupportedHdrType(MediaFeature.HdrType.HDR10_PLUS)
        .build();

通过 ContentResolver#openTypedAssetFileDescriptor() 等方法访问媒体内容时,请使用此对象:

Kotlin

val providerOptions = Bundle().apply {
    putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES, mediaCapabilities)
}
contentResolver.openTypedAssetFileDescriptor(mediaUri, mediaMimeType, providerOptions)
    .use { fileDescriptor ->
        // Content will be transcoded based on values defined in the
        // ApplicationMediaCapabilities provided.
    }

Java

Bundle providerOptions = new Bundle();
providerOptions.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES, mediaCapabilities);
try (AssetFileDescriptor fileDescriptor =  contentResolver.openTypedAssetFileDescriptor(mediaUri, mediaMimeType, providerOptions)) {
    // Content will be transcoded based on values defined in the
    // ApplicationMediaCapabilities provided.
}

使用此方法可以对特定的代码路径进行精细控制,例如只有在将视频文件传输到设备外时才调用转码。它优先于下文所述的方法。

在资源中声明功能

通过在资源中声明功能,可以对转码进行全面控制。此方法只能在非常特定的情况下使用。例如,如果您的应用仅从其他应用接收视频文件(而不是直接打开这些文件)并将其上传到不支持现代视频编解码器的服务器(请参见下文的示例场景 1)。

在并非绝对必要时使用此方法可能会在意外的场景中(例如,在为视频创建缩略图时)调用转码,从而导致用户体验下降。

如需使用此方法,请创建 media_capabilities.xml 资源文件:

<?xml version="1.0" encoding="utf-8"?>
<media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
    <format android:name="HEVC" supported="true"/>
    <format android:name="HDR10" supported="false"/>
    <format android:name="HDR10Plus" supported="false"/>
</media-capabilities>

在此示例中,设备上录制的 HDR 视频被无缝转码为 AVC SDR(标准动态范围)视频,而 HEVC 视频则未转码。

application 标记中使用 property 标记来添加对媒体功能文件的引用。将以下属性添加到 AndroidManifest.xml 文件中:

<property
    android:name="android.media.PROPERTY_MEDIA_CAPABILITIES"
    android:resource="@xml/media_capabilities" />

使用其他应用的媒体功能打开视频文件

如果您的应用与其他应用共享一个视频文件,可能需要先对该视频文件进行转码,然后接收方应用才能将其打开。

您可以通过使用 openTypedAssetFileDescriptor 打开视频文件并指定接收方应用的 UID(可以使用 Binder.getCallingUid 获取该 UID)来处理这种情况。然后,平台会使用接收方应用的媒体功能来确定是否应对视频文件进行转码。

Kotlin

val providerOptions = Bundle().apply {
    putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES_UID, Binder.getCallingUid())
}
contentResolver.openTypedAssetFileDescriptor(mediaUri, mediaMimeType, providerOptions)
    .use { fileDescriptor ->
        // Content will be transcoded based on the media capabilities of the
        // calling app.
    }

Java

Bundle providerOptions = new Bundle();
providerOptions.putParcelable(MediaStore.EXTRA_MEDIA_CAPABILITIES_UID, Binder.getCallingUid());
try (AssetFileDescriptor fileDescriptor =  contentResolver.openTypedAssetFileDescriptor(mediaUri, mediaMimeType, providerOptions)) {
    // Content will be transcoded based on the media capabilities of the
    // calling app.
}

示例场景

下图展示了两个常见用例。在这两种情况下,原始视频以 HEVC 格式存储,而视频共享应用不支持 HEVC。

示例 1. 转码由视频捕捉应用启动。 示例 1 视频共享应用在其媒体功能资源文件中声明它不支持 HEVC。它随后从视频捕捉应用请求视频。视频捕捉应用处理请求并使用 openTypedAssetFileDescriptor 打开文件(指定共享应用的 UID)。这会启动转码流程。收到转码后的视频时,会将其提供给共享应用,共享应用会将其上传到云端服务器。

示例 2. 转码由视频共享应用启动。 示例 2 视频捕捉应用使用 MediaStore URI 与视频共享应用共享视频。视频共享应用使用 openTypedAssetFileDescriptor 打开视频文件,在其媒体功能中指定它不支持 HEVC。这会启动转码流程,完成后,会将文件上传到云端服务器。

未声明的格式

对所有声明为不支持的格式启用兼容的媒体转码,而对所有声明为支持的格式停用兼容的媒体转码。对于未声明的其他格式,由平台决定是否进行转码。在 Android 12 中,对所有未声明的格式停用转码。此行为将来可能会针对新格式发生变化。

开发者选项

您可以使用以下开发者选项来覆盖 Android 的默认转码行为:

  • 覆盖转码默认设置:此设置决定了是否由平台控制自动转码。启用覆盖功能后,系统会忽略平台默认设置,由启用转码设置控制自动转码。此选项在默认情况下处于停用状态。

  • 启用转码:此设置指定是否自动对未声明的格式进行转码。此选项在默认情况下处于启用状态,但只有在同时启用了覆盖转码默认设置时才有效。

  • 假设应用支持现代格式:此设置控制当应用尝试播放未声明的格式的视频时会发生什么情况。如果清单未声明应用是否支持某种特定的格式,或者 Google 尚未将应用添加到服务器端强制转码列表中,就会发生这种情况。启用此设置后,应用不会执行转码操作;停用后,应用会执行转码操作。此选项在默认情况下处于启用状态。

  • 显示转码通知:启用此选项后,因读取不受支持的媒体文件而触发转码时,应用会显示转码进度通知。此选项在默认情况下处于启用状态。

  • 停用转码缓存:如果启用了此选项,则要求转码的应用不使用转码缓存。这在开发过程中会很有用,可以很容易地对不受支持的媒体文件触发转码,但可能会导致设备性能不佳。此选项在默认情况下处于停用状态。