Sì, l’idea è possibile, ma bisogna distinguere due modi molto diversi di far apparire più pezzi come un unico video. 1. 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. 2. 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. 1. Verifica FFmpeg ffmpeg -version Se non è installato: brew install ffmpeg 2. 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 3. 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. 4. 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 5. Verifica localmente ffplay hls_ts_test/master.m3u8 Oppure: open -a VLC hls_ts_test/master.m3u8 6. 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. 7. 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 8. 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 9. Trova il dispositivo Cast catt scan Annota il nome esatto mostrato, per esempio: TV Salotto 10. 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. 1. 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. 2. 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. 3. 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. 4. 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. 5. 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. 6. 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. 7. 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. 1. 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 2. Provalo localmente open -a VLC hls_ts_test/master.m3u8 Verifica che video e audio funzionino per tutti i 15 secondi. 3. 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. 4. 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 5. 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. 6. 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. 1. Prova locale con VLC open -a VLC hls_ts_test/master.m3u8 Verifica che video e audio funzionino per tutti i 15 secondi. 2. 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. 3. 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. 4. 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 5. Trova il dispositivo Cast catt scan Prendi nota del nome esatto, per esempio: TV Salotto 6. 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. 7. 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. 1. 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 2. Verifica che esista ls -lh hls_cors_server.py Controlla anche che la directory sia nella posizione corretta: ls -lh hls_ts_test 3. 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. 4. 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 5. 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: * 6. 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 1. 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. 2. 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: * 3. 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. 4. 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' 5. 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. 1. 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. 2. Verifica se catt è installato catt --version Se il comando non esiste: brew install pipx pipx ensurepath pipx install catt Poi: source ~/.zshrc 3. Cerca i dispositivi Cast catt scan Annota il nome esatto del televisore. 4. 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' 5. 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.