# Raspberry Pi NDI Gateway **Work in Progress – erste öffentliche Veröffentlichung** Ein Installationsskript für einen Raspberry Pi 4 (ARM64) mit Raspberry Pi OS / Debian 13 „Trixie“. Das Projekt richtet einen headless NDI-Gateway mit GStreamer ein: - USB-HDMI-Capture → NDI - NDI → HDMI über DRM/KMS - optionales Capture-Audio → NDI Der aktuelle Stand ist ausdrücklich **nicht** als vollständig getestete oder produktionsreife Lösung zu verstehen. Siehe [Bekannte Einschränkungen](#bekannte-einschränkungen). ## Funktionsumfang des aktuellen Skripts | Bereich | Implementierter Stand | |---|---| | Betriebssystem | Debian 13 / Raspberry Pi OS Trixie, ARM64 | | GStreamer | Debian-/Raspberry-Pi-OS-Systempakete | | NDI-Plugin | `gst-plugin-ndi` aus `gst-plugins-rs`, standardmäßig Tag `0.14.5` | | NDI-Runtime | Vorhandene NDI-6-Runtime wird gesucht; andernfalls wird ein SDK-Archiv benötigt | | Capture-Video | Automatische oder manuelle V4L2-Geräteauswahl | | Capture-Formate | MJPEG und YUYV, sofern vom Gerät angeboten | | Capture-Audio | Optional; automatische oder manuelle ALSA-Geräteauswahl | | NDI-Empfang | NDI-Quelle per Name oder direkter IP:Port-Adresse | | HDMI-Video | Direkte Ausgabe über `kmssink` / DRM-KMS, ohne Desktop | | HDMI-Audio | **Noch nicht implementiert**; empfangenes NDI-Audio wird aktuell verworfen | | Dienste | `ndi-capture.service` und `ndi-display.service` | | Diagnose | `/opt/ndi-pi/bin/ndi-pi-diagnose` | ## Architektur ### Capture: USB-Capture → NDI ```text USB-HDMI-Capture ├── V4L2-Video │ ├── MJPEG → jpegdec ───────────┐ │ └── YUYV → videoconvert ───────┤ │ ▼ │ kurze Queue │ │ └── USB-Audio → ALSA → convert/resample │ ▼ ndisinkcombiner │ ndisink │ NDI OUT ``` Wenn kein passendes Capture-Audiogerät gefunden wird oder `ENABLE_AUDIO=false` gesetzt ist, wird eine Video-only-Pipeline gestartet. Die Audiozuordnung erfolgt anhand des USB-Gerätepfads; wenn keine eindeutige Übereinstimmung gefunden wird, kann derzeit ersatzweise das erste gefundene USB-Audiogerät gewählt werden. ### Display: NDI → HDMI ```text NDI IN │ ndisrc │ ndisrcdemux │ ├── Video → queue → videoscale → kmssink → HDMI │ └── Audio → queue → fakesink (wird verworfen) ``` Die Videoausgabe verwendet DRM/KMS und benötigt daher keine Desktop-Umgebung wie X11 oder Wayland. Wenn die Live-Pipeline beendet wird, versucht der Dienst einen schwarzen Standby-Bildschirm mit Statusanzeige zu starten und die NDI-Verbindung erneut aufzubauen. ## Voraussetzungen - Raspberry Pi 4 mit 64-Bit-ARM-System - Raspberry Pi OS / Debian 13 „Trixie“ - Netzwerkverbindung; Ethernet wird empfohlen - USB-HDMI-Capture-Gerät mit Linux-V4L2-Unterstützung - HDMI-Display am Pi für die lokale Ausgabe - NDI-kompatible Gegenstelle im Netzwerk - Root-/sudo-Rechte für die Installation Das Skript prüft Architektur und Debian-Codename. Andere Raspberry-Pi-Modelle, Betriebssystemversionen oder 32-Bit-Systeme sind nicht als unterstützt anzusehen. ## Installation 1. Repository auf dem Raspberry Pi klonen. 2. Ein offizielles NDI-SDK-Archiv lokal bereitstellen oder eine Download-URL angeben. 3. Installationsskript mit Root-Rechten ausführen. Beispiel mit lokal heruntergeladenem SDK-Archiv: ```bash sudo NDI_SDK_TARBALL=/pfad/zum/ndi-sdk.tar.gz ./install-trixie-ndi-pi-v18.sh ``` Alternativ kann die URL über die Umgebungsvariable übergeben werden: ```bash sudo NDI_SDK_URL="https://" ./install-trixie-ndi-pi-v18.sh ``` `` ist durch die aktuelle Download-URL des NDI-Herstellers zu ersetzen. Die URL ist absichtlich nicht im Skript fest hinterlegt. Wenn die NDI-6-Runtime bereits an einem der vom Skript geprüften Orte installiert ist, wird kein SDK-Archiv benötigt: - `/usr/local/lib` - `/usr/lib/aarch64-linux-gnu` Das Skript installiert Systemabhängigkeiten, baut das NDI-GStreamer-Plugin und legt Konfiguration, Hilfsskripte und systemd-Units an. Es aktiviert und startet anschließend beide Dienste. > **Hinweis:** Das Installationsskript sollte vor dem Einsatz geprüft werden. Es verändert Systempakete, installiert Bibliotheken und schreibt Dateien unter `/opt/ndi-pi`, `/etc` und `/etc/systemd/system`. ## Warum GStreamer aus den Distributionspaketen kommt GStreamer selbst wird nicht aus einem Git-Repository gebaut. Das Skript installiert die GStreamer-Komponenten aus den Debian-/Raspberry-Pi-OS-Paketen. Das zusätzliche NDI-Plugin wird separat aus `gst-plugins-rs` gebaut. Standardmäßig verwendet das Skript den Tag `0.14.5`: ```text Debian / Raspberry Pi OS GStreamer │ └── gst-plugin-ndi (gst-plugins-rs 0.14.5) │ └── NDI 6 Runtime ``` Der Plugin-Tag kann über `NDI_GST_TAG` überschrieben werden: ```bash sudo NDI_GST_TAG=0.14.5 NDI_SDK_TARBALL=/pfad/zum/ndi-sdk.tar.gz ./install-trixie-ndi-pi-v18.sh ``` ## Konfiguration Die zentrale Konfigurationsdatei lautet: ```text /etc/ndi-pi.conf ``` Eine neue Installation legt sie mit folgenden wesentlichen Standardwerten an: ```ini PI_NAME= NDI_CAPTURE_NAME=-Capture NDI_DISPLAY_SOURCE=PC01-Capture NDI_DISPLAY_ADDRESS=auto NDI_USE_DIRECT_ADDRESS=true CAPTURE_DEVICE=auto CAPTURE_WIDTH=1920 CAPTURE_HEIGHT=1080 CAPTURE_FPS=30 CAPTURE_FORMAT=MJPEG CAPTURE_AUTO_FALLBACK=true CAPTURE_QUEUE_BUFFERS=2 ENABLE_AUDIO=true CAPTURE_AUDIO_DEVICE=auto CAPTURE_AUDIO_RATE=48000 CAPTURE_AUDIO_CHANNELS=2 ENABLE_HDMI_AUDIO=false HDMI_AUDIO_DEVICE=auto KMS_DRIVER=vc4 KMS_CONNECTOR_ID=auto NDI_MAX_QUEUE=0 DISPLAY_WIDTH=1920 DISPLAY_HEIGHT=1080 DISPLAY_FPS=60 ``` `` steht hier nur als Platzhalter für den vom Installer ermittelten Hostnamen. Die tatsächliche Datei enthält konkrete Werte. ### Wichtige Einstellungen | Einstellung | Bedeutung | |---|---| | `NDI_CAPTURE_NAME` | Name der vom Pi gesendeten NDI-Quelle | | `NDI_DISPLAY_SOURCE` | Name der NDI-Quelle, die der Pi empfangen soll | | `NDI_DISPLAY_ADDRESS` | `auto` für mDNS-Auflösung oder feste Adresse im Format `IP:Port` | | `NDI_USE_DIRECT_ADDRESS` | Bei `true` wird die aufgelöste bzw. konfigurierte IP:Port-Adresse für `ndisrc` verwendet | | `CAPTURE_DEVICE` | `auto` oder ein konkreter V4L2-Gerätepfad | | `CAPTURE_WIDTH`, `CAPTURE_HEIGHT`, `CAPTURE_FPS` | Bevorzugter Capture-Modus | | `CAPTURE_FORMAT` | `MJPEG`, `YUYV` oder `auto` | | `CAPTURE_AUTO_FALLBACK` | Erlaubt die Suche nach einem anderen unterstützten Modus | | `CAPTURE_QUEUE_BUFFERS` | Anzahl der Videobuffer in der Capture-Queue | | `ENABLE_AUDIO` | Aktiviert/deaktiviert Capture-Audio → NDI | | `CAPTURE_AUDIO_DEVICE` | `auto` oder ALSA-Gerät, z. B. `plughw:2,0` | | `KMS_DRIVER` | DRM/KMS-Treiber, Standard `vc4` | | `KMS_CONNECTOR_ID` | `auto` oder eine feste DRM-Connector-ID | | `DISPLAY_WIDTH`, `DISPLAY_HEIGHT`, `DISPLAY_FPS` | Auflösung und Framerate des Standby-Bildes | | `NDI_MAX_QUEUE` | NDI-Empfangsqueue; Standardwert `0` | `ENABLE_HDMI_AUDIO` und `HDMI_AUDIO_DEVICE` sind derzeit vorbereitete Konfigurationswerte, werden von der aktuellen Display-Pipeline aber **noch nicht umgesetzt**. Sie einzuschalten aktiviert daher keine HDMI-Audioausgabe. Bestehende Konfigurationen werden vom Installer ergänzt, ohne vorhandene Werte generell zu überschreiben. Bei älteren Konfigurationsdateien sollten die benötigten Capture-Werte kontrolliert und gegebenenfalls ergänzt werden. ## Capture-Geräteauswahl und Videoformate Mit: ```ini CAPTURE_DEVICE=auto ``` untersucht das Skript `/dev/video*` und berücksichtigt Geräte mit V4L2-Video-Capture-Funktion und einem unterstützten Modus. Für eine feste Installation kann ein konkreter Pfad eingetragen werden, vorzugsweise ein stabiler Symlink: ```ini CAPTURE_DEVICE=/dev/v4l/by-id/ ``` Die Modusauswahl berücksichtigt die tatsächlich vom Gerät gemeldeten Formate. Die gewünschte Kombination aus Auflösung und Framerate wird zuerst versucht. Wenn `CAPTURE_AUTO_FALLBACK=true` gesetzt ist, folgt eine festgelegte Fallback-Liste. **Wichtig zur Formatpriorität:** Bei `CAPTURE_FORMAT=auto` ist die aktuelle Fallback-Reihenfolge YUYV vor MJPEG. Auch die Gerätebewertung bevorzugt YUYV. Das unterscheidet sich von einer MJPEG-first-Strategie. Bei einer neuen Installation steht `CAPTURE_FORMAT=MJPEG` als Standard in der Konfiguration; damit wird die Auswahl auf MJPEG beschränkt, sofern ein passender Modus vorhanden ist und ein Fallback nicht durch die Formatvorgabe ausgeschlossen wird. MJPEG wird mit `jpegdec` dekodiert. Der aktuelle Code verwendet bewusst nicht `v4l2jpegdec`. YUYV wird über `videoconvert` in UYVY konvertiert, bevor es an den NDI-Sink geht. ## Audio ### Capture-Audio → NDI Bei `ENABLE_AUDIO=true` versucht das Skript, ein ALSA-Gerät zu finden, das zum ausgewählten USB-Capture-Gerät gehört. Alternativ kann ein ALSA-Gerät fest vorgegeben werden: ```ini CAPTURE_AUDIO_DEVICE=plughw:2,0 ``` Die automatische Zuordnung ist heuristisch und sollte mit der konkreten Capture-Hardware überprüft werden. Wird kein Gerät gefunden, startet die Capture-Pipeline als Video-only. ### NDI-Audio → HDMI Diese Richtung ist im aktuellen Skript **noch nicht implementiert**. Der Audio-Pad von `ndisrcdemux` wird derzeit an `fakesink` angeschlossen und damit verworfen. Es gibt aktuell keine ALSA-Wiedergabepipeline für den NDI-Ton. ## NDI-Discovery und Netzwerk Der Display-Dienst verwendet standardmäßig: ```ini NDI_DISPLAY_SOURCE=PC01-Capture NDI_DISPLAY_ADDRESS=auto NDI_USE_DIRECT_ADDRESS=true ``` Bei `auto` versucht das Skript, den NDI-Dienst über Avahi/mDNS (`_ndi._tcp`) aufzulösen und die ermittelte IP-Adresse samt Port an `ndisrc` zu übergeben. Eine feste Adresse kann so angegeben werden: ```ini NDI_DISPLAY_ADDRESS=192.168.1.50:5961 ``` Der tatsächliche Port ist abhängig von der NDI-Quelle und muss passend ermittelt werden. Wenn keine direkte Adresse verfügbar ist, verwendet der initiale Start `ndi-name` als Fallback. Die Wiederverbindungslogik ist derzeit noch eingeschränkt; siehe [Bekannte Einschränkungen](#bekannte-einschränkungen). Für Discovery über VLAN- oder Subnetzgrenzen hinweg kann eine gesonderte NDI-Discovery-Server-Konfiguration erforderlich sein. Das wird vom Installationsskript nicht eingerichtet. ## systemd-Dienste und Logs Es werden zwei getrennte Dienste angelegt: ```text ndi-capture.service ndi-display.service ``` Status prüfen: ```bash systemctl status ndi-capture systemctl status ndi-display ``` Logs verfolgen: ```bash journalctl -u ndi-capture -f journalctl -u ndi-display -f ``` Beide Dienste verwenden `Restart=always` und aktuell `RestartSec=1`. Nach einer Änderung an `/etc/ndi-pi.conf`: ```bash sudo systemctl restart ndi-capture ndi-display ``` ## Diagnose Das Diagnoseprogramm liegt unter: ```bash sudo /opt/ndi-pi/bin/ndi-pi-diagnose ``` Es sammelt unter anderem: - Kernel- und Betriebssysteminformationen - GStreamer-Version und ausgewählte Plugin-Informationen - V4L2-Geräte und gemeldete Capture-Formate - ALSA-Aufnahme- und Wiedergabegeräte - DRM-Geräte und Connector-Status - NDI-Konfiguration und NDI/mDNS-Discovery - systemd-Status und letzte Service-Logs Für Fehlerberichte ist die vollständige Ausgabe hilfreich. Zugangsdaten oder andere private Netzwerkinformationen sollten vor dem Veröffentlichen entfernt werden. ## Bekannte Einschränkungen Der folgende Abschnitt beschreibt bewusst den Stand des aktuellen Skripts und ist Teil der WIP-Kennzeichnung. 1. **NDI-Audio wird am HDMI-Ausgang nicht wiedergegeben.** Der Empfangs-Audiozweig endet aktuell in `fakesink`. 2. **Automatische USB-Audiozuordnung ist nicht garantiert.** Wenn die Zuordnung zum Video-Gerät nicht gelingt, kann ein anderes erstes USB-Audiogerät als Fallback ausgewählt werden. 3. **Wiederverbindung nach NDI-Ausfall ist eingeschränkt.** Die Standby-Schleife prüft eine direkte IP:Port-Adresse. Wenn nur der Namens-Fallback aktiv ist oder mDNS die Quelle nicht auflösen kann, wird die Quelle nicht in allen Fällen erneut gefunden. 4. **Signalbehandlung des Display-Helfers muss weiter getestet werden.** Das aktuelle Trap räumt den Standby-Prozess auf; das Verhalten beim Beenden durch systemd sollte auf dem Zielgerät überprüft werden. 5. **Ältere Konfigurationsdateien benötigen Prüfung.** Die Migration ergänzt einige Schlüssel, aber nicht alle möglichen Capture-Parameter. Fehlende Werte wie `CAPTURE_WIDTH` oder `CAPTURE_FPS` können bei älteren Konfigurationen problematisch sein. 6. **NDI-Runtime-Pfad in systemd:** Der Installer erkennt die Runtime auch unter `/usr/lib/aarch64-linux-gnu`; die erzeugten systemd-Units setzen aktuell jedoch fest `NDI_RUNTIME_DIR_V6=/usr/local/lib`. Bei einer Runtime-Installation im Multiarch-Verzeichnis muss das geprüft werden. 7. **Keine vollständige Hardware-/Laufzeitvalidierung durch Syntaxprüfung:** Ein erfolgreicher Bash-Syntaxcheck bestätigt weder GStreamer-Pipeline-Negotiation noch NDI-Discovery, DRM/KMS-Ausgabe oder das Verhalten einer konkreten Capture-Karte. Weitere bekannte Entwicklungsaufgaben sind eine umfassendere Plugin-Prüfung, robustere Installer-Rollback-Strategien und eine Prüfung des SDK-Archivs anhand eines verifizierten Hashes bzw. einer Signatur. ## Sicherheit und Lizenzhinweise Das NDI SDK bzw. die NDI-Runtime wird vom NDI-Hersteller bereitgestellt und unterliegt dessen Lizenz- und Distributionsbedingungen. Das SDK-Archiv und die Runtime-Bibliotheken sollten nicht ohne Prüfung dieser Bedingungen in ein öffentliches Repository eingecheckt oder weiterverteilt werden. Das Repository enthält das Installationsskript und die Projektdateien; Nutzer müssen die NDI-Runtime entsprechend den geltenden Herstellerbedingungen selbst beziehen bzw. bereitstellen. Das Installationsskript entpackt ein bereitgestelltes SDK-Archiv mit Root-Rechten. Verwende nur ein vertrauenswürdiges Archiv aus einer offiziellen Quelle. Eine automatisierte Hash-/Signaturprüfung des Archivs ist im aktuellen Skript noch nicht implementiert. ## Projektstatus Dieses Projekt wird aktiv entwickelt. Hardwarekombinationen, Capture-Modi, NDI-Versionen und HDMI-Ausgabeverhalten können sich unterscheiden. Rückmeldungen mit Diagnoseausgabe, Pi-Modell, OS-Version und genauer Capture-Hardware sind willkommen. **Vor einem produktiven Einsatz sind Tests mit der eigenen Hardware und Netzwerkinfrastruktur erforderlich.**