parent
a38dcbb3fe
commit
880ea82efe
1 changed files with 104 additions and 0 deletions
@ -0,0 +1,104 @@ |
|||||||
|
from msgq.visionipc import VisionIpcClient, VisionStreamType |
||||||
|
import os |
||||||
|
import signal |
||||||
|
import subprocess |
||||||
|
from subprocess import DEVNULL |
||||||
|
import time |
||||||
|
import atexit |
||||||
|
|
||||||
|
XVFB_DISPLAY = ":99" |
||||||
|
RESOLUTION = "2160x1080" |
||||||
|
PIXEL_DEPTH = "24" |
||||||
|
FRAMERATE = 20 |
||||||
|
FFMPEG_OUTPUT = "output.mp4" # TODO: input |
||||||
|
ROUTE = "a2a0ccea32023010/2023-07-27--13-01-19/0" # TODO: input |
||||||
|
|
||||||
|
def wait_for_video(): |
||||||
|
vipc = VisionIpcClient('camerad', VisionStreamType.VISION_STREAM_ROAD, True) |
||||||
|
while True: |
||||||
|
if not vipc.is_connected(): |
||||||
|
vipc.connect(True) |
||||||
|
new_data = vipc.recv() |
||||||
|
if new_data is not None and new_data.data.any(): |
||||||
|
return |
||||||
|
time.sleep(0.01) # Prevent tight loop |
||||||
|
|
||||||
|
def ensure_xvfb(): |
||||||
|
"""Start Xvfb and return the process.""" |
||||||
|
xvfb_cmd = [ |
||||||
|
"Xvfb", XVFB_DISPLAY, |
||||||
|
"-screen", "0", f"{RESOLUTION}x{PIXEL_DEPTH}" |
||||||
|
] |
||||||
|
xvfb_proc = subprocess.Popen(xvfb_cmd, stdout=DEVNULL, stderr=DEVNULL) |
||||||
|
time.sleep(1) # Give Xvfb time to start |
||||||
|
if xvfb_proc.poll() is not None: |
||||||
|
raise RuntimeError("Failed to start Xvfb") |
||||||
|
return xvfb_proc |
||||||
|
|
||||||
|
def main(): |
||||||
|
# Set up environment |
||||||
|
env = os.environ.copy() |
||||||
|
env["DISPLAY"] = XVFB_DISPLAY # Set DISPLAY for all processes |
||||||
|
env["QT_QPA_PLATFORM"] = "xcb" |
||||||
|
|
||||||
|
# Start Xvfb |
||||||
|
xvfb_proc = ensure_xvfb() |
||||||
|
atexit.register(lambda: xvfb_proc.terminate()) # Ensure cleanup on exit |
||||||
|
|
||||||
|
# Start UI process |
||||||
|
ui_args = ["./selfdrive/ui/ui"] |
||||||
|
ui_proc = subprocess.Popen(ui_args, env=env, stdout=DEVNULL, stderr=DEVNULL) |
||||||
|
atexit.register(lambda: ui_proc.terminate()) |
||||||
|
|
||||||
|
# Start replay process |
||||||
|
replay_proc = subprocess.Popen( |
||||||
|
["./tools/replay/replay", "--no-loop", ROUTE], |
||||||
|
env=env, stdout=DEVNULL, stderr=DEVNULL |
||||||
|
) |
||||||
|
atexit.register(lambda: replay_proc.terminate()) |
||||||
|
|
||||||
|
# Wait for video data |
||||||
|
wait_for_video() |
||||||
|
|
||||||
|
# Start FFmpeg |
||||||
|
ffmpeg_cmd = [ |
||||||
|
"ffmpeg", |
||||||
|
"-y", # Overwrite output |
||||||
|
"-video_size", RESOLUTION, |
||||||
|
"-framerate", str(FRAMERATE), |
||||||
|
"-f", "x11grab", |
||||||
|
"-draw_mouse", "0", |
||||||
|
"-i", XVFB_DISPLAY, |
||||||
|
"-c:v", "libx264", |
||||||
|
"-preset", "ultrafast", |
||||||
|
"-pix_fmt", "yuv420p", |
||||||
|
FFMPEG_OUTPUT, |
||||||
|
] |
||||||
|
ffmpeg_proc = subprocess.Popen(ffmpeg_cmd, env=env, stdout=DEVNULL, stderr=DEVNULL) |
||||||
|
atexit.register(lambda: ffmpeg_proc.terminate()) |
||||||
|
|
||||||
|
# Wait for replay to finish |
||||||
|
replay_proc.wait() |
||||||
|
|
||||||
|
# Stop FFmpeg gracefully |
||||||
|
ffmpeg_proc.send_signal(signal.SIGINT) |
||||||
|
ffmpeg_proc.wait(timeout=5) |
||||||
|
|
||||||
|
# Clean up |
||||||
|
ui_proc.terminate() |
||||||
|
ui_proc.wait(timeout=5) |
||||||
|
xvfb_proc.terminate() |
||||||
|
xvfb_proc.wait(timeout=5) |
||||||
|
|
||||||
|
print(f"Recording complete: {FFMPEG_OUTPUT}") |
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
try: |
||||||
|
main() |
||||||
|
except KeyboardInterrupt: |
||||||
|
print("Interrupted by user") |
||||||
|
except Exception as e: |
||||||
|
print(f"Error: {e}") |
||||||
|
finally: |
||||||
|
# Ensure all processes are terminated |
||||||
|
atexit._run_exitfuncs() |
Loading…
Reference in new issue