101 lines
3.1 KiB
Python
101 lines
3.1 KiB
Python
import subprocess
|
||
import threading
|
||
import time
|
||
import shlex
|
||
import sys
|
||
from datetime import datetime
|
||
|
||
FFMPEG = "ffmpeg" # или r"C:\ffmpeg\bin\ffmpeg.exe"
|
||
|
||
# ВХОД: обычно listener (vMix = caller)
|
||
IN_URL = "srt://127.0.0.1:9000?mode=caller&reuseaddr=1"
|
||
|
||
# ВЫХОД: обычно caller (приемник = listener)
|
||
# OUT_URL = "srt://127.0.0.1:9001?mode=listener&latency=200000&transtype=live&connect_timeout=5000"
|
||
# OUT_URL = "srt://0.0.0.0:9001?mode=listener&transtype=live&connect_timeout=5000"
|
||
OUT_URL = "srt://0.0.0.0:9001?mode=listener&reuseaddr=1"
|
||
|
||
# Если хочешь, чтобы ffmpeg "держал" выход и ждал подключение (удобно для VLC):
|
||
# OUT_URL = "srt://0.0.0.0:9001?mode=listener&latency=200000&reuseaddr=1&transtype=live"
|
||
# тогда VLC открывай как caller: srt://127.0.0.1:9001?latency=200000
|
||
|
||
LOGLEVEL = "info" # debug для диагностики
|
||
|
||
def now():
|
||
return datetime.now().strftime("%H:%M:%S")
|
||
|
||
def reader_thread(pipe, prefix):
|
||
"""Читает stderr ffmpeg и печатает, чтобы не забивался буфер."""
|
||
try:
|
||
for line in iter(pipe.readline, ""):
|
||
if not line:
|
||
break
|
||
line = line.rstrip("\n")
|
||
if line:
|
||
print(f"[{now()}] {prefix} {line}")
|
||
except Exception:
|
||
pass
|
||
|
||
def build_cmd():
|
||
return [
|
||
FFMPEG,
|
||
"-hide_banner",
|
||
"-loglevel", LOGLEVEL,
|
||
|
||
# полезно для лайва
|
||
"-fflags", "+genpts",
|
||
"-flags", "low_delay",
|
||
|
||
# вход
|
||
"-i", IN_URL,
|
||
|
||
# без перекодирования (минимальная задержка/нагрузка)
|
||
"-c", "copy",
|
||
"-f", "mpegts",
|
||
|
||
# выход
|
||
OUT_URL,
|
||
]
|
||
|
||
def run_forever():
|
||
backoff = 1.0
|
||
backoff_max = 10.0
|
||
|
||
while True:
|
||
cmd = build_cmd()
|
||
print(f"[{now()}] Starting ffmpeg:\n {shlex.join(cmd) if hasattr(shlex,'join') else ' '.join(cmd)}")
|
||
|
||
try:
|
||
proc = subprocess.Popen(
|
||
cmd,
|
||
stdin=subprocess.DEVNULL,
|
||
stdout=subprocess.DEVNULL,
|
||
stderr=subprocess.PIPE,
|
||
text=True,
|
||
bufsize=1,
|
||
)
|
||
|
||
t = threading.Thread(target=reader_thread, args=(proc.stderr, "ffmpeg:"), daemon=True)
|
||
t.start()
|
||
|
||
rc = proc.wait()
|
||
print(f"[{now()}] ffmpeg exited with code {rc}")
|
||
|
||
except FileNotFoundError:
|
||
print(f"[{now()}] ERROR: ffmpeg not found. Set FFMPEG path correctly.")
|
||
sys.exit(2)
|
||
except Exception as e:
|
||
print(f"[{now()}] ERROR running ffmpeg: {e}")
|
||
rc = -1
|
||
|
||
# Если поток отвалился или приемник недоступен — ffmpeg завершится.
|
||
# Мы НЕ падаем, а перезапускаем.
|
||
sleep_s = backoff
|
||
print(f"[{now()}] Restarting in {sleep_s:.1f}s...\n")
|
||
time.sleep(sleep_s)
|
||
|
||
backoff = min(backoff * 1.5, backoff_max)
|
||
|
||
if __name__ == "__main__":
|
||
run_forever()
|