Files
mitus/cht/stream/tracker.py
2026-04-01 21:14:30 -03:00

81 lines
2.4 KiB
Python

"""
RecordingTracker: monitors the growing recording file and reports duration.
Probes with ffprobe every cycle. No bitrate estimation — initial burst frames
make calibration unreliable. Falls back to file-size heuristic only when
ffprobe returns nothing (e.g. file too new).
"""
import logging
import time
from threading import Thread
import ffmpeg as ffmpeg_lib
log = logging.getLogger(__name__)
class RecordingTracker:
"""Tracks a growing recording file and reports its duration."""
def __init__(self, recording_path, on_duration_update=None):
self._path = recording_path
self._on_duration = on_duration_update
self._duration = 0.0
self._stop = False
self._thread = None
@property
def duration(self):
return self._duration
def start(self):
self._stop = False
self._thread = Thread(target=self._poll_loop, daemon=True, name="rec_tracker")
self._thread.start()
log.info("RecordingTracker started: %s", self._path)
def stop(self):
self._stop = True
log.info("RecordingTracker stopped")
def _poll_loop(self):
while not self._stop:
time.sleep(2)
if not self._path.exists():
continue
size = self._path.stat().st_size
if size < 10_000:
continue
duration = self._probe_duration()
if duration and duration > self._duration:
self._duration = duration
log.info("Duration: %.1fs", duration)
if self._on_duration:
self._on_duration(self._duration)
def _probe_duration(self):
"""Probe recording duration via ffprobe."""
try:
info = ffmpeg_lib.probe(str(self._path))
# Format-level duration is 0 for fragmented MP4 (empty_moov)
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 as e:
log.debug("ffprobe failed: %s", e)
# Last resort: file size heuristic (~500kbps for this stream type)
try:
return self._path.stat().st_size / 65_000
except Exception:
return None