1524 lines
31 KiB
Markdown
1524 lines
31 KiB
Markdown
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.
|