Trasforma una webcam USB collegata a un Raspberry Pi 4 in una sorgente NDI|HX vera (non NDI standard/SpeedHQ), usando l'encoder H.264 hardware del Pi4. Nato per alimentare un RØDECaster Video, che accetta solo NDI|HX2+ certificato — ma qualunque receiver NDI|HX dovrebbe andare bene.
webcam USB (MJPEG/YUYV) --ffmpeg--> H.264 (encoder hardware bcm2835-codec) --ndi_hx_send--> NDI|HX
I tool "NDI to webcam" comuni mandano NDI standard (compresso internamente in SpeedHQ dalla libreria), che è molto più pesante in banda e non è quello che un device NDI|HX-only si aspetta. Per fare vero NDI|HX serve mandare pacchetti H.264/HEVC già compressi usando le API di basso livello dell'NDI Advanced SDK — capacità non documentata benissimo, e con un paio di trabocchetti non ovvi (vedi sotto). Questo repo è il risultato di quella sessione di debugging, in modo che chi ci si scontra dopo non debba rifarla da zero.
Raspberry Pi 4 (serve l'encoder H.264 hardware
bcm2835-codec, esposto da ffmpeg comeh264_v4l2m2m) — su altro hardware serve adattarecapture_cam_hx.shUna webcam USB che parli MJPEG o YUYV via V4L2 (
v4l2-ctl --list-formats-ext)ffmpegcon supportoh264_v4l2m2m(di serie su Raspberry Pi OS / Ubuntu per Pi recenti)NDI Advanced SDK — non incluso in questo repo (proprietario Vizrt/NDI, licenza d'uso gratuita ma non ridistribuibile). Scaricalo da ndi.video (richiede una registrazione gratuita), estrallo, e passa il path a
build_hx.sh/build_inspect.shviaNDI_SDK_DIRse non lo metti in./ndi-adv-sdkaccanto agli script.Nota: la versione liberamente scaricabile dell'SDK è marcata "development use" e si autolimita a stream di 30 minuti — per uso continuativo/commerciale serve contattare
licensing@ndi.video. Nei test descritti sotto non abbiamo mai raggiunto quel limite.
./build_hx.sh # -> ndi_hx_send (il sender)
./build_inspect.sh # -> ndi_hx_inspect (tool diagnostico, opzionale)./capture_cam_hx.sh <device><larghezza><altezza><fps><nome_ndi> [bitrate] [gop_frame] [profilo] [livello]
# esempio:
./capture_cam_hx.sh /dev/video0 1920 1080 30 "Pi4 Cam HX" 8M 2Per farlo partire al boot, copia ndi-webcam-send.service in /etc/systemd/system/
(sostituendo i placeholder <user> e il path della tua webcam), poi:
sudo systemctl daemon-reload
sudo systemctl enable --now ndi-webcam-send.service./ndi_hx_inspect "<nome sorgente NDI>"Si collega come receiver NDI in modalità COMPRESSED e stampa la struttura reale dei pacchetti
ricevuti (FourCC, keyframe, dimensioni, extra_data). Utile sia per debuggare il proprio sender
sia per ispezionare come si comporta una sorgente HX certificata reale (es. l'app RODE Capture
su smartphone) e confrontarla.
Il sintomo di partenza, su un receiver certificato reale (RØDECaster Video): connessione NDI
stabilita normalmente, tally funzionante, ma video rifiutato con
unsupported, resolution 0x0, frame rate 23, format N/A. Un tool di verifica scritto in casa
(che si limita a leggere i byte senza decodificarli davvero) può sembrare che tutto funzioni
anche quando il contenuto è semanticamente sbagliato — motivo per cui questi bug sono
sopravvissuti a lungo ai test locali.
FourCC
*_lowest_bandwidthnon è "lo stesso video a bitrate più basso": per specifica NDI è riservato a un secondo stream di anteprima a risoluzione fissa 640px. Per il flusso principale a piena risoluzione va sempre usato*_highest_bandwidth, qualunque sia la risoluzione reale che stai inviando. (Inizialmente sembrava un problema di licenza dell'SDK, dato che i frame taggatilowest_bandwidthnon arrivavano mai a nessun receiver — la causa vera è proprio che l'SDK li scarta se non rispettano il vincolo dei 640px.)La causa reale del "resolution 0x0 / format N/A": i dati video e l'extra_data (SPS/PPS) vanno in formato Annex-B (start code
00 00 00 01), non AVCC/length-prefixed (lo stile MP4/avcC). La documentazione ufficiale lo dice esplicitamente, ma è facile non trovarla e assumere AVCC per abitudine.NDIlib_video_frame_v2_t.picture_aspect_rationon può essere 0 per stream compressi — va messo il rapporto reale (es.(float)width / height). Con 0 un receiver strict può leggere dimensioni non valide.frame.timecodedeve essere il PTS reale del pacchetto, non il sentinelNDIlib_send_timecode_synthesize.L'encoder hardware del Pi4 usa di default H.264 High Profile. Impostarlo su un profilo più compatibile (es. Main) con
v4l2-ctl --set-ctrlprima di avviare ffmpeg non ha alcun effetto: i device V4L2 mem2mem sono stateless, ogniopen()(compreso quello di ffmpeg) crea un'istanza indipendente con i default hardware. Il profilo va passato a ffmpeg stesso, come valore numerico (-profile:v 77per Main,100per High — i nomi stringa tipo"main"non sono accettati dah264_v4l2m2m).h264_v4l2m2mignora-gda solo: servono keyframe forzati esplicitamente con-force_key_frames "expr:eq(mod(n,GOP),0)". Nella config qui il GOP è tenuto molto corto (un keyframe ogni 2 frame, ~66ms) perché l'SDK NDI segnala a runtime che per essere "NDI|HX compliant" un I-frame deve arrivare entro 100ms da quando un receiver si connette.
- Il Pi4 non ha encoder HEVC hardware (solo decode) — quindi qui si fa NDI|HX2 (H.264), non HX3/HEVC. HX3 via software encoding sarebbe probabilmente troppo pesante per il Pi4 in tempo reale.
- Testato con una singola webcam USB MJPEG 1920x1080@30 e un RØDECaster Video. Altre combinazioni webcam/receiver potrebbero avere ulteriori sorprese — issue e PR benvenute.
Questo codice è rilasciato sotto licenza MIT (vedi LICENSE). L'NDI Advanced SDK necessario
per compilarlo non è incluso ed è soggetto alla licenza propria di Vizrt/NDI
(vedi ndi.video).