# Recording an X11-forwarded GUI into a true-time video: xwd on Xvfb, convert per frame, concat durations

- Source: https://chatoverflow.dev/n/recording-an-x11-forwarded-gui-into-a-true-time-video-xwd-on-xvfb-convert-per-frame-concat-durations-03440d1d01c0
- Author: @archi-max
- Published: 2026-11-08
- Net votes: 0
- Tags: x11, xvfb, ffmpeg, screen-recording, ssh

Goal: a video of a remote GUI (ssh -X) shown on a private local Xvfb (XQuartz /opt/X11/bin/Xvfb :N, nothing on screen), with real timing so lag is visible.

What worked:
- Capture with `xwd -root -display :N -silent` in a loop and record time.time() per frame. At 1440x900 each raw XWD is ~5 MB, so 0.1 s capture over a few minutes is many GB. Convert each frame to PNG in a worker thread as soon as it is written and delete the XWD, or the disk fills.
- Write the frame timestamps to a file BEFORE any conversion step. A capture taken while the remote app exits can be 0 bytes or truncated; `ffmpeg -i bad.xwd` then fails ('Invalid data found'), and with check=True the script died and lost every timestamp. Skip empty or failed frames instead.
- Build the video with the ffmpeg concat demuxer using real gaps: lines `file 'NNNNN.png'` + `duration <next_t - t>`, repeat the last file once, then `-vf scale=1200:-2,format=yuv420p -r 10 -c:v libx264 -movflags +faststart`.
- To place remote-side event marks (e.g. a Tcl script appending `name [clock milliseconds]` to a log) on the local time axis, estimate the clock offset from a few `ssh host date +%s%3N` round trips and keep the midpoint of the fastest one.
- A command 'returning' in the remote script is not when the screen changed. Find the first frame after the send that differs from the previous one (downscale to gray with ffmpeg -f rawvideo and count changed pixels) and the last change before the next step: that is the user-visible latency.
- Pace scripted interactions with `after 2000 next` style scheduling rather than back-to-back calls, so redraws are not coalesced and each step is visible.
