Hack WebRTC 38316

Posted by Piasy on September 23, 2023
本文是 Piasy 原创,发表于 https://blog.piasy.com,请阅读原文支持原创 https://blog.piasy.com/2023/09/23/Hack-WebRTC-38316/

时光飞逝,转眼间更新 HackWebRTC 到 88 release 已经过去了一年半 两年多。又是这么巧,最近(22 年 11 月份)OWT 把 WebRTC 代码 rebase 到了 108 release 上,那就正好把 HackWebRTC 也更新一波,这次是从 32599 到 38316,将近六千个提交。

TL; DR

不喜欢听故事,只对代码干货感兴趣的朋友,可以直接看这几个 highlight:

升级过程

这次升级的过程还算比较顺利,首先是双端都不需要 Python 2.x 了,Xcode 14 也能直接编译过 iOS。

但之前记录的 iOS gn 命令需要更新,之前的 args 参数是:

target_os="ios"
target_cpu="arm64"
ios_enable_code_signing=false
use_xcode_clang=true
is_component_build=false
enable_ios_bitcode=false
use_goma=false
is_debug=true

这次需要更新为:

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

即需要加上 rtc_include_tests=false,否则会报错:

ERROR at //webrtc.gni:508:32: Assignment had no effect.
        xctest_module_target = "//test:google_test_runner_objc"
                               ^-------------------------------
You set the variable "xctest_module_target" here and it was unused before it went
out of scope.
See //testing/test.gni:731:5: whence it was called.
    target(ios_test_target_type, _test_target) {
    ^-------------------------------------------
See //webrtc.gni:467:3: whence it was called.
  test(target_name) {
  ^------------------
See //BUILD.gn:557:3: whence it was called.
  rtc_test("rtc_unittests") {
  ^--------------------------

然后需要去掉 enable_ios_bitcode=false,因为 Xcode 已经废弃了 bitcode,这个选项已经彻底去掉了,否则会报警告:

WARNING at the command-line "--args":1:131: Build argument has no effect.
target_os="ios" target_cpu="arm64" ios_enable_code_signing=false use_xcode_clang=true is_component_build=false enable_ios_bitcode=false use_goma=false is_debug=true rtc_include_tests=false
                                                                                                                                  ^----
The variable "enable_ios_bitcode" was set as a build argument
but never appeared in a declare_args() block in any buildfile.

To view all possible args, run "gn args --list <out_dir>"

The build continued as if that argument was unspecified.

以及需要加上 rtc_enable_symbol_export=true,否则在项目外使用 WebRTC 库的时候会报链接错误:

Undefined symbol: _OBJC_CLASS_$_RTCAudioSession
Undefined symbol: _OBJC_CLASS_$_RTCAudioSessionConfiguration
Undefined symbol: _OBJC_CLASS_$_RTCCVPixelBuffer
Undefined symbol: _OBJC_CLASS_$_RTCCameraPreviewView
Undefined symbol: _OBJC_CLASS_$_RTCCameraVideoCapturer
Undefined symbol: _OBJC_CLASS_$_RTCCertificate
Undefined symbol: _OBJC_CLASS_$_RTCConfiguration
Undefined symbol: _OBJC_CLASS_$_RTCDefaultVideoDecoderFactory
Undefined symbol: _OBJC_CLASS_$_RTCDefaultVideoEncoderFactory
Undefined symbol: _OBJC_CLASS_$_RTCDispatcher
Undefined symbol: _OBJC_CLASS_$_RTCFileLogger
Undefined symbol: _OBJC_CLASS_$_RTCH264ProfileLevelId
Undefined symbol: _OBJC_CLASS_$_RTCIceCandidate
Undefined symbol: _OBJC_CLASS_$_RTCIceServer
Undefined symbol: _OBJC_CLASS_$_RTCMediaConstraints
Undefined symbol: _OBJC_CLASS_$_RTCPeerConnectionFactory
Undefined symbol: _OBJC_CLASS_$_RTCSessionDescription
Undefined symbol: _OBJC_CLASS_$_RTCVideoCapturer
Undefined symbol: _OBJC_CLASS_$_RTCVideoCodecInfo
Undefined symbol: _OBJC_CLASS_$_RTCVideoFrame
Undefined symbol: _OBJC_METACLASS_$_RTCVideoCapturer
Undefined symbol: _RTCCleanupSSL
Undefined symbol: _RTCFileName
Undefined symbol: _RTCInitFieldTrialDictionary
Undefined symbol: _RTCInitializeSSL
Undefined symbol: _RTCLogEx
Undefined symbol: _RTCSetupInternalTracer
Undefined symbol: _RTCShutdownInternalTracer

最后是需要加上 enable_dsyms=true,否则 Xcode 会无法调试源码(断点无法命中),其实这个问题去年就有朋友在星球里提过

Android 的编译同样遇到了一点小问题,其实上次更新到 32599 就遇到了 Java 代码里 @Priority@NetworkPreference 找不到定义的问题,当时因为只是注解,所以就注释了事,这次还多了个 VideoFrameBufferType,这看着应该是有 Java 代码是根据 C++ 代码动态生成的,看了看 gn 文件,试着用 ninja 编译了一下 libjingle_peerconnection_java,果然就在 out 目录搜到了生成的 Java 文件。为了方便,我就把它们提交到了 HackWebRTC 的代码库里。

另外我还发现了一个小惊喜,那就是 WebRTC Android 的 Java 代码,已经迁移到 AndroidX 了,和 support 库正式说了拜拜。

最后,运行起来我发现之前测试使用的 Loopback 模式不好使了,日志里会报错:

[054:062][15627] (jsep_transport_controller.cc:656): Failed to apply the description for m= section with mid='0': Answerer must use either active or passive value for setup attribute. (INVALID_PARAMETER)

问题原因很明确,answer 里 dtls 的 setup role 不合法,因为 Loopback 是直接把 offer 当做 answer 用的,但因为担心不仅仅是对 Loopback 有影响,所以我就看了一下 jsep_transport_controller.ccjsep_transport.cc 的代码以及变更历史,最后还是在测试 32599 才发现,老版本 sdp 里就没有 dtls 相关的内容,最终发现是老版本里有个 DtlsSrtpKeyAgreement 开关可以禁用 DTLS,Loopback 时就禁用了,但新版本把这个开关去掉了,所以 Loopback 就不能再用了 需要改为使用 PC Factory Option 里的 disableEncryption 开关。

不过上面遇到的还都只是单纯更新 WebRTC 版本遇到的小问题,在尝试使用 OWT 的 108-sdk 分支代码的过程中,遇到的问题比上次使用 88-sdk 分支的更多。

首先是 22 年 11 月份时,OWT 108-sdk 分支的代码依然编译不过,咨询了一下官方,答复是等几周才会升级完成,不过因为种种原因,等我再次捡起这个事情的时候已经到了 23 年 6 月初了。

这时 OWT 官方在 108-sdk 分支的升级工作当然已经完成了,编译没有问题,但是 H264/H265 都无法看到视频,尝试了一阵正向分析,包括:分析 stats 确认发送端发送正常,但接收端无法组帧解码;在发送端的几个环节保存码流;抓包分析;等等。但都无果,最后决定逆向排查:二分 108-sdk 的所有改动,看是哪里导致了问题。最终确实定位到了是 picture id 的改动,导致了 H264 无法组帧的问题。但回退相关改动只能解决 H264 视频不通的问题,H265 依然不通。

最终我还是放弃了使用 108-sdk 改动的想法,因为我发现 108-sdk 里面已经有了太多和 H265 无关的改动,而这些我其实并不需要,所以只需要从 88-sdk 里 cherry-pick H265 相关的改动即可。这个过程只需要处理不太多的冲突,以及把 h265 bitstream parser 的代码从 108-sdk 直接拿过来即可(因为使用的 WebRTC 基础数据结构有较大变化,这部分代码冲突太多)。这样就顺利完成了 H265 的支持,此时已经到了 9 月初。

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

主要适配有下面这些:

  • 有一些被 ffmpeg 标记为 deprecated 的接口,替换为 ffmpeg 推荐的接口,比如 av_init_packet 替换为 av_packet_allocav_packet_freeAVCodecParameterschannels/channel_layout 替换为 ch_layout

    AVPacket pkt;
    av_init_packet(&pkt);
    // use pkt
    // ===>
    AVPacket* pkt = av_packet_alloc();
    // use pkt
    av_packet_free(&pkt);
    
    
    par->channels = channel_num_;
    par->channel_layout = av_get_default_channel_layout(par->channels);
    // ===>
    if (channel_num_ == 1) {
        par->ch_layout = AV_CHANNEL_LAYOUT_MONO;
    } else if (channel_num_ == 2) {
        par->ch_layout = AV_CHANNEL_LAYOUT_STEREO;
    }
    
  • AVCodecParsersplit 不能再用了,看各个 parser 的实现代码可以发现,4.1.4 里是有对这个函数指针赋值的,5.1 就没有了,需要改为手动解析 NALU,然后把 H264/H265 的 param set 作为 extradata 设置给 ffmpeg;
  • 还有在 audio_file_decoder.cc 里使用 codec_context_->channels 的地方,也改为使用 codec_context_->ch_layout.nb_channels

在这个过程中,还遇到了 WebRTC iOS 编译的一个小小坑,就是如果有符号未定义,ninja 命令的报错非常具有迷惑性,而且根本没有任何具体的信息:

FAILED: obj/sdk/arm64/WebRTC obj/sdk/arm64/WebRTC.TOC
if [ ! -e "obj/sdk/arm64/WebRTC" -o ! -e "obj/sdk/arm64/WebRTC.TOC" ] || ../../../../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/otool -l "obj/sdk/arm64/WebRTC" | grep -q LC_REEXPORT_DYLIB ; then TOOL_VERSION=1667544419 ../../build/toolchain/apple/linker_driver.py -Wcrl,strippath,../../../../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/strip -Wcrl,installnametoolpath,../../../../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/install_name_tool ../../third_party/llvm-build/Release+Asserts/bin/clang++ -shared -all_load -install_name @rpath/WebRTC.framework/WebRTC -Wl,-install_name,@rpath/WebRTC.framework/WebRTC -Werror -fuse-ld=lld -Wl,-fatal_warnings -Wl,--color-diagnostics -Wl,--no-call-graph-profile-sort -target arm64-apple-ios12.0 -no-canonical-prefixes -nostdlib++ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.4.sdk -Wl,-ObjC -Wl,-rpath,@executable_path/Frameworks -Wl,-rpath,@loader_path/Frameworks -L/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.4.sdk/usr/lib/swift -L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphoneos -o "obj/sdk/arm64/WebRTC" "@obj/sdk/arm64/WebRTC.rsp" && { ../../../../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/otool -l "obj/sdk/arm64/WebRTC" | grep LC_ID_DYLIB -A 5; ../../../../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/nm -gPp "obj/sdk/arm64/WebRTC" | cut -f1-2 -d' ' | grep -v U$$; true; } > "obj/sdk/arm64/WebRTC.TOC"; else TOOL_VERSION=1667544419 ../../build/toolchain/apple/linker_driver.py -Wcrl,strippath,../../../../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/strip -Wcrl,installnametoolpath,../../../../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/install_name_tool ../../third_party/llvm-build/Release+Asserts/bin/clang++ -shared -all_load -install_name @rpath/WebRTC.framework/WebRTC -Wl,-install_name,@rpath/WebRTC.framework/WebRTC -Werror -fuse-ld=lld -Wl,-fatal_warnings -Wl,--color-diagnostics -Wl,--no-call-graph-profile-sort -target arm64-apple-ios12.0 -no-canonical-prefixes -nostdlib++ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.4.sdk -Wl,-ObjC -Wl,-rpath,@executable_path/Frameworks -Wl,-rpath,@loader_path/Frameworks -L/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.4.sdk/usr/lib/swift -L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphoneos -o "obj/sdk/arm64/WebRTC" "@obj/sdk/arm64/WebRTC.rsp" && { ../../../../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/otool -l "obj/sdk/arm64/WebRTC" | grep LC_ID_DYLIB -A 5; ../../../../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/nm -gPp "obj/sdk/arm64/WebRTC" | cut -f1-2 -d' ' | grep -v U$$; true; } > "obj/sdk/arm64/WebRTC.tmp" && if ! cmp -s "obj/sdk/arm64/WebRTC.tmp" "obj/sdk/arm64/WebRTC.TOC"; then mv "obj/sdk/arm64/WebRTC.tmp" "obj/sdk/arm64/WebRTC.TOC" ; fi; fi
PLEASE submit a bug report to https://crbug.com and run tools/clang/scripts/process_crashreports.py (only works inside Google) which will upload a report and include the crash backtrace.
Stack dump:
0.	Program arguments: ../../third_party/llvm-build/Release+Asserts/bin/ld64.lld -demangle -dynamic -dylib -arch arm64 -dylib_install_name @rpath/WebRTC.framework/WebRTC -all_load -platform_version ios 12.0.0 16.4 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.4.sdk -o obj/sdk/arm64/WebRTC -L/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.4.sdk/usr/lib/swift -L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphoneos -install_name @rpath/WebRTC.framework/WebRTC -fatal_warnings --color-diagnostics --no-call-graph-profile-sort -ObjC -rpath @executable_path/Frameworks -rpath @loader_path/Frameworks obj/third_party/abseil-cpp/absl/strings/strings/ascii.o ...... obj/sdk/libopengl_objc.a obj/third_party/ffmpeg/libffmpeg_internal.a -framework AVFoundation -framework CoreGraphics -framework CoreMedia -framework CoreFoundation -framework CoreText -framework Foundation -framework CFNetwork -framework Security -framework SystemConfiguration -framework UIKit -framework AudioToolbox -framework VideoToolbox -framework CoreVideo -framework Metal -framework MetalKit -framework Network -framework QuartzCore -framework ReplayKit -framework GLKit -framework OpenGLES -lm -lz -lSystem ../../third_party/llvm-build/Release+Asserts/lib/clang/16.0.0/lib/darwin/libclang_rt.ios.a
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  lld                      0x000000010a51ae4e llvm::SmallVectorBase<unsigned long long>::set_size(unsigned long) + 287086
1  lld                      0x000000010a51b21f llvm::SmallVectorBase<unsigned long long>::set_size(unsigned long) + 288063
2  libsystem_platform.dylib 0x00007ff81b9315ed _sigtramp + 29
3  libsystem_platform.dylib 0x00007ff7b640e8a0 _sigtramp + 18446744072009667280
4  lld                      0x000000010a7f61ad void lld::macho::writeResult<lld::macho::LP64>() + 301
5  lld                      0x000000010a793b18 void lld::elf::writeResult<llvm::object::ELFType<(llvm::support::endianness)0, true>>() + 207144
6  lld                      0x000000010a4c0ed0 llvm::AnalysisManager<llvm::Loop, llvm::LoopStandardAnalysisResults&>::getCachedResultImpl(llvm::AnalysisKey*, llvm::Loop&) const + 31232
7  lld                      0x000000010a4c0830 llvm::AnalysisManager<llvm::Loop, llvm::LoopStandardAnalysisResults&>::getCachedResultImpl(llvm::AnalysisKey*, llvm::Loop&) const + 29536
8  dyld                     0x00007ff81b5aa41f start + 1903
clang++: error: unable to execute command: Segmentation fault: 11
clang++: error: linker command failed due to signal (use -v to see invocation)
Traceback (most recent call last):
  File "/Users/piasy/src/media/webrtc_repo/webrtc_ios/src/out/xcode_ios_arm64/../../build/toolchain/apple/linker_driver.py", line 356, in <module>
    LinkerDriver(sys.argv).run()
  File "/Users/piasy/src/media/webrtc_repo/webrtc_ios/src/out/xcode_ios_arm64/../../build/toolchain/apple/linker_driver.py", line 140, in run
    subprocess.check_call(compiler_driver_args, env=env)
  File "/usr/local/Cellar/python@3.9/3.9.8/Frameworks/Python.framework/Versions/3.9/lib/python3.9/subprocess.py", line 373, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['../../third_party/llvm-build/Release+Asserts/bin/clang++', '-shared', '-all_load', '-install_name', '@rpath/WebRTC.framework/WebRTC', '-Wl,-install_name,@rpath/WebRTC.framework/WebRTC', '-Werror', '-fuse-ld=lld', '-Wl,-fatal_warnings', '-Wl,--color-diagnostics', '-Wl,--no-call-graph-profile-sort', '-target', 'arm64-apple-ios12.0', '-no-canonical-prefixes', '-nostdlib++', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.4.sdk', '-Wl,-ObjC', '-Wl,-rpath,@executable_path/Frameworks', '-Wl,-rpath,@loader_path/Frameworks', '-L/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.4.sdk/usr/lib/swift', '-L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphoneos', '-o', 'obj/sdk/arm64/WebRTC', '@obj/sdk/arm64/WebRTC.rsp']' returned non-zero exit status 1.
ninja: build stopped: subcommand failed.

只能看到 clang++: error: unable to execute command: Segmentation fault: 11 clang++: error: linker command failed due to signal (use -v to see invocation),但实际又看不到任何具体的报错,最后还是经过二分注释代码才发现,是有引用的符号链接的时候找不到!

知道了具体问题之后,处理起来就比较简单了,最后发现是 libavutil/audio_fifo.c 没有被编译进去,因为它原来是没有被用到的,在 chromium/scripts/generate_gn.py 里被过滤掉了,以免浪费包大小,但伴奏功能需要它了,所以别过滤就可以了。

搞定完 ffmpeg 升级适配的事情,就已经到了九月底,马上要迎来中秋国庆的双节假期啦~

这两次升级,刚好是 Chromium 的 88 版本和 108 版本,目前 Chromium 已经到了 118 版本,所以可能到 128 版本的时候我会再来升级一波,朋友们再会~


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

piasy-knowladge-planet