summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag/gzf2_video.py
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-11-15 16:00:06 -0500
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-11-15 16:00:06 -0500
commit43bbf490dca972cb96fcf1d69dd0b6859377e452 (patch)
treeac836f0dfcd50b3302d29a88402b1753127f7cf2 /tools/guest-diag/gzf2_video.py
parentf37f1f6a92939a8c147da1ed5a9032d9e8604ddc (diff)
downloadopen-iov-43bbf490dca972cb96fcf1d69dd0b6859377e452.tar.gz
open-iov-43bbf490dca972cb96fcf1d69dd0b6859377e452.zip
tools/guest-diag: tag BGRA capture records as GZF2 fmt 33
sdl_cap.c wrote the same 32 in the GZF2 record format field for every capture path, so gzf2_video.py's auto mode decoded ARGB8888/X-surface frames (BGRA byte order on little-endian) as RGBA and swapped red and blue. Make the field the documented pixel-order tag: 32 = RGBA8 from the GL swap path (glReadPixels GL_RGBA), 33 = BGRA8 from SDL_RenderReadPixels(ARGB8888) and X window surfaces; xgrab.c tags its ZPixmap BGRA records 33 as well. gzf2_video.py continues to accept both tags (and old all-32 captures via --pix-fmt bgra), checks ffmpeg/ffprobe before indexing, and keeps the final resampled endpoint so a 90-frame 60 fps capture encodes as 90 frames instead of 88.
Diffstat (limited to 'tools/guest-diag/gzf2_video.py')
-rw-r--r--tools/guest-diag/gzf2_video.py25
1 files changed, 20 insertions, 5 deletions
diff --git a/tools/guest-diag/gzf2_video.py b/tools/guest-diag/gzf2_video.py
index aa88aa0..8635ca0 100644
--- a/tools/guest-diag/gzf2_video.py
+++ b/tools/guest-diag/gzf2_video.py
@@ -9,9 +9,11 @@
#
# Format: [0..8) = "GZCAPV01", zero pad to 4096; then records:
# 4B "GZF2" | u32 w | u32 h | u32 stride | u32 fmt | u64 ts_ns | u64 seq |
-# stride*h bytes, top-down. fmt 32 = RGBA8 (GL swap path), 33 = BGRA8
-# (SDL_RenderReadPixels ARGB8888 / X surface). A trailing
-# "GZF2" + u64 0xffffffffffffffff marks a clean stop.
+# stride*h bytes, top-down. fmt is a pixel-order tag: 32 = RGBA8 (GL swap
+# path, glReadPixels GL_RGBA), 33 = BGRA8 (SDL_RenderReadPixels ARGB8888 /
+# X surface). A trailing "GZF2" + u64 0xffffffffffffffff marks a clean stop.
+# Captures written before the 33 tag existed recorded BGRA paths as 32;
+# decode those with --pix-fmt bgra.
#
# Usage:
# gzf2_video.py CAPTURE OUT.mp4 [--fps 60] [--skip 5] [--duration 20]
@@ -22,6 +24,7 @@ import bisect
import hashlib
import json
import os
+import shutil
import struct
import subprocess
import sys
@@ -89,7 +92,13 @@ def pick_frames(recs, fps, skip_s, duration_s):
cand = [r for r in recs if r["ts"] >= t0] or recs
ts = [r["ts"] for r in cand]
span = (ts[-1] - ts[0]) / 1e9
- n = int(duration_s * fps) if duration_s else max(1, int(span * fps))
+ if duration_s:
+ n = max(1, int(round(duration_s * fps)))
+ else:
+ # Nearest-frame sampling must include both endpoints: a capture that
+ # spans k frame periods yields k+1 output frames (int() alone drops
+ # the last frame and can lose one more to float formatting).
+ n = max(1, int(round(span * fps)) + 1)
picks, held = [], 0
for i in range(n):
want = ts[0] + int(i * 1e9 / fps)
@@ -179,10 +188,16 @@ def main():
default="libx264")
ap.add_argument("--crf", type=int, default=18)
ap.add_argument("--pix-fmt", choices=["auto", "rgba", "bgra"],
- default="auto", help="auto uses record fmt (33=BGRA)")
+ default="auto",
+ help="auto uses the record tag (32=RGBA, 33=BGRA); "
+ "old BGRA captures tagged 32 need bgra")
ap.add_argument("--info-only", action="store_true")
args = ap.parse_args()
+ for tool in ("ffmpeg", "ffprobe"):
+ if not shutil.which(tool):
+ die(f"{tool} not found in PATH")
+
recs, clean, corrupt = index_capture(args.capture)
uniq = len({r["md5"] for r in recs})
span = (recs[-1]["ts"] - recs[0]["ts"]) / 1e9