mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-24 00:58:22 +03:00
Merge pull request #41 from ibs0d/codex/modify-preview-storage-logic-for-multi-path
Write preview mp4s to camera recordings path and add preview dir helper
This commit is contained in:
commit
38d6052aa4
@ -100,7 +100,9 @@ class OutputProcess(FrigateProcess):
|
|||||||
jsmpeg_cameras[camera] = JsmpegCamera(
|
jsmpeg_cameras[camera] = JsmpegCamera(
|
||||||
camera_config, self.stop_event, websocket_server
|
camera_config, self.stop_event, websocket_server
|
||||||
)
|
)
|
||||||
preview_recorders[camera] = PreviewRecorder(camera_config)
|
preview_recorders[camera] = PreviewRecorder(
|
||||||
|
camera_config, self.config.get_camera_recordings_path(camera)
|
||||||
|
)
|
||||||
preview_write_times[camera] = 0
|
preview_write_times[camera] = 0
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import numpy as np
|
|||||||
|
|
||||||
from frigate.comms.inter_process import InterProcessRequestor
|
from frigate.comms.inter_process import InterProcessRequestor
|
||||||
from frigate.config import CameraConfig, RecordQualityEnum
|
from frigate.config import CameraConfig, RecordQualityEnum
|
||||||
from frigate.const import CACHE_DIR, CLIPS_DIR, INSERT_PREVIEW, PREVIEW_FRAME_TYPE
|
from frigate.const import CACHE_DIR, INSERT_PREVIEW, PREVIEW_FRAME_TYPE
|
||||||
from frigate.ffmpeg_presets import (
|
from frigate.ffmpeg_presets import (
|
||||||
FPS_VFR_PARAM,
|
FPS_VFR_PARAM,
|
||||||
EncodeTypeEnum,
|
EncodeTypeEnum,
|
||||||
@ -58,6 +58,11 @@ PREVIEW_QMAX_PARAM = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_preview_dir(recordings_root: str, camera: str) -> str:
|
||||||
|
"""Get the output directory for generated preview mp4 files."""
|
||||||
|
return os.path.join(recordings_root, "preview", camera)
|
||||||
|
|
||||||
|
|
||||||
def get_cache_image_name(camera: str, frame_time: float) -> str:
|
def get_cache_image_name(camera: str, frame_time: float) -> str:
|
||||||
"""Get the image name in cache."""
|
"""Get the image name in cache."""
|
||||||
return os.path.join(
|
return os.path.join(
|
||||||
@ -119,14 +124,15 @@ class FFMpegConverter(threading.Thread):
|
|||||||
config: CameraConfig,
|
config: CameraConfig,
|
||||||
frame_times: list[float],
|
frame_times: list[float],
|
||||||
requestor: InterProcessRequestor,
|
requestor: InterProcessRequestor,
|
||||||
|
preview_dir: str,
|
||||||
):
|
):
|
||||||
super().__init__(name=f"{config.name}_preview_converter")
|
super().__init__(name=f"{config.name}_preview_converter")
|
||||||
self.config = config
|
self.config = config
|
||||||
self.frame_times = frame_times
|
self.frame_times = frame_times
|
||||||
self.requestor = requestor
|
self.requestor = requestor
|
||||||
self.path = os.path.join(
|
self.path = os.path.join(
|
||||||
CLIPS_DIR,
|
preview_dir,
|
||||||
f"previews/{self.config.name}/{self.frame_times[0]}-{self.frame_times[-1]}.mp4",
|
f"{self.frame_times[0]}-{self.frame_times[-1]}.mp4",
|
||||||
)
|
)
|
||||||
|
|
||||||
# write a PREVIEW at fps and 1 key frame per clip
|
# write a PREVIEW at fps and 1 key frame per clip
|
||||||
@ -203,12 +209,13 @@ class FFMpegConverter(threading.Thread):
|
|||||||
|
|
||||||
|
|
||||||
class PreviewRecorder:
|
class PreviewRecorder:
|
||||||
def __init__(self, config: CameraConfig) -> None:
|
def __init__(self, config: CameraConfig, recordings_root: str) -> None:
|
||||||
self.config = config
|
self.config = config
|
||||||
self.start_time = 0
|
self.start_time = 0
|
||||||
self.last_output_time = 0
|
self.last_output_time = 0
|
||||||
self.offline = False
|
self.offline = False
|
||||||
self.output_frames = []
|
self.output_frames = []
|
||||||
|
self.preview_dir = get_preview_dir(recordings_root, config.name)
|
||||||
|
|
||||||
if config.detect.width > config.detect.height:
|
if config.detect.width > config.detect.height:
|
||||||
self.out_height = PREVIEW_HEIGHT
|
self.out_height = PREVIEW_HEIGHT
|
||||||
@ -254,9 +261,7 @@ class PreviewRecorder:
|
|||||||
)
|
)
|
||||||
|
|
||||||
Path(PREVIEW_CACHE_DIR).mkdir(exist_ok=True)
|
Path(PREVIEW_CACHE_DIR).mkdir(exist_ok=True)
|
||||||
Path(os.path.join(CLIPS_DIR, f"previews/{config.name}")).mkdir(
|
Path(self.preview_dir).mkdir(parents=True, exist_ok=True)
|
||||||
parents=True, exist_ok=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# check for existing items in cache
|
# check for existing items in cache
|
||||||
start_ts = (
|
start_ts = (
|
||||||
@ -393,6 +398,7 @@ class PreviewRecorder:
|
|||||||
self.config,
|
self.config,
|
||||||
self.output_frames,
|
self.output_frames,
|
||||||
self.requestor,
|
self.requestor,
|
||||||
|
self.preview_dir,
|
||||||
).start()
|
).start()
|
||||||
else:
|
else:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
@ -442,6 +448,7 @@ class PreviewRecorder:
|
|||||||
self.config,
|
self.config,
|
||||||
self.output_frames,
|
self.output_frames,
|
||||||
self.requestor,
|
self.requestor,
|
||||||
|
self.preview_dir,
|
||||||
).start()
|
).start()
|
||||||
|
|
||||||
self.reset_frame_cache(frame_time)
|
self.reset_frame_cache(frame_time)
|
||||||
|
|||||||
@ -1,11 +1,18 @@
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import unittest
|
import unittest
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
|
||||||
|
# Allow importing preview module in environments without opencv installed
|
||||||
|
sys.modules.setdefault("cv2", types.SimpleNamespace())
|
||||||
|
sys.modules.setdefault("numpy", types.SimpleNamespace(ndarray=object))
|
||||||
|
|
||||||
from frigate.output.preview import (
|
from frigate.output.preview import (
|
||||||
PREVIEW_CACHE_DIR,
|
PREVIEW_CACHE_DIR,
|
||||||
PREVIEW_FRAME_TYPE,
|
PREVIEW_FRAME_TYPE,
|
||||||
get_most_recent_preview_frame,
|
get_most_recent_preview_frame,
|
||||||
|
get_preview_dir,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -78,3 +85,15 @@ class TestPreviewLoader(unittest.TestCase):
|
|||||||
def test_get_most_recent_preview_frame_no_directory(self):
|
def test_get_most_recent_preview_frame_no_directory(self):
|
||||||
shutil.rmtree(PREVIEW_CACHE_DIR)
|
shutil.rmtree(PREVIEW_CACHE_DIR)
|
||||||
self.assertIsNone(get_most_recent_preview_frame("test_camera"))
|
self.assertIsNone(get_most_recent_preview_frame("test_camera"))
|
||||||
|
|
||||||
|
def test_get_preview_dir_with_explicit_camera_path(self):
|
||||||
|
self.assertEqual(
|
||||||
|
get_preview_dir("/video2", "front"),
|
||||||
|
"/video2/preview/front",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_get_preview_dir_with_default_recordings_path(self):
|
||||||
|
self.assertEqual(
|
||||||
|
get_preview_dir("/media/frigate/recordings", "front"),
|
||||||
|
"/media/frigate/recordings/preview/front",
|
||||||
|
)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user