Add ability to configure preview quality

This commit is contained in:
Nick Mowen 2023-11-29 13:28:25 -07:00
parent 32e30853fb
commit e5e42aa659
3 changed files with 39 additions and 10 deletions

View File

@ -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)

View File

@ -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."
)

View File

@ -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: