ExoPlayer supporta HLS con più formati di container. Devono essere supportati anche i formati di esempio audio e video contenuti (per i dettagli, consulta la sezione sui formati di esempio ). Invitiamo vivamente i produttori di contenuti HLS a generare stream HLS di alta qualità, come descritto in questo post del blog.
| Funzionalità | Supportato | Commenti |
|---|---|---|
| Container | ||
| MPEG-TS | SÌ | |
| FMP4/CMAF | SÌ | |
| ADTS (AAC) | SÌ | |
| MP3 | SÌ | |
| Sottotitoli codificati / sottotitoli | ||
| CEA-608 | SÌ | |
| CEA-708 | SÌ | |
| WebVTT | SÌ | |
| Metadati | ||
| ID3 | SÌ | |
| SCTE-35 | NO | |
| Protezione dei contenuti | ||
| AES-128 | SÌ | |
| Esempio di AES-128 | NO | |
| Widevine | SÌ | API 19+ (schema "cenc") e 25+ (schema "cbcs") |
| PlayReady SL2000 | SÌ | Solo Android TV |
| Controllo del server | ||
| Aggiornamenti delta | SÌ | |
| Blocco del ricaricamento della playlist | SÌ | |
| Blocco del caricamento dei suggerimenti di precaricamento | SÌ | Tranne per gli intervalli di byte con lunghezze non definite |
| Inserimento degli annunci | ||
| Inserimento degli annunci guidato dal server (interstitial) | In parte | Solo VOD con X-ASSET-URI.
I live streaming e X-ASSET-LIST verranno aggiunti in un secondo momento. |
| Annunci lato server e lato client IMA | SÌ | Guida all'inserimento degli annunci |
| Riproduzione live | ||
| Riproduzione live normale | SÌ | |
| HLS a bassa latenza (Apple) | SÌ | |
| HLS a bassa latenza (community) | NO | |
| Dati client multimediali comuni CMCD | SÌ | Guida all'integrazione di CMCD |
Utilizzo di MediaItem
Per riprodurre uno stream HLS, devi dipendere dal modulo HLS.
Kotlin
implementation("androidx.media3:media3-exoplayer-hls:1.10.0")
Alla moda
implementation "androidx.media3:media3-exoplayer-hls:1.10.0"
Puoi quindi creare un MediaItem per un URI della playlist HLS e passarlo al player.
Kotlin
// Create a player instance. val player = ExoPlayer.Builder(context).build() // Set the media item to be played. player.setMediaItem(MediaItem.fromUri(hlsUri)) // Prepare the player. player.prepare()
Java
// Create a player instance. ExoPlayer player = new ExoPlayer.Builder(context).build(); // Set the media item to be played. player.setMediaItem(MediaItem.fromUri(hlsUri)); // Prepare the player. player.prepare();
Se l'URI non termina con .m3u8, puoi passare MimeTypes.APPLICATION_M3U8 a setMimeType di MediaItem.Builder per indicare esplicitamente il tipo di contenuti.
L'URI dell'elemento multimediale può rimandare a una playlist multimediale o a una playlist multivariante. Se l'URI rimanda a una playlist multivariante che dichiara più tag #EXT-X-STREAM-INF, ExoPlayer si adatterà automaticamente tra le varianti, tenendo conto sia della larghezza di banda disponibile sia delle funzionalità del dispositivo.
Utilizzo di HlsMediaSource
Per ulteriori opzioni di personalizzazione, puoi creare un HlsMediaSource e passarlo direttamente al player anziché a un MediaItem.
Kotlin
// Create a data source factory. val dataSourceFactory: DataSource.Factory = DefaultHttpDataSource.Factory() // Create a HLS media source pointing to a playlist uri. val hlsMediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(hlsUri)) // Create a player instance. val player = ExoPlayer.Builder(context).build() // Set the HLS media source as the playlist with a single media item. player.setMediaSource(hlsMediaSource) // Prepare the player. player.prepare()
Java
// Create a data source factory. DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory(); // Create a HLS media source pointing to a playlist uri. HlsMediaSource hlsMediaSource = new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(hlsUri)); // Create a player instance. ExoPlayer player = new ExoPlayer.Builder(context).build(); // Set the HLS media source as the playlist with a single media item. player.setMediaSource(hlsMediaSource); // Prepare the player. player.prepare();
Accesso al manifest
Puoi recuperare il manifest corrente chiamando Player.getCurrentManifest.
Per HLS, devi eseguire il cast dell'oggetto restituito a HlsManifest. Viene chiamata anche la richiamata onTimelineChanged di Player.Listener ogni volta che viene caricato il manifest. Ciò accadrà una volta per i contenuti on-demand e possibilmente molte volte per i contenuti live. Il seguente snippet di codice mostra come un'app può eseguire un'azione ogni volta che viene caricato il manifest.
Kotlin
player.addListener( object : Player.Listener { override fun onTimelineChanged( timeline: Timeline, @Player.TimelineChangeReason reason: Int, ) { val manifest = player.currentManifest if (manifest is HlsManifest) { // Do something with the manifest. } } } )
Java
player.addListener( new Player.Listener() { @Override public void onTimelineChanged( Timeline timeline, @Player.TimelineChangeReason int reason) { Object manifest = player.getCurrentManifest(); if (manifest != null) { HlsManifest hlsManifest = (HlsManifest) manifest; // Do something with the manifest. } } });
Riprodurre stream HLS con interstitial
La specifica HLS definisce gli interstitial HLS che possono essere utilizzati per includere informazioni sugli interstitial in una playlist multimediale. Per impostazione predefinita, ExoPlayer ignora questi interstitial. Il supporto può essere aggiunto utilizzando HlsInterstitialsAdsLoader. Non supportiamo tutte le funzionalità della specifica fin dall'inizio. Se non è supportato
il tuo stream, comunicacelo segnalando un problema su GitHub e inviandoci l'
URI dello stream, in modo che possiamo aggiungere il supporto per il tuo stream.
Utilizzare un MediaItem con l'API Playlist
Il modo più pratico per riprodurre stream HLS con interstitial è creare un'istanza di ExoPlayer con un HlsInterstitialsAdsLoader.AdsMediaSourceFactory.
In questo modo è possibile utilizzare l'API MediaItem basata su playlist dell'Player
interfaccia per riprodurre gli interstitial HLS.
Il MediaSource.Factory di ExoPlayer può essere inserito nel builder durante la creazione dell'istanza del player:
Kotlin
val hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context) // Create a MediaSource.Factory for HLS streams with interstitials. val hlsMediaSourceFactory = HlsInterstitialsAdsLoader.AdsMediaSourceFactory( hlsInterstitialsAdsLoader, playerView, DefaultMediaSourceFactory(context), ) // Build player with interstitials media source factory val player = ExoPlayer.Builder(context).setMediaSourceFactory(hlsMediaSourceFactory).build() // Set the player on the ads loader. hlsInterstitialsAdsLoader.setPlayer(player) playerView.setPlayer(player)
Java
HlsInterstitialsAdsLoader hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(context); // Create a MediaSource.Factory for HLS streams with interstitials. MediaSource.Factory hlsMediaSourceFactory = new HlsInterstitialsAdsLoader.AdsMediaSourceFactory( hlsInterstitialsAdsLoader, playerView, new DefaultMediaSourceFactory(context)); // Build player with interstitials media source factory ExoPlayer player = new ExoPlayer.Builder(context).setMediaSourceFactory(hlsMediaSourceFactory).build(); // Set the player on the ads loader. hlsInterstitialsAdsLoader.setPlayer(player); playerView.setPlayer(player);
Con una configurazione del player di questo tipo, la riproduzione degli interstitial HLS consiste semplicemente nell'impostare un elemento multimediale con un AdsConfiguration sul player:
Kotlin
// Build an HLS media item with ads configuration to be played. player.setMediaItem( MediaItem.Builder() .setUri("https://www.example.com/media.m3u8") .setAdsConfiguration( MediaItem.AdsConfiguration.Builder("hls://interstitials".toUri()) .setAdsId("ad-tag-0") // must be unique within playlist .build() ) .build() ) player.prepare() player.play()
Java
// Build an HLS media item with ads configuration to be played. player.setMediaItem( new MediaItem.Builder() .setUri("https://www.example.com/media.m3u8") .setAdsConfiguration( new AdsConfiguration.Builder(Uri.parse("hls://interstitials")) .setAdsId("ad-tag-0") // must be unique within playlist .build()) .build()); player.prepare(); player.play();
Utilizzare l'API basata sull'origine multimediale
In alternativa, l'istanza di ExoPlayer può essere creata senza sostituire la factory di origine multimediale predefinita. Per supportare gli interstitial, un'app può quindi
utilizzare HlsInterstitialsAdsLoader.AdsMediaSourceFactory direttamente per creare un
MediaSource e fornirlo a ExoPlayer utilizzando l'API Playlist basata sull'origine multimediale:
Kotlin
val hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context) // Create a MediaSource.Factory for HLS streams with interstitials. val hlsMediaSourceFactory = HlsInterstitialsAdsLoader.AdsMediaSourceFactory( hlsInterstitialsAdsLoader, playerView, context, ) // Build player with default media source factory. val player = ExoPlayer.Builder(context).build() // Create an media source from an HLS media item with ads configuration. val mediaSource = hlsMediaSourceFactory.createMediaSource( MediaItem.Builder() .setUri("https://www.example.com/media.m3u8") .setAdsConfiguration( MediaItem.AdsConfiguration.Builder("hls://interstitials".toUri()) .setAdsId("ad-tag-0") .build() ) .build() ) // Set the media source on the player. player.setMediaSource(mediaSource) player.prepare() player.play()
Java
HlsInterstitialsAdsLoader hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(context); // Create a MediaSource.Factory for HLS streams with interstitials. MediaSource.Factory hlsMediaSourceFactory = new HlsInterstitialsAdsLoader.AdsMediaSourceFactory( hlsInterstitialsAdsLoader, playerView, context); // Build player with default media source factory. ExoPlayer player = new ExoPlayer.Builder(context).build(); // Create a media source from an HLS media item with ads configuration. MediaSource mediaSource = hlsMediaSourceFactory.createMediaSource( new MediaItem.Builder() .setUri("https://www.example.com/media.m3u8") .setAdsConfiguration( new MediaItem.AdsConfiguration.Builder(Uri.parse("hls://interstitials")) .setAdsId("ad-tag-0") .build()) .build()); // Set the media source on the player. player.setMediaSource(mediaSource); player.prepare(); player.play();
Ascoltare gli eventi degli annunci
È possibile aggiungere un Listener a HlsInterstitialsAdsLoader per monitorare gli eventi relativi alle modifiche dello stato della riproduzione degli interstitial HLS. In questo modo, un'app o un SDK può monitorare gli annunci riprodotti, gli elenchi di asset caricati, le origini multimediali degli annunci preparate o rilevare gli errori di caricamento dell'elenco di asset e di preparazione degli annunci. Inoltre, è possibile ricevere i metadati emessi dalle origini multimediali degli annunci per una verifica dettagliata della riproduzione degli annunci o per monitorare l'avanzamento della riproduzione degli annunci.
Kotlin
class AdsLoaderListener : HlsInterstitialsAdsLoader.Listener { override fun onStart(mediaItem: MediaItem, adsId: Any, adViewProvider: AdViewProvider) { // Do something when HLS media item with interstitials is started. } override fun onMetadata( mediaItem: MediaItem, adsId: Any, adGroupIndex: Int, adIndexInAdGroup: Int, metadata: Metadata, ) { // Do something with metadata that is emitted by the ad media source of the given ad. } override fun onAdCompleted( mediaItem: MediaItem, adsId: Any, adGroupIndex: Int, adIndexInAdGroup: Int, ) { // Do something when ad completed playback. } // ... See JavaDoc for further callbacks of HlsInterstitialsAdsLoader.Listener. override fun onStop(mediaItem: MediaItem, adsId: Any, adPlaybackState: AdPlaybackState) { // Do something with the resulting ad playback state when stopped. } }
Java
@OptIn(markerClass = UnstableApi.class) private static class AdsLoaderListener implements HlsInterstitialsAdsLoader.Listener { // implement HlsInterstitialsAdsLoader.Listener @Override public void onStart(MediaItem mediaItem, Object adsId, AdViewProvider adViewProvider) { // Do something when HLS media item with interstitials is started. } @Override public void onMetadata( MediaItem mediaItem, Object adsId, int adGroupIndex, int adIndexInAdGroup, Metadata metadata) { // Do something with metadata that is emitted by the ad media source of the given ad. } @Override public void onAdCompleted( MediaItem mediaItem, Object adsId, int adGroupIndex, int adIndexInAdGroup) { // Do something when ad completed playback. } // ... See JavaDoc for further callbacks @Override public void onStop(MediaItem mediaItem, Object adsId, AdPlaybackState adPlaybackState) { // Do something with the resulting ad playback state when stopped. } }
Per la documentazione dettagliata di tutte le richiamate disponibili, consulta il JavaDoc di HlsInterstitialsAdsLoader.Listener per la documentazione dettagliata.
Il listener può quindi essere aggiunto al caricatore degli annunci:
Kotlin
val listener = AdsLoaderListener() // Add the listener to the ads loader to receive ad loader events. hlsInterstitialsAdsLoader.addListener(listener)
Java
AdsLoaderListener listener = new AdsLoaderListener(); // Add the listener to the ads loader to receive ad loader events. hlsInterstitialsAdsLoader.addListener(listener);
Ciclo di vita di HlsInterstitialsAdsLoader
È possibile riutilizzare un'istanza di HlsInterstitialsAdsLoader o
HlsInterstitialsAdsLoader.AdsMediaSourceFactory per più
istanze del player che creano più origini multimediali per le quali devono essere
caricati gli annunci.
Ad esempio, è possibile creare un'istanza nel metodo onCreate di un Activity e riutilizzarla per più istanze del player. Questa operazione funziona a condizione che sia in uso da una singola istanza del giocatore singolo contemporaneamente. Questa operazione è utile per il caso d'uso comune in cui l'app viene messa in background, l'istanza del player viene eliminata e poi viene creata una nuova istanza quando l'app viene riportata in primo piano.
Ripresa della riproduzione con uno stato di riproduzione degli annunci
Per evitare che gli utenti debbano guardare di nuovo gli annunci, lo stato di riproduzione degli annunci può essere salvato e ripristinato quando il player viene ricreato. Per farlo, chiama
getAdsResumptionStates() quando il player sta per essere rilasciato e memorizza
gli oggetti AdsResumptionState restituiti. Quando il player viene ricreato, lo stato può essere ripristinato chiamando addAdResumptionState() sull'istanza del caricatore degli annunci. AdsResumptionState è raggruppabile, quindi può essere memorizzato in un bundle onSaveInstanceState di Activity. Tieni presente che la ripresa degli annunci è supportata solo per gli stream VOD.
Kotlin
class HlsInterstitialsActivity : Activity() { companion object { const val ADS_RESUMPTION_STATE_KEY = "ads_resumption_state" } private var hlsInterstitialsAdsLoader: HlsInterstitialsAdsLoader? = null private var playerView: PlayerView? = null private var player: ExoPlayer? = null private var adsResumptionStates: List<HlsInterstitialsAdsLoader.AdsResumptionState>? = null @Suppress("DEPRECATION") // getParcelableArrayList without class type is deprecated in API 33+ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Create the ads loader instance. hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(this) // Restore ad resumption states. val bundles = if (Build.VERSION.SDK_INT >= 33) { savedInstanceState?.getParcelableArrayList(ADS_RESUMPTION_STATE_KEY, Bundle::class.java) } else { savedInstanceState?.getParcelableArrayList(ADS_RESUMPTION_STATE_KEY) } adsResumptionStates = bundles?.map(HlsInterstitialsAdsLoader.AdsResumptionState::fromBundle) } override fun onStart() { super.onStart() // Build a player and set it on the ads loader. initializePlayer() hlsInterstitialsAdsLoader?.setPlayer(player) // Add any stored ad resumption states to the ads loader. adsResumptionStates?.forEach { hlsInterstitialsAdsLoader?.addAdResumptionState(it) } adsResumptionStates = null // Consume the states } override fun onStop() { super.onStop() // Get ad resumption states. adsResumptionStates = hlsInterstitialsAdsLoader?.adsResumptionStates releasePlayer() } override fun onDestroy() { // Release the ads loader when not used anymore. hlsInterstitialsAdsLoader?.release() hlsInterstitialsAdsLoader = null super.onDestroy() } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) // Store the ad resumption states. adsResumptionStates?.let { outState.putParcelableArrayList( ADS_RESUMPTION_STATE_KEY, ArrayList(it.map(HlsInterstitialsAdsLoader.AdsResumptionState::toBundle)), ) } } fun initializePlayer() { if (player == null) { // Create a media source factory for HLS streams. val hlsMediaSourceFactory = HlsInterstitialsAdsLoader.AdsMediaSourceFactory( checkNotNull(hlsInterstitialsAdsLoader), playerView!!, /* context= */ this, ) // Build player with interstitials media source player = ExoPlayer.Builder(/* context= */ this) .setMediaSourceFactory(hlsMediaSourceFactory) .build() // Set the player on the ads loader. hlsInterstitialsAdsLoader?.setPlayer(player) playerView?.player = player } // Use a media item with an HLS stream URI, an ad tag URI and ads ID. player?.setMediaItem( MediaItem.Builder() .setUri("https://www.example.com/media.m3u8") .setMimeType(MimeTypes.APPLICATION_M3U8) .setAdsConfiguration( MediaItem.AdsConfiguration.Builder("hls://interstitials".toUri()) .setAdsId("ad-tag-0") // must be unique within ExoPlayer playlist .build() ) .build() ) player?.prepare() player?.play() } fun releasePlayer() { player?.release() player = null hlsInterstitialsAdsLoader?.setPlayer(null) playerView?.player = null } }
Java
@OptIn(markerClass = UnstableApi.class) public static class HlsInterstitialsActivity extends Activity { public static final String ADS_RESUMPTION_STATE_KEY = "ads_resumption_state"; @Nullable private HlsInterstitialsAdsLoader hlsInterstitialsAdsLoader; @Nullable private PlayerView playerView; @Nullable private ExoPlayer player; private List<HlsInterstitialsAdsLoader.AdsResumptionState> adsResumptionStates; @SuppressWarnings( "deprecation") // getParcelableArrayList without class type is deprecated in API 33+ @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create the ads loader instance. hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(this); // Restore ad resumption states. if (savedInstanceState != null) { ArrayList<Bundle> bundles; if (Build.VERSION.SDK_INT >= 33) { bundles = savedInstanceState.getParcelableArrayList(ADS_RESUMPTION_STATE_KEY, Bundle.class); } else { bundles = savedInstanceState.getParcelableArrayList(ADS_RESUMPTION_STATE_KEY); } if (bundles != null) { adsResumptionStates = new ArrayList<>(); for (Bundle bundle : bundles) { adsResumptionStates.add( HlsInterstitialsAdsLoader.AdsResumptionState.fromBundle(bundle)); } } } } @Override protected void onStart() { super.onStart(); // Build a player and set it on the ads loader. initializePlayer(); // Add any stored ad resumption states to the ads loader. if (adsResumptionStates != null) { for (HlsInterstitialsAdsLoader.AdsResumptionState state : adsResumptionStates) { hlsInterstitialsAdsLoader.addAdResumptionState(state); } adsResumptionStates = null; // Consume the states } } @Override protected void onStop() { super.onStop(); // Get ad resumption states before releasing the player. if (hlsInterstitialsAdsLoader != null) { adsResumptionStates = hlsInterstitialsAdsLoader.getAdsResumptionStates(); } releasePlayer(); } @Override protected void onDestroy() { // Release the ads loader when not used anymore. if (hlsInterstitialsAdsLoader != null) { hlsInterstitialsAdsLoader.release(); hlsInterstitialsAdsLoader = null; } super.onDestroy(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // Store the ad resumption states. if (adsResumptionStates != null) { ArrayList<Bundle> bundles = new ArrayList<>(); for (HlsInterstitialsAdsLoader.AdsResumptionState state : adsResumptionStates) { bundles.add(state.toBundle()); } outState.putParcelableArrayList(ADS_RESUMPTION_STATE_KEY, bundles); } } private void initializePlayer() { if (player == null) { // Create a media source factory for HLS streams. MediaSource.Factory hlsMediaSourceFactory = new HlsInterstitialsAdsLoader.AdsMediaSourceFactory( checkNotNull(hlsInterstitialsAdsLoader), playerView, /* context= */ this); // Build player with interstitials media source player = new ExoPlayer.Builder(/* context= */ this) .setMediaSourceFactory(hlsMediaSourceFactory) .build(); // Set the player on the ads loader. hlsInterstitialsAdsLoader.setPlayer(player); playerView.setPlayer(player); } // Use a media item with an HLS stream URI, an ad tag URI and ads ID. player.setMediaItem( new MediaItem.Builder() .setUri("https://www.example.com/media.m3u8") .setMimeType(MimeTypes.APPLICATION_M3U8) .setAdsConfiguration( new MediaItem.AdsConfiguration.Builder(Uri.parse("hls://interstitials")) .setAdsId("ad-tag-0") // must be unique within ExoPlayer playlist .build()) .build()); player.prepare(); player.play(); } private void releasePlayer() { if (player != null) { player.release(); player = null; } if (hlsInterstitialsAdsLoader != null) { hlsInterstitialsAdsLoader.setPlayer(null); } if (playerView != null) { playerView.setPlayer(null); } } }
Personalizzare la riproduzione
ExoPlayer offre diversi modi per personalizzare l'esperienza di riproduzione in base alle esigenze della tua app. Per esempi, consulta la pagina Personalizzazione.
Disattivare la preparazione senza blocchi
Per impostazione predefinita, ExoPlayer utilizzerà la preparazione senza blocchi. Ciò significa che ExoPlayer utilizzerà solo le informazioni nella playlist multivariante per preparare lo stream, che funziona se i tag #EXT-X-STREAM-INF contengono l'attributo CODECS.
Potresti dover disattivare questa funzionalità se i segmenti multimediali contengono tracce di sottotitoli codificati muxate che non sono dichiarate nella playlist multivariante con un tag #EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS. In caso contrario, queste tracce di sottotitoli codificati non verranno rilevate e riprodotte. Puoi disattivare la preparazione senza blocchi in HlsMediaSource.Factory come mostrato nel seguente snippet. Tieni presente che questo aumenterà il tempo di avvio, poiché ExoPlayer deve scaricare un segmento multimediale per scoprire queste tracce aggiuntive ed è preferibile dichiarare le tracce di sottotitoli codificati nella playlist multivariante.
Kotlin
val hlsMediaSource = HlsMediaSource.Factory(dataSourceFactory) .setAllowChunklessPreparation(false) .createMediaSource(MediaItem.fromUri(hlsUri))
Java
HlsMediaSource hlsMediaSource = new HlsMediaSource.Factory(dataSourceFactory) .setAllowChunklessPreparation(false) .createMediaSource(MediaItem.fromUri(hlsUri));
Creare contenuti HLS di alta qualità
Per sfruttare al meglio ExoPlayer, puoi seguire alcune linee guida per migliorare i tuoi contenuti HLS. Per una spiegazione completa, leggi il nostro post di Medium sulla riproduzione HLS in ExoPlayer. I punti principali sono:
- Utilizza durate dei segmenti precise.
- Utilizza uno stream multimediale continuo; evita modifiche alla struttura multimediale tra i segmenti.
- Utilizza il tag
#EXT-X-INDEPENDENT-SEGMENTS. - Preferisci gli stream demuxati, anziché i file che includono sia video sia audio.
- Includi tutte le informazioni possibili nella playlist multivariante.
Le seguenti linee guida si applicano in modo specifico ai live streaming:
- Utilizza il tag
#EXT-X-PROGRAM-DATE-TIME. - Utilizza il tag
#EXT-X-DISCONTINUITY-SEQUENCE. - Fornisci una finestra live lunga. Un minuto o più è l'ideale.