c876a46e61
Player/transcoding settings gain encoder controls: - Rate-control mode VBR / CBR / QP (constant quantizer). x264: VBR uses -b:v/-maxrate/-bufsize, CBR adds -x264-params nal-hrd=cbr (min=max=b:v), QP uses -qp N (no bitrate). VP9 maps QP -> -crf N -b:v 0, CBR -> min=max=b:v. - Preset list expanded to ultrafast/superfast/veryfast/faster/fast/medium (was only ultrafast/superfast); for VP9 the preset maps to cpu-used. - New QP field in the UI, shown when rate control = QP, otherwise the bitrate field is shown. Both /play.mp4 (archive) and /live.mp4 accept rc + qp query params. All three modes verified producing valid output on the live restream. VERSION -> 0.0.119. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
130 lines
4.8 KiB
Python
130 lines
4.8 KiB
Python
"""Общие параметры перекодирования H.264/VP9: профиль, битрейт, пресет, GOP, режим RC."""
|
||
from __future__ import annotations
|
||
|
||
H264_PROFILES = ("baseline", "main", "high")
|
||
BITRATES = ("500k", "1000k", "2000k", "4000k", "8000k", "10000k", "16000k", "20000k", "24000k")
|
||
PRESETS = ("ultrafast", "superfast", "veryfast", "faster", "fast", "medium")
|
||
KEYINTS = (1, 2, 5) # интервал ключевых кадров, секунды
|
||
CODECS = ("h264", "vp9")
|
||
RC_MODES = ("vbr", "cbr", "qp") # контроль битрейта: переменный / постоянный / постоянный квантизатор
|
||
|
||
DEFAULT_PROFILE = "main"
|
||
DEFAULT_BITRATE = "2000k"
|
||
DEFAULT_PRESET = "superfast"
|
||
DEFAULT_KEYINT = 2
|
||
DEFAULT_CODEC = "h264"
|
||
DEFAULT_RC = "vbr"
|
||
DEFAULT_QP = 23
|
||
QP_MIN, QP_MAX = 0, 51
|
||
|
||
# пресет x264 → cpu-used VP9 (больше число = быстрее кодирование, ниже качество)
|
||
_VP9_CPU = {"ultrafast": "8", "superfast": "8", "veryfast": "7",
|
||
"faster": "6", "fast": "5", "medium": "4"}
|
||
|
||
|
||
def norm_profile(p: str | None) -> str:
|
||
return p if p in H264_PROFILES else DEFAULT_PROFILE
|
||
|
||
|
||
def norm_codec(c: str | None) -> str:
|
||
return c if c in CODECS else DEFAULT_CODEC
|
||
|
||
|
||
def norm_bitrate(b: str | None) -> str:
|
||
return b if b in BITRATES else DEFAULT_BITRATE
|
||
|
||
|
||
def norm_preset(p: str | None) -> str:
|
||
return p if p in PRESETS else DEFAULT_PRESET
|
||
|
||
|
||
def norm_rc(m: str | None) -> str:
|
||
return m if m in RC_MODES else DEFAULT_RC
|
||
|
||
|
||
def norm_keyint(k) -> int:
|
||
try:
|
||
k = int(k)
|
||
except (TypeError, ValueError):
|
||
return DEFAULT_KEYINT
|
||
return k if k in KEYINTS else DEFAULT_KEYINT
|
||
|
||
|
||
def norm_qp(q) -> int:
|
||
try:
|
||
q = int(q)
|
||
except (TypeError, ValueError):
|
||
return DEFAULT_QP
|
||
return max(QP_MIN, min(QP_MAX, q))
|
||
|
||
|
||
def _kbps(bitrate: str) -> int:
|
||
return int(bitrate[:-1])
|
||
|
||
|
||
def x264_args(profile: str, bitrate: str, low_latency: bool = False,
|
||
preset: str | None = None, keyint_sec=None,
|
||
rc: str = DEFAULT_RC, qp=DEFAULT_QP) -> list[str]:
|
||
"""Аргументы libx264: профиль, пресет, GOP и режим контроля битрейта.
|
||
|
||
rc: vbr — целевой битрейт с потолком (-b:v/-maxrate); cbr — постоянный
|
||
битрейт (nal-hrd=cbr, min=max=b:v); qp — постоянный квантизатор (-qp, без
|
||
битрейта). Ключевые кадры расставляются по времени (-force_key_frames)."""
|
||
preset = norm_preset(preset)
|
||
sec = norm_keyint(keyint_sec)
|
||
rc = norm_rc(rc)
|
||
args = [
|
||
"-c:v", "libx264",
|
||
"-preset", preset,
|
||
"-profile:v", profile,
|
||
"-force_key_frames", f"expr:gte(t,n_forced*{sec})",
|
||
"-pix_fmt", "yuv420p",
|
||
]
|
||
if rc == "qp":
|
||
args += ["-qp", str(norm_qp(qp))]
|
||
elif rc == "cbr":
|
||
kb = _kbps(bitrate)
|
||
args += ["-b:v", bitrate, "-minrate", bitrate, "-maxrate", bitrate,
|
||
"-bufsize", f"{kb}k", "-x264-params", "nal-hrd=cbr"]
|
||
else: # vbr
|
||
kb = _kbps(bitrate)
|
||
args += ["-b:v", bitrate, "-maxrate", bitrate, "-bufsize", f"{kb * 2}k"]
|
||
if low_latency:
|
||
args += ["-tune", "zerolatency"]
|
||
return args
|
||
|
||
|
||
def vp9_args(bitrate: str, low_latency: bool = False,
|
||
preset: str | None = None, keyint_sec=None,
|
||
rc: str = DEFAULT_RC, qp=DEFAULT_QP) -> list[str]:
|
||
"""Аргументы libvpx-VP9 (профиль H.264 неприменим).
|
||
|
||
rc: vbr/cbr — по битрейту; qp — постоянное качество (-crf N -b:v 0, у VP9
|
||
нет прямого -qp). cpu-used маппится из пресета. realtime для live."""
|
||
sec = norm_keyint(keyint_sec)
|
||
rc = norm_rc(rc)
|
||
args = [
|
||
"-c:v", "libvpx-vp9",
|
||
"-deadline", "realtime",
|
||
"-cpu-used", _VP9_CPU.get(norm_preset(preset), "6"),
|
||
"-row-mt", "1",
|
||
"-force_key_frames", f"expr:gte(t,n_forced*{sec})",
|
||
"-pix_fmt", "yuv420p",
|
||
]
|
||
if rc == "qp":
|
||
args += ["-crf", str(norm_qp(qp)), "-b:v", "0"]
|
||
elif rc == "cbr":
|
||
args += ["-b:v", bitrate, "-minrate", bitrate, "-maxrate", bitrate]
|
||
else: # vbr
|
||
args += ["-b:v", bitrate, "-maxrate", bitrate]
|
||
return args
|
||
|
||
|
||
def encode_args(codec: str, profile: str, bitrate: str, low_latency: bool = False,
|
||
preset: str | None = None, keyint_sec=None,
|
||
rc: str = DEFAULT_RC, qp=DEFAULT_QP) -> list[str]:
|
||
"""Аргументы видеокодировщика под выбранный кодек (h264 | vp9)."""
|
||
if norm_codec(codec) == "vp9":
|
||
return vp9_args(bitrate, low_latency, preset, keyint_sec, rc, qp)
|
||
return x264_args(profile, bitrate, low_latency, preset, keyint_sec, rc, qp)
|