From db485ddafa692f4cd84736805fc78327e0052914 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:48:08 -0600 Subject: [PATCH] remove frame extraction logic --- frigate/api/media.py | 3 +- frigate/output/preview.py | 154 ++++++++++---------------------------- 2 files changed, 39 insertions(+), 118 deletions(-) diff --git a/frigate/api/media.py b/frigate/api/media.py index 45f320647..8b7a7da16 100644 --- a/frigate/api/media.py +++ b/frigate/api/media.py @@ -165,9 +165,8 @@ async def latest_frame( frame_processor.get_current_frame_time(camera_name) + retry_interval ): last_frame_time = frame_processor.get_current_frame_time(camera_name) - config: FrigateConfig = request.app.frigate_config preview_path = get_most_recent_preview_frame( - camera_name, before=last_frame_time, ffmpeg_config=config.ffmpeg + camera_name, before=last_frame_time ) if preview_path: diff --git a/frigate/output/preview.py b/frigate/output/preview.py index 1493c1b73..f16bb3bd7 100644 --- a/frigate/output/preview.py +++ b/frigate/output/preview.py @@ -8,14 +8,13 @@ import subprocess as sp import threading import time from pathlib import Path -from typing import Any, Optional +from typing import Any import cv2 import numpy as np -from peewee import DoesNotExist from frigate.comms.inter_process import InterProcessRequestor -from frigate.config import CameraConfig, FfmpegConfig, RecordQualityEnum +from frigate.config import CameraConfig, RecordQualityEnum from frigate.const import CACHE_DIR, CLIPS_DIR, INSERT_PREVIEW, PREVIEW_FRAME_TYPE from frigate.ffmpeg_presets import ( FPS_VFR_PARAM, @@ -24,12 +23,7 @@ from frigate.ffmpeg_presets import ( ) from frigate.models import Previews from frigate.track.object_processing import TrackedObject -from frigate.util.image import ( - copy_yuv_to_position, - get_blank_yuv_frame, - get_yuv_crop, - run_ffmpeg_snapshot, -) +from frigate.util.image import copy_yuv_to_position, get_blank_yuv_frame, get_yuv_crop logger = logging.getLogger(__name__) @@ -63,121 +57,49 @@ def get_cache_image_name(camera: str, frame_time: float) -> str: ) -def get_most_recent_preview_frame( - camera: str, before: float = None, ffmpeg_config: Optional[FfmpegConfig] = None -) -> str | None: - """Get the most recent preview frame for a camera. - - First tries to find a cached preview webp frame. If none exists (e.g., they've been - cleaned up after preview video creation), attempts to extract the last frame from - the most recent preview video. - - Args: - camera: Camera name - before: Optional timestamp to find frames before this time - ffmpeg_config: Optional ffmpeg configuration for extracting from video fallback - - Returns: - Path to a preview frame image file, or None if not available - """ +def get_most_recent_preview_frame(camera: str, before: float = None) -> str | None: + """Get the most recent preview frame for a camera.""" if not os.path.exists(PREVIEW_CACHE_DIR): - logger.debug(f"Preview cache directory does not exist: {PREVIEW_CACHE_DIR}") - else: - try: - # files are named preview_{camera}-{timestamp}.webp - # we want the largest timestamp that is less than or equal to before - preview_files = [ - f - for f in os.listdir(PREVIEW_CACHE_DIR) - if f.startswith(f"preview_{camera}-") - and f.endswith(f".{PREVIEW_FRAME_TYPE}") - ] + return None - if preview_files: - # sort by timestamp in descending order - # filenames are like preview_front-1712345678.901234.webp - preview_files.sort(reverse=True) + try: + # files are named preview_{camera}-{timestamp}.webp + # we want the largest timestamp that is less than or equal to before + preview_files = [ + f + for f in os.listdir(PREVIEW_CACHE_DIR) + if f.startswith(f"preview_{camera}-") + and f.endswith(f".{PREVIEW_FRAME_TYPE}") + ] - if before is None: - return os.path.join(PREVIEW_CACHE_DIR, preview_files[0]) + if not preview_files: + return None - for file_name in preview_files: - try: - # Extract timestamp: preview_front-1712345678.901234.webp - # Split by dash and extension - timestamp_part = file_name.split("-")[-1].split( - f".{PREVIEW_FRAME_TYPE}" - )[0] - timestamp = float(timestamp_part) + # sort by timestamp in descending order + # filenames are like preview_front-1712345678.901234.webp + preview_files.sort(reverse=True) - if timestamp <= before: - return os.path.join(PREVIEW_CACHE_DIR, file_name) - except (ValueError, IndexError): - continue - except Exception as e: - logger.error(f"Error searching for cached preview frames: {e}") + if before is None: + return os.path.join(PREVIEW_CACHE_DIR, preview_files[0]) - # If no cached frames exist and ffmpeg_config is provided, try extracting from preview video - if ffmpeg_config is not None: - try: - logger.debug( - f"No cached preview frames found for {camera}, attempting to extract from preview video" - ) + for file_name in preview_files: + try: + # Extract timestamp: preview_front-1712345678.901234.webp + # Split by dash and extension + timestamp_part = file_name.split("-")[-1].split( + f".{PREVIEW_FRAME_TYPE}" + )[0] + timestamp = float(timestamp_part) - query = Previews.select().where(Previews.camera == camera) + if timestamp <= before: + return os.path.join(PREVIEW_CACHE_DIR, file_name) + except (ValueError, IndexError): + continue - if before is not None: - query = query.where(Previews.end_time <= before) - - preview = query.order_by(Previews.end_time.desc()).limit(1).get() - - if preview and os.path.exists(preview.path): - logger.debug( - f"Found preview video for {camera}: {preview.path} (duration: {preview.duration}s)" - ) - - # Extract the last frame from the video - # Use webp for consistency with cached frames - image_data, error_msg = run_ffmpeg_snapshot( - ffmpeg_config, - preview.path, - codec="webp", - seek_time=max(0, preview.duration - 0.1), # Seek to near the end - height=PREVIEW_HEIGHT, - timeout=10, - ) - - if image_data: - # Write to a temporary file in the cache directory - # Use timestamp from the preview end time - temp_frame_path = os.path.join( - PREVIEW_CACHE_DIR, - f"preview_{camera}-{preview.end_time}_from_video.{PREVIEW_FRAME_TYPE}", - ) - - with open(temp_frame_path, "wb") as f: - f.write(image_data) - - logger.debug( - f"Successfully extracted last frame from preview video to {temp_frame_path}" - ) - return temp_frame_path - else: - logger.warning( - f"Failed to extract frame from preview video {preview.path}: {error_msg}" - ) - else: - if preview: - logger.warning(f"Preview video path does not exist: {preview.path}") - else: - logger.debug(f"No preview video found in database for {camera}") - - except DoesNotExist: - logger.debug(f"No preview video found in database for {camera}") - except Exception as e: - logger.error(f"Error extracting frame from preview video: {e}") - - return None + return None + except Exception as e: + logger.error(f"Error searching for most recent preview frame: {e}") + return None class FFMpegConverter(threading.Thread):