forked from blakeblackshear/frigate
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add API and WebUI to export recordings (blakeblackshear#6550)
* Add ability to export frigate clips * Add http endpoint * Add dir to nginx * Add webUI * Formatting * Cleanup unused * Optimize timelapse * Fix pts * Use JSON body for params * Use hwaccel to encode when available * Print ffmpeg command when fail * Print ffmpeg command when fail * Add separate ffmpeg preset for timelapse * Add docs outlining the export directory * Add export docs * Use '' * Fix playlist max time * Lower max playlist time * Add api docs for export * isort fixes
- Loading branch information
Showing
13 changed files
with
288 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
"""Export recordings to storage.""" | ||
|
||
import datetime | ||
import logging | ||
import os | ||
import subprocess as sp | ||
import threading | ||
from enum import Enum | ||
|
||
from frigate.config import FrigateConfig | ||
from frigate.const import EXPORT_DIR, MAX_PLAYLIST_SECONDS | ||
from frigate.ffmpeg_presets import ( | ||
EncodeTypeEnum, | ||
parse_preset_hardware_acceleration_encode, | ||
) | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class PlaybackFactorEnum(str, Enum): | ||
realtime = "realtime" | ||
timelapse_25x = "timelapse_25x" | ||
|
||
|
||
class RecordingExporter(threading.Thread): | ||
"""Exports a specific set of recordings for a camera to storage as a single file.""" | ||
|
||
def __init__( | ||
self, | ||
config: FrigateConfig, | ||
camera: str, | ||
start_time: int, | ||
end_time: int, | ||
playback_factor: PlaybackFactorEnum, | ||
) -> None: | ||
threading.Thread.__init__(self) | ||
self.config = config | ||
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"https://127.0.0.1:5000/vod/{self.camera}/start/{self.start_time}/end/{self.end_time}/index.m3u8" | ||
ffmpeg_input = ( | ||
f"-y -protocol_whitelist pipe,file,http,tcp -i {playlist_lines}" | ||
) | ||
else: | ||
playlist_lines = [] | ||
playlist_start = self.start_time | ||
|
||
while playlist_start < self.end_time: | ||
playlist_lines.append( | ||
f"file 'https://127.0.0.1:5000/vod/{self.camera}/start/{playlist_start}/end/{min(playlist_start + MAX_PLAYLIST_SECONDS, self.end_time)}/index.m3u8'" | ||
) | ||
playlist_start += MAX_PLAYLIST_SECONDS | ||
|
||
ffmpeg_input = "-y -protocol_whitelist pipe,file,http,tcp -f concat -safe 0 -i /dev/stdin" | ||
|
||
if self.playback_factor == PlaybackFactorEnum.realtime: | ||
ffmpeg_cmd = ( | ||
f"ffmpeg -hide_banner {ffmpeg_input} -c copy {file_name}" | ||
).split(" ") | ||
elif self.playback_factor == PlaybackFactorEnum.timelapse_25x: | ||
ffmpeg_cmd = ( | ||
parse_preset_hardware_acceleration_encode( | ||
self.config.ffmpeg.hwaccel_args, | ||
ffmpeg_input, | ||
f"-vf setpts=0.04*PTS -r 30 -an {file_name}", | ||
EncodeTypeEnum.timelapse, | ||
) | ||
).split(" ") | ||
|
||
p = sp.run( | ||
ffmpeg_cmd, | ||
input="\n".join(playlist_lines), | ||
encoding="ascii", | ||
capture_output=True, | ||
) | ||
|
||
if p.returncode != 0: | ||
logger.error( | ||
f"Failed to export recording for command {' '.join(ffmpeg_cmd)}" | ||
) | ||
logger.error(p.stderr) | ||
return | ||
|
||
logger.debug(f"Updating finalized export {file_name}") | ||
os.rename(file_name, final_file_name) | ||
logger.debug(f"Finished exporting {file_name}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.