Hack WebRTC 43659

Posted by Piasy on January 19, 2025
本文是 Piasy 原创,发表于 https://blog.piasy.com,请阅读原文支持原创 https://blog.piasy.com/2025/01/19/Hack-WebRTC-43659/

再次时光飞逝,距离上次耗时半年多的更新 HackWebRTC 到 108 release 也已经过去了近一年半。

这一年半里,发生的事情有点多。首先是 OWT 在 24 年 10 月份把仓库都 archive 了,并在 11 月底解散了微信交流群。然后是 OWT 把 RTP 协议栈里 H265 相关的代码,都合入了 WebRTC 官方仓库里。最后,WebRTC 也迎来了 m133 版本,又新增了五千多个提交。

TL; DR

不喜欢听故事,只对代码干货感兴趣的朋友,可以直接看 hack_webrtc_43659 分支

升级过程

这次升级的过程是目前最顺利的一次,因为几乎所有 H265 支持的 C++ 代码都已经合入 WebRTC 官方仓库了,没有了那么多冲突处理、问题调试(不过我一开始并不确认这一点,所以还是处理了所有的冲突,编译尝试才确认了这一点的)。

在 macOS 15.2 + Xcode 16.2 的组合下,WebRTC iOS 编译毫无问题。

同样的,双端的 gn 命令都需要更新了。

iOS 之前的 args 参数是:

target_os="ios"
target_cpu="arm64"
rtc_enable_symbol_export=true
ios_enable_code_signing=false
is_component_build=false
use_goma=false
is_debug=true
enable_dsyms=true
rtc_include_tests=false

这次需要更新为:

target_os="ios"
target_cpu="arm64"
target_environment="device"
proprietary_codecs=true
rtc_enable_symbol_export=true
ios_enable_code_signing=false
is_component_build=false
is_debug=true
enable_dsyms=true
rtc_include_tests=false

即需要加上 target_environment="device"(编译模拟器时设置为 simulator),否则会报错:

ERROR at //build/config/apple/mobile_config.gni:63:3: Assertion failed.
  assert(filter_include([ target_environment ], _target_environments) != [],
  ^-----
target_environment must be in ["simulator", "device", "catalyst"]:
See //build/config/ios/ios_sdk.gni:5:1: whence it was imported.
import("//build/config/apple/mobile_config.gni")
^----------------------------------------------
See //build/config/sysroot.gni:73:5: whence it was imported.
    import("//build/config/ios/ios_sdk.gni")
    ^--------------------------------------
See //build/config/linux/pkg_config.gni:5:1: whence it was imported.
import("//build/config/sysroot.gni")
^----------------------------------
See //BUILD.gn:24:1: whence it was imported.
import("//build/config/linux/pkg_config.gni")
^-------------------------------------------

以及加上 proprietary_codecs=true 以启用 H265 的支持。

然后需要去掉 use_goma=false 这个无用参数。

Android 的 args 参数要更新为:

target_os="android"
target_cpu="arm64"
is_debug=false
proprietary_codecs=true
is_component_build=false
rtc_include_tests=false
enable_rust=true
enable_rust_cxx=true

除了 proprietary_codecs=true,主要是需要加上 enable_rust=true enable_rust_cxx=true 这两个选项,否则会报错:

ERROR at //build/rust/rust_target.gni:220:3: Assertion failed.
  assert(!defined(invoker.cxx_bindings) || enable_rust_cxx,
  ^-----
cxx bindings are not supported when building rust targets outside the Chromium build.
See //build/rust/rust_static_library.gni:214:3: whence it was called.
  rust_target(_target_name) {
  ^--------------------------
See //base/test/BUILD.gn:42:1: whence it was called.
rust_static_library("test_rust_logger_consumer") {
^-------------------------------------------------
See //testing/android/native_test/BUILD.gn:22:5: which caused the file to be included.
    "//base/test:test_support",

看起来 Android 端已经开始在探索使用 Rust 了,这个我们以后再一探究竟。

还有一点就是 Android 的 Java 代码已经开始使用 Java 11 的特性了,所以 Gradle 里需要设置使用 Java 11。

因为几乎所有 H265 支持的 C++ 代码都已经合入 WebRTC 官方仓库,所以添加 H265 的支持,就只需要把硬编硬解的 Java/ObjC 代码 cherry-pick 过来就行了,变更量大大减少,这个过程也很顺利,没遇到什么坑。

不过有两个小点需要注意:

  1. gn 命令的 args 参数需要加上 proprietary_codecs=true(前面已经说了);
  2. 需要设置 WebRTC-Video-H26xPacketBuffer field trail,以启用新的 packet buffer,双端启用方式如下:
// iOS
NSDictionary *fieldTrials = @{
    @"WebRTC-Video-H26xPacketBuffer": @"Enabled"
};
RTCInitFieldTrialDictionary(fieldTrials);

// Android
fieldTrials += "WebRTC-Video-H26xPacketBuffer/Enabled/";
PeerConnectionFactory.initialize(
    PeerConnectionFactory.InitializationOptions.builder(appContext)
        .setFieldTrials(fieldTrials)
        ...);

接着还是 cherry-pick 本地录制、伴奏功能,这次我发现 WebRTC 已经把 in-tree ffmpeg 升级到了 7.1,所以还需要适配 ffmpeg 从 5.1 升级到 7.1 的接口变动。

主要就是适配了 swresample 初始化时设置声道的变化(参考 ffmpeg 的 resample_audio.c 这个 example):

int64_t input_channel_layout =
    (input_channel_num_ == 1) ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO;
int64_t output_channel_layout =
    (output_channel_num_ == 1) ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO;
av_opt_set_int(context_.get(), "in_channel_layout", input_channel_layout, 0);

// ===>

AVChannelLayout input_channel_layout;
if (input_channel_num_ == 1) {
    input_channel_layout = AV_CHANNEL_LAYOUT_MONO;
} else {
    input_channel_layout = AV_CHANNEL_LAYOUT_STEREO;
}
av_opt_set_chlayout(context_.get(), "in_chlayout", &input_channel_layout, 0);

搞定 🤝

总的来说还是比较顺利,基本只花了一个周末的时间就完成了这次升级,下次再到 15x 版本的时候再看看升级一波,朋友们再会~


欢迎大家加入 Hack WebRTC 星球,和我一起钻研 WebRTC。

piasy-knowladge-planet