Muxer — Statistical Multiplex

TSDuck-based MPEG Transport Stream multiplexer with full PSI/SI support

Multiplexer MPTS e Adaptive Hybrid Statmux

Il mux di Stream Manager assembla più stream SPTS in un unico output MPTS CBR (DVB/IPTV). L'Adaptive Hybrid Statmux combina un riempimento core CBR con encoder VBR bilanciati per un output mux costante.

1. Livello encoder (FFmpeg per stream)

Quando il mux padre ha statmux abilitato, ogni encoder FFmpeg esegue VBR con tetto: -b:v <avg> -minrate <min> -maxrate <max> -bufsize <max×2>. Buffer VBV = 2× max fornisce ~2 s di margine per i picchi. Gli stream statmux ottengono anche un offset GOP deterministico (-force_key_frames) per stream_id così i picchi I-frame non coincidono tra i canali.

2. Livello Mux (pipeline tsp TSDuck)

tsp -I null (∞ pacchetti NULL @ line rate) → -P merge tsp -I ip <stream> (sovrappone i pacchetti reali) → -P continuity (allarme drop) → -P pcradjust --bitrate X (riscrive i PCR) → -P pcrverify --jitter-max 500µs → -P bitrate_monitor (allarme rate ±5%) → -P regulate --bitrate X --packet-burst 32 → -O ip <out> --packet-burst 7 --enforce-burst.

3. Livello output (UDP)

Il plugin regulate emette esattamente mux.bitrate bps sul wire. Se gli input sono in ritardo → pacchetti NULL riempiono il gap. Se gli input sono in picco → il buffer FIFO interno (4096 pacchetti ≈ 0.3 s @ 19 Mbps) assorbe. --enforce-burst forza 7 pacchetti TS per datagramma UDP (1316 B, standard IPTV).

Dimensionamento consigliato (Σ min / avg / max vs mux.bitrate)

La barra superiore del mux mostra 3 somme con stato colore. Regole:

Σ avg ≤ 85 % mux.bitrate — somma del target medio su tutti i canali più l'audio. È il 'budget' del mux. Il 15 % di riserva statistica protegge dalla coincidenza dei picchi. Verde ≤ 85 %, ambra ≤ 100 %, rosso > 100 %.

Σ max ≤ 160 % mux.bitrate — somma tetto picco. Consentito superare la capacità (questo è il punto dello statmux — i picchi raramente coincidono). Verde ≤ 160 %, ambra ≤ 200 %, rosso > 200 % (drop su picco sostenuto).

Σ min ≤ 50 % mux.bitrate — somma floor encoder. Un floor sicuro garantisce che su scene statiche l'encoder non scenda a qualità inutilmente bassa. Verde ≤ 50 %, ambra ≤ 90 %, rosso > 90 %.

Esempio: 4 canali HD in un mux DVB-T da 19 Mbps

Budget disponibile dopo PSI/SI/EIT: ~18.5 Mbps. Σ avg = 4 × 4.0 = 16 Mbps (86 % — ambra al limite, abbassare ulteriormente). Σ max = 4 × 6.0 = 24 Mbps (130 % — verde, OK). Σ min = 4 × 3.0 = 12 Mbps (65 % — ambra, OK). Target: avg ~85 % del budget, max ≤ 160 %.

Diagnostica e allarmi

Con ≥ 10 allarmi in 60s appare una notifica flash nell'UI del Mux che raccomanda una riduzione del bitrate. Il back-off automatico NON è attivo — l'operatore decide.

-P bitrate_monitor --periodic 60 --min/max ±5% — allarme se la frequenza effettiva si discosta oltre ±5 % dal target. Significa che regulate è a corto di input (input morto).

-P continuity — traccia i contatori di continuità TS. Registra un avviso ad ogni drop (sovra-sottoscrizione, crash FFmpeg, jitter NIC).

-P pcrverify --jitter-max 500000 — DVB consente jitter PCR ≤ 500 µs. Allarme = pcradjust non riesce a stare al passo o la sorgente ha PCR interrotti.

Glitch e loro cause

Coincidenza GOP — tutti gli encoder emettono I-frame simultaneamente, coincidenza di picco. Fix: offset GOP (già attivo per statmux, hash(stream_id) mod gop_size).

Perdita segnale input — singolo canale va in nero, il mux continua (grazie alla base -I null). Durata ~3-8 s per il riavvio FFmpeg.

Sovra-sottoscrizione (picchi sostenuti > capacità) — blocchi/audio stutter ~50-200 ms. Fix: abbassare l'avg dello stream del 5-10 % o rimuovere un canale.

Deriva PCR — stutter STB ogni pochi secondi. Fix: pcradjust nella pipeline (già presente), monitorare gli allarmi pcrverify.

VBV underrun STB — l'STB perde la sincronizzazione e la riacquista (~2-3 s). Fix: encoder bufsize = 2 × maxrate (già impostato), abbassare maxrate.

Parametri pipeline tsp (mux_worker.ex)

--max-input-packets 4096 / --max-flushed-packets 4096 (FIFO interno ≈ 0.3 s @ 19 Mbps). --receive-buffer-size 16777216 (16 MB buffer rcv UDP per input). --packet-burst 32 (regulate) / 7 (output, standard IPTV). --enforce-burst (ogni datagramma UDP è esattamente 7 pacchetti).

Calcolo capacità empirico (CLT)

Il badge nella barra superiore `📐 rec X.XM` mostra la capacità mux raccomandata derivata dal teorema del limite centrale. La somma degli stream VBR indipendenti si approssima a una distribuzione normale, quindi Mux = μ_total + z·σ_total + margine. σ_i è stimato dall'intervallo VBR di ciascun input come (max−min)/4 (assumendo che l'intervallo copra ±2σ). Il margine = 10% di μ_total compensa l'overhead PSI/SI, il jitter del rate-control encoder e la correlazione degli stream che il CLT puro sottostima.

Colore badge: 🟢 verde = cap ≥ 99.7% rec (conservativo) · 🟡 ambra = tra 99% e 99.7% · 🔴 rosso = cap < 99% (sotto-provisioning, rischio di drop) · 🔵 blu = cap non impostato.

z-score per target di copertura: 90% z=1.28 · 95% z=1.65 · 99% z=2.33 · 99.7% z=3.00 (default) · 99.99% z=3.72 (qualità broadcast). Il tooltip del badge elenca tutti i livelli; facendo clic si applica la raccomandazione al 99.7% a mux.bitrate (richiesto riavvio mux).

σ_eff = σ_total × statmux_factor (statmux_factor = 0.7 quando Statmux è ON, altrimenti 1.0)

Valori consigliati per canale (HD H.264)

Canale HD 1080p25: avg = 4000-5000 kbps, min = avg × 0.75 (~3000-3750), max = avg × 1.5 (~6000-7500). L'UI compila automaticamente min/max dall'avg. L'UI Statmux compila automaticamente avg dal profilo transcoder dello stream. Canale SD: avg = 2000-2500 kbps. UHD: avg = 12000-18000 kbps.

Statmux Ibrido Adattivo

L'Adaptive Hybrid Statmux è un meccanismo runtime che sposta la capacità inutilizzata dagli stream core (protetti, VBR completo) agli stream bilanciati (sacrificabili). "Hybrid" perché due regimi di rate-control distinti funzionano affiancati (riempimento CBR core + VBR bilanciato); "adaptive" perché il router rivaluta una volta al secondo dalla previsione HLS-lookahead. L'operatore contrassegna gli stream come bilanciati tramite la checkbox sul lato destro; il sistema li pre-codifica in tre livelli (HIGH/MED/MIN) e il router scambia atomicamente il symlink in base alla capacità residua dopo aver previsto i picchi core.

Capacità — come viene diviso il bitrate

Capacità Mux 38 Mbps
  ┌──────────────────────────────────────────────────────────────────┐
  │█████████████████████████████████████████████████████░░░░░░░░░░░░│
  └──────────────────────────────────────────────────────────────────┘
   ◀────── core_max (previsto da 6 segmenti HLS) ────▶◀── bilanciato ──▶
                                                       per_balanced
                                                       = (cap − core_max
                                                          − overhead SI
                                                          − audio) / N

  Esempio: cap=38000  core_max=29000  SI=200  audio_total=1280  N_balanced=2
           remaining = 38000 − 29000 − 200 − 1280 = 7520 kbps
           per_balanced_max = 7520 / 2 = 3760 kbps
           clamp(3760, 500, 3000) = 3000 kbps  ← tetto per VBR bilanciato

Guadagno pratico (10 stream × 3 Mbps avg, 4 Mbps max)

Senza Adaptive Hybrid Statmux si deve dimensionare il mux per il caso peggiore Σ max. Con Adaptive Hybrid Statmux si dimensiona attorno a Σ avg + un buffer statistico (correlazione broadcast ρ=0.15). Guadagno reale ~5 Mbps su un mux da 38 Mbps, oppure +1 stream alla stessa cap.

CBR puro (encoder fisso)

VBR senza Statmux

Statmux Ibrido Adattivo ON

Esclusività URL ↔ Statmux

L'override URL di input personalizzato e Statmux si escludono a vicenda. Con Statmux attivo, il transcoder bilanciato si basa sull'HLS encoder locale per controllare il bitrate. Un URL esterno bypasserebbe quel ciclo di controllo. L'UI applica questo in entrambi i sensi — la checkbox si blocca quando c'è una combinazione in conflitto, e il tooltip mostra quale stream sta bloccando.

Pipeline (encoder → transcoder bilanciato → mux ffmpeg → tsp)

┌─────────────────────────┐
          │  Encoder (NVDEC→NVENC)  │ ← UDP satellite
   STREAM │  /dev/shm/stream_X/h264/│
          │  → muxed.m3u8 + segmenti│
          └────────────┬────────────┘
                       │
              ┌────────┴────────┐
              │                 │
        role="core"        role="balanced"
        (protetto)         (sacrificabile)
              │                 │
              │                 ▼
              │     ┌───────────────────────────┐
              │     │  BalancedTranscoder       │
              │     │  ffmpeg p1 NVENC VBR      │
              │     │  -maxrate dinamico        │ ← router Adaptive Hybrid
              │     │  /dev/shm/stream_X_       │   tick 1 s (prevede da
              │     │   balanced/h264/...m3u8   │   6 segmenti HLS, swap
              │     └───────────┬───────────────┘   atomico symlink HIGH/MED/MIN)
              │                 │
              ▼                 ▼
          ┌─────────────────────────┐
          │  ffmpeg-as-mux          │ ← /dev/shm/mux_X/pipe.sh
          │  -f mpegts pipe:1       │
          │  -muxrate <cap>         │
          └────────────┬────────────┘
                       │ shell pipe
                       ▼
          ┌─────────────────────────┐
          │  tsp -I file -          │
          │  continuity → regulate  │
          │  → pcradjust → bitrate_ │
          │     monitor             │
          │  -P nit -P sdt -P inject│
          │     (tabelle DVB SI)    │
          └────────────┬────────────┘
                       │
                       ▼ UDP multicast
                  239.x.x.x:1234

Dimensionamento VBR core min/avg/max per statmux

Il guadagno statistico avviene solo quando gli encoder hanno un ampio intervallo VBR — quando max ≈ avg l'encoder è di fatto CBR e il router non ha nulla da ridistribuire. Configurare `min/avg/max` per stream così Σmax sia comodamente sopra la cap del mux (sovra-sottoscrizione = margine per il guadagno statistico) mentre Σavg si trova comodamente sotto di essa.

Esempio pratico — mux 38 Mbps, 10 HD core + 2 bilanciati:

  Vecchio (CBR fisso):       min=2000  avg=3000  max=4000
     Σmax = 10×4000 + 2×7600 = 55200 kbps  (1.45× cap — piccolo guadagno)
     σ_per_stream = (4000−2000)/4 = 500 kbps  (banale — piatto)
     Σ margine statistico ≈ 1.5 Mbps

  Consigliato (VBR ampio): min=1500  avg=3000  max=6000
     Σmax = 10×6000 + 2×7600 = 75200 kbps  (1.98× cap — guadagno sano)
     Σavg = 10×3000 + 2×3000 =  36000 kbps  (95% cap — comodo)
     Σmin = 10×1500 + 2×500  =  16000 kbps  (42% cap — floor comodo)
     σ_per_stream = (6000−1500)/4 = 1125 kbps  (significativo)
     Σ margine statistico ≈ 4 Mbps

  Beneficio reale: su scene d'azione i core picchi a 5−6 Mbps (dettaglio HD),
  il router abbassa i bilanciati al livello MIN, il wire rimane a 38 Mbps.
Intervallo consigliato per stream core:

  • min  ≈ avg × 0.5  (l'encoder può abbassarsi su scene statiche)
  • avg  = target nominale per risoluzione (HD ≈ 3 Mbps, FullHD ≈ 5 Mbps, UHD ≈ 12 Mbps)
  • max  ≈ avg × 2.0  (l'encoder può piccare su scene d'azione — sport, film d'azione)

Guardie capacità mux (devono essere rispettate):

  • Σ avg core + Σ avg balanced  ≤  mux.bitrate × 0.85   (target confortevole)
  • Σ max core + Σ max balanced  ≤  mux.bitrate × 2.0    (guadagno sovra-sottoscrizione)
  • Σ min core + Σ min balanced  ≤  mux.bitrate × 0.6    (floor caso peggiore)

σ_per_stream = (max − min) / 4  (assume intervallo ±2σ)
σ_total = √((1−ρ) · Σσ²ᵢ + ρ · (Σσᵢ)²)   ρ = 0.15 tipico broadcast
σ_eff = σ_total × 0.7                     (fattore statmux quando ON)
Mux_99.7% = μ_total + 3 · σ_eff           (mostrato nella barra superiore 📐 rec)

Suggerimenti e risoluzione dei problemi

Almeno 1 stream bilanciato — senza di esso il router Adaptive Hybrid non ha nulla da ridurre durante i picchi, quindi guadagno = 0

I canali musicali e informativi sono i migliori candidati — tolleranza ≥ 90% alla riduzione del rate

Monitorare tramite le voci di log `[balance-router]` — mostra `core_max`, `remaining`, transizioni di livello per ogni tick da 1 s

Se `core_max > capacity` (avviso CORE OVERPEAK nel log), ridurre il numero di stream core o abbassare il maxrate encoder

Flusso di lavoro operatore

Aggiungere stream al mux (l'assegnazione dei canali rimane manuale)

Attivare Statmux nell'intestazione del mux (chip giallo). Quando abilitato, tutti gli encoder passano a VBR e si riavviano

Contrassegnare gli stream sacrificabili tramite la checkbox arancione sul lato destro — tipicamente canali musicali/informativi dove la riduzione del rate è invisibile

Fatto. Il router Adaptive Hybrid legge una previsione HLS a 6 segmenti dagli stream core ogni 1 s e scambia atomicamente il symlink bilanciato tra i livelli HIGH/MED/MIN

StatMux — multiplex statistico nativo

Più canali a bitrate variabile condividono un transport stream fisso (MPTS). I bit vengono riassegnati dinamicamente in base alla complessità del contenuto — una scena impegnativa ne riceve di più, una statica di meno — così la qualità rimane equalizzata e la somma non supera mai la capacità del wire.

Classe: statmux feedback closed-loop con C1 look-ahead (feedforward) su encoder VBR — principio statmux broadcast, guidato dalla complessità della sorgente.

Architettura e flusso dati

Architecture diagram

UDP multicast SSM, per canale

Encoder

complessità sorgente

Loopback

linea/stream proprio

allocatore (Σ ≤ pool)

buffer

padding null → CBR

Wire CBR a cap

anticipa la scena complessa prima del calo

wire misurato — calibrazione

bitrate target

Ogni canale corre sulla propria linea fino al mux (encoder separato + connessione loopback), dove tutti si uniscono in un'unica linea MPTS multicast a un bitrate esattamente fisso. L'allocatore è il cervello centrale (1 GenServer per mux), tick ~0.85 s.

Cervello: feedback + C1 look-ahead

L'allocatore combina due segnali — una previsione dalla complessità della sorgente (in avanti) e il wire misurato (in dietro). Questo si avvicina agli statmux broadcast che conoscono il costo di un frame prima della codifica.

C1 misura la complessità del contenuto al decoder — che anticipa l'output encoder della latenza della pipeline. La variazione relativa della complessità (correlazione r≈0.92 con i bit) viene applicata al bitrate target in ENTRAMBE le direzioni PRIMA che l'encoder la produca: una scena in salita viene pre-allocata (nessun calo), una in discesa libera bit in anticipo per gli altri.

Wire misurato (feedback)

I bit esatti per stream emessi dal mux (contatori lato sender) calibrano la mappatura complessità→bitrate e mantengono la somma esattamente al pool. Ancora ground-truth — la previsione C1 non è perfetta (r 0.92), il wire trailing la corregge.

Erogabilità

Il claim di uno stream è supportato da ciò che l'encoder produce effettivamente (× headroom di picco). Un canale facile non può accaparrare larghezza di banda che non può spendere — la rilascia ai canali affamati.

Qualità equalizzata

L'obiettivo non è il bitrate uguale ma la qualità uguale — la pressione continua sposta i bit dai canali ad alta qualità (QP basso) verso quelli a bassa qualità (QP alto).

Ridistribuzione predittiva

Quando C1 segnala un calo di un canale, la sua larghezza di banda viene ceduta agli altri nello stesso tick — salgono prima che il calo raggiunga effettivamente il wire.

Pool = cap N − overhead (PSI/EIT) − audio − riserva (slider). Misurato dal wire, windowed-max contro i burst.

Domanda per stream = wire deliverabile × C1 look-ahead (variazione relativa della complessità) + pressione di equalizzazione della qualità.

Allocazione pool proporzionale alla domanda, limitata a [min, max] dal profilo video; la larghezza di banda libera va ai più bisognosi.

Target encoder = alloc / calibrazione (EMA misurata/target per stream); inviato con una soglia (Δ≥3%/1s) e limite di slew contro il churning.

Il servo buffer-hold mantiene il livello della coda a ~1 s (banda), backstop d'emergenza in caso di sovraccarico; il padding null completa il resto al CBR.

Wire CBR: buffer + padding null + pacing

L'output è un flusso CBR esatto alla cap configurata. Tre livelli lo garantiscono:

Buffer stabile ~1 s

La coda di invio mantiene un livello stabile di ~1 s di wire che assorbe i cali GOP sub-secondo (tra I-frame). Verificato dalla registrazione: 0.5–1 s assorbe l'88–91% dei cali.

Padding null

Quando il contenuto è sotto la cap, la differenza viene riempita con pacchetti null MPEG-TS (PID 0x1FFF). Il wire quindi RAGGIUNGE SEMPRE la cap — conforme DVB.

Pacing esatto

L'egress è paced all'esatta cap (busy-spin ABSTIME); un clamp debito di 12 ms previene i burst di recupero dopo un breve stallo → nessun jitter PCR, nessun picco oltre-cap.

Grafico output (fiume)

Un grafico in pila live del wire reale — ogni banda è un canale, che scorre senza interruzioni da destra. I dati sono misurati sul wire (probe tsp + contatori NIF), non stimati.

Bande colorate

Ogni canale ha un colore stabile; lo spessore = il suo bitrate istantaneo. L'overhead (PSI/EIT) è grigio in basso, il padding null è bianco con texture in alto.

Linea Cap / Max

La linea tratteggiata in alto = cap configurata (soffitto wire). Contenuto + padding null insieme stanno sempre sotto di essa.

Banda null

La banda bianca puntinata in alto = padding null (capacità libera). Più è sottile, più il wire è pieno; scompare su contenuti impegnativi.

Segni di drop

Segni rossi sulla timeline = overflow della coda di invio (drop). Zero in stato stabile; burst solo all'avvio/flood.

Intervallo (30s–30m)

I pulsanti cambiano la finestra temporale istantaneamente (lato client), quello attivo ha sfondo grigio. Il grafico accumula 30 min nel browser.

Tooltip

Il passaggio del mouse mostra il bitrate per canale, QP e indice di qualità in quel momento (valore storico, non live).

cap %

Livello corrente di regolazione del pool (pool/(pool−trim)). >100% = sovrapressione (riempimento del buffer), <100% = trimming.

Riserva (slider)

Banda di tolleranza sopra la cap — quanto mantenere come riserva null. 0 = riempi esattamente N; positivo = riserva maggiore (più sicuro); negativo = leggero overfill.

Buffer (drop)

L'intestazione mostra l'utilizzo % della coda di invio e i drop cumulativi. Il pulsante "azzera drop" reimposta il contatore.

GOP

Lunghezza GOP comune per tutti i canali nel mux (bloccata nei profili video all'avvio). Lo stagger a parete distribuisce i frame I nel tempo così non collidono.

VBV

Dimensione buffer VBV encoder (ms). Più piccolo appiattisce gli I-frame (buono per canali in movimento), più grande dà un I-frame più dettagliato. Default 850; override per stream possibile.

Schede stream e stati

Ogni canale nel mux ha una scheda con nome, PNR, bitrate corrente e target, e stato:

In esecuzione — il canale sta codificando e fluisce verso il wire.

Avvio — l'encoder si sta avviando / stabilizzando.

Spento — il canale è nel mux ma non in esecuzione (0 kbps).

Guasto — input perso / encoder in crash.

Tabelle PSI/SI (set DVB completo)

StatMux genera il set completo di tabelle MPEG-TS / DVB obbligatorie direttamente nel mux NIF — nessun inseritore SI esterno. Le tabelle sono temporizzate uniformemente nel flusso così un STB acquisisce sempre rapidamente PSI/SI dopo il retuning.

Tabella

PID

Intervallo

Scopo / sorgente

EIT ↔ EPG: integrazione completa

EIT (Event Information Table, PID 0x12) non è statica — è completamente collegata al sistema EPG dell'applicazione. L'operatore assegna a ciascun canale nel mux un servizio EPG (eit_epg_service_id), e il mux genera EIT live dai suoi eventi pianificati.

Dimensionamento canale (min / avg / max)

min = floor inferiore (scena statica). Consigliato avg × 0.5–0.75. Impostare abbastanza basso da risparmiare su contenuti tranquilli per gli altri.

avg = livello target. Σ avg di tutti i canali ≈ video pool (cap − overhead − audio).

max = soffitto (headroom VBR per picchi/tagli). Consigliato avg × 1.5. Non limitare duramente a avg — il VBR ha bisogno di spazio per i picchi.

Esempi (avg): SD 1.5–2.5M · HD 2.5–4M · FHD 4–6M · UHD 12–18M. Per statmux mantenere una differenza ragionevole così il pool può essere ridistribuito.

Relazione con gli statmux broadcast

Facciamo vero multiplexing statistico — ridistribuzione dinamica dei bit tra canali VBR che condividono un wire fisso, con equalizzazione della qualità. Meccanicamente siamo vicini agli statmux headend professionali (allineamento cap, buffer 1 s, padding null, GOP/B-frame, override floor).

Cosa abbiamo

Wire CBR a cap · buffer 1 s · allocazione proporzionale per stream · feedforward C1 look-ahead · equalizzazione qualità · ridistribuzione predittiva · padding null · pacing esatto.

Dove differiamo

Uno statmux broadcast usa la stima rate-distortion dell'encoder (costo esatto del frame, l'encoder è uno schiavo dell'allocatore). Noi usiamo la complessità sorgente C1 (r≈0.92) + un encoder VBR che sceglie il proprio QP — un limite hardware, non di design. Classe: feedback + look-ahead invece di puro feedforward.

Risoluzione dei problemi

Sintomo

Causa / fix

Gap / picchi null (5–7M) nel grafico

Cali di contenuto — o GOP tra I-frame (assorbiti dal buffer da 1 s) o uno stallo collettivo del contenuto (gestito dalla ridistribuzione). Il C1 look-ahead li ha ridotti sostanzialmente.

Drop > 0 in aumento

Coda di invio in overflow — contenuto sopra la cap. Controllare la riserva (slider), l'utilizzo e che l'allocatore non sia bloccato (tick in esecuzione). I burst di avvio sono normali.

Un canale rimane a basso bitrate / alto QP

O contenuto statico (corretto — rilascia larghezza di banda) o max troppo basso nel profilo. Controllare min/avg/max e la reale complessità della sorgente.

Grafico a scatti / bordo sinistro cancellato

Lato client — aggiornamento forzato (Ctrl+Shift+R) per il nuovo bundle JS. I dati scorrono via push incrementale, asse = intervallo selezionato.

cap % / buffer non cambia dopo la modifica

LiveView re-render — aggiornamento forzato. I valori si aggiornano una volta al secondo tramite assign tracciato.

Architettura (end-to-end)

SAT UDP SSM ─┐
             ├─ gst_encoder.py(K1)  CBR ─ TCP127 ─┐        complessità ZMQ
   (per      ├─ gst_encoder.py(K2)  CBR ─ TCP127 ─┤            ▲   │
   canale)   └─ gst_encoder.py(Kn)  CBR ─ TCP127 ─┤            │   ▼
                                                  ▼     BalanceRouter
                                   ┌─────── NIF StatMux ───────┐  │ set_bitrate
                                   │ ring per-input (drop-old) │◄─┘
                                   │ drain 2-pass (A prio, V)  │
                                   │ allocatore: w=cmplx×max   │
                                   │ ×headroom% (slider 78-90) │
                                   │ esclusione stale (>2s)    │
                                   │ NULL pad -> wire cap CBR  │
                                   │ PCR pass-through          │
                                   └────────────┬──────────────┘
                                                ▼
                                   MPTS  UDP multicast (CBR cap)

gst_encoder.py per stream: ingresso SAT UDP SSM → nvh264enc rc-mode=cbr. Il bitrate è guidato da BalanceRouter tramite set_bitrate. La complessità della scena (SAD/px) viene riportata tramite ZMQ all'allocatore.

Ring per-input (loopback TCP dall'encoder), drain 2-pass (priorità audio, video completo), allocatore proporzionale, padding NULL esattamente alla cap wire (CBR), PCR pass-through dall'encoder.

Un MPTS (PAT/PMT/SDT generato dal NIF) → UDP multicast sull'VLAN di output. Il wire è rigorosamente CBR a mux.bitrate (stuffing NULL).

Riga 1: ⚠ (problemi di validazione) · PNR · nome in un frame (colore = stato).

Riga 2+: min·avg·max kbps · complessità prevista Δ bassa/media/alta/picco · INPUT PERSO · ⚙URL · encoder Mbps · SID · service_type.

Colore nome = stato dello stream:

Mbps
 37 ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌  <= cap 37.0 Mbit (rosso, soffitto CBR wire)
    · · · · · · · · · · · · · · · · ·     <= spazio libero = padding NULL
    ·· grigio · punti neri · riserva ··   (capacità inutilizzata)
 31 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  <= headroom 85% · 31.5 Mbit (arancione,
    ▓▓▓▓ JOJ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓     i target encoder puntano qui)
    ▒▒▒▒ Markíza ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
    ░░░░ TA3 ░░░░░░░░░░░░░░░░░░░░░░░░░░  <= clic banda -> espandi scheda
  0 └──────────────────────────────────
        tempo ->           10m │ 1h │ 24h     CC0 PCR0 BR0 OVF0

Linea rossa 'cap': soffitto CBR wire (mux.bitrate) — il NULL riempie fino a qui.

Linea arancione 'headroom N%': = cap × slider headroom / 100. I target set_bitrate encoder puntano qui. Si muove con lo slider.

Area grigia con punti neri tra contenuto e cap = padding NULL (riserva inutilizzata). Più punti = più riserva.

Clic sulla banda colore di un canale → espande automaticamente la scheda di quel canale in basso.

Pianificazione

MuxEitAdapter legge gli eventi dal sistema EPG per servizio, costruisce sezioni DVB EIT (nome, ora, durata, descrizione, genere) e le pacchettizza sul PID 0x12. Le versioni delle sezioni incrementano al cambio EPG — l'STB raccoglie l'aggiornamento.

L'EIT viene iniettato direttamente nel mux NIF, uniformemente NELLO spazio null (1 pacchetto / N output + p/f periodicamente) — sostituendo la cascata tsp esterna a burst. Picco/avg ≈ 1.1, nessuna rimozione di contenuto, la cap regge.

Tabella 0x4E — l'evento in corso + quello successivo. Iniettata ogni ≤2 s per la valuta istantanea "ora/dopo" sull'STB.

Il mux rigenera periodicamente l'EIT .bin dall'EPG (~15 s) — una modifica alla pianificazione EPG raggiunge il wire senza riavvio. L'intestazione del mux mostra "EIT ✓" quando attivo.

Tabelle 0x50/0x51 — la pianificazione completa ore/giorni in avanti (segmenti). Distribuita uniformemente nel flusso.

Gauge differenziale (sopra il grafico)

Indicatore del flusso reale vs cap. Centro = cap (0). Indicatore a sinistra (verde) = riserva (Σ contenuto sotto cap); destra (rosso) = overflow. Live, si muove ad ogni tick.

-5 Mbps CAP 37000 kbps +5 Mbps

[#### verde = riserva ####│::: rosso = oltre cap :::]

^ -3200k

(Σ contenuto ~3.2 Mbps sotto cap = riserva sana)

Slider headroom (78–90 %, default 85)

Più basso (→78): più riserva NULL, più sicuro, meno utilizzo stretto della cap.

Più alto (→90): utilizzo cap più stretto, meno NULL.

⚠ ≥88 %: lo slider diventa giallo — vicino al soffitto di freeze ring-overflow. Hard-clamp 78–90 (non può raggiungere 100 — guardia contro video congelato).

Asimmetria: abbassare aggiunge in modo affidabile riserva; alzare è limitato dal clamp max_bps per stream (l'allocatore non spingerà un canale oltre il suo max → sicuro).

Controlla quanto della cap puntano gli encoder: target encoder = budget × headroom% / 100 (linea arancione nel grafico). Cambia live (effetto dal prossimo tick di 10 ms), persistito in SystemConfig.

Perdita segnale / guasto canale (isolamento)

Un canale si guasta (crash encoder / perdita segnale) ⇒ il mux continua a funzionare, gli altri canali non sono interessati, il wire mantiene la cap (il NULL assorbe il guasto), CC=0 per i sopravvissuti. I thread ring per-input sono indipendenti. Il canale guasto mostra un nome lampeggiante rosso + badge INPUT PERSO; l'encoder viene riavviato supervisionato e il canale si auto-ripristina.

PCR / temporizzazione

PCR e PES PTS/DTS sono pass-through dall'encoder (dominio di clock unico ⇒ coerente, nessuna deriva). Il jitter PCR residuo ~decine di ms è intrinseco (tick output 10 ms + UDP) — il PLL STB/VLC lo tollera, CC=0. La conformità hardware DVB-S2 è fuori scopo per il caso d'uso IPTV corrente.

CAT — accesso condizionale (solo con scrambling / ECM).

EIT — informazioni evento (EPG) — dal sistema EPG, vedi sotto.

NIT — info rete (ID rete, nome, transport stream).

PAT — elenco programmi (mappa PNR → PID PMT).

PMT — per programma: PID video/audio/PCR, codec, descrittori.

SDT — nomi e provider dei servizi (ciò che l'STB mostra nell'elenco).

TDT/TOT — ora e data + offset locale (orologio STB).

TSDuck Plugins Used

merge
regulate
pat
pmt
sdt
nit
eitinject
timeref
bitrate_monitor
continuity
psimerge
count