scrub optimization

This commit is contained in:
2026-04-03 06:40:08 -03:00
parent 9dfa252727
commit 84dc1405dc
13 changed files with 813 additions and 68 deletions

View File

@@ -51,6 +51,8 @@ class MonitorWidget(Gtk.Box):
self._live_loaded = False
self._review_player = None
self._scrub_offset = 0.0 # global offset of the loaded scrub source
self._scrub_active = False # True when scrub source is loaded
self._stack = Gtk.Stack()
self._stack.set_hexpand(True)
@@ -87,6 +89,21 @@ class MonitorWidget(Gtk.Box):
self._recording_path = path
log.info("Recording path: %s", path)
def set_scrub_source(self, proxy_path, global_offset=0.0):
"""Load a proxy file for frame-accurate scrubbing."""
self._recording_path = proxy_path
self._scrub_offset = global_offset
self._scrub_active = True
if self._review_player:
self._review_player.load_at(proxy_path, 0, pause=True, hr_seek=True)
self._stack.set_visible_child_name("review")
log.info("Scrub source: %s (offset %.1fs)", proxy_path, global_offset)
def scrub_to(self, seconds):
"""Seek the review player to an exact frame (for scrub bar dragging)."""
if self._review_player:
self._review_player.show_frame_at(seconds)
def get_live_position(self):
"""Return the live player's current time_pos, or None."""
if self._live_player:
@@ -105,6 +122,8 @@ class MonitorWidget(Gtk.Box):
self._live_source_url = None
self._recording_path = None
self._live_loaded = False
self._scrub_active = False
self._scrub_offset = 0.0
if self._live_player:
self._live_player.command("stop")
if self._review_player:
@@ -180,6 +199,7 @@ class MonitorWidget(Gtk.Box):
current = self._stack.get_visible_child_name()
if s.live:
self._scrub_active = False
# Ensure live player is loaded and playing
if self._live_player and not self._live_loaded and self._live_source_url:
self._live_player.load_live(self._live_source_url)
@@ -190,17 +210,22 @@ class MonitorWidget(Gtk.Box):
if current != "live":
self._stack.set_visible_child_name("live")
else:
# Scrub mode
if current == "live":
# Scrub / review mode
if self._scrub_active:
# Scrub mode: driven directly by scrub_to(), not by timeline
if current != "review":
self._stack.set_visible_child_name("review")
return
elif current == "live":
# Transitioning from live: load MKV at cursor position atomically
pos = s.cursor # already set by toggle_live()
pos = s.cursor
if self._review_player and self._recording_path:
self._review_player.load_at(self._recording_path, pos, pause=s.paused)
if not s.paused:
self._review_player.play()
self._stack.set_visible_child_name("review")
else:
# Already in review: seek if cursor moved, then apply pause/play
# Already in review (non-scrub): seek if cursor moved
if self._review_player:
player_pos = self._review_player.time_pos or 0
if abs(s.cursor - player_pos) > 1.0:
@@ -212,9 +237,12 @@ class MonitorWidget(Gtk.Box):
def _sync_cursor_from_player(self):
s = self._timeline.state
if self._scrub_active:
# Scrub mode: don't sync cursor from player — scrub bar drives cursor
return True
if not s.live and not s.paused and self._review_player:
pos = self._review_player.time_pos
if pos is not None and pos > 0:
self._timeline.set_cursor(pos)
# Live mode: cursor driven by tick_live() in window.py
# Live mode: cursor driven by tick_live()
return True