make audio maintainer respond to dynamic config updates

This commit is contained in:
Josh Hawkins 2026-05-11 15:54:30 -05:00
parent 9731f8687f
commit f391406a94
2 changed files with 39 additions and 25 deletions

View File

@ -428,18 +428,11 @@ class FrigateApp:
self.camera_maintainer.start() self.camera_maintainer.start()
def start_audio_processor(self) -> None: def start_audio_processor(self) -> None:
audio_cameras = [ self.audio_process = AudioProcessor(
c self.config, self.camera_metrics, self.stop_event
for c in self.config.cameras.values() )
if c.enabled and c.audio.enabled_in_config self.audio_process.start()
] self.processes["audio_detector"] = self.audio_process.pid or 0
if audio_cameras:
self.audio_process = AudioProcessor(
self.config, audio_cameras, self.camera_metrics, self.stop_event
)
self.audio_process.start()
self.processes["audio_detector"] = self.audio_process.pid or 0
def start_timeline_processor(self) -> None: def start_timeline_processor(self) -> None:
self.timeline_processor = TimelineProcessor( self.timeline_processor = TimelineProcessor(

View File

@ -84,7 +84,6 @@ class AudioProcessor(FrigateProcess):
def __init__( def __init__(
self, self,
config: FrigateConfig, config: FrigateConfig,
cameras: list[CameraConfig],
camera_metrics: DictProxy, camera_metrics: DictProxy,
stop_event: MpEvent, stop_event: MpEvent,
): ):
@ -93,12 +92,11 @@ class AudioProcessor(FrigateProcess):
) )
self.camera_metrics = camera_metrics self.camera_metrics = camera_metrics
self.cameras = cameras
self.config = config self.config = config
def run(self) -> None: def run(self) -> None:
self.pre_run_setup(self.config.logger) self.pre_run_setup(self.config.logger)
audio_threads: list[AudioEventMaintainer] = [] audio_threads: dict[str, AudioEventMaintainer] = {}
threading.current_thread().name = "process:audio_manager" threading.current_thread().name = "process:audio_manager"
@ -112,32 +110,55 @@ class AudioProcessor(FrigateProcess):
else: else:
self.transcription_model_runner = None self.transcription_model_runner = None
if len(self.cameras) == 0: config_subscriber = CameraConfigUpdateSubscriber(
return self.config,
self.config.cameras,
[
CameraConfigUpdateEnum.add,
CameraConfigUpdateEnum.audio,
CameraConfigUpdateEnum.ffmpeg,
],
)
for camera in self.cameras: def spawn_if_needed(camera: CameraConfig) -> None:
audio_thread = AudioEventMaintainer( if camera.name in audio_threads:
return
if not camera.enabled or not camera.audio.enabled:
return
# ffmpeg update may not have arrived yet; wait for next poll
if not any("audio" in i.roles for i in camera.ffmpeg.inputs):
return
thread = AudioEventMaintainer(
camera, camera,
self.config, self.config,
self.camera_metrics, self.camera_metrics,
self.transcription_model_runner, self.transcription_model_runner,
self.stop_event, # type: ignore[arg-type] self.stop_event, # type: ignore[arg-type]
) )
audio_threads.append(audio_thread) audio_threads[camera.name] = thread
audio_thread.start() thread.start()
self.logger.info(f"Audio maintainer started for {camera.name}")
for camera in self.config.cameras.values():
spawn_if_needed(camera)
self.logger.info(f"Audio processor started (pid: {self.pid})") self.logger.info(f"Audio processor started (pid: {self.pid})")
while not self.stop_event.wait(): # poll for newly added cameras or cameras flipped to audio.enabled at runtime
pass while not self.stop_event.wait(timeout=1.0):
config_subscriber.check_for_updates()
for camera in self.config.cameras.values():
spawn_if_needed(camera)
for thread in audio_threads: config_subscriber.stop()
for thread in audio_threads.values():
thread.join(1) thread.join(1)
if thread.is_alive(): if thread.is_alive():
self.logger.info(f"Waiting for thread {thread.name:s} to exit") self.logger.info(f"Waiting for thread {thread.name:s} to exit")
thread.join(10) thread.join(10)
for thread in audio_threads: for thread in audio_threads.values():
if thread.is_alive(): if thread.is_alive():
self.logger.warning(f"Thread {thread.name} is still alive") self.logger.warning(f"Thread {thread.name} is still alive")