89 lines
3.2 KiB
Python
89 lines
3.2 KiB
Python
"""Общие параметры перекодирования H.264 (профиль, битрейт, пресет, GOP)."""
|
|
from __future__ import annotations
|
|
|
|
H264_PROFILES = ("baseline", "main", "high")
|
|
BITRATES = ("500k", "1000k", "2000k", "4000k", "8000k", "10000k", "16000k", "20000k", "24000k")
|
|
PRESETS = ("ultrafast", "superfast")
|
|
KEYINTS = (1, 2, 5) # интервал ключевых кадров, секунды
|
|
CODECS = ("h264", "vp9")
|
|
|
|
DEFAULT_PROFILE = "main"
|
|
DEFAULT_BITRATE = "2000k"
|
|
DEFAULT_PRESET = "superfast"
|
|
DEFAULT_KEYINT = 2
|
|
DEFAULT_CODEC = "h264"
|
|
|
|
|
|
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_keyint(k) -> int:
|
|
try:
|
|
k = int(k)
|
|
except (TypeError, ValueError):
|
|
return DEFAULT_KEYINT
|
|
return k if k in KEYINTS else DEFAULT_KEYINT
|
|
|
|
|
|
def x264_args(profile: str, bitrate: str, low_latency: bool = False,
|
|
preset: str | None = None, keyint_sec=None) -> list[str]:
|
|
"""Аргументы libx264: профиль, битрейт, пресет и интервал ключевых кадров.
|
|
|
|
Ключевые кадры расставляются по времени (через -force_key_frames), поэтому
|
|
интервал в секундах соблюдается независимо от fps камеры."""
|
|
bufsize = f"{int(bitrate[:-1]) * 2}k"
|
|
preset = norm_preset(preset)
|
|
sec = norm_keyint(keyint_sec)
|
|
args = [
|
|
"-c:v", "libx264",
|
|
"-preset", preset,
|
|
"-profile:v", profile,
|
|
"-b:v", bitrate,
|
|
"-maxrate", bitrate,
|
|
"-bufsize", bufsize,
|
|
"-force_key_frames", f"expr:gte(t,n_forced*{sec})",
|
|
"-pix_fmt", "yuv420p",
|
|
]
|
|
if low_latency:
|
|
args += ["-tune", "zerolatency"]
|
|
return args
|
|
|
|
|
|
def vp9_args(bitrate: str, low_latency: bool = False,
|
|
preset: str | None = None, keyint_sec=None) -> list[str]:
|
|
"""Аргументы кодировщика libvpx-VP9 (профиль H.264 неприменим)."""
|
|
sec = norm_keyint(keyint_sec)
|
|
# быстрый пресет → выше cpu-used (быстрее, ниже качество); realtime для live
|
|
cpu = "8" if norm_preset(preset) == "ultrafast" else "6"
|
|
return [
|
|
"-c:v", "libvpx-vp9",
|
|
"-b:v", bitrate,
|
|
"-maxrate", bitrate,
|
|
"-deadline", "realtime",
|
|
"-cpu-used", cpu,
|
|
"-row-mt", "1",
|
|
"-force_key_frames", f"expr:gte(t,n_forced*{sec})",
|
|
"-pix_fmt", "yuv420p",
|
|
]
|
|
|
|
|
|
def encode_args(codec: str, profile: str, bitrate: str, low_latency: bool = False,
|
|
preset: str | None = None, keyint_sec=None) -> list[str]:
|
|
"""Аргументы видеокодировщика под выбранный кодек (h264 | vp9)."""
|
|
if norm_codec(codec) == "vp9":
|
|
return vp9_args(bitrate, low_latency, preset, keyint_sec)
|
|
return x264_args(profile, bitrate, low_latency, preset, keyint_sec)
|