mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-08-02 09:02:15 +03:00
Upgrade ffmpeg to 8 by default (#23393)
* Upgrade to ffmpeg 8 * Remove workaround * Cleanup ffmpeg version resolution * Include older 7.0 for testing purposes * include
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -465,16 +465,6 @@ PRESETS_RECORD_OUTPUT = {
|
||||
"-c:a",
|
||||
"aac",
|
||||
],
|
||||
# NOTE: This preset originally used "-c:a copy" to pass through audio
|
||||
# without re-encoding. FFmpeg 7.x introduced a threaded pipeline where
|
||||
# demuxing, encoding, and muxing run in parallel via a Scheduler. This
|
||||
# broke audio streamcopy from RTSP sources: packets are demuxed correctly
|
||||
# but silently dropped before reaching the muxer (0 bytes written). The
|
||||
# issue is specific to RTSP + streamcopy; file inputs and transcoding both
|
||||
# work. Transcoding AAC audio is very lightweight (~30KiB per 10s segment)
|
||||
# and adds negligible CPU overhead, so this is an acceptable workaround.
|
||||
# The benefits of FFmpeg 7.x — particularly the removal of gamma correction
|
||||
# hacks required by earlier versions — outweigh this trade-off.
|
||||
"preset-record-generic-audio-copy": [
|
||||
"-f",
|
||||
"segment",
|
||||
@@ -486,10 +476,8 @@ PRESETS_RECORD_OUTPUT = {
|
||||
"1",
|
||||
"-strftime",
|
||||
"1",
|
||||
"-c:v",
|
||||
"-c",
|
||||
"copy",
|
||||
"-c:a",
|
||||
"aac",
|
||||
],
|
||||
"preset-record-mjpeg": [
|
||||
"-f",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
+27
-1
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user