This commit is contained in:
2026-04-01 21:14:30 -03:00
parent 0f7e4424bc
commit 172f21a845
9 changed files with 139 additions and 106 deletions

View File

@@ -56,7 +56,7 @@ class StreamManager:
@property
def recording_path(self):
return self.stream_dir / "recording.mkv"
return self.stream_dir / "recording.mp4"
# -- Recording --
@@ -81,9 +81,13 @@ class StreamManager:
def _detect():
processed_time = 0.0
frame_count = 0
idle_cycles = 0 # consecutive cycles with no new frames
while "stop" not in self._stop_flags:
time.sleep(5)
# Adaptive sleep: 1s after finding frames, then 2→4→8→10s backoff
sleep_secs = 1 if idle_cycles == 0 else min(2, 2 ** idle_cycles)
time.sleep(sleep_secs)
if not self.recording_path.exists():
continue
@@ -91,16 +95,14 @@ class StreamManager:
if size < 100_000:
continue
# Get current duration. Use a 6s safety margin — MKV tail can
# be corrupt for several seconds after the last flush, causing
# ffmpeg to crash even with a 3s margin.
# 2s safety margin — fragmented MP4 is valid up to last complete
# keyframe fragment (~1 keyframe interval); 2s covers worst case.
safe_duration = self._estimate_safe_duration()
if safe_duration is None or safe_duration <= processed_time + 8:
if safe_duration is None:
continue
# Process from last checkpoint to safe point
process_to = safe_duration - 6 # 6s safety margin for MKV tail
if process_to <= processed_time:
process_to = safe_duration - 2
if process_to <= processed_time + 0.5:
continue
log.info("Scene detection: %.1fs → %.1fs", processed_time, process_to)
@@ -112,9 +114,12 @@ class StreamManager:
if new_frames:
frame_count += len(new_frames)
idle_cycles = 0 # reset — check again quickly
log.info("Found %d new scene frames (total: %d)", len(new_frames), frame_count)
if self._on_new_frames:
self._on_new_frames(new_frames)
else:
idle_cycles += 1 # back off: 2s, 4s, 8s, 10s
processed_time = process_to
@@ -125,13 +130,23 @@ class StreamManager:
self._threads["scene_detector"] = t
def _estimate_safe_duration(self):
"""Estimate recording duration. Uses ffprobe, falls back to file size."""
"""Estimate recording duration. Uses ffprobe, falls back to file size.
For fragmented MP4 (empty_moov), format-level duration is 0 so we
check stream duration from the last video stream instead.
"""
try:
import ffmpeg as ffmpeg_lib
info = ffmpeg_lib.probe(str(self.recording_path))
# Format duration works for non-fragmented; 0 for empty_moov fMP4
dur = float(info.get("format", {}).get("duration", 0))
if dur > 0:
return dur
# Fragmented MP4: check video stream duration
for stream in info.get("streams", []):
sdur = float(stream.get("duration", 0))
if sdur > 0:
return sdur
except Exception:
pass