# Architektur ## Überblick ``` Technik-Netz Musubi (MacBook, Regie) Ausgänge ────────────── ─────────────────────────── ───────────────────── ZowieBox MV ─NDI|HX3─▶ NDIKit ──▶ VideoOutput ───────────────▶ Regie-Bildschirm ZowieBox PGM ─NDI|HX3─▶ │ └──▶ AudioMatrix ──────────────────▶ MOTU M2 Out 1/2 → Ui12 Headset-Mikro ─────────────────▶ AudioMatrix ATEM ─────────Status───▶ Tally ──▶ Intercom ◀──WebRTC──▶ Handy im OP (Klinik-Netz) MusubiCore (Szenen) ─Bonjour─▶ Mac mini (Knoten, Bühne) ``` Der Mac mini empfängt NDI selbst direkt aus dem Technik-Netz und wird von der Regie nur gesteuert. ## Module | Modul | Aufgabe | Technik | |---|---|---| | MusubiCore | Modelle (Quelle, Ausgang, Route, Szene), Persistenz, Einstellungen | Swift, Codable/JSON, Swift Testing | | NDIKit | Quellen finden, empfangen (Video+Audio oder nur Audio), Statistik | NDI SDK (C), `NDIlib_find_*`, `NDIlib_recv_*`, Bandbreite `audio_only` für reinen Ton | | VideoOutput | Frames darstellen, ein Vollbildfenster je `NSScreen`, Anzeigemodus 50 Hz | Metal oder `AVSampleBufferDisplayLayer`, `CGDisplayCopyAllDisplayModes` | | AudioMatrix | Quellen → Ausgangskanäle, Gain, Mute, Pegel, Sprechtaste | Core Audio / `AVAudioEngine`, Kanalzuordnung (`channelMap`) | | Remote | Regie ↔ Knoten: Szene setzen, Status melden | Bonjour (`NWListener`/`NWBrowser`), JSON-Nachrichten | | Intercom | Web-Seite, Signalisierung, Audio hin/zurück, PGM-Vorschau | eingebetteter HTTPS-Server, WebRTC (Opus, H.264 via VideoToolbox), Bindung an Schnittstelle | | Tally | PGM/Vorschau-Eingänge des ATEM lesen | Blackmagic ATEM Switchers SDK | ## Datenmodell (Entwurf) ```swift struct Source: Codable, Hashable { var ndiName: String; var label: String } enum Output: Codable, Hashable { case screen(displayID: UInt32), audioChannel(deviceUID: String, channel: Int) } struct Route: Codable, Hashable { var source: Source; var output: Output; var mode: Mode enum Mode: String, Codable { case videoAndAudio, videoOnly, audioOnly } } struct Scene: Codable, Identifiable { var id: UUID; var name: String; var role: Role; var routes: [Route] } enum Role: String, Codable { case control, node } ``` ## Threads - NDI-Empfang je Quelle auf eigener Queue. - Video: Frames ohne Kopie an die Darstellung (CVPixelBuffer), Rendern im Takt des Displays. - Audio: Ringpuffer zwischen NDI-Empfang und Render-Callback, keine Allokationen im Callback, Drift-Ausgleich (Resampling) zwischen NDI-Takt und Interface-Takt. ## Netzwerk-Bindung - NDI, Bonjour, Remote: nur Technik-Schnittstelle. - Intercom: nur Klinik-Schnittstelle (oder TURN-Relay nach außen). - Schnittstellen in den Einstellungen per Name wählbar (z. B. `en7` Technik, `en0` Klinik).