螢幕錄製測試
此測試透過將有頭瀏覽器工作階段錄製為 MP4 檔案,驗證完整的 VNC/瀏覽器堆疊。它證明了:
- Xvfb 正在運行並接受
:99上的連線 - Chromium 可以在虛擬顯示器上以有頭模式啟動
- ffmpeg 可以透過 x11grab 擷取顯示畫面
- 產生的 MP4 是有效且可播放的影片
- 輪詢
xdpyinfo -display :99直到 X 顯示器準備就緒(與 entrypoint 相同的模式) - 啟動 ffmpeg 以 10 fps 錄製顯示畫面
- 透過 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 未啟動 — 請檢查 entrypoint 是否執行了 VNC 堆疊 |
ffmpeg: x11grab error | 顯示器不匹配或 ffmpeg 缺少 x11grab 支援 |
Playwright launch error | Chromium 未安裝或缺少 --no-sandbox 參數 |
| MP4 為 0 位元組 | ffmpeg 在寫入前被終止 — 請檢查 SIGTERM 處理 |