mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-01-22 20:18:30 +03:00
Add CPU fallback
This commit is contained in:
parent
c2d09f4029
commit
100c7165ee
@ -42,3 +42,8 @@ class ExportRecordingsCustomBody(BaseModel):
|
||||
title="FFmpeg output arguments",
|
||||
description="Custom FFmpeg output arguments. If not provided, defaults to timelapse output args.",
|
||||
)
|
||||
cpu_fallback: bool = Field(
|
||||
default=False,
|
||||
title="CPU Fallback",
|
||||
description="If true, retry export without hardware acceleration if the initial export fails.",
|
||||
)
|
||||
|
||||
@ -484,6 +484,7 @@ def export_recording_custom(
|
||||
existing_image = sanitize_filepath(body.image_path) if body.image_path else None
|
||||
ffmpeg_input_args = body.ffmpeg_input_args
|
||||
ffmpeg_output_args = body.ffmpeg_output_args
|
||||
cpu_fallback = body.cpu_fallback
|
||||
|
||||
export_case_id = body.export_case_id
|
||||
if export_case_id is not None:
|
||||
@ -569,6 +570,7 @@ def export_recording_custom(
|
||||
export_case_id,
|
||||
ffmpeg_input_args,
|
||||
ffmpeg_output_args,
|
||||
cpu_fallback,
|
||||
)
|
||||
exporter.start()
|
||||
return JSONResponse(
|
||||
|
||||
@ -62,6 +62,7 @@ class RecordingExporter(threading.Thread):
|
||||
export_case_id: Optional[str] = None,
|
||||
ffmpeg_input_args: Optional[str] = None,
|
||||
ffmpeg_output_args: Optional[str] = None,
|
||||
cpu_fallback: bool = False,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.config = config
|
||||
@ -75,6 +76,7 @@ class RecordingExporter(threading.Thread):
|
||||
self.export_case_id = export_case_id
|
||||
self.ffmpeg_input_args = ffmpeg_input_args
|
||||
self.ffmpeg_output_args = ffmpeg_output_args
|
||||
self.cpu_fallback = cpu_fallback
|
||||
|
||||
# ensure export thumb dir
|
||||
Path(os.path.join(CLIPS_DIR, "export")).mkdir(exist_ok=True)
|
||||
@ -179,7 +181,9 @@ class RecordingExporter(threading.Thread):
|
||||
|
||||
return thumb_path
|
||||
|
||||
def get_record_export_command(self, video_path: str) -> list[str]:
|
||||
def get_record_export_command(
|
||||
self, video_path: str, use_hwaccel: bool = True
|
||||
) -> list[str]:
|
||||
if (self.end_time - self.start_time) <= MAX_PLAYLIST_SECONDS:
|
||||
playlist_lines = f"http://127.0.0.1:5000/vod/{self.camera}/start/{self.start_time}/end/{self.end_time}/index.m3u8"
|
||||
ffmpeg_input = (
|
||||
@ -219,10 +223,15 @@ class RecordingExporter(threading.Thread):
|
||||
ffmpeg_input = "-y -protocol_whitelist pipe,file,http,tcp -f concat -safe 0 -i /dev/stdin"
|
||||
|
||||
if self.ffmpeg_input_args is not None and self.ffmpeg_output_args is not None:
|
||||
hwaccel_args = (
|
||||
self.config.cameras[self.camera].record.export.hwaccel_args
|
||||
if use_hwaccel
|
||||
else None
|
||||
)
|
||||
ffmpeg_cmd = (
|
||||
parse_preset_hardware_acceleration_encode(
|
||||
self.config.ffmpeg.ffmpeg_path,
|
||||
self.config.cameras[self.camera].record.export.hwaccel_args,
|
||||
hwaccel_args,
|
||||
f"{self.ffmpeg_input_args} -an {ffmpeg_input}".strip(),
|
||||
f"{self.ffmpeg_output_args} -movflags +faststart".strip(),
|
||||
EncodeTypeEnum.timelapse,
|
||||
@ -241,7 +250,9 @@ class RecordingExporter(threading.Thread):
|
||||
|
||||
return ffmpeg_cmd, playlist_lines
|
||||
|
||||
def get_preview_export_command(self, video_path: str) -> list[str]:
|
||||
def get_preview_export_command(
|
||||
self, video_path: str, use_hwaccel: bool = True
|
||||
) -> list[str]:
|
||||
playlist_lines = []
|
||||
codec = "-c copy"
|
||||
|
||||
@ -310,10 +321,15 @@ class RecordingExporter(threading.Thread):
|
||||
)
|
||||
|
||||
if self.ffmpeg_input_args is not None and self.ffmpeg_output_args is not None:
|
||||
hwaccel_args = (
|
||||
self.config.cameras[self.camera].record.export.hwaccel_args
|
||||
if use_hwaccel
|
||||
else None
|
||||
)
|
||||
ffmpeg_cmd = (
|
||||
parse_preset_hardware_acceleration_encode(
|
||||
self.config.ffmpeg.ffmpeg_path,
|
||||
self.config.cameras[self.camera].record.export.hwaccel_args,
|
||||
hwaccel_args,
|
||||
f"{self.ffmpeg_input_args} {TIMELAPSE_DATA_INPUT_ARGS} {ffmpeg_input}".strip(),
|
||||
f"{self.ffmpeg_output_args} -movflags +faststart {video_path}".strip(),
|
||||
EncodeTypeEnum.timelapse,
|
||||
@ -379,6 +395,34 @@ class RecordingExporter(threading.Thread):
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
# If export failed and cpu_fallback is enabled, retry without hwaccel
|
||||
if (
|
||||
p.returncode != 0
|
||||
and self.cpu_fallback
|
||||
and self.ffmpeg_input_args is not None
|
||||
and self.ffmpeg_output_args is not None
|
||||
):
|
||||
logger.warning(
|
||||
f"Export with hardware acceleration failed, retrying without hwaccel for {self.export_id}"
|
||||
)
|
||||
|
||||
if self.playback_source == PlaybackSourceEnum.recordings:
|
||||
ffmpeg_cmd, playlist_lines = self.get_record_export_command(
|
||||
video_path, use_hwaccel=False
|
||||
)
|
||||
else:
|
||||
ffmpeg_cmd, playlist_lines = self.get_preview_export_command(
|
||||
video_path, use_hwaccel=False
|
||||
)
|
||||
|
||||
p = sp.run(
|
||||
ffmpeg_cmd,
|
||||
input="\n".join(playlist_lines),
|
||||
encoding="ascii",
|
||||
preexec_fn=lower_priority,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
if p.returncode != 0:
|
||||
logger.error(
|
||||
f"Failed to export {self.playback_source.value} for command {' '.join(ffmpeg_cmd)}"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user