remove frame extraction logic

This commit is contained in:
Josh Hawkins 2026-01-22 10:48:08 -06:00
parent a6c5f4a82b
commit db485ddafa
2 changed files with 39 additions and 118 deletions

View File

@ -165,9 +165,8 @@ async def latest_frame(
frame_processor.get_current_frame_time(camera_name) + retry_interval frame_processor.get_current_frame_time(camera_name) + retry_interval
): ):
last_frame_time = frame_processor.get_current_frame_time(camera_name) last_frame_time = frame_processor.get_current_frame_time(camera_name)
config: FrigateConfig = request.app.frigate_config
preview_path = get_most_recent_preview_frame( 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: if preview_path:

View File

@ -8,14 +8,13 @@ import subprocess as sp
import threading import threading
import time import time
from pathlib import Path from pathlib import Path
from typing import Any, Optional from typing import Any
import cv2 import cv2
import numpy as np import numpy as np
from peewee import DoesNotExist
from frigate.comms.inter_process import InterProcessRequestor 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.const import CACHE_DIR, CLIPS_DIR, INSERT_PREVIEW, PREVIEW_FRAME_TYPE
from frigate.ffmpeg_presets import ( from frigate.ffmpeg_presets import (
FPS_VFR_PARAM, FPS_VFR_PARAM,
@ -24,12 +23,7 @@ from frigate.ffmpeg_presets import (
) )
from frigate.models import Previews from frigate.models import Previews
from frigate.track.object_processing import TrackedObject from frigate.track.object_processing import TrackedObject
from frigate.util.image import ( from frigate.util.image import copy_yuv_to_position, get_blank_yuv_frame, get_yuv_crop
copy_yuv_to_position,
get_blank_yuv_frame,
get_yuv_crop,
run_ffmpeg_snapshot,
)
logger = logging.getLogger(__name__) 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( def get_most_recent_preview_frame(camera: str, before: float = None) -> str | None:
camera: str, before: float = None, ffmpeg_config: Optional[FfmpegConfig] = None """Get the most recent preview frame for a camera."""
) -> 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
"""
if not os.path.exists(PREVIEW_CACHE_DIR): if not os.path.exists(PREVIEW_CACHE_DIR):
logger.debug(f"Preview cache directory does not exist: {PREVIEW_CACHE_DIR}") return None
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}")
]
if preview_files: try:
# sort by timestamp in descending order # files are named preview_{camera}-{timestamp}.webp
# filenames are like preview_front-1712345678.901234.webp # we want the largest timestamp that is less than or equal to before
preview_files.sort(reverse=True) 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: if not preview_files:
return os.path.join(PREVIEW_CACHE_DIR, preview_files[0]) return None
for file_name in preview_files: # sort by timestamp in descending order
try: # filenames are like preview_front-1712345678.901234.webp
# Extract timestamp: preview_front-1712345678.901234.webp preview_files.sort(reverse=True)
# Split by dash and extension
timestamp_part = file_name.split("-")[-1].split(
f".{PREVIEW_FRAME_TYPE}"
)[0]
timestamp = float(timestamp_part)
if timestamp <= before: if before is None:
return os.path.join(PREVIEW_CACHE_DIR, file_name) return os.path.join(PREVIEW_CACHE_DIR, preview_files[0])
except (ValueError, IndexError):
continue
except Exception as e:
logger.error(f"Error searching for cached preview frames: {e}")
# If no cached frames exist and ffmpeg_config is provided, try extracting from preview video for file_name in preview_files:
if ffmpeg_config is not None: try:
try: # Extract timestamp: preview_front-1712345678.901234.webp
logger.debug( # Split by dash and extension
f"No cached preview frames found for {camera}, attempting to extract from preview video" 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: return None
query = query.where(Previews.end_time <= before) except Exception as e:
logger.error(f"Error searching for most recent preview frame: {e}")
preview = query.order_by(Previews.end_time.desc()).limit(1).get() return None
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
class FFMpegConverter(threading.Thread): class FFMpegConverter(threading.Thread):