31 KiB
Sì, l’idea è possibile, ma bisogna distinguere due modi molto diversi di far apparire più pezzi come un unico video.
- Non basta dividere l’MP4 in file più piccoli
Non possiamo prendere l’MP4 completo e spezzarlo così:
video_part_01.mp4 video_part_02.mp4 video_part_03.mp4
per poi concatenarli lato server come se fossero un unico file.
Il receiver avrebbe comunque bisogno di:
una dimensione totale stabile; offset Range coerenti; un’unica timeline; timestamp continui; intestazioni e indici validi; transizioni senza reset del decoder.
Inoltre, il problema attuale non è la dimensione di segment_00000.m4s: il receiver lo interrompe sempre dopo 589.824 byte, anche se il segmento completo è circa 2,34 MB. Questo indica che trova dati che non riesce a interpretare, non che il segmento sia semplicemente troppo grande.
Ridurre i segmenti fMP4 da circa 3 secondi a 1 secondo probabilmente non risolverebbe, perché il formato interno rimarrebbe:
moof + mdat
che è proprio quello che il receiver attuale sta rifiutando.
- La soluzione compatibile è HLS con segmenti MPEG-TS
La tua idea funziona bene se ogni pacchetto è un segmento autonomo MPEG Transport Stream:
master.m3u8 segment_00000.ts segment_00001.ts segment_00002.ts
La playlist li presenta come un unico video continuo:
#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:3.000, segment_00000.ts #EXTINF:3.000, segment_00001.ts #EXTINF:3.000, segment_00002.ts
Il receiver vede una sola timeline, anche se il server consegna tanti file. Google Cast documenta il supporto del contenitore MP2T, oltre a HLS come protocollo di streaming.
Questa strada è probabilmente più compatibile con il vecchio runtime del tuo TV:
CrKey/1.56 Chrome 92 Android 9
Perché MPEG-TS è diverso da fMP4
Nel nostro HLS fMP4 abbiamo:
init.mp4 segment_00000.m4s segment_00001.m4s
Ogni .m4s dipende da init.mp4 e dal supporto Media Source Extensions del player.
Con MPEG-TS avremmo invece:
segment_00000.ts segment_00001.ts
Ogni segmento contiene le informazioni necessarie per trasportare audio e video. Il player può elaborare i segmenti in successione senza EXT-X-MAP e senza initialization segment fMP4.
Possiamo conservare la transcodifica hardware
Sì. La parte costosa che già funziona può rimanere:
MediaCodec decoder → ridimensionamento 1080p → MediaCodec H.264 High Level 4.1 → AAC
Dobbiamo sostituire solamente il muxer finale:
oggi: H.264 + AAC → fragmented MP4
nuovo: H.264 + AAC → MPEG-TS
Il problema è che Media3 Transformer offre il muxer MP4 e fragmented MP4 che abbiamo usato, ma non ci sta fornendo direttamente un’esportazione HLS MPEG-TS. Media3 permette comunque di sostituire componenti della pipeline quando serve più controllo.
Architettura possibile
La pipeline dovrebbe diventare:
Media3 / MediaCodec ↓ campioni codificati H.264 e AAC ↓ MPEG-TS muxer ↓ segment_00000.ts.part ↓ segmento completo segment_00000.ts ↓ aggiornamento master.m3u8
Dopo tre segmenti:
start.jpg → circa 4-6 secondi di preparazione → queue HLS → segmenti TS letti dal Default Receiver → muxing continua in background
Il comportamento sarebbe trasparente come desideri.
Dimensione consigliata dei pacchetti
Userei inizialmente:
durata segmento: circa 3 secondi buffer iniziale: 3 segmenti contenuto pronto prima del play: circa 9 secondi
Non bisogna creare segmenti estremamente piccoli. Google specifica che il Web Receiver non supporta segmenti sotto 0,1 secondi, ma segmenti da 2-4 secondi sono una scelta molto più normale e gestibile.
Dato che il telefono produce circa tre secondi di video in un secondo di elaborazione, preparare tre segmenti dovrebbe richiedere all’incirca:
4-6 secondi
come nel test fMP4.
“Uno unico” anche senza HLS?
Esiste anche una variante server-side:
GET /video.ts → il server scrive una sequenza continua di pacchetti MPEG-TS → Transfer-Encoding: chunked
Il TV vedrebbe un singolo URL invece di una playlist.
Non la consiglierei per il primo test, perché:
il seek è più difficile; non conosciamo la lunghezza finale; il client può chiudere e riaprire la connessione; il recupero dopo errori è meno robusto; HLS gestisce già timeline, retry e disponibilità progressiva.
Quindi la soluzione più pulita resta:
molti segmenti .ts → una sola playlist .m3u8 → un solo video logico sul TV
Raccomandazione
Sì, conviene provare HLS MPEG-TS prima di rinunciare all’avvio rapido o adottare un Custom Receiver.
Non conviene invece:
ridurre semplicemente i .m4s; concatenare file MP4 indipendenti; servire un MP4 incompleto in crescita; simulare un file unico con Content-Length variabile. Ordine di sviluppo consigliato Conservare il codice HLS fMP4 come prototipo funzionante. Aggiungere un muxer MPEG-TS nativo. Riutilizzare H.264 Level 4.1 e AAC già prodotti. Pubblicare segmenti .ts atomicamente. Generare playlist HLS senza EXT-X-MAP. Servire i segmenti con: Content-Type: video/mp2t
Testare con VLC. Testare con CC1AD845. Se il Default Receiver legge segment_00000.ts, segment_00001.ts e segment_00002.ts, collegare il flusso alla transcodifica simultanea. Aggiungere Foreground Service, WakeLock e WiFiLock per lo schermo spento.
In breve: sì, possiamo fare pacchetti piccoli che il TV vede come un unico video, ma devono essere segmenti HLS MPEG-TS correttamente muxati, non pezzi arbitrari di MP4.
Sì, è più difficile del percorso fMP4 che abbiamo appena realizzato, ma non è fuori portata. Direi difficoltà medio-alta, soprattutto perché Media3 Transformer non offre direttamente un output HLS MPEG-TS pronto: dovremo intercettare i campioni H.264/AAC già codificati e inserirli correttamente nei pacchetti MPEG-TS. Media3 permette comunque di sostituire componenti della pipeline quando serve maggiore controllo.
La parte già risolta
Possiamo riutilizzare quasi tutto ciò che abbiamo costruito:
risoluzione del file originale; transcodifica hardware con MediaCodec; H.264 High Profile Level 4.1; AAC; riduzione a 1080p; bitrate video; cache per mediaId; playlist dinamica; pubblicazione atomica; endpoint HTTP HLS; segnale READY; schermata start.jpg; integrazione della queue Cast.
Google Cast supporta sia HLS sia il contenitore MPEG-2 Transport Stream, identificato normalmente come video/mp2t.
Quindi non ripartiamo da zero. Cambia principalmente questo componente:
attuale: H.264 + AAC → Fragmented MP4 → moof + mdat → segmenti .m4s
nuovo: H.264 + AAC → MPEG-TS muxer → pacchetti TS → segmenti .ts
La parte complessa
Un muxer MPEG-TS deve costruire correttamente:
pacchetti da 188 byte; PAT, Program Association Table; PMT, Program Map Table; PID separati per audio e video; PES per H.264 e AAC; PTS e DTS; continuity counter; access unit H.264 in formato Annex B; intestazioni ADTS per AAC, se richieste; segmentazione solo in corrispondenza di un keyframe; timestamp continui fra un segmento e il successivo.
Non basta quindi sostituire l’estensione:
.m4s → .ts
I dati interni devono essere rimuxati realmente.
Quanto lavoro richiede
Una prima implementazione diagnostica richiede indicativamente questi passaggi:
Creare un Muxer.Factory personalizzato per Media3. Ricevere le tracce H.264 e AAC da Transformer. Convertire i campioni H.264 nel formato atteso dal Transport Stream. Generare PAT e PMT. Incapsulare video e audio in PES. Suddividere i PES in pacchetti TS da 188 byte. Chiudere un segmento .ts ogni circa 3 secondi, sul primo keyframe utile. Aggiornare atomicamente la playlist. Validare il flusso con FFmpeg e VLC. Provarlo sul Default Media Receiver.
La difficoltà maggiore è la correttezza di timestamp, keyframe e pacchettizzazione MPEG-TS. La parte Dart, server e queue è invece già quasi pronta.
Strategia più sicura
Non sostituirei subito il percorso fMP4. Creerei una pipeline parallela:
cast_ts_cache.dart cast_ts_optimizer.dart CastTsSegmentingMuxer.kt
Con un nuovo output:
cache/aves_cast_ts/1000119720/ ├── master.m3u8 ├── segment_00000.ts ├── segment_00001.ts ├── segment_00002.ts ├── session.json └── complete.flag
La playlist sarebbe più semplice:
#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:EVENT #EXTINF:3.000, segment_00000.ts #EXTINF:3.000, segment_00001.ts
Non servirebbe:
#EXT-X-MAP init.mp4
Il server esporrebbe i segmenti con:
Content-Type: video/mp2t
Prima versione consigliata
Per ridurre la complessità, partirei con una versione molto vincolata:
Video: H.264 Audio: AAC-LC Risoluzione massima: 1920x1080 Frame rate: 30 fps Keyframe: ogni secondo Segmento: circa 3 secondi Una sola qualità Nessun sottotitolo Nessun HDR Nessun seek durante la transcodifica iniziale
Questo evita di costruire subito un muxer MPEG-TS universale.
Tempo di avvio previsto
Con le prestazioni già misurate:
3 secondi di video prodotti in circa 1 secondo
potremmo pubblicare tre segmenti da circa 3 secondi in approssimativamente:
4-6 secondi
Quindi il comportamento finale resterebbe:
start.jpg → pochi secondi → video HLS MPEG-TS → transcodifica continua in background
Rischio tecnico
Il rischio principale non è riuscire a produrre MPEG-TS. Il rischio è scoprire un’altra peculiarità del vecchio runtime Cast dopo aver completato il muxer.
Per limitarlo, la prima milestone dovrebbe essere molto piccola:
generare soltanto 10-15 secondi → 4 o 5 segmenti .ts → playlist VOD statica → provare immediatamente con CC1AD845
Se il Default Receiver legge:
segment_00000.ts segment_00001.ts segment_00002.ts
e passa a PLAYING, allora investiamo nel muxer incrementale completo.
Se fallisce già sul primo .ts, fermiamo lo sviluppo prima di costruire tutta la pipeline.
Valutazione finale Difficoltà: medio-alta. Fattibilità: sì. Codice riutilizzabile: molto, soprattutto Dart, cache, server e queue. Parte nuova critica: muxer MPEG-TS nativo. Approccio prudente: produrre prima un breve HLS-TS statico di prova. Beneficio potenziale: avvio rapido senza Custom Receiver.
La decisione più razionale è quindi procedere con un prototipo MPEG-TS breve e statico, non con il muxer completo. Se quel prototipo viene riprodotto da CC1AD845, completiamo poi la generazione on the fly.
Sì, ed è la scelta migliore prima di modificare ancora Flutter. Possiamo creare sul Mac un piccolo HLS MPEG-TS di 15 secondi, servirlo con CORS e inviarlo direttamente al Default Media Receiver tramite catt.
Google Cast supporta HLS e il contenitore MPEG-TS/MP2T.
- Verifica FFmpeg ffmpeg -version
Se non è installato:
brew install ffmpeg
- Usa il filmato originale oppure l’MP4 convertito
Se hai ancora sul Mac:
cast_1000119720.mp4
usa quello, perché ha già H.264 Level 4.1 e AAC compatibili.
Verifica:
ls -lh cast_1000119720.mp4
- Crea un HLS MPEG-TS breve rm -rf hls_ts_test mkdir hls_ts_test
Genera 15 secondi con segmenti di 3 secondi:
ffmpeg
-y
-i cast_1000119720.mp4
-t 15
-c:v libx264
-profile:v high
-level:v 4.1
-preset veryfast
-b:v 5000k
-maxrate 6000k
-bufsize 12000k
-r 30
-g 90
-keyint_min 90
-sc_threshold 0
-c:a aac
-b:a 160k
-ac 2
-ar 48000
-f hls
-hls_time 3
-hls_list_size 0
-hls_playlist_type vod
-hls_segment_type mpegts
-hls_flags independent_segments
-hls_segment_filename 'hls_ts_test/segment_%05d.ts'
hls_ts_test/master.m3u8
FFmpeg può generare una playlist HLS con segmenti .ts; i tagli avvengono sui keyframe, quindi abbiamo allineato il GOP a tre secondi a 30 fps.
- Controlla l’output ls -lh hls_ts_test
Dovresti trovare:
master.m3u8 segment_00000.ts segment_00001.ts segment_00002.ts segment_00003.ts segment_00004.ts
Controlla la playlist:
cat hls_ts_test/master.m3u8
Deve contenere riferimenti ai file .ts, senza:
EXT-X-MAP init.mp4
- Verifica localmente ffplay hls_ts_test/master.m3u8
Oppure:
open -a VLC hls_ts_test/master.m3u8
- Crea un piccolo server HTTP con CORS
Il Default Receiver richiede che le risorse siano raggiungibili dal televisore e che il server risponda con CORS. Google indica CORS come requisito importante per la riproduzione dei media Cast.
Crea questo server nella cartella corrente:
cat > hls_cors_server.py <<'PY' from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer import os
ROOT = os.path.abspath("hls_ts_test") PORT = 8000
class Handler(SimpleHTTPRequestHandler): extensions_map = { **SimpleHTTPRequestHandler.extensions_map, ".m3u8": "application/vnd.apple.mpegurl", ".ts": "video/mp2t", }
def end_headers(self):
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header(
"Access-Control-Expose-Headers",
"Content-Length, Content-Type, Accept-Ranges",
)
self.send_header("Cache-Control", "no-cache")
super().end_headers()
def do_OPTIONS(self):
self.send_response(204)
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS")
self.send_header("Access-Control-Allow-Headers", "Range")
self.end_headers()
if name == "main": os.chdir(ROOT) server = ThreadingHTTPServer(("0.0.0.0", PORT), Handler) print(f"Serving {ROOT} on port {PORT}") server.serve_forever() PY
Avvialo:
python3 hls_cors_server.py
Lascialo attivo.
- Trova l’IP del Mac
In un altro terminale:
ipconfig getifaddr en0
Se non restituisce nulla:
ipconfig getifaddr en1
Supponiamo che restituisca:
192.168.1.50
L’URL sarà:
http://192.168.1.50:8000/master.m3u8
Verificalo:
curl -I
'http://192.168.1.50:8000/master.m3u8'
Deve mostrare:
Content-Type: application/vnd.apple.mpegurl Access-Control-Allow-Origin: *
Verifica un segmento:
curl -I
'http://192.168.1.50:8000/segment_00000.ts'
Deve mostrare:
Content-Type: video/mp2t
- Installa catt
La versione corrente di catt richiede Python 3.11 o superiore e può inviare URL direttamente a un dispositivo Cast.
Metodo consigliato:
brew install pipx pipx ensurepath pipx install catt
Chiudi e riapri il terminale, oppure esegui:
source ~/.zshrc
Verifica:
catt --version
- Trova il dispositivo Cast catt scan
Annota il nome esatto mostrato, per esempio:
TV Salotto
- Invia direttamente l’HLS MPEG-TS
catt -d 'TV Salotto' cast
'http://192.168.1.50:8000/master.m3u8'
Se hai un solo dispositivo Cast, puoi provare anche:
catt cast
'http://192.168.1.50:8000/master.m3u8'
Interpretazione Se il video viene riprodotto
Abbiamo una conferma importante:
Default Media Receiver → HLS MPEG-TS compatibile
A quel punto vale la pena implementare il muxer MPEG-TS in Android e riutilizzare tutta la pipeline incrementale già costruita.
Se fallisce
Prima controlla il terminale del server:
il TV ha richiesto master.m3u8? ha richiesto segment_00000.ts? ha proseguito con segment_00001.ts?
Se chiede più segmenti ma non riproduce, il problema può essere nella codifica o nei timestamp. Se non richiede nessun segmento, può essere un problema di URL, firewall o CORS.
Se catt non gestisce correttamente l’HLS
Puoi usare una pagina Web che invia stream HLS direttamente a Chromecast, ma catt è preferibile perché consente di mantenere il test da riga di comando. catt supporta URL e media locali tramite il Default Cast controller.
Vantaggio del test
Questa prova è completamente separata da:
Flutter; Media3; il plugin Kotlin; CastServer; la queue di Aves; il segmenter fMP4.
Se funziona, dimostra in modo pulito che MPEG-TS è la strada compatibile con il tuo TV.
Perfetto, FFmpeg è già pronto e include tutto ciò che serve:
FFmpeg 8.1.1 libx264 AAC HLS muxer ffplay
Ora possiamo creare una prova HLS MPEG-TS completamente esterna a Flutter.
- Verifica il file MP4 locale
Se hai già scaricato l’MP4 convertito:
ls -lh cast_1000119720.mp4
Se il nome è diverso, sostituiscilo nei comandi successivi.
- Genera 15 secondi di HLS MPEG-TS rm -rf hls_ts_test mkdir hls_ts_test
Poi:
ffmpeg
-y
-i cast_1000119720.mp4
-t 15
-c:v libx264
-profile:v high
-level:v 4.1
-preset veryfast
-b:v 5000k
-maxrate 6000k
-bufsize 12000k
-r 30
-g 90
-keyint_min 90
-sc_threshold 0
-c:a aac
-b:a 160k
-ac 2
-ar 48000
-f hls
-hls_time 3
-hls_list_size 0
-hls_playlist_type vod
-hls_segment_type mpegts
-hls_flags independent_segments
-hls_segment_filename 'hls_ts_test/segment_%05d.ts'
hls_ts_test/master.m3u8
Questo dovrebbe produrre una playlist HLS VOD e circa cinque segmenti MPEG-TS da tre secondi. FFmpeg segmenta in corrispondenza dei keyframe, perciò il GOP è impostato a 90 frame, cioè tre secondi a 30 fps.
- Controlla i file prodotti ls -lh hls_ts_test
Dovresti trovare qualcosa di simile:
master.m3u8 segment_00000.ts segment_00001.ts segment_00002.ts segment_00003.ts segment_00004.ts
Visualizza la playlist:
cat hls_ts_test/master.m3u8
Deve contenere file .ts, senza init.mp4 e senza EXT-X-MAP.
- Verifica localmente ffplay hls_ts_test/master.m3u8
Oppure:
open -a VLC hls_ts_test/master.m3u8
Se video e audio funzionano per tutti i 15 secondi, il test locale è valido.
- Avvia il server HTTP con CORS
Crea il server:
cat > hls_cors_server.py <<'PY' from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer import os
ROOT = os.path.abspath("hls_ts_test") PORT = 8000
class Handler(SimpleHTTPRequestHandler): extensions_map = { **SimpleHTTPRequestHandler.extensions_map, ".m3u8": "application/vnd.apple.mpegurl", ".ts": "video/mp2t", }
def end_headers(self):
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header(
"Access-Control-Expose-Headers",
"Content-Length, Content-Type, Accept-Ranges",
)
self.send_header("Cache-Control", "no-cache")
super().end_headers()
def do_OPTIONS(self):
self.send_response(204)
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header(
"Access-Control-Allow-Methods",
"GET, HEAD, OPTIONS",
)
self.send_header(
"Access-Control-Allow-Headers",
"Range",
)
self.end_headers()
if name == "main": os.chdir(ROOT) server = ThreadingHTTPServer(("0.0.0.0", PORT), Handler) print(f"Serving {ROOT} on port {PORT}", flush=True) server.serve_forever() PY
Avvialo:
python3 hls_cors_server.py
Lascialo in esecuzione.
- Recupera l’IP Wi-Fi del Mac
In un secondo terminale:
ipconfig getifaddr en0
Se non restituisce nulla:
ipconfig getifaddr en1
Supponendo che restituisca 192.168.1.50, verifica:
curl -I
'http://192.168.1.50:8000/master.m3u8'
e:
curl -I
'http://192.168.1.50:8000/segment_00000.ts'
Il primo deve riportare:
Content-Type: application/vnd.apple.mpegurl Access-Control-Allow-Origin: *
Il secondo:
Content-Type: video/mp2t Access-Control-Allow-Origin: *
Google Cast supporta HLS e il contenitore MP2T, quindi questa prova isola con precisione la compatibilità del Default Media Receiver con HLS MPEG-TS.
- Passaggio successivo
Quando questi controlli sono riusciti, installiamo catt, individuiamo il televisore e inviamo direttamente:
http://IP_DEL_MAC:8000/master.m3u8
senza coinvolgere Flutter, Media3 o il server di Aves.
Perfetto, l’output è corretto: FFmpeg ha generato una playlist HLS MPEG-TS di circa 15 secondi con 5 segmenti autonomi da circa 3 secondi ciascuno. Ora verifichiamo la playlist e la proviamo direttamente sul TV, senza Flutter.
- Controlla la playlist cat hls_ts_test/master.m3u8
Dovrebbe contenere una struttura simile:
#EXTM3U #EXT-X-VERSION:6 #EXT-X-TARGETDURATION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXT-X-INDEPENDENT-SEGMENTS #EXTINF:3.000000, segment_00000.ts ... #EXT-X-ENDLIST
Non devono comparire:
#EXT-X-MAP init.mp4 .m4s
- Provalo localmente open -a VLC hls_ts_test/master.m3u8
Verifica che video e audio funzionino per tutti i 15 secondi.
- Avvia il server HTTP con CORS
Se hai già creato hls_cors_server.py, avvialo dalla directory che contiene hls_ts_test:
python3 hls_cors_server.py
Dovrebbe mostrare:
Serving .../hls_ts_test on port 8000
Lascialo aperto.
- Recupera l’IP Wi-Fi del Mac
In un secondo terminale:
ipconfig getifaddr en0
Se non restituisce nulla:
ipconfig getifaddr en1
Supponendo che restituisca, per esempio:
192.168.1.50
l’URL sarà:
http://192.168.1.50:8000/master.m3u8
- Verifica dal Mac
Sostituisci l’IP con quello effettivo:
curl -I
'http://192.168.1.50:8000/master.m3u8'
Devi vedere:
Content-Type: application/vnd.apple.mpegurl Access-Control-Allow-Origin: *
Poi:
curl -I
'http://192.168.1.50:8000/segment_00000.ts'
Devi vedere:
Content-Type: video/mp2t Access-Control-Allow-Origin: *
Google Cast supporta HLS e il contenitore MPEG-TS/MP2T, quindi questo è il formato giusto per verificare la compatibilità con il Default Media Receiver.
- Installa e usa catt
Se non lo hai già:
brew install pipx pipx ensurepath pipx install catt
Poi riapri il terminale oppure esegui:
source ~/.zshrc
Trova il dispositivo:
catt scan
Quindi invia la playlist, usando il nome esatto del TV:
catt -d 'NOME TV' cast
'http://192.168.1.50:8000/master.m3u8'
catt può inviare URL multimediali direttamente a un dispositivo Cast.
Cosa osservare
Nel terminale del server dovresti vedere:
GET /master.m3u8 GET /segment_00000.ts GET /segment_00001.ts GET /segment_00002.ts
Se il TV riproduce tutti i 15 secondi e avanza tra i segmenti, avremo dimostrato che:
Default Media Receiver
- HLS MPEG-TS = compatibile
A quel punto l’implementazione Android del muxer MPEG-TS avrà senso.
Perfetto, la playlist è corretta per una prova HLS MPEG-TS VOD:
5 segmenti da 3 secondi durata complessiva di 15 secondi segmenti .ts nessun init.mp4 nessun EXT-X-MAP playlist chiusa con EXT-X-ENDLIST
Ora testiamola direttamente sul televisore.
- Prova locale con VLC open -a VLC hls_ts_test/master.m3u8
Verifica che video e audio funzionino per tutti i 15 secondi.
- Avvia il server HTTP con CORS
Dalla directory che contiene hls_ts_test:
python3 hls_cors_server.py
Dovresti vedere:
Serving .../hls_ts_test on port 8000
Lascia questo terminale aperto.
- Recupera l’IP Wi-Fi del Mac ipconfig getifaddr en0
Se non restituisce nulla:
ipconfig getifaddr en1
Supponendo che l’IP sia 192.168.1.50, verifica la playlist:
curl -I
'http://192.168.1.50:8000/master.m3u8'
Deve includere:
Content-Type: application/vnd.apple.mpegurl Access-Control-Allow-Origin: *
Controlla anche un segmento:
curl -I
'http://192.168.1.50:8000/segment_00000.ts'
Deve includere:
Content-Type: video/mp2t Access-Control-Allow-Origin: *
Google Cast dichiara il supporto per HLS e per il contenitore MPEG-TS/MP2T, quindi questa prova è adatta a verificare la compatibilità del receiver predefinito.
- Installa catt, se necessario
Verifica prima:
catt --version
Se non è installato:
brew install pipx pipx ensurepath pipx install catt
Poi ricarica la shell:
source ~/.zshrc
- Trova il dispositivo Cast catt scan
Prendi nota del nome esatto, per esempio:
TV Salotto
- Invia la playlist al TV
catt -d 'TV Salotto' cast
'http://192.168.1.50:8000/master.m3u8'
Sostituisci:
TV Salotto con il nome restituito da catt scan 192.168.1.50 con l’IP reale del Mac
catt consente di inviare URL multimediali direttamente a un dispositivo Cast.
- Cosa osservare
Nel terminale del server dovrebbero comparire richieste simili:
GET /master.m3u8 GET /segment_00000.ts GET /segment_00001.ts GET /segment_00002.ts GET /segment_00003.ts GET /segment_00004.ts
Test riuscito
Se il TV riproduce i 15 secondi e richiede tutti i segmenti:
Default Media Receiver
- HLS MPEG-TS = compatibile
A quel punto possiamo procedere con il prototipo Android del muxer MPEG-TS.
Test fallito
Se richiede ripetutamente solo:
segment_00000.ts
oppure non richiede alcun segmento, controlliamo l’output di catt e del server prima di modificare Flutter.
Il prossimo dato utile è quindi l’output di:
catt scan
Nessun problema. Crealo nella cartella che contiene hls_ts_test, poi avvialo da lì. Questo server assegna i MIME corretti e aggiunge gli header CORS richiesti dal test Cast.
- Crea hls_cors_server.py
Dalla directory in cui vedi:
hls_ts_test/
esegui:
cat > hls_cors_server.py <<'PY' from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer import os
ROOT = os.path.abspath("hls_ts_test") PORT = 8000
class HlsHandler(SimpleHTTPRequestHandler): extensions_map = { **SimpleHTTPRequestHandler.extensions_map, ".m3u8": "application/vnd.apple.mpegurl", ".ts": "video/mp2t", }
def end_headers(self):
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header(
"Access-Control-Expose-Headers",
"Content-Length, Content-Type, Accept-Ranges",
)
self.send_header("Cache-Control", "no-cache")
super().end_headers()
def do_OPTIONS(self):
self.send_response(204)
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header(
"Access-Control-Allow-Methods",
"GET, HEAD, OPTIONS",
)
self.send_header(
"Access-Control-Allow-Headers",
"Range",
)
self.end_headers()
if name == "main": if not os.path.isdir(ROOT): raise SystemExit(f"Directory non trovata: {ROOT}")
os.chdir(ROOT)
server = ThreadingHTTPServer(
("0.0.0.0", PORT),
HlsHandler,
)
print(
f"Serving {ROOT} on http://0.0.0.0:{PORT}",
flush=True,
)
server.serve_forever()
PY
- Verifica che esista ls -lh hls_cors_server.py
Controlla anche che la directory sia nella posizione corretta:
ls -lh hls_ts_test
- Avvia il server python3 hls_cors_server.py
Dovresti vedere:
Serving /percorso/completo/hls_ts_test on http://0.0.0.0:8000
Lascia questo terminale aperto.
Se macOS mostra la richiesta di autorizzazione per accettare connessioni in ingresso, premi Consenti.
- Recupera l’IP del Mac
In un altro terminale:
ipconfig getifaddr en0
Se non mostra nulla:
ipconfig getifaddr en1
Puoi anche usare:
route -n get default
| grep interface
e poi passare il nome dell’interfaccia a:
ipconfig getifaddr NOME_INTERFACCIA
- Verifica il server
Supponendo che l’IP del Mac sia 192.168.1.50:
curl -I
'http://192.168.1.50:8000/master.m3u8'
Devi vedere:
HTTP/1.0 200 OK Content-Type: application/vnd.apple.mpegurl Access-Control-Allow-Origin: *
Poi verifica il segmento:
curl -I
'http://192.168.1.50:8000/segment_00000.ts'
Deve mostrare:
HTTP/1.0 200 OK Content-Type: video/mp2t Access-Control-Allow-Origin: *
- Prova prima in VLC via rete
open -a VLC
'http://192.168.1.50:8000/master.m3u8'
Usa il vero IP del Mac. Se VLC riproduce tutti i 15 secondi, il server è configurato correttamente.
Dopo potrai eseguire:
catt scan
e inviare l’URL al televisore.
Esatto: stai eseguendo lo script da dentro hls_ts_test, mentre lo script aggiunge già hls_ts_test al percorso. Perciò cerca erroneamente:
.../hls_ts_test/hls_ts_test
Hai due soluzioni.
Soluzione rapida
Torna alla cartella superiore:
cd ..
Verifica:
pwd ls -lh hls_ts_test ls -lh hls_cors_server.py
Poi avvia:
python3 hls_cors_server.py
Dovrebbe mostrare:
Serving /Volumes/terrain/aves_mio0.70/hls_ts_test on http://0.0.0.0:8000
Soluzione migliore: rendere lo script indipendente dalla cartella corrente
Sostituisci:
ROOT = os.path.abspath("hls_ts_test")
con:
SCRIPT_DIR = os.path.dirname(os.path.abspath(file)) ROOT = os.path.join(SCRIPT_DIR, "hls_ts_test")
Puoi applicare la modifica automaticamente:
python3 - <<'PY' from pathlib import Path
path = Path("hls_cors_server.py") text = path.read_text(encoding="utf-8")
text = text.replace( 'ROOT = os.path.abspath("hls_ts_test")', 'SCRIPT_DIR = os.path.dirname(os.path.abspath(file))\n' 'ROOT = os.path.join(SCRIPT_DIR, "hls_ts_test")', )
path.write_text(text, encoding="utf-8") print("[OK] server aggiornato") PY
Questa operazione va eseguita dalla cartella in cui si trova hls_cors_server.py.
Poi avvia:
python3 hls_cors_server.py
Se vuoi tenere lo script dentro hls_ts_test
Se invece sia lo script sia i file HLS sono già dentro:
/Volumes/terrain/aves_mio0.70/hls_ts_test
imposta semplicemente:
ROOT = os.path.dirname(os.path.abspath(file))
Oppure avvia al volo un server senza script, dalla directory hls_ts_test:
python3 -m http.server 8000 --bind 0.0.0.0
Quest’ultima variante può non assegnare tutti i MIME e gli header CORS desiderati, quindi per il test Cast è preferibile lo script dedicato.
Una volta che compare:
Serving ... on http://0.0.0.0:8000
lascia il terminale aperto e recupera l’IP del Mac con:
ipconfig getifaddr en0
Perfetto. Con l’IP del Mac 192.168.1.2, l’URL del test HLS è:
http://192.168.1.2:8000/master.m3u8
- Assicurati che il server sia attivo
Nel terminale del server devi vedere qualcosa come:
Serving .../hls_ts_test on http://0.0.0.0:8000
Lascialo aperto.
- Verifica playlist e segmento
In un altro terminale:
curl -I 'http://192.168.1.2:8000/master.m3u8'
Deve comparire:
Content-Type: application/vnd.apple.mpegurl Access-Control-Allow-Origin: *
Poi:
curl -I 'http://192.168.1.2:8000/segment_00000.ts'
Deve comparire:
Content-Type: video/mp2t Access-Control-Allow-Origin: *
- Verifica via rete con VLC
open -a VLC
'http://192.168.1.2:8000/master.m3u8'
Se riproduce tutti i 15 secondi, il server è raggiungibile correttamente.
- Individua il televisore con catt catt scan
Prendi il nome esatto del dispositivo. Poi invia la playlist:
catt -d 'NOME ESATTO TV' cast
'http://192.168.1.2:8000/master.m3u8'
Se hai un solo dispositivo Cast, puoi anche provare:
catt cast
'http://192.168.1.2:8000/master.m3u8'
- Osserva il terminale del server
Se il Default Media Receiver è compatibile con HLS MPEG-TS, dovresti vedere:
GET /master.m3u8 GET /segment_00000.ts GET /segment_00001.ts GET /segment_00002.ts GET /segment_00003.ts GET /segment_00004.ts
Se invece ripete solo segment_00000.ts, incolla l’output del server e di catt. Il test è importante perché Google Cast dichiara supporto per HLS e il contenitore MP2T.
Perfetto, il server è configurato correttamente:
Playlist: application/vnd.apple.mpegurl Segmenti: video/mp2t CORS: Access-Control-Allow-Origin: *
Ora facciamo la prova diretta sul televisore.
- Prova prima via rete con VLC
open -a VLC
'http://192.168.1.2:8000/master.m3u8'
Se riproduce tutti i 15 secondi, l’URL di rete è valido.
- Verifica se catt è installato catt --version
Se il comando non esiste:
brew install pipx pipx ensurepath pipx install catt
Poi:
source ~/.zshrc
- Cerca i dispositivi Cast catt scan
Annota il nome esatto del televisore.
- Invia l’HLS MPEG-TS
Sostituisci NOME TV con il nome restituito da catt scan:
catt -d 'NOME TV' cast
'http://192.168.1.2:8000/master.m3u8'
Se hai un solo dispositivo puoi provare:
catt cast
'http://192.168.1.2:8000/master.m3u8'
- Osserva il server
Nel terminale dove gira hls_cors_server.py, l’esito positivo dovrebbe mostrare richieste sequenziali:
GET /master.m3u8 GET /segment_00000.ts GET /segment_00001.ts GET /segment_00002.ts GET /segment_00003.ts GET /segment_00004.ts
Se il TV riproduce tutti i 15 secondi, avremo dimostrato che il Default Media Receiver è compatibile con:
HLS + MPEG-TS + H.264 + AAC
Google Cast dichiara il supporto sia per HLS sia per il contenitore MP2T.
Se invece non parte, incollami:
catt scan
l’output del comando catt cast e le richieste comparse nel terminale del server.