namespace TeamsISO.Engine.Pipeline; /// /// Tap point for per-ISO output recording. Each can be /// wired with one recorder; when present, every that /// flows from to is also fed /// to the recorder for persistence to disk. /// /// Lifecycle: /// 1. is called once when the pipeline starts (or restarts). /// 2. is called for every processed frame, in order. /// 3. is called once when the pipeline stops or fails. /// /// Implementations must be tolerant of out-of-order calls (Close before Open, /// double Close, WriteFrame after Close) — the supervisor's restart logic can /// race in unusual ways. The simplest correct implementation is to track an /// _isOpen flag and short-circuit when not open. /// public interface IRecorderSink : IAsyncDisposable { /// /// Open the underlying file/encoder and prepare to receive frames. Width/Height /// match the pipeline's normalized output resolution; FPS is the target framerate /// (incoming frames may arrive with timing jitter, but the recorder writes at the /// nominal rate for downstream playback consistency). /// /// Used to derive the output filename. /// Directory under which the recording is created. /// Frame width in pixels. /// Frame height in pixels. /// Nominal framerate. void Open(string participantDisplayName, string outputDirectory, int width, int height, double fps); /// /// Write one processed frame. Implementations should not block — if encoding is /// expensive, queue the frame to a worker thread and return promptly. Returning /// false means the recorder dropped the frame (disk full, queue overflow); the /// pipeline carries on regardless so a recorder failure never kills the live ISO. /// bool WriteFrame(ProcessedFrame frame); /// /// Flush and finalize the output. Idempotent. /// void Close(); /// True between successful and . bool IsRecording { get; } /// /// Drop a timestamped marker into the recording. Used by the operator to /// chapter a recording in real time — "host intro starts here", "guest /// answer", etc. — so post-production can jump to the right moment without /// scrubbing through the raw stream. The label is free-form; an empty /// label means "unnamed marker." No-op when not recording. /// void AddMarker(string label); }