try 2
This commit is contained in:
@@ -64,21 +64,25 @@ def receive_record_and_relay(stream_url, output_path, relay_url):
|
||||
|
||||
|
||||
def receive_record_relay_and_detect(stream_url, output_path, relay_url,
|
||||
frames_dir, scene_threshold=0.10,
|
||||
start_number=1):
|
||||
scene_threshold=0.10):
|
||||
"""Single process: receive TCP → record fMP4 + relay UDP + scene detect.
|
||||
|
||||
One ffmpeg process, three output branches from the same TCP input:
|
||||
1. File output — c=copy to fMP4
|
||||
2. UDP relay — c=copy to mpegts for live display
|
||||
3. Scene frames — CUDA decode → select(scene) → showinfo → JPEG files
|
||||
1. File output — c=copy to fMP4 (raw packets, no decode)
|
||||
2. UDP relay — c=copy to mpegts for live display (raw packets)
|
||||
3. Scene frames — Vulkan decode + scdet_vulkan (GPU scene comparison,
|
||||
sc_pass=1 drops non-scene frames on GPU) → hwdownload (only scene
|
||||
frames hit CPU) → showinfo → MJPEG piped to stdout
|
||||
|
||||
The scene filter runs on decoded frames in-process, so detection latency
|
||||
is near-zero (no polling, no file re-reading, no separate process).
|
||||
Stderr must be read continuously to parse showinfo lines.
|
||||
Scene frames are piped to stdout as image2pipe/mjpeg to avoid the image2
|
||||
muxer's one-frame buffering delay. The caller reads JPEG data from stdout
|
||||
and writes files itself. Stderr carries showinfo lines with timestamps.
|
||||
Both stdout and stderr must be read continuously.
|
||||
"""
|
||||
stream = ffmpeg.input(stream_url, fflags="nobuffer", flags="low_delay",
|
||||
hwaccel="cuda")
|
||||
stream = ffmpeg.input(
|
||||
stream_url, fflags="nobuffer", flags="low_delay",
|
||||
hwaccel="vulkan", hwaccel_output_format="vulkan",
|
||||
)
|
||||
|
||||
# Copy outputs (raw packet remux, no decode)
|
||||
file_out = ffmpeg.output(
|
||||
@@ -93,13 +97,19 @@ def receive_record_relay_and_detect(stream_url, output_path, relay_url,
|
||||
c="copy", f="mpegts",
|
||||
)
|
||||
|
||||
# Scene detection output (decode + filter → JPEG)
|
||||
select_expr = f"gt(scene,{scene_threshold})"
|
||||
scene_stream = stream.filter("select", select_expr).filter("showinfo")
|
||||
# Scene detection on Vulkan GPU — only scene-change frames leave the GPU
|
||||
scdet_threshold = scene_threshold * 100 # config 0-1 → scdet 0-100
|
||||
scene_stream = (
|
||||
stream
|
||||
.filter("scdet_vulkan", threshold=scdet_threshold, sc_pass=1)
|
||||
.filter("hwdownload")
|
||||
.filter("format", "yuv420p")
|
||||
.filter("showinfo")
|
||||
)
|
||||
scene_out = ffmpeg.output(
|
||||
scene_stream, str(frames_dir / "F%04d.jpg"),
|
||||
vsync="vfr", flush_packets=1, **{"q:v": "2"},
|
||||
start_number=start_number,
|
||||
scene_stream, "pipe:1",
|
||||
f="image2pipe", vcodec="mjpeg",
|
||||
vsync="vfr", **{"q:v": "2"},
|
||||
)
|
||||
|
||||
return ffmpeg.merge_outputs(file_out, relay_out, scene_out).global_args(*GLOBAL_ARGS)
|
||||
|
||||
Reference in New Issue
Block a user