屏幕录制测试
此测试通过将有界面的浏览器会话录制为 MP4 文件来验证完整的 VNC/浏览器技术栈。它证明了:
- Xvfb 正在运行并接受
:99上的连接 - Chromium 可以在虚拟显示器上以有界面模式启动
- ffmpeg 可以通过 x11grab 捕获显示内容
- 生成的 MP4 是有效的、可播放的视频
- 轮询
xdpyinfo -display :99直到 X 显示器就绪(与入口脚本相同的模式) - 以 10 fps 启动 ffmpeg 录制显示内容
- 通过 Playwright 的
sync_api启动有界面的 Chromium - 导航到
https://example.com(暂停 3 秒),然后导航到https://httpbin.org(暂停 3 秒) - 向 ffmpeg 发送 SIGTERM 信号以完成 MP4 文件的正常写入
- 报告输出路径和文件大小
将以下内容保存为容器内的 vnc-browser-test.py,或从本仓库的 docs/tests/ 目录复制:
#!/usr/bin/env python3"""VNC/browser stack integration test.
Proves that Xvfb, headed Chromium (via Playwright), and ffmpeg screenrecording all work together inside the devcontainer by recording abrowser navigating to two websites and producing an MP4 video.
Usage: python3 vnc-browser-test.py [output_path]Default output: /tmp/recording.mp4"""
import osimport signalimport subprocessimport sysimport time
DISPLAY = os.environ.get("DISPLAY", ":99")DEFAULT_OUTPUT = "/tmp/recording.mp4"RESOLUTION = "1280x1024"FRAMERATE = "10"XVFB_TIMEOUT = 50 # iterations (~5 s at 0.1 s each)SITES = [ ("https://example.com", 3), ("https://httpbin.org", 3),]
def wait_for_xvfb(): """Poll xdpyinfo until the X display is ready (mirrors entrypoint.sh).""" print(f"Waiting for Xvfb on {DISPLAY} ...") for i in range(XVFB_TIMEOUT): result = subprocess.run( ["xdpyinfo", "-display", DISPLAY], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) if result.returncode == 0: print(f" Display ready after {i * 0.1:.1f}s") return time.sleep(0.1) sys.exit(f"ERROR: Xvfb display {DISPLAY} not ready after {XVFB_TIMEOUT * 0.1:.1f}s")
def start_recording(output_path): """Start ffmpeg x11grab recording and return the process.""" cmd = [ "ffmpeg", "-y", "-f", "x11grab", "-video_size", RESOLUTION, "-framerate", FRAMERATE, "-i", f"{DISPLAY}.0", "-c:v", "libx264", "-preset", "ultrafast", "-pix_fmt", "yuv420p", output_path, ] print(f"Starting ffmpeg recording -> {output_path}") return subprocess.Popen( cmd, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, )
def stop_recording(proc): """Send SIGTERM and wait for ffmpeg to finalize the MP4.""" print("Stopping ffmpeg ...") proc.send_signal(signal.SIGTERM) try: proc.wait(timeout=10) except subprocess.TimeoutExpired: proc.kill() proc.wait()
def browse_sites(): """Launch headed Chromium via Playwright and visit each site.""" from playwright.sync_api import sync_playwright # noqa: PLC0415
with sync_playwright() as pw: browser = pw.chromium.launch( headless=False, args=[ "--no-sandbox", "--disable-gpu", f"--display={DISPLAY}", ], ) page = browser.new_page(viewport={"width": 1280, "height": 1024})
for url, pause in SITES: print(f" Navigating to {url} ...") try: page.goto(url, timeout=15000) except Exception as exc: # noqa: BLE001 print(f" Warning: navigation error ({exc}), continuing") time.sleep(pause)
browser.close()
def main(): output_path = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_OUTPUT
wait_for_xvfb()
ffmpeg = start_recording(output_path) # Give ffmpeg a moment to initialize before browser activity time.sleep(1)
try: browse_sites() finally: stop_recording(ffmpeg)
if os.path.isfile(output_path): size = os.path.getsize(output_path) print(f"\nSUCCESS: {output_path} ({size:,} bytes)") else: sys.exit(f"ERROR: output file not created: {output_path}")
if __name__ == "__main__": main()python3 vnc-browser-test.py /tmp/recording.mp4podman run --rm \ -v $(pwd)/output:/output \ ghcr.io/f5-sales-demo/devcontainer:latest \ python3 vnc-browser-test.py /output/recording.mp4Waiting for Xvfb on :99 ... Display ready after 0.3sStarting ffmpeg recording -> /tmp/recording.mp4 Navigating to https://example.com ... Navigating to https://httpbin.org ...Stopping ffmpeg ...
SUCCESS: /tmp/recording.mp4 (1,234,567 bytes)MP4 文件应显示 Chromium 窗口在 fluxbox 桌面上渲染两个网站的画面。使用 ffplay、vlc 播放,或从容器中下载文件进行验证。
| 症状 | 可能的原因 |
|---|---|
Xvfb display :99 not ready | Xvfb 未启动——检查入口脚本是否运行了 VNC 技术栈 |
ffmpeg: x11grab error | 显示器不匹配或 ffmpeg 缺少 x11grab 支持 |
Playwright launch error | Chromium 未安装或缺少 --no-sandbox 参数 |
| MP4 文件为 0 字节 | ffmpeg 在写入前被终止——检查 SIGTERM 处理逻辑 |