WebRTC H.264 编解码器硬件编码的实现方法

本文介绍了WebRTC中H.264编解码器的硬件编码的实现功能,以及在Android设备(Android 5.0及更高版本)设备上启用它的方法。

为什么选择H.264?

通过WebRTC连接时,参与会话的所有设备都会传输不同的通信参数,包括视频和音频编解码器。如果设备支持多个编解码器(例如VP8和H.264),就会首先列出平台优先级最高的编解码器。这些编解码器会在WebRTC的协商阶段得到应用,此后会仅保留所有设备支持的编解码器。有关此类数据的详细示例,请参见本文档

在视频通话中,如果有一个设备不支持H.264编解码器,那么两个设备都可以切换到其他编解码器,例如不依赖于设备硬件实现的VP8编解码器。但是我们的应用程序可在不同设备上运行,包括较老旧的智能手机。这就是为什么我们希望可以使用硬件编码进行视频通信,因为这样可以减少处理器的负担,消耗较少的电池电量,这一点对于老旧设备的运行至关重要。此外,与上述VP8相比,许多设备都支持H.264硬件编码。

Android对H.264的支持

根据多媒体格式支持的信息,H.264 Baseline Profile解码应该可以在所有Android设备上运行,而编码要在Android 3.0及更高版本的设备上才能运行。在Badoo和Bumble中,我们支持Android 5.0及更高版本的设备。按理说我们应该不会遇到任何问题。但其实没有这么简单,因为即使在使用5.x版本的情况下,我们发现设备都有大大小小的问题。

可能是什么原因导致的呢?

众所周知,在开发新的Android设备时,任何制造商都应进行兼容性测试套件(Compatibility Test Suite)这一步骤。该步骤在连接到新设备的PC上运行,然后结果需要发送给Google,以确认该设备符合Android OS要求。只有这样该设备才能投放市场。

我们更关心该步骤中的多媒体测试,特别是视频编码和解码测试。我选择了EncodeDecodeTestMediaCodecTestDecoderTestEncoderTest,因为它们包含在所有4.3及更高的Android版本中。这些测试的SLOC计数如下所示:

WebRTC H.264 编解码器硬件编码的实现方法

在4.3版发布之前,大多数这样的测试还没出现。而在5版和7版发布时,测试数量已大大增加。因此可以肯定地说,在Android 4.3发布之前,Google并未测试设备是否符合其自己的视频编码和解码规范,而只是发布了版本5.0。

有人认为,从5.0版本开始,编码操作应该很顺利。但根据我以前对Android视频流解码的研究来说,情况并非如此。Google Groop discuss-webrtc中大量有关编码的帖子验证了这一点。

在我们寻找背后的漏洞时多亏了开源的WebRTC代码。接下来让我们详细了解下这个代码。

WebRTC的H.264支持

让我们从HardwareVideoEncoderFactory开始。

这里我们看到了一个很直白的名为HardwareSupportedInCurrentSdkH264的方法:

private boolean isHardwareSupportedInCurrentSdkH264(MediaCodecInfo info) {
  // First, H264 hardware might perform poorly on this model.
  if (H264_HW_EXCEPTION_MODELS.contains(Build.MODEL)) {
    return false;
  }
  String name = info.getName();
  // QCOM H264 encoder is supported in KITKAT or later.
  return (name.startsWith(QCOM_PREFIX) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
      // Exynos H264 encoder is supported in LOLLIPOP or later.
      || (name.startsWith(EXYNOS_PREFIX)
             && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP);

可以看到,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执行,它具有两个子类:

  1. DynamicBitrateAdjuster :根据数据量调整比特率。
  2. 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格式就可以了。

调整比特率

标准的WebRTC代码包含以下内容:

private BitrateAdjuster createBitrateAdjuster(VideoCodecMimeType type, String codecName) {
  if (codecName.startsWith(EXYNOS_PREFIX)) {
    if (type == VideoCodecMimeType.VP8) {
      // Exynos VP8 encoders need dynamic bitrate adjustment.
      return new DynamicBitrateAdjuster();
    } else {
      // Exynos VP9 and H264 encoders need framerate-based bitrate adjustment.
      return new FramerateBitrateAdjuster();
    }
  }
  // Other codecs don't need bitrate adjustment.
  return new BaseBitrateAdjuster();
}

从该代码可以看出,除Exynos外,其他所有芯片组都禁用了比特率调整功能。但是因为标准代码仅支持Exynos和Qualcomm,所以此设置仅适用于Qualcomm。在测试了此设置的不同值,也进行了一番网络搜索之后,我们发现带“ OMX.MTK”前缀的编解码器也应该启用这个功能。对于带有“ OMX.IMG.TOPAZ。”、“ OMX.hisi”和“ OMX.k3”前缀的HUAWEI编解码器。这是因为这些编解码器不使用帧时间点调整比特率,它们假定所有帧都以编解码器配置期间最初设置的速率到达。

最后,来看看我们收到的针对Android 5.0和5.1的编码器列表。我们对此很感兴趣,主要是因为在更新的Android版本上情况有所改善,非典型编解码器的数量正在减少。

如下图所示。为了更突出地显示非典型编解码器的情况,我们使用了对数标度。

WebRTC H.264 编解码器硬件编码的实现方法

如图,大多数设备都安装有展讯、联发科、华为和MARVELL的芯片组,这就是为什么我们的更改有助于在这些设备上启用硬件编码。

结论

尽管我们预测某些设备在使用H.264时会出现问题,但Android让我们非常惊艳。从用户统计数据中我们可以看到,有很多2014年至2016年期间生产,但现在仍在使用中的设备,它们要么不想更新,要么不能更新。尽管现在Android设备的新操作系统更新情况比几年前要好很多,但仍在使用的老旧设备所占比率正在缓慢下降。所以接下来很长一段时间内我们必须继续支持它们以实现更好的性能。

如今,Google正在积极开发WebRTC,并已在Stadia项目中应用该项目。因此,该项目会进一步发展,且很有可能成为视频通话实施的标准。我希望本文能帮助你了解使用H.264和WebRTC的细节,使你能把这些知识用到自己的项目中去。

作者:Ivan Dyatlov
原文链接:https://medium.com/bumble-tech/webrtc-on-android-how-to-enable-hardware-encoding-on-multiple-devices-5bd819c0ce5

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(1)

发表回复

登录后才能评论