Offload preview encoding and Plus upload off the API event loop (#23552)

* offload preview ffmpeg encoding to a thread to avoid blocking the api event loop

* offload Frigate+ recording snapshot upload to a thread to avoid blocking the api event loop
This commit is contained in:
Josh Hawkins 2026-06-24 07:17:23 -05:00 committed by GitHub
parent b3ce4486b9
commit 4e5e8e3c59
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -380,7 +380,9 @@ async def submit_recording_snapshot_to_plus(
) )
nd = cv2.imdecode(np.frombuffer(image_data, dtype=np.int8), cv2.IMREAD_COLOR) nd = cv2.imdecode(np.frombuffer(image_data, dtype=np.int8), cv2.IMREAD_COLOR)
request.app.frigate_config.plus_api.upload_image(nd, camera_name) await asyncio.to_thread(
request.app.frigate_config.plus_api.upload_image, nd, camera_name
)
return JSONResponse( return JSONResponse(
content={ content={
@ -1517,14 +1519,14 @@ async def event_preview(request: Request, event_id: str):
end_ts = start_ts + ( end_ts = start_ts + (
min(event.end_time - event.start_time, 20) if event.end_time else 20 min(event.end_time - event.start_time, 20) if event.end_time else 20
) )
return preview_gif(request, event.camera, start_ts, end_ts) return await preview_gif(request, event.camera, start_ts, end_ts)
@router.get( @router.get(
"/{camera_name}/start/{start_ts}/end/{end_ts}/preview.gif", "/{camera_name}/start/{start_ts}/end/{end_ts}/preview.gif",
dependencies=[Depends(require_camera_access)], dependencies=[Depends(require_camera_access)],
) )
def preview_gif( async def preview_gif(
request: Request, request: Request,
camera_name: str, camera_name: str,
start_ts: float, start_ts: float,
@ -1587,7 +1589,8 @@ def preview_gif(
"-", "-",
] ]
process = sp.run( process = await asyncio.to_thread(
sp.run,
ffmpeg_cmd, ffmpeg_cmd,
capture_output=True, capture_output=True,
) )
@ -1654,7 +1657,8 @@ def preview_gif(
"-", "-",
] ]
process = sp.run( process = await asyncio.to_thread(
sp.run,
ffmpeg_cmd, ffmpeg_cmd,
input=str.encode("\n".join(selected_previews)), input=str.encode("\n".join(selected_previews)),
capture_output=True, capture_output=True,
@ -1683,7 +1687,7 @@ def preview_gif(
"/{camera_name}/start/{start_ts}/end/{end_ts}/preview.mp4", "/{camera_name}/start/{start_ts}/end/{end_ts}/preview.mp4",
dependencies=[Depends(require_camera_access)], dependencies=[Depends(require_camera_access)],
) )
def preview_mp4( async def preview_mp4(
request: Request, request: Request,
camera_name: str, camera_name: str,
start_ts: float, start_ts: float,
@ -1763,7 +1767,8 @@ def preview_mp4(
path, path,
] ]
process = sp.run( process = await asyncio.to_thread(
sp.run,
ffmpeg_cmd, ffmpeg_cmd,
capture_output=True, capture_output=True,
) )
@ -1827,7 +1832,8 @@ def preview_mp4(
path, path,
] ]
process = sp.run( process = await asyncio.to_thread(
sp.run,
ffmpeg_cmd, ffmpeg_cmd,
input=str.encode("\n".join(selected_previews)), input=str.encode("\n".join(selected_previews)),
capture_output=True, capture_output=True,
@ -1880,9 +1886,9 @@ async def review_preview(
) )
if format == "gif": if format == "gif":
return preview_gif(request, review.camera, start_ts, end_ts) return await preview_gif(request, review.camera, start_ts, end_ts)
else: else:
return preview_mp4(request, review.camera, start_ts, end_ts) return await preview_mp4(request, review.camera, start_ts, end_ts)
@router.get( @router.get(