Allinea i doc al flusso live senza OverlayRelay.
Documenta overlay in app, YoutubeRelay copy+AAC in Sidekiq e HLS sul path camera; aggiorna checklist e troubleshooting. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -47,7 +47,7 @@ Poi tocca la card o conferma dal foglio: si apre il wizard (Partita → Trasmiss
|
||||
|
||||
1. Nel wizard, seleziona **YouTube Live** (se il piano lo consente e il canale è pronto)
|
||||
2. Completa i passi → **Camera**
|
||||
3. Il telefono invia RTMP a MediaMTX; il server avvia **automaticamente** overlay (tabellone + logo) e RTMPS verso YouTube — nessun intervento manuale
|
||||
3. Il telefono invia RTMP a MediaMTX (con overlay tabellone già nel flusso, se attivo); il server avvia **automaticamente** il relay RTMPS verso YouTube (`Streams::YoutubeRelay`) — nessun intervento manuale
|
||||
|
||||
## Sviluppo locale
|
||||
|
||||
@@ -67,9 +67,9 @@ Vedi [YOUTUBE_MORNING_CHECKLIST.md](./YOUTUBE_MORNING_CHECKLIST.md) — APK **1.
|
||||
| YouTube disabilitato | Premium Light o Full |
|
||||
| YouTube non selezionabile | Token canale Match Live TV sul server (`admin` → YouTube piattaforma) |
|
||||
| OAuth «app non verificata» | Utente di test Google + Avanzate |
|
||||
| Live non su YouTube | Pipeline automatica (`Youtube::LivePipeline`): overlay → ingest active → activate. Controlla `docker logs infra-sidekiq-1` per `[OverlayRelay] started` e `[YoutubeBroadcastActivate]`. |
|
||||
| YouTube «In programma» / copertina | Nessun intervento manuale: entro ~20–90s il server avvia overlay (tee RTMPS) e attiva la broadcast quando ingest è `active`. |
|
||||
| YouTube resta in «waiting» | `log/overlay_relay_<session_id>.log` in **sidekiq**; poll ogni 5s (`StreamPublisherSyncJob`). Attivazione ritenta ~6 min. |
|
||||
| Live non su YouTube | Pipeline `Youtube::LivePipeline` + `YoutubeRelay`. Controlla `docker logs infra-sidekiq-1` per `[YoutubeRelay] started` / `[YoutubeBroadcastActivate]`. |
|
||||
| YouTube «In programma» / copertina | Entro ~20–90s il server avvia il relay RTMPS e attiva la broadcast quando l’ingest è pronto; in pausa MediaMTX manda la slate. |
|
||||
| YouTube resta in «waiting» | Log `log/youtube_relay_*.log` in **sidekiq**; poll `StreamPublisherSyncJob`. Attivazione ritenta ~6 min. |
|
||||
| Banner «sync limitata: Cannot add event after closing» | APK aggiornato: il wizard non apre un secondo WebSocket `controller` dopo la camera. |
|
||||
| Timer «IN DIRETTA» sbagliato (es. 10:33 subito) | `started_at` arriva dal server al primo frame RTMP; fino ad allora il timer resta a 0. |
|
||||
| App 0 Mbps / 0 fps ma LIVE | RTMP non pubblica: controlla permessi camera, messaggio «Connessione RTMP»; relay YouTube non parte finché il telefono non è online su MediaMTX. |
|
||||
|
||||
+22
-19
@@ -2,7 +2,7 @@
|
||||
|
||||
Documento di riferimento per l'intero sistema: rete, container Docker, flussi video, replay, monitoraggio ops e rilasci.
|
||||
|
||||
**Ultimo aggiornamento:** 2026-06-14
|
||||
**Ultimo aggiornamento:** 2026-07-29
|
||||
**Produzione:** `eminux@192.168.1.146` → `/opt/matchlivetv`
|
||||
|
||||
---
|
||||
@@ -193,39 +193,41 @@ Vedi anche [`LIVE_STREAMING.md`](LIVE_STREAMING.md) per pause, overlay e player
|
||||
### Flusso ingest
|
||||
|
||||
```
|
||||
App Android (RTMP 720p, AAC mono)
|
||||
App (Android/iOS) RTMP 720p AAC mono
|
||||
+ overlay GPU (tabellone/watermark) se overlay_kind ≠ none
|
||||
│
|
||||
▼
|
||||
MediaMTX :1935 — path live/match_{uuid} (grezzo, telefono)
|
||||
│
|
||||
▼ overlay ffmpeg (Streams::OverlayRelay)
|
||||
path live/match_{uuid}_air (tabellone + badge bruciati nel video)
|
||||
MediaMTX :1935 — path live/match_{uuid}
|
||||
│
|
||||
├──► HLS :8888 ──► edge /hls/ ──► spettatori web
|
||||
└──► ffmpeg -c copy ──► YouTube RTMP (Streams::YoutubeRelay, Premium)
|
||||
└──► (solo platform=youtube)
|
||||
Streams::YoutubeRelay in Sidekiq
|
||||
ffmpeg: -c:v copy, -c:a aac ──► YouTube RTMPS
|
||||
```
|
||||
|
||||
| Componente | Ruolo |
|
||||
|------------|--------|
|
||||
| `Mediamtx::PathManager` | Crea path dinamici via API :9997 |
|
||||
| Path dinamici via API :9997 | `Mediamtx::Client` (`create_path`, recording patch, …) |
|
||||
| `alwaysAvailable` + slate | Copertina `/slates/offline.mp4` senza interrompere HLS |
|
||||
| `Streams::OverlayRelay` | Ricodifica con PNG 1280×720 aggiornata ogni ~2s |
|
||||
| `Streams::YoutubeRelay` | Legge HLS da `mediamtx:8888` (non più via Rails) |
|
||||
| `Mediamtx::PublisherSync` | Stato publisher online/offline in Rails |
|
||||
| Overlay tabellone | **App nativa** (non più ricodifica server) |
|
||||
| `Streams::YoutubeRelay` | Legge RTMP (o HLS) da MediaMTX; copy video + AAC → YouTube |
|
||||
| `Mediamtx::PublisherSync` | Publisher online/offline, go_live, recording on/off |
|
||||
|
||||
Dettaglio pause, player e ruolo ffmpeg: [`LIVE_STREAMING.md`](LIVE_STREAMING.md).
|
||||
|
||||
### Pausa e continuità
|
||||
|
||||
1. App chiama `PATCH /sessions/:id/pause` → stop RTMP.
|
||||
2. MediaMTX passa alla slate sullo **stesso path** (stesso URL HLS).
|
||||
3. YouTube relay continua sulla stessa uscita (vede slate).
|
||||
3. Se attivo, YouTube relay continua sulla stessa uscita (vede slate).
|
||||
4. Ripresa: `resume` → app ripubblica RTMP; MediaMTX concatena camera senza nuovo URL.
|
||||
|
||||
**Recording inline MediaMTX disabilitato** (`record: false` su path live): il recorder al switch slate→camera distruggeva il muxer HLS. I replay usano job dedicato (vedi sotto).
|
||||
**Recording:** path creato con `record: false`; acceso solo con publisher online e entitlement (`PublisherSync`). I replay usano job dedicato (vedi sotto).
|
||||
|
||||
### Pagina live web
|
||||
|
||||
- URL: `/live/:session_id`
|
||||
- Player: HLS.js su `HLS_PUBLIC_URL/live/match_{uuid}_air/index.m3u8`
|
||||
- Player: HLS.js su `HLS_PUBLIC_URL/live/match_{uuid}/index.m3u8`
|
||||
- Stato: `GET /live/:id/status.json` (badge, recovery buffer, no-store)
|
||||
|
||||
---
|
||||
@@ -278,8 +280,8 @@ App Android ◄──── REST API (/api/v1/...) ────► Rails
|
||||
│
|
||||
Sidekiq ◄── Redis ◄──────┘
|
||||
│
|
||||
├── HealthMonitorJob, UploadJob, overlay refresh
|
||||
├── YoutubeRelay, OverlayRelay (ffmpeg)
|
||||
├── HealthMonitorJob, UploadJob (merge/thumbnail ffmpeg)
|
||||
├── YoutubeRelay (ffmpeg copy+AAC, solo dirette YouTube)
|
||||
└── PublishToYoutubeJob, purge replay, ecc.
|
||||
```
|
||||
|
||||
@@ -395,10 +397,10 @@ cd infra && cp .env.example .env && docker compose up -d --build
|
||||
| Documento | Contenuto |
|
||||
|-----------|-----------|
|
||||
| [`infrastructure/SERVER_DEPLOYMENT.md`](infrastructure/SERVER_DEPLOYMENT.md) | Bootstrap server, NPM, cron, backup |
|
||||
| [`LIVE_STREAMING.md`](LIVE_STREAMING.md) | MediaMTX, pause, overlay, HLS.js |
|
||||
| [`LIVE_STREAMING.md`](LIVE_STREAMING.md) | MediaMTX, pausa, HLS, ruolo ffmpeg |
|
||||
| [`REPLAY_MODULE.md`](REPLAY_MODULE.md) | Garage, retention, YouTube VOD |
|
||||
| [`OPS_MONITORING.md`](OPS_MONITORING.md) | ntfy, variabili ops, troubleshooting |
|
||||
| [`TABELLONI_E_OVERLAY.md`](TABELLONI_E_OVERLAY.md) | Grafica in stream |
|
||||
| [`TABELLONI_E_OVERLAY.md`](TABELLONI_E_OVERLAY.md) | Tabelloni e overlay (app nativa) |
|
||||
| [`PRODUCT_PREMIUM.md`](PRODUCT_PREMIUM.md) | Piani e funzionalità premium |
|
||||
| [`native/android/README.md`](../native/android/README.md) | App Android |
|
||||
|
||||
@@ -412,5 +414,6 @@ cd infra && cp .env.example .env && docker compose up -d --build
|
||||
| 2026-06 | Replay: redirect 302 a `/media/` → Garage (fuori da Puma) |
|
||||
| 2026-06 | HLS live: edge → MediaMTX (fuori da Puma) |
|
||||
| 2026-06 | Ops: check `http_rails` + `http_public` separati, latenza p95 `UpLatencyTracker` |
|
||||
| 2026-06 | `RAILS_MAX_THREADS=5`; relay YouTube legge `mediamtx:8888` diretto |
|
||||
| 2026-06 | `RAILS_MAX_THREADS=5`; relay YouTube da MediaMTX (non via Puma) |
|
||||
| 2026-06 | Cleanup path MediaMTX orfani (`Mediamtx::CleanupOrphanPaths`); delete path sempre a fine diretta |
|
||||
| 2026-07 | Rimosso overlay server (`OverlayRelay`); tabellone bruciato in app; HLS su path camera (non `*_air`); `YoutubeRelay` = copy video + AAC |
|
||||
|
||||
+47
-32
@@ -1,23 +1,25 @@
|
||||
# Diretta live — architettura MediaMTX
|
||||
|
||||
## Idea (non è folle)
|
||||
**Ultimo aggiornamento:** 2026-07-29
|
||||
|
||||
## Idea
|
||||
|
||||
MediaMTX espone **un unico flusso di uscita** per path (`live/match_{uuid}`):
|
||||
|
||||
1. **Telefono in onda** → publisher RTMP (camera).
|
||||
1. **Telefono in onda** → publisher RTMP (camera + **overlay grafico bruciato in app**).
|
||||
2. **Pausa / rete assente** → `alwaysAvailable` con file slate (`/slates/offline.mp4`) **senza interrompere** l’HLS verso il sito.
|
||||
3. **YouTube** → `ffmpeg` in container Rails/Sidekiq legge **sempre** quell’uscita (`-c copy`) e inoltra a `rtmp://a.rtmp.youtube.com/live2/...` per tutta la sessione.
|
||||
3. **YouTube** (solo se `platform=youtube`) → `Streams::YoutubeRelay` in **Sidekiq**: un processo `ffmpeg` legge RTMP/HLS da MediaMTX e inoltra a YouTube (`-c:v copy`, audio ricodificato in AAC).
|
||||
|
||||
Il telefono **non** invia la copertina: risparmia banda; lo switch è lato server.
|
||||
Il telefono **non** invia la copertina di pausa: risparmia banda; lo switch slate ↔ camera è lato MediaMTX.
|
||||
|
||||
## Componenti
|
||||
|
||||
| Pezzo | Ruolo |
|
||||
|--------|--------|
|
||||
| App Android | RTMP 48 kHz mono, 720p — solo quando in onda |
|
||||
| MediaMTX | Path dinamico, `alwaysAvailable` + slate |
|
||||
| `Streams::YoutubeRelay` | Relay continuo verso YouTube (no hook wget su distroless) |
|
||||
| `Mediamtx::PublisherSync` | Stato Rails da API paths (publisher online) |
|
||||
| App Android / iOS | RTMP 720p AAC mono; overlay tabellone/watermark in GPU sul flusso |
|
||||
| MediaMTX | Path dinamico, `alwaysAvailable` + slate, HLS |
|
||||
| `Streams::YoutubeRelay` | Relay continuo verso YouTube (solo Sidekiq con `YOUTUBE_RELAY_WORKER=1`) |
|
||||
| `Mediamtx::PublisherSync` | Stato Rails da API paths (publisher online) + recording on/off |
|
||||
| `/hls/...` (edge → MediaMTX in prod; Rails proxy in dev) | Player web |
|
||||
|
||||
## Pausa e continuità
|
||||
@@ -27,36 +29,44 @@ Il telefono **non** invia la copertina: risparmia banda; lo switch è lato serve
|
||||
3. MediaMTX → passa alla slate sul **medesimo path** (senza interrompere l’uscita HLS).
|
||||
4. **Ripresa** → API `resume` → `connecting`, recording on, app `resumeStream()`; MediaMTX concatena di nuovo camera sulla stessa uscita HLS.
|
||||
5. **Sito** → **un solo** player HLS per tutta la sessione (mai reload in pausa/ripresa). Copertina brand (`brand/CopertinaCanale_6.png` → `infra/slates/offline.mp4`) muxata da MediaMTX; in pausa solo un messaggio leggero sopra il video.
|
||||
6. **YouTube** → relay ffmpeg continuo; vede la stessa uscita MediaMTX.
|
||||
6. **YouTube** → se attivo, il relay ffmpeg continua sulla stessa uscita MediaMTX (vede slate in pausa).
|
||||
|
||||
Non fare `patch` del path MediaMTX in pausa: ricarica il path e interrompe gli HLS reader.
|
||||
|
||||
**Recording**: disabilitato sul path live (`record: false`) — il recorder MediaMTX al switch slate→camera distruggeva il muxer HLS (`too many reordered frames`). I replay vanno gestiti con job dedicato (vedi `REPLAY_MODULE.md`).
|
||||
**Recording**: sul path live parte `record: false`; viene acceso via API solo con publisher online e entitlement (`PublisherSync`). I replay finiscono in Garage con job dedicato (vedi `REPLAY_MODULE.md`).
|
||||
|
||||
Il player web resta attivo finché `ready|available|online` sul path (non solo quando il telefono è `online`).
|
||||
|
||||
## Infrastruttura vs browser
|
||||
|
||||
```
|
||||
Telefono RTMP ──► MediaMTX path live/match_{uuid}
|
||||
Telefono RTMP (camera + overlay app)
|
||||
│
|
||||
publisher ON ├─► camera (H.264/AAC)
|
||||
publisher OFF└─► alwaysAvailable → /slates/offline.mp4
|
||||
▼
|
||||
MediaMTX path live/match_{uuid}
|
||||
│
|
||||
├─► HLS (segmenti ~1s) ──► edge /hls/ → MediaMTX (prod) o proxy Rails (dev) ──► player web (HLS.js)
|
||||
└─► RTMP lettura ──► Streams::YoutubeRelay (ffmpeg -c copy) ──► YouTube
|
||||
publisher ON → camera H.264/AAC (già con tabellone se overlay ≠ none)
|
||||
publisher OFF → alwaysAvailable → /slates/offline.mp4
|
||||
│
|
||||
├──► HLS (segmenti ~1s) ──► edge /hls/ → MediaMTX ──► player web (HLS.js)
|
||||
└──► (solo platform=youtube)
|
||||
Streams::YoutubeRelay
|
||||
ffmpeg: -c:v copy, -c:a aac ──► RTMPS YouTube
|
||||
```
|
||||
|
||||
| Responsabilità | Dove |
|
||||
|----------------|------|
|
||||
| Switch copertina ↔ live | **MediaMTX** (stesso path, stesso URL HLS) |
|
||||
| Continuità YouTube | **Relay ffmpeg** sulla stessa uscita |
|
||||
| Badge «In onda» / stato pausa | **Rails** (`status.json`, `PublisherSync`) |
|
||||
| Tabellone / watermark sullo stream | **App nativa** (GPU → RTMP) |
|
||||
| Continuità YouTube | **`Streams::YoutubeRelay`** (ffmpeg in Sidekiq) |
|
||||
| Badge «In onda» / stato pausa | **Rails** (`status.json`, `PublisherSync`) + UI web |
|
||||
| Uscire dal buffer copertina dopo ripresa | **Browser** (`pendingLiveRecovery`, reload HLS) |
|
||||
| Fluidità playback | **Browser** (evitare seek continui su `liveSyncPosition`) |
|
||||
|
||||
Lo switch slate→camera **non** richiede un nuovo URL lato player: MediaMTX concatena sulla playlist. Il sito può però restare «indietro» nel buffer HLS (ancora segmenti della slate) anche con `publisher_online: true` — da qui i recovery controllati in `show.html.erb`, **senza** chiamare `jumpToLiveEdge()` a ogni frammento bufferizzato.
|
||||
|
||||
URL HLS pubblico: `https://www.matchlivetv.it/hls/live/match_{uuid}/index.m3u8` (`StreamSession#effective_hls_path_name` = path camera, **non** più `*_air`).
|
||||
|
||||
### Player web — anti-scatti (HLS.js)
|
||||
|
||||
Configurazione in `backend/app/views/public/live/show.html.erb`:
|
||||
@@ -67,27 +77,32 @@ Configurazione in `backend/app/views/public/live/show.html.erb`:
|
||||
- Sync iniziale una volta su `LEVEL_LOADED`; recovery solo se `liveEdgeLagSec() > 4` o `pendingLiveRecovery`.
|
||||
- Interval 6s solo durante recovery attivo (`tryEscapeCoverBuffer`).
|
||||
|
||||
### Grafica nel video (overlay server)
|
||||
### Grafica nel video (overlay)
|
||||
|
||||
Tabellone, badge stato («In onda», «In pausa», «Copertina», …) e banner **Match Live TV** sono **bruciati nel flusso** da `Streams::OverlayRelay`:
|
||||
**Non** c’è più un relay server `Streams::OverlayRelay` che ricodifica con PNG.
|
||||
|
||||
```
|
||||
Telefono → live/match_{uuid} (grezzo)
|
||||
└─► ffmpeg + PNG 1280×720 (Streams::Overlay::Refresh ogni ~2s)
|
||||
└─► live/match_{uuid}_air
|
||||
├─► HLS (sito, anche fullscreen)
|
||||
└─► YouTube relay (-c copy)
|
||||
```
|
||||
Il tabellone è composito **sul telefono** prima dell’RTMP (vedi [`TABELLONI_E_OVERLAY.md`](TABELLONI_E_OVERLAY.md)):
|
||||
|
||||
- `Streams::Overlay::SvgBuilder` + `rsvg-convert` generano `tmp/stream_overlays/{session_id}/overlay.png`
|
||||
- `Streams::Overlay::Refresh` + `bin/overlay_png_feeder.rb` aggiornano la PNG ogni ~2s (e su punteggio/stato); il feeder la invia a ffmpeg via FIFO perché `-loop 1` non rilegge il file su disco
|
||||
- Volume Docker condiviso `stream_overlays` tra **rails** (ffmpeg) e **sidekiq** (OverlayRefreshJob); altrimenti il job scrive PNG su un filesystem diverso e badge/punteggio restano congelati
|
||||
- La pagina live **non** sovrappone più HTML sul player (evita incongruenze con fullscreen / YouTube)
|
||||
- **Qualità video**: il relay normalizza a **30 fps CFR** (l’app RTMP può inviare timestamp errati) e ricodifica a **≥4.5 Mbps** (`fast`, no B-frame). Override: `OVERLAY_RELAY_VIDEO_KBPS`, `OVERLAY_RELAY_X264_PRESET`, `OVERLAY_RELAY_FPS`.
|
||||
1. `OverlayState` da `effectiveOverlayKind` + `ScoreState`
|
||||
2. `OverlayRenderer` / canvas → bitmap
|
||||
3. Filtro GPU sul encoder RTMP
|
||||
|
||||
La pagina live può mostrare badge HTML di stato (in onda / pausa / attesa); non sostituisce il tabellone nel video.
|
||||
|
||||
### Ruolo di ffmpeg oggi
|
||||
|
||||
| Quando | Cosa | Peso CPU |
|
||||
|--------|------|----------|
|
||||
| Diretta `platform=youtube` | `YoutubeRelay`: demux + **copy video** + AAC audio → RTMPS | Medio-basso **per diretta**, scala con N processi |
|
||||
| Diretta solo `matchlivetv` | Nessun ffmpeg in live | Quasi zero |
|
||||
| Fine diretta | `UploadFromSession`: concat segmenti (`-c copy`) | Picco breve |
|
||||
| Post-process | `GenerateThumbnail`: un frame → JPEG | Trascurabile |
|
||||
|
||||
Non esiste più la ricodifica H.264 server-side dell’overlay (era il carico principale).
|
||||
|
||||
### Punteggio (persistenza)
|
||||
|
||||
L’app mobile persiste il punteggio con `PATCH /api/v1/sessions/:id/score` (`Scoring::SyncState`), poi refresh overlay.
|
||||
L’app mobile persiste il punteggio con `PATCH /api/v1/sessions/:id/score` (o `score_action`) → `Scoring::SyncState`, poi aggiorna l’overlay locale in camera.
|
||||
|
||||
`GET /live/:id/status.json` resta per messaggi sotto il player e recovery HLS; `Cache-Control: no-store`.
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
Questo documento descrive come Match Live TV modella gli sport, applica i regolamenti di punteggio e li traduce in **tabelloni** (logica + controlli) e **overlay** (grafica sul video in diretta).
|
||||
|
||||
**Importante (2026-07):** l’overlay video è composito **solo sull’app nativa** (Android/iOS) e viaggia già nel RTMP verso MediaMTX. Non esiste più un relay server (`Streams::OverlayRelay`) che ricodifica con PNG. Vedi anche [`LIVE_STREAMING.md`](LIVE_STREAMING.md).
|
||||
|
||||
---
|
||||
|
||||
## Panoramica
|
||||
@@ -162,12 +164,14 @@ Esempio: il basket ammette `[basket, none]` — si può trasmettere con tabellon
|
||||
|
||||
### Layout app mobile (Android e iOS)
|
||||
|
||||
Sull’app nativa l’overlay è composito in GPU senza ricodifica aggiuntiva:
|
||||
Sull’app nativa l’overlay è composito in GPU **prima** dell’encode RTMP (nessuna ricodifica server aggiuntiva):
|
||||
|
||||
1. `BroadcastScreen` costruisce un `OverlayState` a partire da `effectiveOverlayKind` e `ScoreState`.
|
||||
2. `OverlayRenderer` → `OverlayCanvasRenderer` disegna gli elementi su bitmap trasparente.
|
||||
3. Il bitmap viene applicato come filtro sul flusso RTMP (OpenGL su Android, HaishinKit su iOS).
|
||||
|
||||
MediaMTX e `YoutubeRelay` ricevono quindi un flusso già “brandizzato”; il relay YouTube fa solo `-c:v copy` (+ AAC).
|
||||
|
||||
Elementi grafici (`OverlayCanvasRenderer`):
|
||||
|
||||
| Elemento | Quando è attivo |
|
||||
|
||||
@@ -1,56 +1,32 @@
|
||||
# YouTube Live — verificato ✅ (5 giu 2026)
|
||||
# YouTube Live — checklist operativa
|
||||
|
||||
## Test completato automaticamente
|
||||
**Aggiornato:** 2026-07-29
|
||||
Pipeline attuale: telefono → MediaMTX → `Streams::YoutubeRelay` (ffmpeg copy+AAC in Sidekiq) → YouTube. Overlay tabellone in **app**, non più `OverlayRelay` server.
|
||||
|
||||
Pipeline end-to-end **funzionante** sul server di produzione:
|
||||
|
||||
| Step | Esito |
|
||||
|------|--------|
|
||||
| Setup broadcast + stream_key | ✅ |
|
||||
| Overlay ffmpeg con tee RTMPS YouTube | ✅ |
|
||||
| Copertina / tabellone su YouTube | ✅ |
|
||||
| RTMP simulato (test pattern) | ✅ |
|
||||
| Broadcast `lifecycle=live` | ✅ |
|
||||
|
||||
**Prova visiva:** https://www.youtube.com/watch?v=0WmCsW_Luik
|
||||
(Titolo: *Tigers Volley vs Avversario prova* — sessione di test, ora terminata)
|
||||
|
||||
## APK da installare sul telefono
|
||||
|
||||
```bash
|
||||
# Sul PC (già compilato):
|
||||
mobile/build/app/outputs/flutter-apk/app-release.apk
|
||||
# Versione: 1.2.10+13 — API https://www.matchlivetv.it
|
||||
```
|
||||
|
||||
Installa sul Redmi (sostituisce versioni precedenti).
|
||||
|
||||
## Avvio diretta dall’app (2 minuti)
|
||||
|
||||
1. Partita → **YouTube Live** → wizard → **Camera**
|
||||
2. Attendi sul passo rete che compaia il link YouTube (poll automatico, fino a 3 min)
|
||||
3. Avvia: entro ~90s su YouTube compare **copertina**, poi video con tabellone quando il telefono pubblica RTMP
|
||||
|
||||
## Test server senza telefono
|
||||
## Verifica rapida su server
|
||||
|
||||
```bash
|
||||
ssh eminux@192.168.1.146
|
||||
docker exec infra-rails-1 bin/rails youtube:e2e
|
||||
docker exec infra-rails-1 bin/rails youtube:e2e # se il task è ancora presente
|
||||
docker exec infra-sidekiq-1 pgrep -a ffmpeg # deve contenere rtmps://...youtube.com/live2
|
||||
docker logs infra-sidekiq-1 2>&1 | grep -E 'YoutubeRelay|YoutubeBroadcastActivate|LivePipeline' | tail -40
|
||||
```
|
||||
|
||||
## Avvio diretta dall’app
|
||||
|
||||
1. Partita → **YouTube Live** → wizard → **Camera**
|
||||
2. Attendi sul passo rete che compaia il link YouTube (poll automatico)
|
||||
3. Avvia: entro ~90s su YouTube compare video (slate MediaMTX se il telefono non pubblica ancora; tabellone quando l’app pubblica RTMP con overlay)
|
||||
|
||||
## Se qualcosa non va
|
||||
|
||||
| Sintomo | Cosa fare |
|
||||
|---------|-----------|
|
||||
| «YouTube temporaneamente occupato» | Attendi 15–20 min (rate limit Google). **Non** creare molte sessioni di fila. |
|
||||
| Pagina YouTube vuota | `docker logs infra-sidekiq-1 \| grep OverlayRelay` — manca `stream_key` o tee RTMPS |
|
||||
| App errore avvio diretta | APK 1.2.10+13; controlla rete verso matchlivetv.it |
|
||||
| Pagina YouTube vuota | `docker logs infra-sidekiq-1 \| grep YoutubeRelay` — manca `stream_key` o relay non partito |
|
||||
| App 0 Mbps / 0 fps ma LIVE | RTMP non pubblica: permessi camera / messaggio «Connessione RTMP»; relay YouTube non parte finché MediaMTX non vede il publisher |
|
||||
| YouTube resta in waiting | Log relay in sidekiq; `StreamPublisherSyncJob` + activate retry |
|
||||
|
||||
## Fix deployati (server)
|
||||
## Note storiche
|
||||
|
||||
- Retry setup broadcast su rate limit (fino a 15 tentativi)
|
||||
- Un solo job setup per sessione (lock Redis)
|
||||
- Throttle API YouTube globale
|
||||
- Riavvio overlay quando arriva `stream_key` dopo start senza tee
|
||||
- Activate solo con overlay attivo + broadcast pronta
|
||||
Checklist precedente (giu 2026) citava `OverlayRelay` / tee RTMPS dall’overlay server: **deprecato**. Non cercare più log `OverlayRelay` o `overlay_relay_*.log`.
|
||||
|
||||
@@ -7,7 +7,7 @@ Match Live TV supporta due modalità YouTube, legate al piano società:
|
||||
| **Premium Light** | `matchlivetv_light` | Canale YouTube **Match Live TV** (gestito da te) |
|
||||
| **Premium Full** | `team` | Canale YouTube **della società** (OAuth del club) |
|
||||
|
||||
Flusso tecnico: telefono → RTMP MediaMTX → (relay ffmpeg) → YouTube Live.
|
||||
Flusso tecnico: telefono (camera + overlay app) → RTMP MediaMTX → `Streams::YoutubeRelay` (ffmpeg `-c:v copy`, AAC) → YouTube Live.
|
||||
|
||||
---
|
||||
|
||||
@@ -85,5 +85,5 @@ YOUTUBE_MOCK_STREAM_KEY=mock-stream-key
|
||||
## 6. Produzione
|
||||
|
||||
- `YOUTUBE_REDIRECT_URI` deve essere **esattamente** quello registrato in Google (HTTPS, dominio pubblico).
|
||||
- Il relay ffmpeg gira nel container MediaMTX (`runOnReady`).
|
||||
- Il relay ffmpeg (`Streams::YoutubeRelay`) gira nel container **sidekiq** (`YOUTUBE_RELAY_WORKER=1`), non su MediaMTX `runOnReady`.
|
||||
- Apri RTMP **1935** sul router verso il server.
|
||||
|
||||
Reference in New Issue
Block a user