Add grace period for recording segment checks to prevent spurious ffmpeg restarts

This commit is contained in:
Josh Hawkins 2026-02-08 07:03:35 -06:00
parent 1fec95f88e
commit fe3677c7df

View File

@ -214,6 +214,7 @@ class CameraWatchdog(threading.Thread):
self.latest_valid_segment_time: float = 0 self.latest_valid_segment_time: float = 0
self.latest_invalid_segment_time: float = 0 self.latest_invalid_segment_time: float = 0
self.latest_cache_segment_time: float = 0 self.latest_cache_segment_time: float = 0
self.record_enable_time: datetime | None = None
def _update_enabled_state(self) -> bool: def _update_enabled_state(self) -> bool:
"""Fetch the latest config and update enabled state.""" """Fetch the latest config and update enabled state."""
@ -261,6 +262,9 @@ class CameraWatchdog(threading.Thread):
def run(self) -> None: def run(self) -> None:
if self._update_enabled_state(): if self._update_enabled_state():
self.start_all_ffmpeg() self.start_all_ffmpeg()
# If recording is enabled at startup, set the grace period timer
if self.config.record.enabled:
self.record_enable_time = datetime.now().astimezone(timezone.utc)
time.sleep(self.sleeptime) time.sleep(self.sleeptime)
while not self.stop_event.wait(self.sleeptime): while not self.stop_event.wait(self.sleeptime):
@ -270,13 +274,15 @@ class CameraWatchdog(threading.Thread):
self.logger.debug(f"Enabling camera {self.config.name}") self.logger.debug(f"Enabling camera {self.config.name}")
self.start_all_ffmpeg() self.start_all_ffmpeg()
# reset all timestamps # reset all timestamps and record the enable time for grace period
self.latest_valid_segment_time = 0 self.latest_valid_segment_time = 0
self.latest_invalid_segment_time = 0 self.latest_invalid_segment_time = 0
self.latest_cache_segment_time = 0 self.latest_cache_segment_time = 0
self.record_enable_time = datetime.now().astimezone(timezone.utc)
else: else:
self.logger.debug(f"Disabling camera {self.config.name}") self.logger.debug(f"Disabling camera {self.config.name}")
self.stop_all_ffmpeg() self.stop_all_ffmpeg()
self.record_enable_time = None
# update camera status # update camera status
self.requestor.send_data( self.requestor.send_data(
@ -361,6 +367,12 @@ class CameraWatchdog(threading.Thread):
if self.config.record.enabled and "record" in p["roles"]: if self.config.record.enabled and "record" in p["roles"]:
now_utc = datetime.now().astimezone(timezone.utc) now_utc = datetime.now().astimezone(timezone.utc)
# Check if we're within the grace period after enabling recording
# Grace period: 90 seconds allows time for ffmpeg to start and create first segment
in_grace_period = self.record_enable_time is not None and (
now_utc - self.record_enable_time
) < timedelta(seconds=90)
latest_cache_dt = ( latest_cache_dt = (
datetime.fromtimestamp( datetime.fromtimestamp(
self.latest_cache_segment_time, tz=timezone.utc self.latest_cache_segment_time, tz=timezone.utc
@ -386,10 +398,16 @@ class CameraWatchdog(threading.Thread):
) )
# ensure segments are still being created and that they have valid video data # ensure segments are still being created and that they have valid video data
cache_stale = now_utc > (latest_cache_dt + timedelta(seconds=120)) # Skip checks during grace period to allow segments to start being created
valid_stale = now_utc > (latest_valid_dt + timedelta(seconds=120)) cache_stale = not in_grace_period and now_utc > (
latest_cache_dt + timedelta(seconds=120)
)
valid_stale = not in_grace_period and now_utc > (
latest_valid_dt + timedelta(seconds=120)
)
invalid_stale_condition = ( invalid_stale_condition = (
self.latest_invalid_segment_time > 0 self.latest_invalid_segment_time > 0
and not in_grace_period
and now_utc > (latest_invalid_dt + timedelta(seconds=120)) and now_utc > (latest_invalid_dt + timedelta(seconds=120))
and self.latest_valid_segment_time and self.latest_valid_segment_time
<= self.latest_invalid_segment_time <= self.latest_invalid_segment_time