MPTS · TSDuck · PSI/SI






O mux do Stream Manager monta múltiplos streams SPTS num único output MPTS CBR (DVB/IPTV). O Adaptive Hybrid Statmux combina um preenchimento CBR com encoders VBR balanceados para saída constante no mux.
Quando o mux pai tem statmux ativado, cada encoder FFmpeg corre em capped-VBR: -b:v <avg> -minrate <min> -maxrate <max> -bufsize <max×2>. Buffer VBV = 2× max dá ~2 s de headroom de pico. Streams statmux também recebem um offset GOP determinístico (-force_key_frames) por stream_id para que os picos de I-frame não coincidam entre canais.
tsp -I null (∞ NULL packets @ line rate) → -P merge tsp -I ip <stream> (sobrepõe pacotes reais) → -P continuity (alarme de drop) → -P pcradjust --bitrate X (reescreve PCRs) → -P pcrverify --jitter-max 500µs → -P bitrate_monitor (alarme de taxa ±5%) → -P regulate --bitrate X --packet-burst 32 → -O ip <out> --packet-burst 7 --enforce-burst.
O plugin regulate emite exatamente mux.bitrate bps no fio. Entradas atrasadas → pacotes NULL preenchem a lacuna. Entradas em pico → buffer FIFO interno (4096 pacotes ≈ 0,3 s @ 19 Mbps) absorve. --enforce-burst força 7 pacotes TS por datagrama UDP (1316 B, padrão IPTV).
A barra superior do Mux mostra 3 somas com estado de cor. Regras:
Σ avg ≤ 85% mux.bitrate — soma de targets médios de todos os canais mais áudio. Este é o 'orçamento' do mux. A reserva de 15% protege contra coincidência de picos. Verde ≤ 85%, âmbar ≤ 100%, vermelho > 100%.
Σ max ≤ 160% mux.bitrate — soma do teto de pico. Permitido exceder a capacidade (esse é o ponto do statmux — picos raramente coincidem). Verde ≤ 160%, âmbar ≤ 200%, vermelho > 200%.
Σ min ≤ 50% mux.bitrate — soma do piso do encoder. Um piso seguro garante que em cenas estáticas o encoder não cai para qualidade desnecessariamente baixa. Verde ≤ 50%, âmbar ≤ 90%, vermelho > 90%.
Orçamento disponível após PSI/SI/EIT: ~18,5 Mbps. Σ avg = 4 × 4,0 = 16 Mbps (86% — âmbar borderline, baixar mais). Σ max = 4 × 6,0 = 24 Mbps (130% — verde, OK). Σ min = 4 × 3,0 = 12 Mbps (65% — âmbar, OK).
Com ≥ 10 alarmes em 60s aparece uma notificação flash na UI do Mux recomendando redução de bitrate. Recuo automático NÃO está ativo — o operador decide.
-P bitrate_monitor --periodic 60 --min/max ±5% — alarme se a taxa real se afastar além de ±5% do alvo. Significa que regulate está sem entrada (entrada morreu).
-P continuity — rastreia contadores de continuidade TS. Regista aviso em cada drop (sobre-subscrição, crash FFmpeg, jitter NIC).
-P pcrverify --jitter-max 500000 — DVB permite jitter PCR ≤ 500 µs. Alarme = pcradjust não consegue acompanhar ou fonte tem PCRs quebrados.
Coincidência GOP — todos os encoders emitem I-frames simultaneamente, coincidência de picos. Correção: GOP offset (já ativo para statmux, hash(stream_id) mod gop_size).
Perda de sinal de entrada — um canal fica a preto, o mux continua (graças à base -I null). Duração ~3-8 s para reinício do FFmpeg.
Sobre-subscrição (picos sustentados > capacidade) — bloqueios/falhas de áudio ~50-200 ms. Correção: baixar avg do stream em 5-10% ou remover um canal.
Deriva PCR — STB gagueja de poucos em poucos segundos. Correção: pcradjust no pipeline (já existe), observar alarmes pcrverify.
Underrun VBV do STB — STB perde sincronização e readquire (~2-3 s). Correção: bufsize do encoder = 2 × maxrate (já configurado), baixar maxrate.
--max-input-packets 4096 / --max-flushed-packets 4096 (FIFO interno ≈ 0,3 s @ 19 Mbps). --receive-buffer-size 16777216 (16 MB UDP rcv buffer por entrada). --packet-burst 32 (regulate) / 7 (saída, padrão IPTV). --enforce-burst (cada datagrama UDP tem exatamente 7 pacotes).
O badge `📐 rec X.XM` na barra superior mostra a capacidade recomendada do mux derivada do teorema do limite central. A soma de streams VBR independentes aproxima-se de uma distribuição normal, logo Mux = μ_total + z·σ_total + margem. σ_i é estimado de cada intervalo VBR como (max−min)/4. A margem = 10% de μ_total compensa overhead PSI/SI, jitter do rate-control e correlação de streams.
Cor do badge: 🟢 verde = cap ≥ 99,7% rec · 🟡 âmbar = entre 99% e 99,7% · 🔴 vermelho = cap < 99% (sub-provisionado) · 🔵 azul = cap não definido.
z-score por cobertura: 90% z=1,28 · 95% z=1,65 · 99% z=2,33 · 99,7% z=3,00 (padrão) · 99,99% z=3,72 (grau broadcast). O tooltip do badge lista todos os níveis; clicar aplica a recomendação de 99,7% ao mux.bitrate (reinício do mux necessário).
σ_eff = σ_total × statmux_factor (statmux_factor = 0,7 quando Statmux está ON, senão 1,0)
Canal HD 1080p25: avg = 4000-5000 kbps, min = avg × 0,75 (~3000-3750), max = avg × 1,5 (~6000-7500). UI preenche automaticamente min/max a partir de avg. Canal SD: avg = 2000-2500 kbps. UHD: avg = 12000-18000 kbps.
O Adaptive Hybrid Statmux é um mecanismo em tempo de execução que transfere capacidade não utilizada de streams core (protegidos, VBR completo) para streams balanceados (sacrificáveis). 'Híbrido' porque dois regimes de rate-control distintos correm lado a lado; 'adaptativo' porque o router reavalia uma vez por segundo a partir de uma previsão HLS-lookahead.
Capacidade Mux 38 Mbps
┌──────────────────────────────────────────────────────────────────┐
│█████████████████████████████████████████████████████░░░░░░░░░░░░│
└──────────────────────────────────────────────────────────────────┘
◀────── core_max (previsto de 6 segmentos HLS) ─▶◀── balanceado ──▶
per_balanced
= (cap − core_max
− overhead SI
− áudio) / NSem Adaptive Hybrid Statmux é necessário dimensionar o mux para o Σ max de pior caso. Com Adaptive Hybrid Statmux dimensiona-se em torno de Σ avg + um buffer estatístico (correlação broadcast ρ=0,15). Ganho real ~5 Mbps num mux de 38 Mbps, ou +1 stream com a mesma cap.
CBR puro (encoder fixo)
VBR sem Statmux
Statmux Híbrido Adaptativo ATIVO
Override de URL de entrada personalizado e Statmux são mutuamente exclusivos. Com Statmux ativo, o transcoder balanceado depende do HLS do encoder local para controlar o bitrate. Um URL externo contornaria esse loop de controlo.
Pipeline: encoder → transcoder balanceado → ffmpeg mux → tsp
O ganho estatístico só ocorre quando os encoders têm um intervalo VBR amplo — quando max ≈ avg o encoder é efetivamente CBR e o router não tem nada para redistribuir. Configure min/avg/max por stream para que Σmax esteja confortavelmente acima da cap do mux.
Exemplo — mux 38 Mbps, 10 HD core + 2 balanceados: Antigo (CBR-locked): min=2000 avg=3000 max=4000 Recomendado (VBR amplo): min=1500 avg=3000 max=6000 σ_por_stream = (6000−1500)/4 = 1125 kbps (significativo) Headroom estatístico ≈ 4 Mbps
Intervalo recomendado por stream core: • min ≈ avg × 0,5 (encoder pode baixar em cenas estáticas) • avg = target nominal por resolução (HD ≈ 3 Mbps, FullHD ≈ 5 Mbps, UHD ≈ 12 Mbps) • max ≈ avg × 2,0 (encoder pode ter picos em ação — desporto, filme de ação) Guardas de capacidade do mux: • Σ avg core + Σ avg balanced ≤ mux.bitrate × 0,85 • Σ max core + Σ max balanced ≤ mux.bitrate × 2,0 • Σ min core + Σ min balanced ≤ mux.bitrate × 0,6
Pelo menos 1 stream balanceado — sem um, o router Adaptive Hybrid não tem nada para comprimir durante picos, logo ganho = 0
Canais de música e info são os melhores candidatos — tolerância >90% a redução de taxa
Acompanhe via entradas de log `[balance-router]` — mostra core_max, remaining, transições de tier por tick de 1 s
Se core_max > capacity (aviso CORE OVERPEAK no log), reduza o número de streams core ou baixe o maxrate do encoder
Adicionar streams ao mux (atribuição de canal permanece manual)
Ativar Statmux no cabeçalho do mux (chip amarelo). Quando ativado, todos os encoders mudam para VBR e reiniciam
Marcar streams sacrificáveis via a checkbox laranja do lado direito — tipicamente canais de música/info onde a redução de taxa é invisível
Pronto. O router Adaptive Hybrid lê uma previsão de 6 segmentos HLS dos streams core a cada 1 s e troca atomicamente o symlink balanceado entre tiers HIGH/MED/MIN
Múltiplos canais de bitrate variável partilham um transport stream fixo (MPTS). Os bits são realocados dinamicamente pela complexidade do conteúdo — uma cena exigente recebe mais, uma estática menos — para que a qualidade fique equalizada e a soma nunca exceda a capacidade do fio.
Classe: statmux de feedback closed-loop com C1 look-ahead (feedforward) sobre encoders VBR — princípio de statmux broadcast, conduzido pela complexidade da fonte.
UDP multicast SSM, por canal
Codificadores
complexidade da fonte
Loopback
linha própria / stream
alocador (Σ ≤ pool)
buffer
preenchimento nulo → CBR
Fio CBR na cap
antecipar cena complexa antes do dip
fio medido — calibração
bitrate alvo
Cada canal corre na sua própria linha até ao mux (encoder separado + ligação loopback), onde todos se fundem numa linha MPTS multicast a um bitrate exatamente fixo. O alocador é o cérebro central (1 GenServer por mux), tick ~0,85 s.
O alocador combina dois sinais — uma previsão da complexidade da fonte (feedforward) e o fio medido (feedback). Esta abordagem aproxima-se de statmuxes broadcast que conhecem o custo de um frame antes de o codificar.
C1 mede a complexidade do conteúdo no decoder — que antecede a saída do encoder pela latência do pipeline. A mudança relativa de complexidade (correlação r≈0,92 com bits) é aplicada ao bitrate target em AMBAS as direções ANTES do encoder o produzir: uma cena crescente é pré-alocada (sem dip), uma decrescente liberta bits cedo para os outros.
Bits exatos por stream emitidos pelo mux (contadores do lado do emissor) calibram o mapeamento complexidade→bitrate e mantêm a soma exatamente no pool. Âncora de verdade-do-solo — a previsão C1 não é perfeita (r 0,92), o fio trailing corrige-a.
A reivindicação de um stream é sustentada pelo que o encoder realmente produz (× headroom de pico). Um canal fácil não pode acumular largura de banda que não consegue gastar — liberta-a para os que necessitam.
O objetivo não é bitrate igual mas qualidade igual — pressão contínua move bits de canais de alta qualidade (QP baixo) para canais de baixa qualidade (QP alto).
Quando C1 sinaliza que um canal está a baixar, a sua largura de banda é entregue aos outros no mesmo tick — eles rampeiam antes que o drop chegue ao fio.
Pool = cap N − overhead (PSI/EIT) − áudio − reserva (slider). Medido do fio, janela máxima contra bursts.
Procura por stream = wire entregável × C1 look-ahead (mudança relativa de complexidade) + pressão de equalização de qualidade.
Alocação proporcional do pool por procura, limitada a [min, max] do perfil de vídeo; largura de banda livre vai para os com maior necessidade.
Target do encoder = alocação / calibração (EMA medido/target por stream); enviado com threshold (Δ≥3%/1s) e limite de slew contra oscilação.
Servo de hold de buffer mantém o nível da fila em ~1 s (banda), backstop de emergência em sobrecarga; padding null completa o restante para CBR.
A saída é um stream CBR exato na cap configurada. Três camadas garantem-no:
A fila de envio mantém um nível estável de ~1 s de fio que absorve dips GOP sub-segundo (entre I-frames). Verificado a partir da gravação: 0,5–1 s absorve 88–91% dos dips.
Quando o conteúdo está abaixo da cap, a diferença é preenchida com pacotes null MPEG-TS (PID 0x1FFF). O fio SEMPRE atinge a cap — conforme DVB.
A saída é paced à cap exata (busy-spin ABSTIME); um clamp de débito de 12 ms previne bursts de catch-up após breve paragem → sem jitter PCR, sem picos acima da cap.
Um gráfico stacked ao vivo do fio real — cada banda é um canal, fluindo suavemente da direita. Os dados são medidos no fio (sonda tsp + contadores NIF), não estimados.
Cada canal tem uma cor estável; espessura = seu bitrate instantâneo. Overhead (PSI/EIT) é cinzento na base, padding null é branco texturado no topo.
A linha tracejada no topo = cap configurada (teto do fio). Conteúdo + padding null juntos ficam sempre abaixo dela.
A banda branca pontilhada no topo = padding null (capacidade livre). Quanto mais fina, mais cheio está o fio; desaparece em conteúdo exigente.
Marcas vermelhas na linha temporal = overflow da fila de envio (drop). Zero em estado estável; bursts apenas no arranque/flood.
Os botões trocam a janela de tempo instantaneamente (lado cliente), o ativo tem fundo cinzento. O gráfico acumula 30 min no browser.
Hover mostra bitrate por canal, QP e índice de qualidade nesse momento (valor histórico, não ao vivo).
Nível atual de regulação do pool (pool/(pool−trim)). >100% = sobrepressão (a encher o buffer), <100% = a cortar.
Banda de tolerância acima da cap — quanto manter como reserva null. 0 = preencher exatamente N; positivo = reserva maior (mais seguro); negativo = leve enchimento excessivo.
O cabeçalho mostra utilização da fila de envio % e drops acumulados. O botão 'limpar drops' reinicia o contador.
Comprimento GOP comum para todos os canais no mux (bloqueado em perfis de vídeo no arranque). Wall-stagger espalha I-frames no tempo para que não colidam.
Tamanho do buffer VBV do encoder (ms). Menor achata I-frames (bom para canais de movimento), maior dá I-frames mais detalhados. Padrão 850; override por stream possível.
Cada canal no mux tem um cartão com nome, PNR, bitrate atual e alvo, e estado:
A correr — canal está a codificar e a fluir para o fio.
A iniciar — encoder está a arrancar / a estabilizar.
Desligado — canal está no mux mas não está a correr (0 kbps).
Falha — entrada perdida / encoder em crash.
StatMux gera o conjunto completo de tabelas MPEG-TS / DVB obrigatórias diretamente no mux NIF — sem insertor SI externo. As tabelas são temporizadas uniformemente no stream para que um STB adquira sempre PSI/SI rapidamente após resintonizar.
Tabela
PID
Intervalo
Propósito / fonte
EIT (Event Information Table, PID 0x12) não é estático — está totalmente ligado ao sistema EPG da aplicação. O operador atribui a cada canal no mux um serviço EPG (eit_epg_service_id), e o mux gera EIT ao vivo a partir dos seus eventos agendados.
min = piso inferior (cena estática). Recomendado avg × 0,5–0,75. Definir suficientemente baixo para poupar em conteúdo calmo para os outros.
avg = nível alvo. Σ avg de todos os canais ≈ pool de vídeo (cap − overhead − áudio).
max = teto (headroom VBR para picos/cortes). Recomendado avg × 1,5. Não limitar hard ao avg — VBR precisa de espaço para picos.
Exemplos (avg): SD 1,5–2,5M · HD 2,5–4M · FHD 4–6M · UHD 12–18M. Para statmux manter uma dispersão razoável para que o pool possa ser redistribuído.
Fazemos multiplexagem estatística real — redistribuição dinâmica de bits entre canais VBR que partilham um fio fixo, com equalização de qualidade. Mecanicamente estamos perto de statmuxes de headend profissional.
Fio CBR na cap · buffer 1 s · alocação proporcional por stream · C1 look-ahead feedforward · equalização de qualidade · redistribuição preditiva · padding null · pacing exato.
Um statmux broadcast usa a estimativa rate-distortion do encoder (custo exato do frame, o encoder é escravo do alocador). Usamos complexidade da fonte C1 (r≈0,92) + um encoder VBR que escolhe o seu próprio QP — um limite de hardware, não de design. Classe: feedback + look-ahead em vez de feedforward puro.
Sintoma
Causa / correção
Lacunas / picos null (5–7M) no gráfico
Dips de conteúdo — ou GOP entre I-frames (absorvido pelo buffer de 1 s) ou paragem coletiva de conteúdo (tratada por redistribuição). C1 look-ahead reduziu estes substancialmente.
Drops > 0 a subir
Fila de envio em overflow — conteúdo acima da cap. Verificar a reserva (slider), util e que o alocador não está bloqueado (tick a correr). Bursts de arranque são normais.
Um canal fica com bitrate baixo / QP alto
Conteúdo estático (correto — liberta largura de banda) ou max demasiado baixo no perfil. Verificar min/avg/max e a complexidade real da fonte.
Gráfico treme / borda esquerda apaga
Lado cliente — hard refresh (Ctrl+Shift+R) para o novo bundle JS. Os dados fluem via push incremental, eixo = intervalo selecionado.
cap % / buffer não muda após edição
Re-render LiveView — hard refresh. Os valores atualizam uma vez por segundo via tracked assign.
SAT UDP SSM ─┐
├─ gst_encoder.py(K1) CBR ─ TCP127 ─┐ ZMQ complexidade
(por ├─ gst_encoder.py(K2) CBR ─ TCP127 ─┤ ▲ │
canal) └─ gst_encoder.py(Kn) CBR ─ TCP127 ─┤ │ ▼
▼ BalanceRouter
┌─────── NIF StatMux ───────┐ │ set_bitrate
│ ring por entrada (drop-old)│◄─┘
│ drain 2-pass (A prio, V) │
│ alocador: w=cmplx×max │
│ ×headroom% (slider 78-90) │
│ exclusão stale (>2s) │
│ NULL pad -> wire cap CBR │
│ PCR pass-through │
└────────────┬───────────────┘
▼
MPTS UDP multicast (CBR cap)gst_encoder.py por stream: entrada SAT UDP SSM → nvh264enc rc-mode=cbr. Bitrate controlado pelo BalanceRouter via set_bitrate. Complexidade de cena (SAD/px) reportada via ZMQ ao alocador.
Ring por entrada (loopback TCP do encoder), drain 2-pass (prioridade áudio, vídeo completo), alocador proporcional, NULL pad exatamente à cap do fio (CBR), PCR pass-through do encoder.
Um MPTS (PAT/PMT/SDT gerado pelo NIF) → UDP multicast na VLAN de saída. Fio estritamente CBR à mux.bitrate (preenchimento NULL).
Linha 1: ⚠ (problemas de validação) · PNR · nome numa moldura (cor = estado).
Linha 2+: min·avg·max kbps · complexidade prevista Δ low/med/high/peak · INPUT LOST · ⚙URL · encoder Mbps · SID · service_type.
Cor do nome = estado do stream:
Gráfico de saída do fio (stacked bands por canal)
Linha vermelha 'cap': teto CBR do fio (mux.bitrate) — NULL preenche até aqui.
Linha laranja 'headroom N%': = cap × slider headroom / 100. Os targets set_bitrate do encoder apontam aqui. Move-se com o slider.
Área cinzenta com pontos pretos entre conteúdo e cap = padding NULL (reserva não utilizada). Mais pontos = mais reserva.
Clique na banda colorida de um canal → expande automaticamente o cartão desse canal abaixo.
MuxEitAdapter lê eventos do sistema EPG por serviço, constrói secções DVB EIT (nome, hora, duração, descrição, género) e empacota-as no PID 0x12. As versões de secção incrementam com mudanças EPG — o STB apanha a atualização.
EIT é injetado diretamente no mux NIF, uniformemente NO espaço null (1 pacote / N output + p/f periodicamente) — substituindo a cascata tsp externa bursty. Pico/avg ≈ 1,1, sem expulsão de conteúdo, cap mantida.
Tabela 0x4E — o evento a decorrer + próximo evento. Injetado a cada ≤2 s para 'agora/próximo' imediato no STB.
O mux regenera periodicamente o EIT .bin a partir do EPG (~15 s) — uma mudança de agenda EPG chega ao fio sem reinício. O cabeçalho do mux mostra 'EIT ✓' quando ativo.
Tabelas 0x50/0x51 — a agenda completa horas/dias à frente (segmentos). Distribuída uniformemente no stream.
Indicador do fluxo real vs cap. Centro = cap (0). Marcador à esquerda (verde) = reserva (Σ conteúdo abaixo da cap); direita (vermelho) = overflow. Ao vivo, move-se a cada tick.
Indicador diff do fio vs cap
Mais baixo (→78): mais reserva NULL, mais seguro, utilização da cap menos justa.
Mais alto (→90): utilização da cap mais justa, menos NULL.
⚠ ≥88%: slider fica amarelo — perto do teto de congelamento de overflow de ring. Hard-clamp 78–90 (não pode atingir 100 — guarda contra vídeo congelado).
Assimetria: baixar adiciona fiável reserva; subir é limitado pelo clamp max_bps por stream (o alocador não empurra um canal além do seu max → seguro).
Controla quanto da cap os encoders visam: target encoder = orçamento × headroom% / 100 (linha laranja no gráfico). Muda ao vivo (efeito a partir do próximo tick de 10 ms), persistido em SystemConfig.
Um canal falha (crash do encoder / perda de sinal) ⇒ o mux continua a correr, outros canais não afetados, fio mantém a cap (NULL absorve a interrupção), CC=0 nos sobreviventes. Threads de ring por entrada são independentes. O canal em falha mostra nome a vermelho piscando + badge INPUT LOST; o encoder é reiniciado supervisionado e o canal recupera automaticamente.
PCR e PES PTS/DTS são pass-through do encoder (domínio de relógio único ⇒ coerente, sem drift). Jitter PCR residual ~dezenas de ms é inerente (tick de saída 10 ms + UDP) — STB/VLC PLL tolera, CC=0. Conformidade hard DVB-S2 com modulador está fora do âmbito para o caso de uso IPTV atual.
CAT — acesso condicional (apenas com scrambling / ECM).
EIT — informação de eventos (EPG) — do sistema EPG, ver abaixo.
NIT — info de rede (ID de rede, nome, transport stream).
PAT — lista de programas (mapeia PNR → PMT PID).
PMT — por programa: PIDs vídeo/áudio/PCR, codecs, descritores.
SDT — nomes de serviços e fornecedores (o que o STB mostra na lista).
TDT/TOT — hora e data + offset local (relógio do STB).