diff --git a/docker/main/Dockerfile b/docker/main/Dockerfile index c512ceb848..8cea06f4a9 100644 --- a/docker/main/Dockerfile +++ b/docker/main/Dockerfile @@ -265,7 +265,7 @@ ENV PATH="/usr/local/go2rtc/bin:/usr/local/tempio/bin:/usr/local/nginx/sbin:${PA RUN --mount=type=bind,source=docker/main/install_deps.sh,target=/deps/install_deps.sh \ /deps/install_deps.sh -ENV DEFAULT_FFMPEG_VERSION="7.0" +ENV DEFAULT_FFMPEG_VERSION="8.0" ENV INCLUDED_FFMPEG_VERSIONS="${DEFAULT_FFMPEG_VERSION}:5.0" RUN wget -q https://bootstrap.pypa.io/get-pip.py -O get-pip.py \ diff --git a/docker/main/rootfs/usr/local/ffmpeg/get_ffmpeg_path.py b/docker/main/rootfs/usr/local/ffmpeg/get_ffmpeg_path.py index 0f492cc5c5..9f4d08f2e3 100644 --- a/docker/main/rootfs/usr/local/ffmpeg/get_ffmpeg_path.py +++ b/docker/main/rootfs/usr/local/ffmpeg/get_ffmpeg_path.py @@ -5,11 +5,7 @@ from typing import Any from ruamel.yaml import YAML sys.path.insert(0, "/opt/frigate") -from frigate.const import ( - DEFAULT_FFMPEG_VERSION, - INCLUDED_FFMPEG_VERSIONS, -) -from frigate.util.config import find_config_file +from frigate.util.config import find_config_file, resolve_ffmpeg_path sys.path.remove("/opt/frigate") @@ -29,9 +25,4 @@ except FileNotFoundError: config: dict[str, Any] = {} path = config.get("ffmpeg", {}).get("path", "default") -if path == "default": - print(f"/usr/lib/ffmpeg/{DEFAULT_FFMPEG_VERSION}/bin/ffmpeg") -elif path in INCLUDED_FFMPEG_VERSIONS: - print(f"/usr/lib/ffmpeg/{path}/bin/ffmpeg") -else: - print(f"{path}/bin/ffmpeg") +print(resolve_ffmpeg_path(path, "ffmpeg")) diff --git a/docker/main/rootfs/usr/local/go2rtc/create_config.py b/docker/main/rootfs/usr/local/go2rtc/create_config.py index 5796a58aad..2b0fe3c925 100644 --- a/docker/main/rootfs/usr/local/go2rtc/create_config.py +++ b/docker/main/rootfs/usr/local/go2rtc/create_config.py @@ -11,12 +11,10 @@ sys.path.insert(0, "/opt/frigate") from frigate.config.env import substitute_frigate_vars from frigate.const import ( BIRDSEYE_PIPE, - DEFAULT_FFMPEG_VERSION, - INCLUDED_FFMPEG_VERSIONS, LIBAVFORMAT_VERSION_MAJOR, ) from frigate.ffmpeg_presets import parse_preset_hardware_acceleration_encode -from frigate.util.config import find_config_file +from frigate.util.config import find_config_file, resolve_ffmpeg_path from frigate.util.services import is_restricted_go2rtc_source sys.path.remove("/opt/frigate") @@ -81,12 +79,7 @@ if go2rtc_config.get("rtsp", {}).get("password") is not None: # ensure ffmpeg path is set correctly path = config.get("ffmpeg", {}).get("path", "default") -if path == "default": - ffmpeg_path = f"/usr/lib/ffmpeg/{DEFAULT_FFMPEG_VERSION}/bin/ffmpeg" -elif path in INCLUDED_FFMPEG_VERSIONS: - ffmpeg_path = f"/usr/lib/ffmpeg/{path}/bin/ffmpeg" -else: - ffmpeg_path = f"{path}/bin/ffmpeg" +ffmpeg_path = resolve_ffmpeg_path(path, "ffmpeg") if go2rtc_config.get("ffmpeg") is None: go2rtc_config["ffmpeg"] = {"bin": ffmpeg_path} diff --git a/docs/docs/configuration/reference.md b/docs/docs/configuration/reference.md index 3749697876..0559b37569 100644 --- a/docs/docs/configuration/reference.md +++ b/docs/docs/configuration/reference.md @@ -257,7 +257,7 @@ birdseye: # More information about presets at https://docs.frigate.video/configuration/ffmpeg_presets ffmpeg: # Optional: ffmpeg binary path (default: shown below) - # can also be set to `7.0` or `5.0` to specify one of the included versions + # can also be set to `8.0` or `5.0` to specify one of the included versions # or can be set to any path that holds `bin/ffmpeg` & `bin/ffprobe` path: "default" # Optional: global ffmpeg args (default: shown below) diff --git a/frigate/config/camera/ffmpeg.py b/frigate/config/camera/ffmpeg.py index 05769dc66d..6341cbcd13 100644 --- a/frigate/config/camera/ffmpeg.py +++ b/frigate/config/camera/ffmpeg.py @@ -3,7 +3,7 @@ from typing import Union from pydantic import Field, field_validator -from frigate.const import DEFAULT_FFMPEG_VERSION, INCLUDED_FFMPEG_VERSIONS +from frigate.util.config import resolve_ffmpeg_path from ..base import FrigateBaseModel from ..env import EnvString @@ -49,7 +49,7 @@ class FfmpegConfig(FrigateBaseModel): path: str = Field( default="default", title="FFmpeg path", - description='Path to the FFmpeg binary to use or a version alias ("5.0" or "7.0").', + description='Path to the FFmpeg binary to use or a version alias ("5.0" or "8.0").', ) global_args: Union[str, list[str]] = Field( default=FFMPEG_GLOBAL_ARGS_DEFAULT, @@ -90,21 +90,11 @@ class FfmpegConfig(FrigateBaseModel): @property def ffmpeg_path(self) -> str: - if self.path == "default": - return f"/usr/lib/ffmpeg/{DEFAULT_FFMPEG_VERSION}/bin/ffmpeg" - elif self.path in INCLUDED_FFMPEG_VERSIONS: - return f"/usr/lib/ffmpeg/{self.path}/bin/ffmpeg" - else: - return f"{self.path}/bin/ffmpeg" + return resolve_ffmpeg_path(self.path, "ffmpeg") @property def ffprobe_path(self) -> str: - if self.path == "default": - return f"/usr/lib/ffmpeg/{DEFAULT_FFMPEG_VERSION}/bin/ffprobe" - elif self.path in INCLUDED_FFMPEG_VERSIONS: - return f"/usr/lib/ffmpeg/{self.path}/bin/ffprobe" - else: - return f"{self.path}/bin/ffprobe" + return resolve_ffmpeg_path(self.path, "ffprobe") class CameraRoleEnum(str, Enum): diff --git a/frigate/record/export.py b/frigate/record/export.py index 3a943cb3fe..e89742b1ab 100644 --- a/frigate/record/export.py +++ b/frigate/record/export.py @@ -456,7 +456,7 @@ class RecordingExporter(threading.Thread): diff = max(0.0, float(self.start_time) - float(preview.start_time)) ffmpeg_cmd = [ - "/usr/lib/ffmpeg/7.0/bin/ffmpeg", # hardcode path for exports thumbnail due to missing libwebp support + "/usr/lib/ffmpeg/8.0/bin/ffmpeg", # hardcode path for exports thumbnail due to missing libwebp support "-hide_banner", "-loglevel", "warning", diff --git a/frigate/util/classification.py b/frigate/util/classification.py index 66bacdeb04..30fc4c6687 100644 --- a/frigate/util/classification.py +++ b/frigate/util/classification.py @@ -394,7 +394,7 @@ def collect_state_classification_examples( # Step 3: Extract keyframes from recordings with crops applied keyframes = _extract_keyframes( - "/usr/lib/ffmpeg/7.0/bin/ffmpeg", timestamps, temp_dir, cameras + "/usr/lib/ffmpeg/8.0/bin/ffmpeg", timestamps, temp_dir, cameras ) # Step 4: Select 24 most visually distinct images (they're already cropped) @@ -566,7 +566,7 @@ def _extract_keyframes( relative_time = timestamp - recording.start_time try: - config = FfmpegConfig(path="/usr/lib/ffmpeg/7.0") + config = FfmpegConfig(path="/usr/lib/ffmpeg/8.0") image_data = get_image_from_recording( config, recording.path, diff --git a/frigate/util/config.py b/frigate/util/config.py index 186730dbfb..5a4f3816c0 100644 --- a/frigate/util/config.py +++ b/frigate/util/config.py @@ -8,7 +8,13 @@ from typing import Any, Optional, Union from ruamel.yaml import YAML -from frigate.const import CONFIG_DIR, EXPORT_DIR, REDACTED_CREDENTIAL_SENTINEL +from frigate.const import ( + CONFIG_DIR, + DEFAULT_FFMPEG_VERSION, + EXPORT_DIR, + INCLUDED_FFMPEG_VERSIONS, + REDACTED_CREDENTIAL_SENTINEL, +) from frigate.util.builtin import deep_merge from frigate.util.services import get_video_properties @@ -18,6 +24,26 @@ CURRENT_CONFIG_VERSION = "0.18-0" DEFAULT_CONFIG_FILE = os.path.join(CONFIG_DIR, "config.yml") +def resolve_ffmpeg_path(path: str, binary: str = "ffmpeg") -> str: + """Resolve an ffmpeg version alias or custom path to a binary path. + + A bare version alias that is no longer bundled (for example one that was + dropped when the default version changed) falls back to the default + bundled version so existing configs keep working across an upgrade or a + revert. Custom install paths (anything absolute) are used as-is. + """ + if path == "default" or ( + not path.startswith("/") and path not in INCLUDED_FFMPEG_VERSIONS + ): + version = DEFAULT_FFMPEG_VERSION + elif path in INCLUDED_FFMPEG_VERSIONS: + version = path + else: + return f"{path}/bin/{binary}" + + return f"/usr/lib/ffmpeg/{version}/bin/{binary}" + + def redact_credential(obj: dict[str, Any], key: str) -> None: """Replace obj[key] with the redaction sentinel if a value is saved, else drop.