some changes
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
import gi
|
||||
gi.require_version("Gtk", "4.0")
|
||||
gi.require_version("Adw", "1")
|
||||
from gi.repository import Gtk, Adw, GLib, Pango
|
||||
gi.require_version("GdkPixbuf", "2.0")
|
||||
from gi.repository import Gtk, Adw, GLib, Pango, GdkPixbuf
|
||||
|
||||
from cht.config import APP_NAME
|
||||
from cht.ui.monitor import MonitorWidget
|
||||
@@ -72,16 +75,32 @@ class ChtWindow(Adw.ApplicationWindow):
|
||||
log.info("Session: %s", self._stream_mgr.session_id)
|
||||
self._stream_mgr.setup_dirs()
|
||||
|
||||
# mpv listens on TCP directly (lowest latency, like def scripts)
|
||||
# and records the raw stream to disk for frame extraction
|
||||
stream_url = self._stream_mgr.stream_url
|
||||
record_path = self._stream_mgr.stream_dir / "recording.ts"
|
||||
log.info("Starting mpv on %s, recording to %s", stream_url, record_path)
|
||||
self._monitor.start_stream(stream_url, record_path=record_path)
|
||||
log.info("Monitor started, waiting for sender...")
|
||||
# 1. ffmpeg receives TCP and writes growing recording.ts
|
||||
self._stream_mgr.start_recorder()
|
||||
log.info("Recorder started, waiting for sender...")
|
||||
|
||||
self._stream_mgr.start_frame_extractor_on_recording(record_path)
|
||||
log.info("Frame extractor started")
|
||||
# 2. mpv plays the recording file (DVR: live edge + scrub)
|
||||
# Small delay to let ffmpeg create the file
|
||||
GLib.timeout_add(2000, self._start_playback)
|
||||
|
||||
# 3. ffmpeg scene detection runs periodically on the recording
|
||||
self._stream_mgr.start_scene_detector()
|
||||
log.info("Scene detector started")
|
||||
|
||||
# 4. Poll for new frames and show thumbnails
|
||||
self._known_frames = set()
|
||||
GLib.timeout_add(3000, self._poll_frames)
|
||||
|
||||
def _start_playback(self):
|
||||
"""Start mpv playback once recording file exists."""
|
||||
if self._stream_mgr and self._stream_mgr.recording_path.exists():
|
||||
size = self._stream_mgr.recording_path.stat().st_size
|
||||
if size > 10_000:
|
||||
self._monitor.start_recording(self._stream_mgr.recording_path)
|
||||
log.info("Playback started")
|
||||
return False # stop timer
|
||||
log.info("Waiting for recording data...")
|
||||
return True # retry
|
||||
|
||||
def _stop_stream(self):
|
||||
log.info("Stopping stream...")
|
||||
@@ -279,13 +298,55 @@ class ChtWindow(Adw.ApplicationWindow):
|
||||
end_iter = buf.get_end_iter()
|
||||
buf.insert(end_iter, f"[{entry_id}] {text}\n")
|
||||
|
||||
def add_frame_thumbnail(self, frame_id, pixbuf):
|
||||
def _poll_frames(self):
|
||||
"""Check for new extracted frames and add thumbnails."""
|
||||
if not self._stream_mgr:
|
||||
return False
|
||||
|
||||
index_path = self._stream_mgr.frames_dir / "index.json"
|
||||
if not index_path.exists():
|
||||
return True
|
||||
|
||||
try:
|
||||
with open(index_path) as f:
|
||||
index = json.load(f)
|
||||
except (json.JSONDecodeError, IOError):
|
||||
return True
|
||||
|
||||
for entry in index:
|
||||
fid = entry["id"]
|
||||
if fid in self._known_frames:
|
||||
continue
|
||||
fpath = Path(entry["path"])
|
||||
if not fpath.exists():
|
||||
continue
|
||||
|
||||
self._known_frames.add(fid)
|
||||
try:
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
||||
str(fpath), 160, 90, True
|
||||
)
|
||||
self._add_frame_thumbnail(fid, pixbuf, entry.get("timestamp"))
|
||||
except Exception as e:
|
||||
log.warning("Failed to load thumbnail for %s: %s", fid, e)
|
||||
|
||||
return True # keep polling
|
||||
|
||||
def _add_frame_thumbnail(self, frame_id, pixbuf, timestamp=None):
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=1)
|
||||
|
||||
img = Gtk.Image.new_from_pixbuf(pixbuf)
|
||||
overlay = Gtk.Overlay()
|
||||
overlay.set_child(img)
|
||||
label = Gtk.Label(label=frame_id)
|
||||
label.set_halign(Gtk.Align.START)
|
||||
label.set_valign(Gtk.Align.END)
|
||||
box.append(img)
|
||||
|
||||
label_text = frame_id
|
||||
if timestamp is not None:
|
||||
m, s = divmod(int(timestamp), 60)
|
||||
label_text = f"{frame_id} [{m:02d}:{s:02d}]"
|
||||
|
||||
label = Gtk.Label(label=label_text)
|
||||
label.add_css_class("caption")
|
||||
overlay.add_overlay(label)
|
||||
self._frames_flow.append(overlay)
|
||||
label.set_ellipsize(Pango.EllipsizeMode.END)
|
||||
box.append(label)
|
||||
|
||||
self._frames_flow.append(box)
|
||||
log.info("Added thumbnail: %s", frame_id)
|
||||
|
||||
Reference in New Issue
Block a user