flush configurable
This commit is contained in:
@@ -16,6 +16,7 @@ RELAY_PORT = 4445 # UDP loopback relay for live display
|
|||||||
|
|
||||||
# Frame extraction — scene-only, no interval fallback
|
# Frame extraction — scene-only, no interval fallback
|
||||||
SCENE_THRESHOLD = 0.10 # 0-1, lower = more sensitive; 0.1 catches slide/window changes
|
SCENE_THRESHOLD = 0.10 # 0-1, lower = more sensitive; 0.1 catches slide/window changes
|
||||||
|
SCENE_FLUSH_FRAMES = 2 # extra frames after scene change to flush encoder/muxer buffer (0 to disable)
|
||||||
|
|
||||||
# Segment recording
|
# Segment recording
|
||||||
SEGMENT_DURATION = 60 # seconds per .ts segment
|
SEGMENT_DURATION = 60 # seconds per .ts segment
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ def receive_record_and_relay(stream_url, output_path, relay_url):
|
|||||||
|
|
||||||
|
|
||||||
def receive_record_relay_and_detect(stream_url, output_path, relay_url,
|
def receive_record_relay_and_detect(stream_url, output_path, relay_url,
|
||||||
scene_threshold=0.10):
|
scene_threshold=0.10, flush_frames=2):
|
||||||
"""Single process: receive TCP → record fMP4 + relay UDP + scene detect.
|
"""Single process: receive TCP → record fMP4 + relay UDP + scene detect.
|
||||||
|
|
||||||
One ffmpeg process, three output branches from the same TCP input:
|
One ffmpeg process, three output branches from the same TCP input:
|
||||||
@@ -99,14 +99,16 @@ def receive_record_relay_and_detect(stream_url, output_path, relay_url,
|
|||||||
# Scene detection: CUDA decode (GPU) → select filter (CPU, lightweight)
|
# Scene detection: CUDA decode (GPU) → select filter (CPU, lightweight)
|
||||||
# → showinfo → MJPEG piped to stdout
|
# → showinfo → MJPEG piped to stdout
|
||||||
#
|
#
|
||||||
# Flush trick: select the scene-change frame AND the next 2 frames.
|
|
||||||
# The pipeline has 2 levels of buffering (encoder + muxer), so we
|
|
||||||
# need 2 flush frames to push the real scene-change frame out.
|
|
||||||
# mod(selected_n,3) prevents chaining: after 2 flushes, selected_n
|
|
||||||
# hits a multiple of 3 and the chain stops.
|
|
||||||
scene_expr = f"gt(scene,{scene_threshold})"
|
scene_expr = f"gt(scene,{scene_threshold})"
|
||||||
flush_expr = "eq(n,prev_selected_n+1)*mod(selected_n,3)"
|
if flush_frames > 0:
|
||||||
select_expr = f"{scene_expr}+{flush_expr}"
|
# Flush trick: select extra frames after each scene change to push
|
||||||
|
# the real frame through the encoder+muxer buffer pipeline.
|
||||||
|
# mod(selected_n, 1+flush_frames) prevents chaining.
|
||||||
|
mod_val = 1 + flush_frames
|
||||||
|
flush_expr = f"eq(n,prev_selected_n+1)*mod(selected_n,{mod_val})"
|
||||||
|
select_expr = f"{scene_expr}+{flush_expr}"
|
||||||
|
else:
|
||||||
|
select_expr = scene_expr
|
||||||
scene_stream = stream.filter("select", select_expr).filter("showinfo")
|
scene_stream = stream.filter("select", select_expr).filter("showinfo")
|
||||||
scene_out = ffmpeg.output(
|
scene_out = ffmpeg.output(
|
||||||
scene_stream, "pipe:1",
|
scene_stream, "pipe:1",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from cht.config import (
|
|||||||
STREAM_PORT,
|
STREAM_PORT,
|
||||||
RELAY_PORT,
|
RELAY_PORT,
|
||||||
SCENE_THRESHOLD,
|
SCENE_THRESHOLD,
|
||||||
|
SCENE_FLUSH_FRAMES,
|
||||||
SESSIONS_DIR,
|
SESSIONS_DIR,
|
||||||
AUDIO_EXTRACT_INTERVAL,
|
AUDIO_EXTRACT_INTERVAL,
|
||||||
AUDIO_SAFETY_MARGIN,
|
AUDIO_SAFETY_MARGIN,
|
||||||
@@ -197,6 +198,7 @@ class StreamManager:
|
|||||||
node = ff.receive_record_relay_and_detect(
|
node = ff.receive_record_relay_and_detect(
|
||||||
self.stream_url, self.recording_path, self.relay_url,
|
self.stream_url, self.recording_path, self.relay_url,
|
||||||
scene_threshold=self.scene_threshold,
|
scene_threshold=self.scene_threshold,
|
||||||
|
flush_frames=SCENE_FLUSH_FRAMES,
|
||||||
)
|
)
|
||||||
proc = ff.run_async(node, pipe_stdout=True, pipe_stderr=True)
|
proc = ff.run_async(node, pipe_stdout=True, pipe_stderr=True)
|
||||||
self._procs["recorder"] = proc
|
self._procs["recorder"] = proc
|
||||||
|
|||||||
Reference in New Issue
Block a user