mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-07 11:45:24 +03:00
Add ability to configure preview quality
This commit is contained in:
parent
32e30853fb
commit
e5e42aa659
@ -369,6 +369,11 @@ record:
|
||||
# The -r (framerate) dictates how smooth the output video is.
|
||||
# So the args would be -vf setpts=0.02*PTS -r 30 in that case.
|
||||
timelapse_args: "-vf setpts=0.04*PTS -r 30"
|
||||
# Optional: Recording Preview Settings
|
||||
preview:
|
||||
# Optional: Quality of recording preview (default: shown below).
|
||||
# Options are: very_low, low, medium, high, very_high
|
||||
quality: medium
|
||||
# Optional: Event recording settings
|
||||
events:
|
||||
# Optional: Number of seconds before the event to include (default: shown below)
|
||||
|
||||
@ -260,6 +260,20 @@ class RecordExportConfig(FrigateBaseModel):
|
||||
)
|
||||
|
||||
|
||||
class RecordQualityEnum(str, Enum):
|
||||
very_low = "very_low"
|
||||
low = "low"
|
||||
medium = "medium"
|
||||
high = "high"
|
||||
very_high = "very_high"
|
||||
|
||||
|
||||
class RecordPreviewConfig(FrigateBaseModel):
|
||||
quality: RecordQualityEnum = Field(
|
||||
default=RecordQualityEnum.medium, title="Quality of recording preview."
|
||||
)
|
||||
|
||||
|
||||
class RecordConfig(FrigateBaseModel):
|
||||
enabled: bool = Field(default=False, title="Enable record on all cameras.")
|
||||
sync_recordings: bool = Field(
|
||||
@ -278,6 +292,9 @@ class RecordConfig(FrigateBaseModel):
|
||||
export: RecordExportConfig = Field(
|
||||
default_factory=RecordExportConfig, title="Recording Export Config"
|
||||
)
|
||||
preview: RecordPreviewConfig = Field(
|
||||
default_factory=RecordPreviewConfig, title="Recording Preview Config"
|
||||
)
|
||||
enabled_in_config: Optional[bool] = Field(
|
||||
title="Keep track of original state of recording."
|
||||
)
|
||||
|
||||
@ -12,7 +12,7 @@ from pathlib import Path
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from frigate.config import CameraConfig
|
||||
from frigate.config import CameraConfig, RecordQualityEnum
|
||||
from frigate.const import CACHE_DIR, CLIPS_DIR, INSERT_PREVIEW
|
||||
from frigate.ffmpeg_presets import (
|
||||
FPS_VFR_PARAM,
|
||||
@ -29,6 +29,13 @@ PREVIEW_OUTPUT_FPS = 1
|
||||
PREVIEW_SEGMENT_DURATION = 3600 # one hour
|
||||
# important to have lower keyframe to maintain scrubbing performance
|
||||
PREVIEW_KEYFRAME_INTERVAL = 60
|
||||
PREVIEW_BIT_RATES = {
|
||||
RecordQualityEnum.very_low: 4096,
|
||||
RecordQualityEnum.low: 6144,
|
||||
RecordQualityEnum.medium: 8192,
|
||||
RecordQualityEnum.high: 12288,
|
||||
RecordQualityEnum.very_high: 16384,
|
||||
}
|
||||
|
||||
|
||||
def get_cache_image_name(camera: str, frame_time: float) -> str:
|
||||
@ -50,19 +57,19 @@ class FFMpegConverter(threading.Thread):
|
||||
):
|
||||
threading.Thread.__init__(self)
|
||||
self.name = f"{config.name}_preview_converter"
|
||||
self.camera = config.name
|
||||
self.config = config
|
||||
self.frame_times = frame_times
|
||||
self.inter_process_queue = inter_process_queue
|
||||
self.path = os.path.join(
|
||||
CLIPS_DIR,
|
||||
f"previews/{self.camera}/{self.frame_times[0]}-{self.frame_times[-1]}.mp4",
|
||||
f"previews/{self.config.name}/{self.frame_times[0]}-{self.frame_times[-1]}.mp4",
|
||||
)
|
||||
|
||||
# write a PREVIEW at fps and 1 key frame per clip
|
||||
self.ffmpeg_cmd = parse_preset_hardware_acceleration_encode(
|
||||
config.ffmpeg.hwaccel_args,
|
||||
input="-f concat -y -protocol_whitelist pipe,file -safe 0 -i /dev/stdin",
|
||||
output=f"-g {PREVIEW_KEYFRAME_INTERVAL} -fpsmax {PREVIEW_OUTPUT_FPS} -bf 0 -b:v 9820 {FPS_VFR_PARAM} -movflags +faststart -pix_fmt yuv420p {self.path}",
|
||||
output=f"-g {PREVIEW_KEYFRAME_INTERVAL} -fpsmax {PREVIEW_OUTPUT_FPS} -bf 0 -b:v {PREVIEW_BIT_RATES[self.config.record.preview]} {FPS_VFR_PARAM} -movflags +faststart -pix_fmt yuv420p {self.path}",
|
||||
type=EncodeTypeEnum.preview,
|
||||
)
|
||||
|
||||
@ -75,12 +82,12 @@ class FFMpegConverter(threading.Thread):
|
||||
if t_idx == item_count - 1:
|
||||
# last frame does not get a duration
|
||||
playlist.append(
|
||||
f"file '{get_cache_image_name(self.camera, self.frame_times[t_idx])}'"
|
||||
f"file '{get_cache_image_name(self.config.name, self.frame_times[t_idx])}'"
|
||||
)
|
||||
continue
|
||||
|
||||
playlist.append(
|
||||
f"file '{get_cache_image_name(self.camera, self.frame_times[t_idx])}'"
|
||||
f"file '{get_cache_image_name(self.config.name, self.frame_times[t_idx])}'"
|
||||
)
|
||||
playlist.append(
|
||||
f"duration {self.frame_times[t_idx + 1] - self.frame_times[t_idx]}"
|
||||
@ -102,8 +109,8 @@ class FFMpegConverter(threading.Thread):
|
||||
(
|
||||
INSERT_PREVIEW,
|
||||
{
|
||||
Previews.id: f"{self.camera}_{end}",
|
||||
Previews.camera: self.camera,
|
||||
Previews.id: f"{self.config.name}_{end}",
|
||||
Previews.camera: self.config.name,
|
||||
Previews.path: self.path,
|
||||
Previews.start_time: start,
|
||||
Previews.end_time: end,
|
||||
@ -112,12 +119,12 @@ class FFMpegConverter(threading.Thread):
|
||||
)
|
||||
)
|
||||
else:
|
||||
logger.error(f"Error saving preview for {self.camera} :: {p.stderr}")
|
||||
logger.error(f"Error saving preview for {self.config.name} :: {p.stderr}")
|
||||
|
||||
# unlink files from cache
|
||||
# don't delete last frame as it will be used as first frame in next segment
|
||||
for t in self.frame_times[0:-1]:
|
||||
Path(get_cache_image_name(self.camera, t)).unlink(missing_ok=True)
|
||||
Path(get_cache_image_name(self.config.name, t)).unlink(missing_ok=True)
|
||||
|
||||
|
||||
class PreviewRecorder:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user