Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/docs/configuration/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ ffmpeg:
retry_interval: 10
# Optional: Set tag on HEVC (H.265) recording stream to improve compatibility with Apple players. (default: shown below)
apple_compatibility: false
# Optional: Set the index of the GPU to use for hardware acceleration. (default: shown below)
gpu: 0

# Optional: Detect configuration
# NOTE: Can be overridden at the camera level
Expand Down
2 changes: 2 additions & 0 deletions frigate/config/camera/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,13 +235,15 @@ def _get_ffmpeg_cmd(self, ffmpeg_input: CameraInput):
self.detect.fps,
self.detect.width,
self.detect.height,
self.ffmpeg.gpu,
)
or ffmpeg_input.hwaccel_args
or parse_preset_hardware_acceleration_decode(
camera_arg,
self.detect.fps,
self.detect.width,
self.detect.height,
self.ffmpeg.gpu,
)
or camera_arg
or []
Expand Down
1 change: 1 addition & 0 deletions frigate/config/camera/ffmpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class FfmpegConfig(FrigateBaseModel):
default=False,
title="Set tag on HEVC (H.265) recording stream to improve compatibility with Apple players.",
)
gpu: int = Field(default=0, title="GPU index to use for hardware acceleration.")

@property
def ffmpeg_path(self) -> str:
Expand Down
66 changes: 46 additions & 20 deletions frigate/ffmpeg_presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,51 @@
class LibvaGpuSelector:
"Automatically selects the correct libva GPU."

_selected_gpu = None
_valid_gpus: list[str] | None = None

def get_selected_gpu(self) -> str:
"""Get selected libva GPU."""
def __get_valid_gpus(self) -> None:
"""Get valid libva GPUs."""
if not os.path.exists("/dev/dri"):
return ""
self._valid_gpus = []
return

if self._selected_gpu:
return self._selected_gpu
if self._valid_gpus:
return

devices = list(filter(lambda d: d.startswith("render"), os.listdir("/dev/dri")))

if not devices:
return "/dev/dri/renderD128"
self._valid_gpus = ["/dev/dri/renderD128"]
return

if len(devices) < 2:
self._selected_gpu = f"/dev/dri/{devices[0]}"
return self._selected_gpu
self._valid_gpus = [f"/dev/dri/{devices[0]}"]
return

self._valid_gpus = []
for device in devices:
check = vainfo_hwaccel(device_name=device)

logger.debug(f"{device} return vainfo status code: {check.returncode}")

if check.returncode == 0:
self._selected_gpu = f"/dev/dri/{device}"
return self._selected_gpu
self._valid_gpus.append(f"/dev/dri/{device}")

def get_gpu_arg(self, preset: str, gpu: int) -> str:
if "nvidia" in preset:
return f"-hwaccel_device {gpu}"

if self._valid_gpus is None:
self.__get_valid_gpus()

if not self._valid_gpus:
return ""

return ""
if gpu <= len(self._valid_gpus):
return f"-hwaccel_device {self._valid_gpus[gpu]}"
else:
logger.warning(f"Invalid GPU index {gpu}, using first valid GPU")
return f"-hwaccel_device {self._valid_gpus[0]}"


FPS_VFR_PARAM = "-fps_mode vfr" if LIBAVFORMAT_VERSION_MAJOR >= 59 else "-vsync 2"
Expand All @@ -63,13 +79,15 @@ def get_selected_gpu(self) -> str:
f"FFmpeg Frigate/{VERSION}",
]

# Presets for FFMPEG Stream Decoding (detect role)

PRESETS_HW_ACCEL_DECODE = {
"preset-rpi-64-h264": "-c:v:1 h264_v4l2m2m",
"preset-rpi-64-h265": "-c:v:1 hevc_v4l2m2m",
FFMPEG_HWACCEL_VAAPI: f"-hwaccel_flags allow_profile_mismatch -hwaccel vaapi -hwaccel_device {_gpu_selector.get_selected_gpu()} -hwaccel_output_format vaapi",
"preset-intel-qsv-h264": f"-hwaccel qsv -qsv_device {_gpu_selector.get_selected_gpu()} -hwaccel_output_format qsv -c:v h264_qsv{' -bsf:v dump_extra' if LIBAVFORMAT_VERSION_MAJOR >= 61 else ''}", # https://trac.ffmpeg.org/ticket/9766#comment:17
"preset-intel-qsv-h265": f"-load_plugin hevc_hw -hwaccel qsv -qsv_device {_gpu_selector.get_selected_gpu()} -hwaccel_output_format qsv{' -bsf:v dump_extra' if LIBAVFORMAT_VERSION_MAJOR >= 61 else ''}", # https://trac.ffmpeg.org/ticket/9766#comment:17
FFMPEG_HWACCEL_NVIDIA: "-hwaccel cuda -hwaccel_output_format cuda",
FFMPEG_HWACCEL_VAAPI: "-hwaccel_flags allow_profile_mismatch -hwaccel vaapi -hwaccel_device {3} -hwaccel_output_format vaapi",
"preset-intel-qsv-h264": "-hwaccel qsv -qsv_device {3} -hwaccel_output_format qsv -c:v h264_qsv{' -bsf:v dump_extra' if LIBAVFORMAT_VERSION_MAJOR >= 61 else ''}", # https://trac.ffmpeg.org/ticket/9766#comment:17
"preset-intel-qsv-h265": "-load_plugin hevc_hw -hwaccel qsv -qsv_device {3} -hwaccel_output_format qsv{' -bsf:v dump_extra' if LIBAVFORMAT_VERSION_MAJOR >= 61 else ''}", # https://trac.ffmpeg.org/ticket/9766#comment:17
FFMPEG_HWACCEL_NVIDIA: "{3} -hwaccel cuda -hwaccel_output_format cuda",
"preset-jetson-h264": "-c:v h264_nvmpi -resize {1}x{2}",
"preset-jetson-h265": "-c:v hevc_nvmpi -resize {1}x{2}",
f"{FFMPEG_HWACCEL_RKMPP}-no-dump_extra": "-hwaccel rkmpp -hwaccel_output_format drm_prime",
Expand Down Expand Up @@ -97,6 +115,8 @@ def get_selected_gpu(self) -> str:
FFMPEG_HWACCEL_RKMPP
]

# Presets for FFMPEG Stream Scaling (detect role)

PRESETS_HW_ACCEL_SCALE = {
"preset-rpi-64-h264": "-r {0} -vf fps={0},scale={1}:{2}",
"preset-rpi-64-h265": "-r {0} -vf fps={0},scale={1}:{2}",
Expand Down Expand Up @@ -125,13 +145,15 @@ def get_selected_gpu(self) -> str:
PRESETS_HW_ACCEL_SCALE["preset-rk-h264"] = PRESETS_HW_ACCEL_SCALE[FFMPEG_HWACCEL_RKMPP]
PRESETS_HW_ACCEL_SCALE["preset-rk-h265"] = PRESETS_HW_ACCEL_SCALE[FFMPEG_HWACCEL_RKMPP]

# Presets for FFMPEG Stream Encoding (birdseye feature)

PRESETS_HW_ACCEL_ENCODE_BIRDSEYE = {
"preset-rpi-64-h264": "{0} -hide_banner {1} -c:v h264_v4l2m2m {2}",
"preset-rpi-64-h265": "{0} -hide_banner {1} -c:v hevc_v4l2m2m {2}",
FFMPEG_HWACCEL_VAAPI: "{0} -hide_banner -hwaccel vaapi -hwaccel_output_format vaapi -hwaccel_device {3} {1} -c:v h264_vaapi -g 50 -bf 0 -profile:v high -level:v 4.1 -sei:v 0 -an -vf format=vaapi|nv12,hwupload {2}",
FFMPEG_HWACCEL_VAAPI: "{0} -hide_banner -hwaccel vaapi -hwaccel_output_format vaapi {3} {1} -c:v h264_vaapi -g 50 -bf 0 -profile:v high -level:v 4.1 -sei:v 0 -an -vf format=vaapi|nv12,hwupload {2}",
"preset-intel-qsv-h264": "{0} -hide_banner {1} -c:v h264_qsv -g 50 -bf 0 -profile:v high -level:v 4.1 -async_depth:v 1 {2}",
"preset-intel-qsv-h265": "{0} -hide_banner {1} -c:v h264_qsv -g 50 -bf 0 -profile:v main -level:v 4.1 -async_depth:v 1 {2}",
FFMPEG_HWACCEL_NVIDIA: "{0} -hide_banner {1} -c:v h264_nvenc -g 50 -profile:v high -level:v auto -preset:v p2 -tune:v ll {2}",
FFMPEG_HWACCEL_NVIDIA: "{0} -hide_banner {1} {3} -c:v h264_nvenc -g 50 -profile:v high -level:v auto -preset:v p2 -tune:v ll {2}",
"preset-jetson-h264": "{0} -hide_banner {1} -c:v h264_nvmpi -profile high {2}",
"preset-jetson-h265": "{0} -hide_banner {1} -c:v h264_nvmpi -profile main {2}",
FFMPEG_HWACCEL_RKMPP: "{0} -hide_banner {1} -c:v h264_rkmpp -profile:v high {2}",
Expand All @@ -153,6 +175,8 @@ def get_selected_gpu(self) -> str:
FFMPEG_HWACCEL_RKMPP
]

# Presets for FFMPEG Stream Encoding (timelapse feature)

PRESETS_HW_ACCEL_ENCODE_TIMELAPSE = {
"preset-rpi-64-h264": "{0} -hide_banner {1} -c:v h264_v4l2m2m -pix_fmt yuv420p {2}",
"preset-rpi-64-h265": "{0} -hide_banner {1} -c:v hevc_v4l2m2m -pix_fmt yuv420p {2}",
Expand Down Expand Up @@ -190,6 +214,7 @@ def parse_preset_hardware_acceleration_decode(
fps: int,
width: int,
height: int,
gpu: int,
) -> list[str]:
"""Return the correct preset if in preset format otherwise return None."""
if not isinstance(arg, str):
Expand All @@ -200,7 +225,8 @@ def parse_preset_hardware_acceleration_decode(
if not decode:
return None

return decode.format(fps, width, height).split(" ")
gpu_arg = _gpu_selector.get_gpu_arg(arg, gpu)
return decode.format(fps, width, height, gpu_arg).split(" ")


def parse_preset_hardware_acceleration_scale(
Expand Down Expand Up @@ -262,7 +288,7 @@ def parse_preset_hardware_acceleration_encode(
ffmpeg_path,
input,
output,
_gpu_selector.get_selected_gpu(),
_gpu_selector.get_gpu_arg(arg, 0),
)


Expand Down