Hack WebRTC 32599

Posted by Piasy on June 21, 2021
本文是 Piasy 原创,发表于 https://blog.piasy.com,请阅读原文支持原创 https://blog.piasy.com/2021/06/21/Hack-WebRTC-32599/

《WebRTC Native 开发实战》出版快一周年了,书里引用的代码是 #30432(介于 79 和 80 release 之间的一个节点),已经是一年半以前的版本了,虽然 HackWebRTC 有个稍微新一点的 hack_webrtc_30987 分支,不过也有一年多的历史了。刚好今年初 OWT 把 WebRTC 代码 rebase 到了 88 release 上,让我可以省去维护支持 H265 相关代码的成本,所以是时候更新 HackWebRTC 的代码了。

TL; DR

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

升级过程

这个过程并不顺利,首先是三月份的时候,OWT 的 88-sdk 分支 iOS 编译失败,因为这个事情不是很着急,所以咨询了 OWT 研发之后,我就等了一段时间,到四月份他们修复了编译问题。

不过编译通过还只是第一步,测试的时候 iOS demo 项目要跑起来始终安装失败,几番搜索无果,大概率是 gn 生成的 Xcode 项目没有适配新版本 Xcode,最后我果断决定自己维护一个 iOS demo 的 Xcode 项目,直接引用编译出来的 WebRTC.framework,当然 demo 代码的 import 也需要修改一下。

这时候已经到了五月中旬。

跑起来 demo 后发现 iOS H265 推流时,收流端没有画面,经过一番调试定位(结合 stats + 在视频数据流程加断点),发现是新的 WebRTC 版本使用了 RtpVideoStreamReceiver2 类,而 OWT 只把支持 H265 的逻辑写在了 RtpVideoStreamReceiver 类里,于是我就顺手提了个 PR

同时为了进一步降低支持 H265 相关代码的维护成本,我把 AvConf 里 H265 相关的代码都整理了一番,包括 encoder/decoder 代码去重、增加更多解析 QP 的逻辑,给 OWT 提了个 PR但因为他们坚持「少改 WebRTC 官方代码」的原则,所以宁愿 encoder/decoder 类代码大部分重复,其实都改了那么多代码了,也不差这两个类了

下一步就是不少读者提到过的问题了:明明 WebRTC 项目里有 ffmpeg 源码,为什么做录制、伴奏的时候,又要把 ffmpeg 预编译的库放进来?最初其实是没想到用 WebRTC 项目里的 ffmpeg 源码,因为之前别的项目里已经用了 ffmpeg 库,所以就照搬过来了。

实际上我已经有过在 Linux 上用 in-tree ffmpeg(就是 WebRTC 项目里的 ffmpeg 源码)的经验了,那这次就正好一并优化掉。

Linux 上用 in-tree ffmpeg 很简单:

# Linux x64, inside src/third_party/ffmpeg
# https://docs.google.com/document/d/14bqZ9NISsyEO3948wehhJ7wc9deTIz-yHUhF1MQp7Po/edit

export PATH=<webrtc checkout root>/src/third_party/llvm-build/Release+Asserts/bin:$PATH

python chromium/scripts/build_ffmpeg.py linux x64 --branding \
    Chrome -- --enable-parser=hevc --enable-demuxer=mpegts \
    --enable-protocol=file --enable-muxer=matroska

./chromium/scripts/copy_config.sh
./chromium/scripts/generate_gn.py

其中 --enable-parser=hevc --enable-demuxer=mpegts --enable-protocol=file --enable-muxer=matroska 是自己希望定制的 ffmpeg 编译选项,build_ffmpeg.py 会实际编译一下 ffmpeg 的代码,但编译产物和 WebRTC 的 ninja build 没关系,不会被用到。后两个脚本则是让 WebRTC ninja build 也能按照这些编译选项编译 ffmpeg 的关键:copy_config.sh 是把这次编译的 config header 拷贝到 WebRTC 能引用到的位置去,generate_gn.py 是生成 third_party/ffmpeg/ffmpeg_generated.gni 文件,而它就是 WebRTC ninja build 时会编译到的 ffmpeg 文件列表。

所以上述命令总结下来就是:按照自定义编译选项尝试编译 ffmpeg(验证编译选项没问题),生成对应的 config header,把编译文件列表固化到 ffmpeg_generated.gni 里。

后面再编译 WebRTC 时,gn 命令这么写就行了:

gn gen out\x86\debug_clang --args="target_os=\"win\" target_cpu=\"x86\" \
    is_debug=true rtc_use_h264=true rtc_include_internal_audio_device=true \
    ffmpeg_branding=\"Chrome\""

关键是 rtc_use_h264=trueffmpeg_branding=\"Chrome\"

但事情果然没这么简单,因为上面三个脚本都没支持 iOS 系统,把 build_ffmpeg.py 的参数从 linux 改为 ios 直接就报错了。搜索一番,找到了个帖子证实了这一点,chromium 就不支持在 iOS 上编译 ffmpeg。

但这点小问题岂能挡住我 :)

毕竟我已经在 iOS 上编译过可用的 ffmpeg 了,仔细看了下 FFmpeg-iOS-build-script 的编译脚本,其实关键就是 configure 的时候指定了 cc 和 as,那就照着 build_ffmpeg.py 里的样子,增加了 iOS 的支持。

当然这个过程肯定不是这么一句话就能顺利搞定的,趟平了好几个坑之后,最后总算顺利解决了这个问题,具体可以看看这个编译脚本:build_ios_framework.sh,里面包括了给编译 ffmpeg 相关脚本打 patch、设置编译选项、编译 WebRTC 并使用 in-tree ffmpeg。

至此,就到了五月底。

之后一直都忙着各种事情,所以再次恢复这个事情又过了大半个月,而且又趟了一个坑:cherry-pick 伴奏代码时,用到了 swresample 库,而 WebRTC 原本 in-tree ffmpeg 就只支持用 avcodec, avformat 和 avutil 三个库,于是又是一番探索和尝试,终于在今天顺利完成了升级工作。

再会~


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

piasy-knowladge-planet