Add ffmpeg/x11grab-based video capture (libx264rgb, qp=0, true lossless RGB) and an evdev-based keyboard state logger, orchestrated by a single frame-tick loop so each JSONL input row lines up 1:1 with its video frame. Unit-tested with fake keyboard/video components (no real device or ffmpeg needed); real hardware capture still needs validation on the recording PC. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
61 lines
1.6 KiB
Python
61 lines
1.6 KiB
Python
import json
|
|
from pathlib import Path
|
|
|
|
from spelunkai_recording.session import RecordingSession, session_paths
|
|
|
|
|
|
class FakeKeyboard:
|
|
def __init__(self, keys_sequence):
|
|
self._keys_sequence = keys_sequence
|
|
self._i = 0
|
|
self.started = False
|
|
self.stopped = False
|
|
|
|
def start(self):
|
|
self.started = True
|
|
|
|
def stop(self):
|
|
self.stopped = True
|
|
|
|
def snapshot(self):
|
|
keys = self._keys_sequence[min(self._i, len(self._keys_sequence) - 1)]
|
|
self._i += 1
|
|
return keys
|
|
|
|
|
|
class FakeVideo:
|
|
def __init__(self):
|
|
self.started = False
|
|
self.stopped = False
|
|
|
|
def start(self):
|
|
self.started = True
|
|
|
|
def stop(self):
|
|
self.stopped = True
|
|
|
|
|
|
def test_session_logs_one_row_per_frame_and_writes_manifest(tmp_path: Path):
|
|
paths = session_paths(tmp_path, "test-session")
|
|
keyboard = FakeKeyboard([["LEFT"], [], ["RIGHT", "KEY_Z"]])
|
|
video = FakeVideo()
|
|
session = RecordingSession(paths=paths, keyboard=keyboard, video=video, fps=30, width=1280, height=720)
|
|
|
|
frame_count = session.run(duration_s=0.1)
|
|
|
|
assert frame_count >= 1
|
|
assert keyboard.started and keyboard.stopped
|
|
assert video.started and video.stopped
|
|
|
|
lines = paths.input_log.read_text(encoding="utf-8").splitlines()
|
|
assert len(lines) == frame_count
|
|
|
|
first_row = json.loads(lines[0])
|
|
assert first_row["frame"] == 0
|
|
assert first_row["keys"] == ["LEFT"]
|
|
|
|
manifest = json.loads(paths.manifest.read_text(encoding="utf-8"))
|
|
assert manifest["fps"] == 30
|
|
assert manifest["frame_count"] == frame_count
|
|
assert manifest["video_file"] == paths.video.name
|