腾讯会议 Linux 版在 NVIDIA 显卡上看不到画面:EGL 像素配置与窗口视觉不匹配的排查与修复

文章目录

腾讯会议 Linux 版在 NVIDIA 显卡上看不到画面:EGL 像素配置与窗口视觉不匹配的排查与修复

记录日期 2026-10-06。

摄像头本身工作正常,采集、编码、上行都在跑,腾讯会议界面里自己的画面却一直停在头像占位。查下来,原因是腾讯会议的视频渲染器向 EGL 申请的像素配置带 8 位 alpha,而它交给 EGL 的 X11 窗口是 24 位,NVIDIA 的 EGL 实现严格校验这两者是否匹配,于是创建窗口表面时每次都返回 EGL_BAD_NATIVE_WINDOW。把腾讯会议单独固定到 Mesa 的 EGL 实现后画面恢复,改动只有一个启动包装脚本加一个用户级桌面条目,不需要 root,随时可以撤销。EGL 是图形渲染接口,Linux 上由 glvnd 在多个实现之间分发调用,腾讯会议默认拿到的是 NVIDIA 的实现,修复生效的关键就是让这一个进程改用 Mesa 的实现。

一、现象

会议进行中,界面底部的摄像头按钮显示"停止视频",说明视频处于开启状态,但自己的画面位只有一个头像。麦克风正常,能听到对方说话,对方也能看到我的画面,腾讯会议的日志显示视频流一直在上传。出问题的只是我这边的窗口里没有画面。

第一反应是摄像头坏了或者被别的程序占着,后面的排查否定了这个方向。

适用范围。本文的验证环境是 Ubuntu 26.04 配 GNOME 50 的 Wayland 会话、NVIDIA 专有驱动 595 系列、腾讯会议 3.26 版本。同类现象也可能出现在其他 Qt 或 Electron 应用以及别的 5xx 版驱动上,判断依据是应用日志里创建 EGL 窗口表面失败并返回 0x3005。如果摄像头同时被别的程序占用,表现是设备忙而不是画面缺失,那属于另一类问题,先按第三节的步骤确认设备本身。

二、环境

硬件

部件 型号与规格
CPU AMD Ryzen Threadripper 9960X,24 核 48 线程,L3 缓存 128 MiB(4 个 CCD),单 NUMA 节点
主板 ASUS Pro WS TRX50-SAGE WIFI A,Rev 1.xx
BIOS American Megatrends 0617,日期 2026-02-03
内存 装机容量 64 GB,系统可用 61 GiB(MemTotal: 63858056 kB)。DIMM 频率与通道需要 root 读 dmidecode -t memory,本文未采集
显卡 NVIDIA GeForce RTX 3090(GA102),显存 24 GiB,VBIOS 94.02.26.80.80
显示器 DisplayPort 3840×2160,HDMI 2560×1440
存储 Predator SSD GM9 2 TB(nvme0n1)、ZHITAI Ti600 1 TB(nvme1n1,PCIe a9:00.0)、WDC WD10SDRW 931 GB(USB,sda)
USB 控制器 AMD Turin USB 3.1 xHCI 两个(21:00.4、f1:00.4)、ASMedia ASM4242 USB4 两个(a7:00.0、a8:00.0)、600 系芯片组 USB 3.2(b0:00.0)
网络 Aquantia AQC113 10G(enp173s0,ad:00.0,链路未连接)、Intel I226-LM(enp174s0,ae:00.0,链路未连接)、Wi-Fi wlp175s0(af:00.0)。实际上网走 USB 网卡 enx5c7daecbc30d(MAC 5c:7d:ae:cb:c3:0d,地址 192.168.0.34,默认路由)
摄像头 UGREEN Camera,USB ID 0c45:2283,UVC 1.00,接在 3-1 端口,链路速率为高速(USB 2.0,480 Mbps)。所以 1080p 只能走 MJPEG 格式,YUYV 在 1080p30 下带宽不够
音频 UGREEN CM564 USB Audio(2b89:0234,麦克风)、Focusrite Scarlett Solo 3rd Gen(1235:8211,耳机输出)
键鼠 Logitech G102/G203

摄像头暴露的采集格式(ffmpeg -f v4l2 -list_formats all -i /dev/video0):

复制代码
mjpeg : 1920x1080 1440x1080 1280x960 1280x720 1024x768 800x600 640x480 320x240
yuyv422: 1920x1080 1440x1080 1280x960 1280x720 1024x768 800x600 640x480 320x240

/dev/video0 是采集节点,/dev/video1 是 UVC 的元数据节点。后者不支持内存映射,打不开属于正常现象。

软件

组件 版本
操作系统 Ubuntu 26.04.1 LTS(Resolute Raccoon)
内核 7.0.0-38-generic
桌面 GNOME Shell 50.1,Wayland 会话(XDG_SESSION_TYPE=wayland)
音频服务 PipeWire 1.6.2
NVIDIA 驱动 595.91.07,Open Kernel Module,glvnd 1.7.0
Mesa 26.0.8(libegl-mesa0、libgl1-mesa-dri、libglx-mesa0)
腾讯会议 wemeet 3.26.10.401,自带 Qt 5.15.8
运行平台 启动脚本强制到 X11,QT_QPA_PLATFORM=xcb、WEMEET_XWAYLAND=1、DISPLAY=:0,也就是 XWayland

这里引用的 X11 术语 visual 指窗口的像素格式与颜色布局,EGL 里对应的属性是 EGL_NATIVE_VISUAL_ID。后文提到视觉,指的都是它。

腾讯会议启动脚本 /opt/wemeet/wemeetapp.sh 中这一段是理解后面所有现象的起点:

sh 复制代码
if [ "$XDG_SESSION_TYPE" = "wayland" ];then
  if [ -f "/opt/x11-wayland/x11-ext.sh" ];then
    source /opt/x11-wayland/x11-ext.sh
  else
    export QT_QPA_PLATFORM=xcb
    export XDG_SESSION_TYPE=x11
    unset WAYLAND_DISPLAY
    export WEMEET_XWAYLAND=1
  fi
fi

本机没有 /opt/x11-wayland/x11-ext.sh,因此腾讯会议始终以 XWayland 客户端的身份运行。这不是配置失误,它的视频渲染器只支持 X11,见第五节。

三、先确认摄像头与系统权限没有问题

设备节点存在,权限由 logind 按活动会话发放 ACL,当前用户有读写权限。

console 复制代码
$ ls -l /dev/video*
crw-rw----+ 1 root video 81, 0 Oct  6 09:42 /dev/video0
crw-rw----+ 1 root video 81, 1 Oct  6 09:42 /dev/video1

$ getfacl -p /dev/video0
user::rw-
user:ouyangjiahong:rw-
group::rw-
mask::rw-
other::---

内核驱动识别正常,GNOME 的摄像头总开关也是打开的。

console 复制代码
$ journalctl -k -b | grep -i uvc
uvcvideo 3-1:1.0: Found UVC 1.00 device UGREEN Camera (0c45:2283)

$ gsettings get org.gnome.desktop.privacy disable-camera
false

会议进行时直接抓一帧会被拒绝,因为设备已被会议占用。这同时也是摄像头正在工作的旁证。

console 复制代码
$ ffmpeg -f v4l2 -input_format mjpeg -video_size 640x480 -i /dev/video0 -frames:v 1 out.jpg
[in#0] Error opening input: Device or resource busy

$ fuser -v /dev/video*
/dev/video0:  ouyangjiahong  2486899 F...m wemeetapp
/dev/video1:  ouyangjiahong  2486899 F.... wemeetapp

到这一步可以确定,设备、驱动、权限、占用状态都正常,问题出在腾讯会议自身。

四、从腾讯会议自己的日志定位失败点

腾讯会议的媒体引擎会把日志写到 ~/.local/share/wemeetapp/Saas/Logs/。其中 xcast_<年月日时>.log 记录采集、编码、渲染和网络,wmp_<年月日时>.log 是主程序的二进制日志,不可读。

先看采集与发送的统计:

console 复制代码
$ grep -a 'Snd\[big Cap' xcast_2026100615.log | tail -1
Snd[big Cap:1280x720@28.5/7.4@9/1/5 HW:0 Enc:1280x720@28.5/7.3@6 eType:5
    eBR:1642.3/1369.0/249.9 CoBR:1554.4/1327.4/1500.0/1227.3 ...
    Fill:0 Mut:0 In:28.5 Skp:0.0 Drp:0.0/0

Cap:1280x720@28.5 表示摄像头稳定输出每秒 28.5 帧,Drp:0.0/0 表示没有丢帧,编码与上行同样正常。

异常全部集中在渲染一侧:

console 复制代码
$ grep -c 'enter create_window_surface' xcast_2026100615.log
44
$ grep -c 'EGL_NO_SURFACE' xcast_2026100615.log
44
$ grep -c 'create_window_surface succeeded' xcast_2026100615.log
0

$ grep -a 'EGL_NO_SURFACE' xcast_2026100615.log | head -2
15:39:57.611|W|egl_core.cc:184|25F2E2|eglCreateWindowSurface returned EGL_NO_SURFACE error:3005
15:39:57.627|W|egl_core.cc:184|25F2E2|eglCreateWindowSurface returned EGL_NO_SURFACE error:3005

$ grep -aoE 'render\.avg\.fps\.[0-9.]+' xcast_2026100615.log | sort | uniq -c
      1 render.avg.fps.0.03.
      1 render.avg.fps.0.21.
      1 render.avg.fps.0.18.

三个数字就说明了问题。创建 EGL 窗口表面尝试 44 次,失败 44 次,成功 0 次。错误码 0x3005 是 EGL_BAD_NATIVE_WINDOW。渲染帧率停在每秒 0.03 到 0.21 帧,而要显示 720p 视频本应接近 28 帧。

渲染器拿不到任何可用表面,界面只能退回头像占位。再翻三天前(10 月 3 日)那一次会话的日志,同样是成功 0 次,渲染帧率 0.00 到 0.50 帧。这是长期存在的环境性故障,不是偶发。

日志里还能看到这个渲染器属于哪个组件:

复制代码
'UGREEN Camera: UGREEN Camera' add device:'device.video-renderer.default' renderer-dispatcher 1
DVC|'renderer-dispatcher' start 1 cookie:0x3d9a72f0 run:0

五、为什么它必须走 X11

看一眼 libxcast.so 里未定义的符号,就能知道这个渲染器依赖哪个平台。

console 复制代码
$ nm -D --undefined-only /opt/wemeet/lib/libxcast.so | grep -iE 'egl|wl_|xcb|X[A-Z]'
U eglChooseConfig
U eglCreateContext
U eglCreatePbufferSurface
U eglCreateWindowSurface
U eglGetDisplay
U eglMakeCurrent
U eglSwapBuffers
U XCreateGC
U XGetWindowAttributes
U XGetWindowProperty
U XShmCreatePixmap

符号表里只有 X11(Xlib 与 XShm)和 EGL,没有任何与 wl_surface、wl_egl_window 相关的符号。所以让腾讯会议改用原生 Wayland 来绕开 XWayland 这条路走不通,即便把 Qt 切到 wayland 平台插件,媒体渲染器仍然需要一个 X 窗口句柄。

egl_core.cc 的日志格式同样在说明它想做什么:

复制代码
enter create_window_surface
eglCreateWindowSurface returned EGL_NO_SURFACE error:%x
create_window_surface succeeded, egl_surface[%p]

失败的正是由 X 窗口句柄创建 EGL 窗口表面这一步。

六、验证失败原因:属性数组与最小复现

EGL_BAD_NATIVE_WINDOW 最常见的成因,是 EGL 像素配置所对应的 visual 与窗口自身的 visual 不一致。要验证这一点,先得知道腾讯会议申请的像素配置是什么。

libxcast.so 没有被 strip,直接反汇编 eglChooseConfig 的调用点,可以看到属性数组是从 .rodata 整块搬移到栈上的:

console 复制代码
$ objdump -d --no-show-raw-insn /opt/wemeet/lib/libxcast.so | grep -B20 'call.*eglChooseConfig'
  2a5df0:	movaps 0xecbca9(%rip),%xmm0        # 1171aa0
  2a5df7:	movaps %xmm0,-0x60(%rbp)
  ...
  2a5e54:	call   cf120 <eglChooseConfig@plt>

把 .rodata 中 0x1171a60 起的 128 字节按整数解出来,得到完整的属性表:

复制代码
 0 0x303f CONFORMANT      ...
 2 0x3020 BUFFER_SIZE     32    要 32 位
 4 0x3024 RED_SIZE         8
 6 0x3023 GREEN_SIZE       8
 8 0x3022 BLUE_SIZE        8
10 0x3021 ALPHA_SIZE       8    还要 alpha
12 0x3025 DEPTH_SIZE      -1
14 0x3026 STENCIL_SIZE    -1
16 0x3032 SAMPLE_BUFFERS   0
18 0x3031 SAMPLES          0
20 0x3033 SURFACE_TYPE     4    EGL_WINDOW_BIT
22 0x3040 RENDERABLE_TYPE  4    EGL_OPENGL_ES2_BIT
24 0x3038 NONE

腾讯会议要的是 EGL_BUFFER_SIZE=32 加 EGL_ALPHA_SIZE=8 的配置,也就是 ARGB 视觉。

再看它交给 EGL 的那个窗口:

console 复制代码
$ xdotool search --name '腾讯会议' | head -2
29360170
29360204

$ DISPLAY=:0 xwininfo -id 29360170 | grep -E 'Depth|Visual|Map State'
  Width: 1444
  Height: 964
  Depth: 24
  Visual: 0x2fd
  Map State: IsViewable

Qt 创建的 X11 窗口深度是 24 位。32 位配置配上 24 位窗口,两者不匹配。到这里假设成立,但还需要一次独立的验证。

写一个最小复现程序,只保留这条调用链,用 gcc t.c -lX11 -lEGL 编译(完整源码见附录 A):

c 复制代码
EGLint attrs[] = {
    EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
    EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8,
    EGL_ALPHA_SIZE, 8,
    EGL_NONE
};
eglChooseConfig(dpy, attrs, &cfg, 1, &n);
Window win = XCreateWindow(..., DefaultVisual(dpy, scr), ...);
EGLSurface s = eglCreateWindowSurface(dpy, cfg, (EGLNativeWindowType)win, NULL);

四种组合各跑一遍:

EGL 实现 配置的 visual 窗口的 visual 结果
NVIDIA 0x91,alpha 为 8,缓冲 32 位 0x61,深度 24 失败,error=0x3005
NVIDIA 0x91 0x91,深度 32 成功
Mesa 0x61,alpha 为 8 0x61,深度 24 成功
Mesa(LIBGL_ALWAYS_SOFTWARE=1) 0x61 0x61 成功

结论有两条。第一,NVIDIA 的 EGL 严格检查配置的 visual 是否等于窗口的 visual,不等就返回 EGL_BAD_NATIVE_WINDOW。第二,Mesa 的 EGL 对此宽松,同样输入能顺利创建窗口表面。

这也解释了同一个版本的腾讯会议为什么挑机器。在 Intel 与 AMD 核显的机器上走的是 Mesa,画面一切正常,唯独 NVIDIA 上黑屏,差别不在摄像头兼容性,而在 EGL 实现的严格程度。

七、结论

把整条链条写成因果顺序是这样的。启动脚本检测到 Wayland 会话且找不到它的 X11 辅助脚本,于是把腾讯会议放到 XWayland 上运行。Qt 因此创建深度 24 位的 X11 窗口。libxcast 向 EGL 申请带 8 位 alpha 的 32 位配置。NVIDIA 的 EGL 认为这个组合非法,创建窗口表面时返回 EGL_BAD_NATIVE_WINDOW,44 次全部失败。视频渲染器没有表面可用,渲染帧率停在每秒零点几帧,界面只能显示头像。采集、编码、上行与这条链路无关,所以一切正常。

一句话概括:摄像头把画面交给了腾讯会议,腾讯会议把它交给了 EGL,EGL 因为一个 8 位的 alpha 通道拒绝了它。

八、修复:只让腾讯会议使用 Mesa 的 EGL

__EGL_VENDOR_LIBRARY_FILENAMES 和 LIBGL_ALWAYS_SOFTWARE 会影响所有进程,包括桌面本身,所以这里用启动包装脚本加用户级桌面条目,把影响限制在腾讯会议内部,不需要 root。

包装脚本 ~/.local/bin/wemeet-mesa-egl:

sh 复制代码
#!/bin/sh
# 只对腾讯会议固定 Mesa EGL:
# NVIDIA EGL 对 32 位 alpha 配置配 24 位窗口返回 EGL_BAD_NATIVE_WINDOW(0x3005),
# Mesa EGL 容忍这一不匹配,视频渲染器才能拿到窗口表面。
export __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json
export LIBGL_ALWAYS_SOFTWARE=1

exec /opt/wemeet/wemeetapp.sh "$@"
console 复制代码
$ chmod +x ~/.local/bin/wemeet-mesa-egl

桌面条目 ~/.local/share/applications/wemeetapp.desktop。文件名即桌面条目 ID,与 /usr/share/applications/wemeetapp.desktop 保持一致,用户级目录优先级更高,菜单、Dock 与 wemeet:// 入会链接都会走包装脚本:

ini 复制代码
[Desktop Entry]
Name=WemeetApp
Name[zh_CN]=腾讯会议
Exec=/home/ouyangjiahong/.local/bin/wemeet-mesa-egl %u
Icon=/opt/wemeet/wemeet.svg
Type=Application
Terminal=false
Categories=AudioVideo;
MimeType=x-scheme-handler/wemeet;
console 复制代码
$ update-desktop-database ~/.local/share/applications
$ grep wemeet ~/.local/share/applications/mimeinfo.cache
x-scheme-handler/wemeet=wemeetapp.desktop;

撤销改动只需删掉这两个文件并刷新一次数据库:

console 复制代码
$ rm ~/.local/bin/wemeet-mesa-egl ~/.local/share/applications/wemeetapp.desktop
$ update-desktop-database ~/.local/share/applications

被否决的做法:

做法 结论
让腾讯会议以原生 Wayland 运行(QT_QPA_PLATFORM=wayland) 不可行。libxcast 只引用 X11 与 EGL 符号,需要 X 窗口句柄,见第五节
只设 LIBGL_ALWAYS_SOFTWARE=1 无效。EGL 实现的选择由 glvnd 决定,实测进程内加载的仍然是 NVIDIA 的 EGL
改 GLX 相关变量,如 LIBGL_ALWAYS_INDIRECT、__GLX_VENDOR_LIBRARY_NAME 未实测。这些变量作用于 GLX,腾讯会议走的是 EGL,不应对它生效
等腾讯修复 这是它自身的问题,但周期不可控

九、验证

修复后的环境是否真的生效,可以直接读运行中进程的判断依据:

console 复制代码
$ tr '\0' '\n' < /proc/2749569/environ | grep -E 'EGL_VENDOR|LIBGL_ALWAYS'
__EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json
LIBGL_ALWAYS_SOFTWARE=1

$ grep -aoE 'libEGL[^ ]*\.so[^ ]*' /proc/2749569/maps | sort -u
/usr/lib/x86_64-linux-gnu/libEGL.so.1.1.0
/usr/lib/x86_64-linux-gnu/libEGL_mesa.so.0.0.0
/usr/lib/x86_64-linux-gnu/libgallium-26.0.8-1ubuntu0.3.so

$ grep -ac 'libEGL_nvidia' /proc/2749569/maps
0

进程只加载了 Mesa 的 EGL 与 Mesa 的 Gallium 驱动框架 libgallium(软件光栅化 llvmpipe 就在其中),NVIDIA 的 EGL 一个也没有加载。

媒体引擎侧的变化:

console 复制代码
$ grep -c 'EGL_NO_SURFACE' xcast_2026100616.log
0
$ grep -c 'create_window_surface succeeded' xcast_2026100616.log
3
$ grep -a 'create_window_surface succeeded' xcast_2026100616.log
16:22:08.629|I|egl_core.cc:186|29F502|create_window_surface succeeded, egl_surface[0x704a15ee8fe0]
16:22:08.646|I|egl_core.cc:186|29F502|create_window_surface succeeded, egl_surface[0x704a15eed720]
16:22:08.694|I|egl_core.cc:186|29F502|create_window_surface succeeded, egl_surface[0x704a15ee8fe0]

$ grep -aoE 'render\.avg\.fps\.[0-9.]+' xcast_2026100616.log | sort | uniq -c
      1 render.avg.fps.12.69.
      1 render.avg.fps.28.64.

修复前后的对比:

指标 修复前 修复后
创建窗口表面成功次数 0,共尝试 44 次 3,共尝试 3 次
EGL_NO_SURFACE error:3005 次数 44 0
render.avg.fps 0.03、0.18、0.21 12.69、28.64
界面表现 只有头像 实时画面

界面本身也可以直接取证。腾讯会议是 XWayland 客户端,它的窗口在 X 一侧真实存在,可以用 xwd 把窗口内容抠出来。GNOME 50 已经禁止了 org.gnome.Shell.Screenshot 的 D-Bus 调用,返回 AccessDenied: Screenshot is not allowed,这条路走不通。

console 复制代码
$ DISPLAY=:0 xdotool search --name '腾讯会议'
29360180
$ DISPLAY=:0 xwd -id 29360180 -silent -out win.xwd
$ ffmpeg -i win.xwd win.png

修复前抓到的窗口,按钮写着"停止视频",自己的画面位是一个头像。修复后同样的窗口里是我坐在摄像头前的实时画面,对方仍然是头像,因为对方没有开摄像头。

十、代价与遗留问题

软渲染的代价。LIBGL_ALWAYS_SOFTWARE=1 让腾讯会议的视频合成退到 Mesa 的软件光栅化。720p 的合成量对 24 核的 9960X 可以忽略,渲染帧率稳定在 28 帧以上,代价是不再吃 GPU。若日后想换回硬件路径,删掉 LIBGL_ALWAYS_SOFTWARE=1 只留厂商变量也可以,Mesa 在 NVIDIA 上没有对应的 DRI 驱动,多半会自行退回软件路径并打印 egl: failed to create dri2 screen 警告。

采集侧的队列饥饿。整个会话里持续出现下面三条警告,采集期间大致每秒一到两条,本文这次会话有 225 条,10 月 3 日那次会话累计 2084 条。

复制代码
E|v4l2.c:320|Not enough buffer on device:/dev/video0
E|v4l2.c:333|Could not requeue buffer on device:/dev/video0
W|video_capture_linux.c:416|camera[...].is.busy

它不影响画面显示,但说明采集线程的缓冲区回收存在问题。如果日后远端反馈我的画面卡顿,这是第一个需要复查的地方。

给腾讯的建议。libxcast 把 EGL_ALPHA_SIZE=8 与 EGL_BUFFER_SIZE=32 写死在属性数组里,却不去保证窗口的 visual 与之匹配。去掉 alpha 需求,或者按 EGL_NATIVE_VISUAL_ID 创建匹配的窗口,或者在创建窗口表面失败时退回无 alpha 的配置重试,任一种改法都能让 NVIDIA 用户不再遇到这个问题。

方法本身的适用范围。这套排查路径对任何 Qt 或 Electron 类应用的黑屏、视频不渲染都适用,步骤是先确认数据源正常,再在应用日志里找 EGL 创建窗口表面失败,然后用一个最小程序验证配置与窗口视觉是否匹配,最后按应用固定 EGL 实现。

附录 A 最小复现程序

c 复制代码
// gcc eglvis.c -o eglvis -lX11 -lEGL
// ./eglvis plain alpha   24 位窗口配 32 位 alpha 配置,NVIDIA 下返回 0x3005
// ./eglvis match alpha   32 位窗口配 32 位 alpha 配置,可以通过
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <EGL/egl.h>

int main(int argc, char **argv)
{
    int match      = argc > 1 && !strcmp(argv[1], "match");
    int want_alpha = argc > 2 && !strcmp(argv[2], "alpha");

    Display *dpy = XOpenDisplay(NULL);
    if (!dpy) { puts("XOpenDisplay FAILED"); return 1; }
    int scr = DefaultScreen(dpy);

    EGLDisplay edpy = eglGetDisplay((EGLNativeDisplayType)dpy);
    EGLint major, minor;
    if (edpy == EGL_NO_DISPLAY || !eglInitialize(edpy, &major, &minor)) {
        printf("EGL init FAILED 0x%x\n", eglGetError()); return 1;
    }

    EGLint attrs[] = {
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8,
        want_alpha ? EGL_ALPHA_SIZE : EGL_NONE, want_alpha ? 8 : 0,
        EGL_NONE
    };
    EGLConfig cfgs[64];
    EGLint n = 0;
    if (!eglChooseConfig(edpy, attrs, cfgs, 64, &n) || n < 1) {
        printf("no config 0x%x\n", eglGetError()); return 1;
    }
    EGLConfig cfg = cfgs[0];
    EGLint vis = 0, alpha = 0, bufsz = 0;
    eglGetConfigAttrib(edpy, cfg, EGL_NATIVE_VISUAL_ID, &vis);
    eglGetConfigAttrib(edpy, cfg, EGL_ALPHA_SIZE, &alpha);
    eglGetConfigAttrib(edpy, cfg, EGL_BUFFER_SIZE, &bufsz);

    Visual *v = DefaultVisual(dpy, scr);
    int depth = DefaultDepth(dpy, scr);
    XVisualInfo templ = { .visualid = vis }, *vinfo = NULL;
    int nvi = 0;
    if (match) {
        vinfo = XGetVisualInfo(dpy, VisualIDMask, &templ, &nvi);
        if (nvi > 0) { v = vinfo[0].visual; depth = vinfo[0].depth; }
    }

    XSetWindowAttributes swa = {0};
    swa.colormap = XCreateColormap(dpy, RootWindow(dpy, scr), v, AllocNone);
    swa.event_mask = StructureNotifyMask;
    Window win = XCreateWindow(dpy, RootWindow(dpy, scr), 0, 0, 320, 240, 0, depth,
                               InputOutput, v,
                               CWColormap | CWBackPixel | CWBorderPixel | CWEventMask,
                               &swa);
    XMapWindow(dpy, win);
    XSync(dpy, False);

    printf("vendor=%-14s cfg:visual=0x%02x alpha=%d buffer=%d | window visual=0x%02x depth=%d | ",
           eglQueryString(edpy, EGL_VENDOR), vis, alpha, bufsz,
           (unsigned)XVisualIDFromVisual(v), depth);

    eglBindAPI(EGL_OPENGL_ES_API);
    EGLSurface s = eglCreateWindowSurface(edpy, cfg, (EGLNativeWindowType)win, NULL);
    EGLint err = eglGetError();
    if (s == EGL_NO_SURFACE) printf("SURFACE FAILED error=0x%04x\n", err);
    else                      printf("SURFACE OK\n");
    return s == EGL_NO_SURFACE ? 2 : 0;
}

附录 B 命令速查

sh 复制代码
# 摄像头是否正常
lsusb
ls -l /dev/video*
getfacl -p /dev/video0
journalctl -k -b | grep -i uvc
ffmpeg -f v4l2 -list_formats all -i /dev/video0
fuser -v /dev/video*                 # 谁占着摄像头

# 腾讯会议的媒体日志
L=~/.local/share/wemeetapp/Saas/Logs/xcast_$(date +%Y%m%d%H).log
grep -c 'create_window_surface succeeded' $L
grep -c 'EGL_NO_SURFACE' $L
grep -aoE 'render\.avg\.fps\.[0-9.]+' $L | sort | uniq -c
grep -aoE 'Snd\[big Cap:[0-9x@./]+' $L | tail -3

# 渲染器依赖哪个平台
nm -D --undefined-only /opt/wemeet/lib/libxcast.so | grep -iE 'egl|wl_|X[A-Z]'

# EGL 属性数组在 .rodata 中的位置
objdump -d --no-show-raw-insn /opt/wemeet/lib/libxcast.so | grep -B20 'call.*eglChooseConfig'

# 窗口真实的深度与 visual
DISPLAY=:0 xwininfo -id <wid> | grep -E 'Depth|Visual|Map State'

# 在 XWayland 上抓窗口内容
DISPLAY=:0 xdotool search --name '腾讯会议'
DISPLAY=:0 xwd -id <wid> -silent -out w.xwd && ffmpeg -i w.xwd w.png

# 修复后确认进程环境
tr '\0' '\n' < /proc/<pid>/environ | grep -E 'EGL_VENDOR|LIBGL_ALWAYS'
grep -aoE 'libEGL[^ ]*\.so[^ ]*' /proc/<pid>/maps | sort -u

# 刷新用户级桌面条目
update-desktop-database ~/.local/share/applications

参考

相关推荐
geats人山人海1 小时前
linux 2 基本操作与vim
linux·运维·vim
2603_969579281 小时前
中小团队数据库选型:MySQL vs PostgreSQL,业务场景怎么选
数据库·mysql·postgresql
lisanmengmeng2 小时前
NRPE 添加命令(三)
java·linux·服务器
IT大白鼠2 小时前
图数据库系列 · 第 06 篇——避坑汇总:经典生产问题
数据库·nosql·图库数据库
江湖有缘2 小时前
3开源Sqlite数据库管理工具整理合集,可Docker一键部署!
数据库·sqlite·开源
人工智能培训2 小时前
数字孪生驱动大模型工业知识库:为具身机器人植入领域专业经验
大数据·linux·服务器·前端·人工智能
jyOverQ2 小时前
Redis 持久化怎么做?RDB、AOF 与混合持久化
数据库·redis
j7~2 小时前
【C++ 标准项目】发布订阅消息队列(篇四):sqlite 与 gtest 断言框架介绍及实战应用
数据库·sqlite·gtest·断言·事件机制·tset宏