兼容媒体转码

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

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

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

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

何时使用转码

转码是一个计算密集型操作,在打开视频文件时会增加明显的延迟。例如,在 Pixel 3 手机上,将一个一分钟的 HEVC 视频文件转码为 AVC 大约需要 20 秒。因此,您应该仅在将视频文件发送到设备外部时才进行转码。例如,当与同一应用的其他用户共享视频文件,或与不支持现代视频格式的云服务器共享时。

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

配置转码

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

在代码中声明功能

您可以通过使用构建器构造 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 来处理这种情况,该 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. 转码由视频捕获应用启动。 Example 1 视频共享应用在其媒体功能资源文件中声明它不支持 HEVC。然后,它向视频捕获应用请求视频。视频捕获应用处理请求并使用 openTypedAssetFileDescriptor 打开文件,指定共享应用的 UID。这将启动转码过程。收到转码后的视频后,将其提供给共享应用,后者将其上传到云中的服务器。

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

未声明的格式

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

开发者选项

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

  • 覆盖转码默认值 此设置确定平台是否控制自动转码。启用覆盖时,将忽略平台默认值,并且启用转码设置将控制自动转码。此选项默认情况下处于禁用状态。

  • 启用转码 此设置指定是否自动转码未声明的格式。它默认情况下处于启用状态,但仅在也启用覆盖转码默认值时才有效。

  • 假设应用支持现代格式 此设置控制应用尝试播放未声明格式时发生的情况。当清单未声明应用是否支持特定格式,或 Google 未将应用添加到服务器端强制转码列表时,就会发生这种情况。启用此设置时,应用不会进行转码,禁用此设置时,应用会进行转码。此选项默认情况下处于启用状态。

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

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