《WebRTC Native 开发实战》出版快一周年了,书里引用的代码是 #30432(介于 79 和 80 release 之间的一个节点),已经是一年半以前的版本了,虽然 HackWebRTC 有个稍微新一点的 hack_webrtc_30987 分支,不过也有一年多的历史了。刚好今年初 OWT 把 WebRTC 代码 rebase 到了 88 release 上,让我可以省去维护支持 H265 相关代码的成本,所以是时候更新 HackWebRTC 的代码了。
TL; DR
不喜欢听故事,只对代码干货感兴趣的朋友,可以直接看这几个 highlight commits:
- 脱离 gn 和 ninja 的 demo Xcode 项目
- encoder/decoder 代码去重、增加更多解析 QP 的逻辑
- 给 chromium ffmpeg 编译脚本增加 iOS 支持
- 给 chromium ffmpeg 编译脚本增加 swresample 支持
升级过程
这个过程并不顺利,首先是三月份的时候,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=true
和 ffmpeg_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。