2024年6月6日,QQ For Linux 3.2.9 正式支持了音视频通话功能,这是 QQ Linux 版本的又一个里程碑事件。 2024 年,QQ 音视频正式推出 NTRTC,全平台(iOS/Android/MacOS/Windows/Linux)的支持是 NTRTC 的重要特性之一,本次 Linux 平台的适配也是这次升级过程中重要的一环。
本文详细记录了新版QQ音视频通话在 Linux 平台适配开发过程中的技术方案与实现细节,希望能帮助大家理解在 Linux 平台从 0 到 1 实现音视频通话能力的过程。
/usr/bin/ld: ../../../qav_rtc_sdk/av_engine/android_ios_mac/Lib/Linux/x86_64/libTcH264Enc.a(cabac-a.asm.o): relocation R_X86_64_PC32 against symbol `g_kuiCabacRangeLps' can not be used when making a shared object; recompile with -fPIC
H264 编码和解码库在链接时报 fPIC 的问题,增加 -Bsymbolic 链接,关闭动态库 so 中默认的符号抢占方式,来绕过 fPIC 的检查。
合并净态库:
在输出 avsdk 静态库时,一般都会将各个子库进行合并,生成一个最终 qav_rtc_sdk.a,在 Linux 下没有类似 libtool、libexe 等工具,不过有个 ar 工具,可以达到合并的效果。
这个错误时机上是 ar 提取文件时,复制到待合并文件夹时环节出现的,是不同的静态库有相同命名的 .o 文件,通过重命名,还比较好解决;
2)同一个静态库,相同命名的 .o:
解决了 .o 覆盖的问题,再次 link,还是缺失符号,通过排查还是丢了对应的符号,再次排查哪一步丢的,我们发现一个静态库内出现相同命名的 .o 符号段,两个符号段在不同位置,ar x 提取时,会优先命中第一个搜索到的 .o 段,后面遇到的都会忽略,这就棘手了,是工具提取环节出现的丢失,排查了一些 ar 选项没有解决;
解决方法:通过修改该静态库内相同源文件命名解决。
3)Demo Link & Demo Run:
经过上面2个方面的适配,解决一系列link问题后,较顺利的输出了 x64 版本的 qav_rtc_sdk.a。
我们通过之前提到的 qt_demo, 进行 link 验证,也没有问题,自回环的逻辑也正常跑起来,基于 QT 开发环境也可以正常调试,此时音频、视频能力可以先开始验证。
4)AVSDKPlugin & electron demo:
我们输出了 x64 版本的 AVSDKPlugin.so,搭建了一个 electron demo,用于验证我们的动态库是否可以正常运行;
这里需要在 Linux 安装 electron 环境,具体看 Electron Quick Start。
然后我们遇到了第一个问题:动态库拉不起来!
错误信息:182204.991288: ERROR:ppapi_thread.cc(269) Failed to load Pepper module from ~/robert/AVSDKPluginDemo/app/avsdk/libAVSDKPlugin.so(error cannot open shared object file: Operation not permitted)。
通过错误信息,我们大致能看出来是权限问题,首先通过确认,排除了 so 文件路径错误的问题,那就是权限问题。
既然知道了什么原因,我们暂时先关闭 electron app 的沙箱模式,后面这个问题通过修改 electron 源码来解决。
运行 Electron Demo,Electron 新创建了一个浏览器窗口,并且通过 Pepper Plugin 方式,拉起音视频进程加载了 AVSDKPlugin.so,well done!路通了。
10、工程调试
QT Demo Debug。
首先我们通过 QT 开发环境对运行的 demo app 直接进行调试。
demo link 了 qav_ntrtc_sdk.a , 使用了 xpstl::list 做了一些操作。
通过断点,我们可以方便的进行调试,这是基于直接运行 app 做的操作;
那么如何调试 electron app + plugin 拉起的 AVSDKPlugin.so 呢?
此时有经验的同学会想到挂载进程调试,没错,我们此时也可以通过挂载进程调试正在运行的音视频进程。
音视频进程 AVSDKPlugin.so 调试(CLion 挂载进程调试):
1)打开 CLion->Run->Attach To Process>选择对应进程,确定;
2)调试:正常在 CLion 打断点即可;
3)注意:需要是 Debug 版本的动态库。
demo 拉起 avsdk 各个线程:
通过 log,我们也可以看到输出:
【问题】Linux 挂载进程失败,提示没权限:
这里是 Linux 系统有个权限问题,按 GPT 给出的解决方案,修改一下重启电脑生效。
11、 GLIBC、GLIBCXX 运行依赖
GLIBC 和 GLIBC++ 是两个不同的库,它们在 Linux 系统中扮演着重要的角色。
1)GLIBC:
GLIBC,全称 GNU C Library,是 GNU 项目的 C 标准库的实现,为系统和应用程序提供了系统调用的封装和许多基本的程序接口。这包括输入输出(I/O)、字符串处理、文件操作、内存管理、数学计算等。GLIBC 是大多数基于 Linux 的系统的标准 C 库,并且是编译大多数 C 程序的必要组件。
AVSDKPlugin.so 放到不同 Linux 版本上运行时,比如 Ubuntu 18、Fedora 23、Qlin 等系统上,发现音视频拉不起来?
通过ldd AVSDKPlugin.so 我们发现出现一些依赖库 no found, 或者 GLIBC need 2.29等错误信息。
这个是 Ubuntu 18(x64)的报错:
robert@ubuntu:~/.config/QQ/global/ext_lib/avsdk$ ldd libAVSDKPlugin.so
./libAVSDKPlugin.so: /lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.29' not found (required by ./libAVSDKPlugin.so)
./libAVSDKPlugin.so: /lib/x86_64-linux-gnu/libstdc++.so.6: version `CXXABI_1.3.13' not found (required by ./libAVSDKPlugin.so)
xplatform-ng/xpng/task/coroutine/task.h:31:30: fatal error: use of undeclared identifier 'experimental'
using coroutine_handle = experimental::coroutine_handle<T>;
Warning: vkCreateInstance: Found no drivers!
Warning: vkCreateInstance failed with VK_ERROR_INCOMPATIBLE_DRIVER
at CheckVkSuccessImpl (../../third_party/dawn/src/dawn/native/vulkan/VulkanError.cpp:101)
此时我们通过启动 qq 时增加:
qq --enable-logging=stderr --v=1
又多出一些信息:
[9364:0415/181411.892176:ERROR:gl_utils.cc(412)] [.WebGL-0x200029f800]GL Driver Message (OpenGL, Performance, GL_CLOSE_PATH_NV, High): GPU stall due to ReadPixels
[9364:0415/181411.949701:ERROR:gl_utils.cc(412)] [.WebGL-0x200029f800]GL Driver Message (OpenGL, Performance, GL_CLOSE_PATH_NV, High): GPU stall due to ReadPixels
[9364:0415/181411.976514:ERROR:gl_utils.cc(412)] [.WebGL-0x200029f800]GL Driver Message (OpenGL, Performance, GL_CLOSE_PATH_NV, High): GPU stall due to ReadPixels
[9364:0415/181412.027489:ERROR:gl_utils.cc(412)] [.WebGL-0x200029f800]GL Driver Message (OpenGL, Performance, GL_CLOSE_PATH_NV, High): GPU stall due to ReadPixels (this message will no longer repeat)
#if BUILDFLAG(IS_ANDROID)
if (browser_cmd.HasSwitch(switches::kDisableGpuCompositing)) {
renderer_cmd->AppendSwitch(switches::kDisableGpuCompositing);
}
#elif !BUILDFLAG(IS_CHROMEOS_ASH)
// If gpu compositing is not being used, tell the renderer at startup. This
// is inherently racey, as it may change while the renderer is being
// launched, but the renderer will hear about the correct state eventually.
// This optimizes the common case to avoid wasted work.
if (GpuDataManagerImpl::GetInstance()->IsGpuCompositingDisabled())
renderer_cmd->AppendSwitch(switches::kDisableGpuCompositing);
#endif // BUILDFLAG(IS_ANDROID)
VERBOSE1:gpu_control_list.cc(296)] Control list match for rule #3 in gpu_blocklist.
VERBOSE1:gpu_control_list.cc(296)] Control list match for rule #27 in gpu_blocklist.
VERBOSE1:gpu_control_list.cc(296)] Control list match for rule #28 in gpu_blocklist.
VERBOSE1:gpu_control_list.cc(296)] Control list match for rule #29 in gpu_blocklist.
VERBOSE1:gpu_control_list.cc(296)] Control list match for rule #50 in gpu_blocklist.
VERBOSE1:gpu_control_list.cc(296)] Control list match for rule #134 in gpu_blocklist.
VERBOSE1:gpu_control_list.cc(296)] Control list match for rule #176 in gpu_blocklist.