ffmpeg fix
This commit is contained in:
@@ -72,27 +72,23 @@ def extract_scene_frames(input_path, output_dir, scene_threshold=0.10,
|
||||
meaningfully vs the previous frame. No periodic fallback so static content
|
||||
produces no spurious frames.
|
||||
|
||||
start_time/duration: applied via the select filter expression (NOT as -ss/-t
|
||||
input options, which break h264 scene detection on MKV).
|
||||
Uses -ss input seeking for O(1) startup regardless of file size.
|
||||
pts_time in showinfo output is relative to the seek point.
|
||||
Returns (stdout, stderr) as decoded strings for timestamp parsing.
|
||||
"""
|
||||
scene_expr = f"gt(scene,{scene_threshold})"
|
||||
|
||||
# Add time range filter if specified (incremental processing)
|
||||
time_conditions = []
|
||||
if start_time > 0:
|
||||
time_conditions.append(f"gte(t,{start_time})")
|
||||
# With -ss input seeking, t starts at 0 from the seek point.
|
||||
# Only need end boundary (duration), start is handled by -ss.
|
||||
if duration is not None:
|
||||
time_conditions.append(f"lte(t,{start_time + duration})")
|
||||
scene_expr = f"({scene_expr})*lte(t,{duration})"
|
||||
|
||||
if time_conditions:
|
||||
time_filter = "*".join(time_conditions)
|
||||
select_expr = f"({scene_expr})*{time_filter}"
|
||||
else:
|
||||
select_expr = scene_expr
|
||||
input_kwargs = {}
|
||||
if start_time > 0:
|
||||
input_kwargs["ss"] = start_time
|
||||
|
||||
stream = ffmpeg.input(str(input_path))
|
||||
stream = stream.filter("select", select_expr).filter("showinfo")
|
||||
stream = ffmpeg.input(str(input_path), **input_kwargs)
|
||||
stream = stream.filter("select", scene_expr).filter("showinfo")
|
||||
|
||||
output = (
|
||||
ffmpeg.output(
|
||||
|
||||
@@ -68,6 +68,7 @@ class StreamManager:
|
||||
self._segment_offsets = {0: 0.0} # segment_index → global_offset
|
||||
self.scene_threshold = SCENE_THRESHOLD
|
||||
self.readonly = False # True when loaded from existing session
|
||||
self.telemetry = None # set by window after start
|
||||
log.info("Session: %s", session_id)
|
||||
|
||||
@classmethod
|
||||
@@ -306,6 +307,8 @@ class StreamManager:
|
||||
|
||||
def _detect_scenes(self, start_time, end_time):
|
||||
"""Run ffmpeg scene detection on a time range. Returns list of new frame entries."""
|
||||
import time as _time
|
||||
t0 = _time.monotonic()
|
||||
duration = end_time - start_time
|
||||
start_number = self._next_frame_number()
|
||||
|
||||
@@ -335,7 +338,8 @@ class StreamManager:
|
||||
continue
|
||||
pts_match = re.search(r"pts_time:\s*([\d.]+)", line)
|
||||
if pts_match:
|
||||
pts_time = float(pts_match.group(1))
|
||||
# pts_time is relative to -ss seek point, add start_time for local offset
|
||||
pts_time = float(pts_match.group(1)) + start_time
|
||||
frame_id = f"F{frame_num:04d}"
|
||||
frame_path = self.frames_dir / f"{frame_id}.jpg"
|
||||
if frame_path.exists():
|
||||
@@ -350,6 +354,20 @@ class StreamManager:
|
||||
frame_num += 1
|
||||
|
||||
index_path.write_text(json.dumps(index, indent=2))
|
||||
|
||||
elapsed_ms = (_time.monotonic() - t0) * 1000
|
||||
tel = getattr(self, "telemetry", None)
|
||||
if tel:
|
||||
tel.metric("scene_detection", {
|
||||
"start": start_time, "end": end_time,
|
||||
"duration": duration,
|
||||
"frames_found": len(new_frames),
|
||||
"total_frames": len(index),
|
||||
"threshold": self.scene_threshold,
|
||||
"elapsed_ms": round(elapsed_ms),
|
||||
"file_duration": self._estimate_safe_duration() or 0,
|
||||
})
|
||||
|
||||
return new_frames
|
||||
|
||||
def capture_now(self, on_new_frames=None):
|
||||
|
||||
Reference in New Issue
Block a user