Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions selfdrive/ui/tests/diff/replay.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def run_replay():
if not HEADLESS:
rl.set_config_flags(rl.FLAG_WINDOW_HIDDEN)
gui_app.init_window("ui diff test", fps=FPS)
gui_app.begin_recording()
main_layout = MiciMainLayout()
main_layout.set_rect(rl.Rectangle(0, 0, gui_app.width, gui_app.height))

Expand Down
66 changes: 36 additions & 30 deletions system/ui/lib/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ def __init__(self, width: int | None = None, height: int | None = None):
self._ffmpeg_queue: queue.Queue | None = None
self._ffmpeg_thread: threading.Thread | None = None
self._ffmpeg_stop_event: threading.Event | None = None
self._recording = False
self._textures: dict[str, rl.Texture] = {}
self._target_fps: int = _DEFAULT_FPS
self._last_fps_log_time: float = time.monotonic()
Expand Down Expand Up @@ -283,39 +284,11 @@ def _close(sig, frame):
self._render_texture = rl.load_render_texture(self._width, self._height)
rl.set_texture_filter(self._render_texture.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR)

if RECORD:
output_fps = fps * RECORD_SPEED
ffmpeg_args = [
'ffmpeg',
'-v', 'warning', # Reduce ffmpeg log spam
'-nostats', # Suppress encoding progress
'-f', 'rawvideo', # Input format
'-pix_fmt', 'rgba', # Input pixel format
'-s', f'{self._width}x{self._height}', # Input resolution
'-r', str(fps), # Input frame rate
'-i', 'pipe:0', # Input from stdin
'-vf', 'vflip,format=yuv420p', # Flip vertically and convert to yuv420p
'-r', str(output_fps), # Output frame rate (for speed multiplier)
'-c:v', 'libx264',
'-preset', 'ultrafast',
]
if RECORD_BITRATE:
ffmpeg_args += ['-b:v', RECORD_BITRATE, '-maxrate', RECORD_BITRATE, '-bufsize', RECORD_BITRATE]
ffmpeg_args += [
'-y', # Overwrite existing file
'-f', 'mp4', # Output format
RECORD_OUTPUT, # Output file path
]
self._ffmpeg_proc = subprocess.Popen(ffmpeg_args, stdin=subprocess.PIPE)
self._ffmpeg_queue = queue.Queue(maxsize=60) # Buffer up to 60 frames
self._ffmpeg_stop_event = threading.Event()
self._ffmpeg_thread = threading.Thread(target=self._ffmpeg_writer_thread, daemon=True)
self._ffmpeg_thread.start()

# OFFSCREEN disables FPS limiting for fast offline rendering (e.g. clips)
rl.set_target_fps(0 if OFFSCREEN else fps)

self._target_fps = fps

self._set_styles()
self._load_fonts()
self._patch_text_functions()
Expand Down Expand Up @@ -355,6 +328,39 @@ def _startup_profile_context(self):
print(f"{green}UI window ready in {elapsed_ms:.1f} ms{reset}")
sys.exit(0)

def begin_recording(self):
if not RECORD or self._recording:
return

self._recording = True
output_fps = self._target_fps * RECORD_SPEED
ffmpeg_args = [
'ffmpeg',
'-v', 'warning', # Reduce ffmpeg log spam
'-nostats', # Suppress encoding progress
'-f', 'rawvideo', # Input format
'-pix_fmt', 'rgba', # Input pixel format
'-s', f'{self._width}x{self._height}', # Input resolution
'-r', str(self._target_fps), # Input frame rate
'-i', 'pipe:0', # Input from stdin
'-vf', 'vflip,format=yuv420p', # Flip vertically and convert to yuv420p
'-r', str(output_fps), # Output frame rate (for speed multiplier)
'-c:v', 'libx264',
'-preset', 'ultrafast',
]
if RECORD_BITRATE:
ffmpeg_args += ['-b:v', RECORD_BITRATE, '-maxrate', RECORD_BITRATE, '-bufsize', RECORD_BITRATE]
ffmpeg_args += [
'-y', # Overwrite existing file
'-f', 'mp4', # Output format
RECORD_OUTPUT, # Output file path
]
self._ffmpeg_proc = subprocess.Popen(ffmpeg_args, stdin=subprocess.PIPE)
self._ffmpeg_queue = queue.Queue(maxsize=60) # Buffer up to 60 frames
self._ffmpeg_stop_event = threading.Event()
self._ffmpeg_thread = threading.Thread(target=self._ffmpeg_writer_thread, daemon=True)
self._ffmpeg_thread.start()

def _ffmpeg_writer_thread(self):
"""Background thread that writes frames to ffmpeg."""
while True:
Expand Down Expand Up @@ -560,7 +566,7 @@ def render(self):

rl.end_drawing()

if RECORD:
if self._recording:
image = rl.load_image_from_texture(self._render_texture.texture)
data_size = image.width * image.height * 4
data = bytes(rl.ffi.buffer(image.data, data_size))
Expand Down
2 changes: 2 additions & 0 deletions tools/clip/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,8 @@ def clip(route: Route, output: str, start: int, end: int, headless: bool = True,
if should_render:
main_layout.render()
render_overlays(rl, gui_app, font, FONT_SCALE, metadata, title, start, frame_idx, show_metadata, show_time)
if road_view.frame is not None:
gui_app.begin_recording()
frame_idx += 1
pbar.update(1)
timer.lap("render")
Expand Down
Loading