from __future__ import annotations from abc import ABC, abstractmethod from dataclasses import dataclass from app.config.models import InfinityMirrorConfig from app.core.types import PreviewFrame @dataclass class OutputResult: ok: bool message: str = "" packets_sent: int = 0 device_count: int = 0 @dataclass(frozen=True) class ControllerMetrics: fps: float | None = None live_devices: int = 0 sampled_devices: int = 0 total_devices: int = 0 source: str = "" @dataclass(frozen=True) class OutputDiagnostics: backend_id: str backend_name: str output_enabled: bool worker_running: bool target_fps: float send_fps: float = 0.0 last_send_time_ms: float = 0.0 frames_submitted: int = 0 frames_sent: int = 0 stale_frame_drops: int = 0 send_failures: int = 0 packets_last_frame: int = 0 devices_last_frame: int = 0 packets_sent_total: int = 0 last_message: str = "" send_budget_misses: int = 0 last_schedule_slip_ms: float = 0.0 controller_fps: float | None = None controller_live_devices: int = 0 controller_sampled_devices: int = 0 controller_total_devices: int = 0 controller_source: str = "" class OutputBackend(ABC): backend_id: str display_name: str supports_live_output: bool = False def start(self) -> None: """Hook for backends that need to allocate resources.""" def stop(self) -> None: """Hook for backends that need to release resources.""" def controller_metrics(self, config: InfinityMirrorConfig) -> ControllerMetrics | None: """Optional hook for backends that can report controller-side receive metrics.""" return None @abstractmethod def send_frame(self, config: InfinityMirrorConfig, frame: PreviewFrame) -> OutputResult: raise NotImplementedError