Android 12 中引入的兼容媒体转码功能允许设备使用更现代、存储效率更高的媒体格式进行视频捕获,例如 HEVC,同时保持与应用程序的兼容性。借助此功能,设备制造商可以默认使用 HEVC 而非 AVC 来提高视频质量,同时降低存储和带宽要求。对于启用了兼容媒体转码的设备,当视频被不支持格式的应用打开时,Android 可以自动转换以 HEVC 或 HDR 等格式录制的视频(最长一分钟)。即使在设备上以较新的格式捕获视频,这也允许应用程序运行。
兼容媒体转码功能默认关闭。要请求媒体转码,应用必须声明其媒体功能。有关声明媒体功能的更多信息,请参阅 Android 开发者网站上的兼容媒体转码。
这个怎么运作
兼容媒体转码功能包括两个主要部分:
- 媒体框架中的转码服务:这些服务使用硬件将文件从一种格式转换为另一种格式,以实现低延迟和高质量的转换。这包括转码 API、转码服务、自定义过滤器的 OEM 插件和硬件。有关更多详细信息,请参阅架构概述。
- 媒体提供程序中兼容的媒体转码功能:媒体提供程序中的此组件拦截访问媒体文件的应用程序,并根据应用程序声明的功能提供原始文件或转码文件。如果应用程序支持媒体文件的格式,则不需要特殊处理。如果应用程序不支持该格式,则框架会在应用程序访问文件时将文件转换为旧格式,例如 AVC。
图 1 显示了媒体转码过程的概述。
图 1.兼容媒体转码概述。
支持的格式
兼容的媒体转码功能支持以下格式转换:
- HEVC(8 位)到 AVC:编解码器转换是通过连接一个 mediacodec 解码器和一个 mediacode 编码器来执行的。
- HDR10+(10 位)到 AVC (SDR):使用媒体编解码器实例和供应商插件挂钩到解码器实例执行 HDR 到 SDR 转换。有关详细信息,请参阅HDR 到 SDR 编码。
支持的内容源
兼容的媒体转码功能支持存储在主外部卷的DCIM/Camera/
文件夹中的本地 OEM 相机应用程序生成的设备上媒体。该功能不支持辅助存储上的媒体。不支持通过电子邮件或 SD 卡传递到设备的内容。
应用程序根据各种文件路径访问文件。下面描述了启用或绕过转码的文件路径:
启用转码:
- 通过 MediaStore API 访问应用程序
- 通过直接文件路径 API(包括 Java 和本机代码)访问应用程序
- 通过存储访问框架 (SAF) 访问应用程序
- 通过操作系统共享表 Intents 访问应用程序。 (仅限媒体存储 URI)
- 从手机到 PC 的 MTP/PTP 文件传输
绕过转码:
- 通过弹出 SD 卡从设备传输文件
- 使用附近共享或蓝牙传输等选项将文件从设备传输到设备。
添加自定义文件路径以进行转码
设备制造商可以选择在DCIM/
目录下添加用于媒体转码的文件路径。 DCIM/
目录之外的任何路径都将被拒绝。可能需要添加此类文件路径以满足运营商要求或当地法规。
要添加文件路径,请使用转码路径运行时资源覆盖 (RRO) config_supported_transcoding_relative_paths
。以下是如何添加文件路径的示例:
<string-array name="config_supported_transcoding_relative_paths" translatable="false">
<item>DCIM/JCF/</item>
</string-array>
要验证配置的文件路径,请使用:
adb shell dumpsys activity provider com.google.android.providers.media.module/com.android.providers.media.MediaProvider | head -n 20
架构概述
本节介绍媒体转码功能的架构。
图 2.媒体转码架构。
媒体转码架构由以下组件组成:
- MediaTranscodingManager 系统 API:允许客户端与 MediaTranscoding 服务通信的接口。 MediaProvider模块使用此 API。
- MediaTranscodingService:管理客户端连接、安排转码请求并管理
TranscodingSessions
的簿记的本机服务。 - MediaTranscoder:执行转码的本机库。该库建立在媒体框架 NDK 之上,以与模块兼容。
兼容的媒体转码功能在服务和媒体转码器中记录转码指标。客户端和服务端代码位于 MediaProvider 模块中,以便及时修复和更新错误。
文件访问
兼容媒体转码建立在用户空间文件系统 (FUSE) 文件系统之上,用于范围存储。 FUSE 使 MediaProvider 模块能够检查用户空间中的文件操作,并根据策略限制对文件的访问,以允许、拒绝或编辑访问。
当应用程序尝试访问文件时,FUSE 守护程序会拦截来自应用程序的文件读取访问。如果应用支持较新的格式(例如 HEVC),则返回原始文件。如果应用程序不支持该格式,文件将被转码为旧格式(例如 AVC),或者如果转码版本可用,则从缓存中返回。
请求转码文件
兼容媒体转码功能默认禁用,这意味着如果设备支持 HEVC,Android 不会转码文件,除非应用程序在清单文件或强制转码列表中指定。
应用程序可以使用以下选项请求转码资产:
- 在清单文件中声明不支持的格式。有关详细信息,请参阅在资源中声明功能和在代码中声明功能。
- 将应用程序添加到MediaProvider模块中包含的强制转码列表。这将为尚未更新其清单文件的应用程序启用转码。一旦应用使用不受支持的格式更新其清单文件,就必须将其从强制转码列表中删除。设备制造商可以通过提交补丁或报告错误来提名他们的应用程序从强制转码列表中添加或删除。 Android 团队会定期审核该列表,并可能会从该列表中删除应用程序。
- 在运行时使用应用兼容性框架禁用支持的格式(用户也可以在“设置”中为每个应用禁用此功能)。
- 使用
MediaStore
打开文件,同时使用openTypedAssetFileDescriptor
API 明确指定不支持的格式。
对于 USB 传输(设备到 PC),默认情况下禁用转码,但用户可以使用USB 首选项设置屏幕中的将视频转换为 AVC切换来选择启用转码,如图 3 所示。
图 3.在 USB 首选项屏幕中切换以启用媒体转码。
请求转码文件的限制
为防止转码请求长时间锁定系统资源,请求转码会话的应用仅限于:
- 连续10次
- 总运行时间三分钟
如果应用程序超出所有这些限制,则框架会返回原始文件描述符。
设备要求
要支持兼容媒体转码功能,设备必须满足以下要求:
- 设备在本机相机应用程序上默认启用 HEVC 编码
- (支持 HDR 到 SDR 转码的设备) 设备支持 HDR 视频采集
为了确保媒体转码的设备性能,必须优化视频硬件和存储读/写访问性能。当媒体编解码器配置为优先级等于1
时,编解码器必须以尽可能高的吞吐量运行。我们建议转码性能至少达到 200 fps。要测试您的硬件性能,请在frameworks/av/media/libmediatranscoding/transcoder/benchmark
运行媒体转码器基准测试。
验证
要验证兼容的媒体转码功能,请运行以下 CTS 测试:
-
android.media.mediatranscoding.cts
-
android.mediaprovidertranscode.cts
全局启用媒体转码
要使用转码测试媒体转码框架或应用行为,您可以全局启用或禁用兼容媒体转码功能。在Settings > System > Developer > Media transcoding developer options 页面中,将Override transcoding defaults开关设置为on ,然后将Enable transcoding开关设置为on或off 。如果启用此设置,则可能会在后台为您正在开发的应用程序以外的应用程序进行媒体转码。
检查转码状态
在测试期间,您可以使用以下 ADB shell 命令检查转码状态,包括当前和过去的转码会话:
adb shell dumpsys media.transcoding
延长视频长度限制
出于测试目的,您可以使用以下命令延长转码的一分钟视频长度限制。运行此命令后可能需要重新启动。
adb shell device_config put storage_native_boot transcode_max_duration_ms <LARGE_NUMBER_IN_MS>
AOSP 来源和参考资料
以下是兼容媒体转码相关的AOSP源码。
转码系统 API(仅由 MediaProvider 使用)
应用媒体功能 API
frameworks/base/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
媒体转码服务
frameworks/av/services/mediatranscoding/
-
frameworks/av/media/libmediatranscoding/
原生媒体转码器
frameworks/av/media/libmediatranscoding/transcoder
MediaTranscoder 的 HDR 示例插件
MediaProvider文件截取和转码代码
MediaTranscoder 基准测试
frameworks/av/media/libmediatranscoding/transcoder/benchmark
CTS 测试
cts/tests/tests/mediatranscoding/
HDR 到 SDR 编码
为了支持 HDR 到 SDR 编码,设备制造商可以使用位于/platform/frameworks/av/media/codec2/hidl/plugin/
的 AOSP 示例 Codec 2.0 过滤器插件。本节介绍过滤器插件如何工作,如何实现插件以及如何测试插件。
如果设备不包含支持 HDR 到 SDR 编码的插件,则访问 HDR 视频的应用会获取原始文件描述符,而不管清单中声明的应用的媒体功能如何。
这个怎么运作
本节介绍 Codec 2.0 过滤器插件的一般行为。
背景
Android 在android::hardware::media::c2
提供了Codec 2.0接口和android.hardware.media.c2
HAL 接口之间的适配层实现。对于过滤器插件,AOSP 包含一个包装器机制,将解码器与过滤器插件包装在一起。 MediaCodec
将这些包装的组件识别为具有过滤功能的解码器。
概述
FilterWrapper
类采用供应商编解码器并将包装的编解码器返回给media.c2
适配层。 FilterWrapper
类通过FilterWrapper::Plugin
API 加载libc2filterplugin.so
并记录插件中可用的过滤器。在创建时, FilterWrapper
实例化所有可用的过滤器。只有改变缓冲区的过滤器才会在启动时启动。
图 1.过滤器插件架构。
过滤器插件界面
FilterPlugin.h
接口定义了以下 API 来公开过滤器:
std::shared_ptr<C2ComponentStore>getComponentStore()
返回一个包含过滤器的
C2ComponentStore
对象。这与供应商的 Codec 2.0 实现所公开的内容是分开的。通常,此存储仅包含 teFilterWrapper
类使用的过滤器。bool describe(C2String name, Descriptor *desc)
除了
C2ComponentStore
提供的内容之外,还描述了过滤器。定义了以下描述:-
controlParam
:控制过滤器行为的参数。例如,对于 HDR 到 SDR 色调映射器,控制参数是目标传递函数。 -
affectedParams
的参数:受过滤操作影响的参数。例如,对于 HDR 到 SDR 色调映射器,受影响的参数是颜色方面。
-
bool isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf)
如果过滤器组件更改缓冲区,则返回
true
。例如,如果目标传递函数是 SDR 并且输入传递函数是 HDR(HLG 或 PQ),则色调映射滤波器返回true
。
过滤器包装详细信息
该部分描述了FilterWrapper
类的详细信息。
创建
包装的组件在创建时实例化底层解码器和所有定义的过滤器。
查询和配置
包装的组件根据过滤器描述将传入参数与查询或配置请求分开。例如,过滤器控制参数的配置被路由到相应的过滤器,并且来自过滤器的受影响参数出现在查询上(而不是从具有未受影响参数的解码器读取)。
图 2.查询和配置。
开始
在开始时,被包装的组件启动解码器和所有改变缓冲区的过滤器。如果未启用过滤器,则包装的组件将启动解码器和直通缓冲区,并将命令发送到解码器本身。
缓冲区处理
图 3.缓冲区处理。
排队到包装解码器的缓冲区转到底层解码器。包装的组件通过onWorkDone_nb()
回调从解码器获取输出缓冲区,然后将其排队到过滤器。最后一个过滤器的最终输出缓冲区报告给客户端。
为了使缓冲区处理工作,被包装的组件必须将C2PortBlockPoolsTuning
配置为最后一个过滤器,以便框架从预期的块池中输出缓冲区。
停止、重置和释放
在停止时,包装的组件会停止解码器和所有已启动的启用过滤器。在重置和释放时,所有组件都会重置或释放,无论它们是否启用。
实现示例过滤器插件
要启用插件,请执行以下操作:
- 在库中实现
FilterPlugin
接口并将其放在/vendor/lib[64]/libc2filterplugin.so.
- 如果需要,向
mediacodec.te
添加其他权限。 - 将适配层更新到 Android 12 并重建
media.c2
服务。
测试插件
要测试示例插件,请执行以下操作:
- 重建并刷新设备。
使用以下命令构建示例插件:
m sample-codec2-filter-plugin
重新挂载设备并重命名供应商插件,以便编解码器服务能够识别它。
adb root adb remount adb reboot adb wait-for-device adb root adb remount adb push /out/target/<...>/lib64/sample-codec2-filter-plugin.so \ /vendor/lib64/libc2filterplugin.so adb push /out/target/<...>/lib/sample-codec2-filter-plugin.so \ /vendor/lib/libc2filterplugin.so adb reboot