mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-05 10:45:21 +03:00
Add ability to export frigate clips
This commit is contained in:
parent
d1701e127e
commit
63d95fa1e2
@ -4,6 +4,7 @@ MODEL_CACHE_DIR = f"{CONFIG_DIR}/model_cache"
|
|||||||
BASE_DIR = "/media/frigate"
|
BASE_DIR = "/media/frigate"
|
||||||
CLIPS_DIR = f"{BASE_DIR}/clips"
|
CLIPS_DIR = f"{BASE_DIR}/clips"
|
||||||
RECORD_DIR = f"{BASE_DIR}/recordings"
|
RECORD_DIR = f"{BASE_DIR}/recordings"
|
||||||
|
EXPORT_DIR = f"{BASE_DIR}/exports"
|
||||||
BIRDSEYE_PIPE = "/tmp/cache/birdseye"
|
BIRDSEYE_PIPE = "/tmp/cache/birdseye"
|
||||||
CACHE_DIR = "/tmp/cache"
|
CACHE_DIR = "/tmp/cache"
|
||||||
YAML_EXT = (".yaml", ".yml")
|
YAML_EXT = (".yaml", ".yml")
|
||||||
@ -28,3 +29,4 @@ DRIVER_INTEL_iHD = "iHD"
|
|||||||
|
|
||||||
MAX_SEGMENT_DURATION = 600
|
MAX_SEGMENT_DURATION = 600
|
||||||
SECONDS_IN_DAY = 60 * 60 * 24
|
SECONDS_IN_DAY = 60 * 60 * 24
|
||||||
|
MAX_PLAYLIST_SECONDS = 43200 # only support 12 hours per HLS playlist
|
||||||
|
|||||||
96
frigate/record/export.py
Normal file
96
frigate/record/export.py
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
"""Export recordings to storage."""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import threading
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
import subprocess as sp
|
||||||
|
|
||||||
|
from frigate.const import EXPORT_DIR, MAX_PLAYLIST_SECONDS
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class PlaybackFactorEnum(str, Enum):
|
||||||
|
real_time = "real_time"
|
||||||
|
timelapse_5x = "timelapse_5x"
|
||||||
|
|
||||||
|
|
||||||
|
class RecordingExporter(threading.Thread):
|
||||||
|
"""Exports a specific set of recordings for a camera to storage as a single file."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
camera: str,
|
||||||
|
start_time: int,
|
||||||
|
end_time: int,
|
||||||
|
playback_factor: PlaybackFactorEnum,
|
||||||
|
) -> None:
|
||||||
|
self.camera = camera
|
||||||
|
self.start_time = start_time
|
||||||
|
self.end_time = end_time
|
||||||
|
self.playback_factor = playback_factor
|
||||||
|
|
||||||
|
def get_datetime_from_timestamp(self, timestamp: int) -> str:
|
||||||
|
"""Convenience fun to get a simple date time from timestamp."""
|
||||||
|
return datetime.datetime.fromtimestamp(timestamp).strftime("%Y/%m/%d %I:%M")
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
logger.debug(
|
||||||
|
f"Beginning export for {self.camera} from {self.start_time} to {self.end_time}"
|
||||||
|
)
|
||||||
|
file_name = f"{EXPORT_DIR}/in_progress.{self.camera}_{self.get_datetime_from_timestamp(self.start_time)}-{self.get_datetime_from_timestamp(self.end_time)}.mp4"
|
||||||
|
final_file_name = f"{EXPORT_DIR}/{self.camera}_{self.get_datetime_from_timestamp(self.start_time)}-{self.get_datetime_from_timestamp(self.end_time)}.mp4"
|
||||||
|
|
||||||
|
if (self.end_time - self.start_time) <= MAX_PLAYLIST_SECONDS:
|
||||||
|
playlist_lines = f"http://127.0.0.1:5000/vod/start/{self.start_time}/end/{self.end_time}/index.m3u8"
|
||||||
|
ffmpeg_cmd = [
|
||||||
|
"ffmpeg",
|
||||||
|
"-i",
|
||||||
|
"/dev/stdin",
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
playlist_lines = []
|
||||||
|
playlist_start = self.start_time
|
||||||
|
|
||||||
|
while playlist_start < self.end_time:
|
||||||
|
playlist_lines.append(
|
||||||
|
f"file http://127.0.0.1:5000/vod/start/{playlist_start}/end/{min(playlist_start + MAX_PLAYLIST_SECONDS, self.end_time)}/index.m3u8"
|
||||||
|
)
|
||||||
|
playlist_start += MAX_PLAYLIST_SECONDS
|
||||||
|
|
||||||
|
ffmpeg_cmd = [
|
||||||
|
"ffmpeg",
|
||||||
|
"-y",
|
||||||
|
"-protocol_whitelist",
|
||||||
|
"pipe,file",
|
||||||
|
"-f",
|
||||||
|
"concat",
|
||||||
|
"-safe",
|
||||||
|
"0",
|
||||||
|
"-i",
|
||||||
|
"/dev/stdin",
|
||||||
|
]
|
||||||
|
|
||||||
|
if self.playback_factor == PlaybackFactorEnum.real_time:
|
||||||
|
ffmpeg_cmd.extend(["-c", "copy", file_name])
|
||||||
|
elif self.playback_factor == PlaybackFactorEnum.timelapse_5x:
|
||||||
|
ffmpeg_cmd.extend(["-vf", "setpts=0.25*PTS", "-r", "5", "-an", file_name])
|
||||||
|
|
||||||
|
p = sp.run(
|
||||||
|
ffmpeg_cmd,
|
||||||
|
input="\n".join(playlist_lines)
|
||||||
|
if len(playlist_lines) > 1
|
||||||
|
else playlist_lines,
|
||||||
|
encoding="ascii",
|
||||||
|
capture_output=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if p.returncode != 0:
|
||||||
|
logger.error(p.stderr)
|
||||||
|
return
|
||||||
|
|
||||||
|
os.rename(file_name, final_file_name)
|
||||||
|
logger.debug("Finished exporting")
|
||||||
Loading…
Reference in New Issue
Block a user