可以看到,Android仅对Qualcomm和Exynos芯片组启用了硬件编码支持。那么为什么标准的WebRTC实现不支持其他芯片组呢?最有可能是因为不同制造商的硬件编解码器实现方式不尽相同,因为并非总能找到具体设备,所以这种不同通常到了生产阶段才能发现。
所有编解码器描述都存储在media_codecs.xml文件中,例如Pixel XL和HUAWEI P8 lite。当我们使用MediaCodecList对象中的getCodecInfos()方法接收编解码器列表时,该文件经过解析后,会返回已存储的编解码器。MediaCodecListTest在CTS中介绍了此过程以及制造商填写此文件的准确性,其SLOC计数已从Android 4.3中的160增加到Android 10中的740。
在Badoo中,我们更改了isHardwareSupportedInCurrentSdkH264方法代码,并将编解码器的允许列表替换为WebRTC中列出的软件编解码器前缀的黑名单:
static final String[] SOFTWARE_IMPLEMENTATION_PREFIXES = {“OMX.google.”, “OMX.SEC.”};
但我们确实需要考虑制造特性,才有可能实现对所有编解码器的支持。从专用于Android上硬件编码的describe-webrtc主题名称中,我们可以明显看出此过程中肯定会出错,大多数情况下都是编解码器配置阶段出问题。
编解码器配置参数
用于编码的编解码器初始化是这样的:
MediaCodec mediaCodec = createByCodecName(codecName); MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); format.setInteger(MediaFormat.KEY_BIT_RATE, targetBitrateBps); format.setInteger(MediaFormat.KEY_BITRATE_MODE, VIDEO_ControlRateConstant); format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat); format.setInteger(MediaFormat.KEY_FRAME_RATE, targetFps); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec); mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
其中某些参数容易出错,这会导致编解码器配置过程中出现异常,从而破坏了应用程序的功能。因为编解码器本身无法正确执行,我们可能还需要根据不同的原因调整比特率。在WebRTC中,调整任务由BaseBitrateAdjuster执行,它具有两个子类:
- DynamicBitrateAdjuster :根据数据量调整比特率。
- FramerateBitrateAdjuster:根据帧速率调整比特率。
因此我们要为每个编解码器选择不同的比特率调整方法。接下来让我们看看怎么设置硬件编解码器初始化参数。
流分辨率
在收到每个编解码器的MediaCodecInfo对象后,我们可以在CodecCapabilities类中获取该功能的信息,来更详细地了解编解码器。通过这种方式,我们可以确定该编解码器是否支持选定的分辨率和帧速率。如果它确实支持这些参数,我们就可以安全地进行设置。
但在某些情况下,此规则并不适用。我们发现,当流分辨率不是4:3时,带有“ OMX.MARVELL”前缀的编解码器在某些情况下会出现编码错误,导致屏幕边缘出现绿条,而该编解码器本身声称支持选定的分辨率和帧速率。
比特率模式
所有视频编解码器的标准模式是恒定比特率。但是有时我们必须使用可变比特率:
format.setInteger(MediaFormat.KEY_BITRATE_MODE, VIDEO_ControlRateVariable);
需要使用可变比特率的情况是搭载展讯(现在称为Unisoc)芯片组的联想A1000设备上,该芯片组以“ OMX.sprd”开头。通过网络搜索,我们找到了一份六年前发布的研究Firefox OS的文章,该作者在文章中阐述了问题和解决方案。
色彩模式
在字节缓冲模式下使用编解码器时,我们需要选择适当的格式。通常是在以下功能的帮助下执行的:
@Nullable static Integer selectColorFormat(int[] supportedColorFormats, CodecCapabilities capabilities) { for (int supportedColorFormat : supportedColorFormats) { for (int codecColorFormat : capabilities.colorFormats) { if (codecColorFormat == supportedColorFormat) { return codecColorFormat; } } } return null; }
一般来说我们应该始终选择支持的第一种颜色格式。
但是该格式并不适用于以“ OMX.IMG.TOPAZ。”、“ OMX.hisi”或“OMX.k3.”开头的HUAWEI编解码器。经过一段时间的搜索,某中文论坛上的一篇帖子帮我们解决了这个问题——无论这些编解码器返回的格式如何,使用COLOR_FormatYUV420SemiPlanar格式就可以了。
原文作者:Ivan Dyatlov