兼容媒体转码

在 Android 12(API 级别 31)及更高版本中,如果应用不支持 HEVC,系统可以自动将以 HEVC (H.265) 等格式录制的视频转换为 AVC (H.264)。此功能允许视频捕获应用对设备上录制的视频使用更现代、更节省存储空间的编码,而不会影响与其他应用的兼容性。

以下格式可以自动转码为设备上创建的内容

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

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

何时使用转码

转码是一个计算密集型操作,打开视频文件时会增加明显的延迟。例如,在一台 Pixel 3 手机上,一分钟的 HEVC 视频文件大约需要 20 秒才能转码成 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 获取),来处理这种情况。然后,平台使用接收应用的媒体功能来确定是否应转码视频文件。

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 未将应用添加到服务器端强制转码列表时,就会发生这种情况。启用此设置时,应用不会进行转码;禁用时,应用会进行转码。此选项默认情况下处于启用状态。

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

  • 禁用转码缓存 如果启用,则需要转码的应用不会使用转码缓存。这在开发过程中有助于轻松触发不支持媒体文件的转码,但可能会导致设备性能下降。此选项默认情况下处于禁用状态。