ExoPlayer est compatible avec HLS et plusieurs formats de conteneurs. Les formats des exemples audio et vidéo inclus doivent également être acceptés (pour en savoir plus, consultez la section Formats des exemples). Nous encourageons vivement les producteurs de contenu HLS à générer des flux HLS de haute qualité, comme décrit dans cet article de blog.
Fonctionnalité | Compatible | Commentaires |
---|---|---|
Conteneurs | ||
MPEG-TS | OUI | |
FMP4/CMAF | OUI | |
ADTS (AAC) | OUI | |
MP3 | OUI | |
Sous-titres | ||
CEA-608 | OUI | |
CEA-708 | OUI | |
WebVTT | OUI | |
Métadonnées | ||
ID3 | OUI | |
SCTE-35 | NON | |
Protection du contenu | ||
AES-128 | OUI | |
Exemple AES-128 | NON | |
Widevine | OUI | API 19+ (schéma "cenc") et 25+ (schéma "cbcs") |
PlayReady SL2000 | OUI | Android TV uniquement |
Contrôle du serveur | ||
Mises à jour delta | OUI | |
Bloquer le rechargement de la playlist | OUI | |
Blocage du chargement des indications de préchargement | OUI | Sauf pour les plages d'octets dont la longueur n'est pas définie |
Insertion d'annonces | ||
Insertion d'annonces guidée par le serveur (interstitielles) | En partie | VOD uniquement avec X-ASSET-URI .
Les diffusions en direct et les
X-ASSET-LIST seront ajoutés
ultérieurement. |
Annonces IMA côté serveur et côté client | OUI | Guide d'insertion d'annonces |
Lecture en direct | ||
Lecture en direct normale | OUI | |
HLS à faible latence (Apple) | OUI | |
HLS à faible latence (communauté) | NON | |
Common Media Client Data CMCD | OUI | Guide d'intégration de CMCD |
Utiliser MediaItem
Pour lire un flux HLS, vous devez dépendre du module HLS.
Kotlin
implementation("androidx.media3:media3-exoplayer-hls:1.7.1")
Groovy
implementation "androidx.media3:media3-exoplayer-hls:1.7.1"
Vous pouvez ensuite créer un MediaItem
pour un URI de playlist HLS et le transmettre au lecteur.
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();
Si votre URI ne se termine pas par .m3u8
, vous pouvez transmettre MimeTypes.APPLICATION_M3U8
à setMimeType
de MediaItem.Builder
pour indiquer explicitement le type de contenu.
L'URI de l'élément multimédia peut pointer vers une playlist multimédia ou une playlist multivariante. Si l'URI pointe vers une playlist multivariante qui déclare plusieurs tags #EXT-X-STREAM-INF
, ExoPlayer s'adaptera automatiquement entre les variantes, en tenant compte à la fois de la bande passante disponible et des capacités de l'appareil.
Utiliser HlsMediaSource
Pour obtenir plus d'options de personnalisation, vous pouvez créer un HlsMediaSource
et le transmettre directement au lecteur au lieu d'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();
Accéder au fichier manifeste
Vous pouvez récupérer le fichier manifeste actuel en appelant Player.getCurrentManifest
.
Pour HLS, vous devez caster l'objet renvoyé sur HlsManifest
. Le rappel onTimelineChanged
de Player.Listener
est également appelé chaque fois que le fichier manifeste est chargé. Cela se produira une fois pour les contenus à la demande et peut-être plusieurs fois pour les contenus en direct. L'extrait de code suivant montre comment une application peut effectuer une action chaque fois que le fichier manifeste est chargé.
Kotlin
player.addListener(
object : Player.Listener {
override fun onTimelineChanged(timeline: Timeline, @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.
}
}
});
Lire des flux HLS avec des interstitiels
La spécification HLS définit les interstitiels HLS qui peuvent être utilisés pour inclure des informations interstitielles dans une playlist multimédia. Par défaut, ExoPlayer ignore ces interstitiels. Vous pouvez ajouter l'assistance à l'aide de HlsInterstitialsAdsLoader
. Nous ne prenons pas en charge toutes les fonctionnalités de la spécification dès le début. Si vous ne trouvez pas votre flux, signalez le problème sur GitHub et envoyez-nous l'URI de votre flux pour que nous puissions l'ajouter.
Utiliser un MediaItem
avec l'API Playlist
Le moyen le plus pratique de lire des flux HLS avec des interstitiels consiste à créer une instance ExoPlayer avec un HlsInterstitialsAdsLoader.AdsMediaSourceFactory
.
Cela permet d'utiliser l'API de playlist basée sur MediaItem
de l'interface Player
pour lire des interstitiels HLS.
Le MediaSource.Factory
de ExoPlayer
peut être injecté dans le compilateur lors de la création de l'instance du lecteur :
Kotlin
hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context)
// Create a MediaSource.Factory for HLS streams with interstitials.
var hlsMediaSourceFactory =
HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
hlsInterstitialsAdsLoader,
playerView,
DefaultMediaSourceFactory(context),
)
// Build player with interstitials media source factory
player =
ExoPlayer.Builder(context)
.setMediaSourceFactory(hlsMediaSourceFactory)
.build()
// Set the player on the ads loader.
hlsInterstitialsAdsLoader.setPlayer(player)
playerView.setPlayer(player)
Java
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
player =
new ExoPlayer.Builder(context)
.setMediaSourceFactory(hlsMediaSourceFactory)
.build();
// Set the player on the ads loader.
hlsInterstitialsAdsLoader.setPlayer(player);
playerView.setPlayer(player);
Avec une telle configuration du lecteur, la lecture des interstitiels HLS consiste simplement à définir un élément multimédia avec un AdsConfiguration
sur le lecteur :
Kotlin
player.setMediaItem(
MediaItem.Builder()
.setUri("https://www.example.com/media.m3u8")
.setAdsConfiguration(
AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
.setAdsId("ad-tag-0") // must be unique within playlist
.build())
.build())
player.prepare();
player.play();
Java
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();
Utiliser l'API basée sur la source média
Vous pouvez également créer l'instance ExoPlayer sans remplacer la fabrique de sources multimédias par défaut. Pour prendre en charge les interstitiels, une application peut ensuite utiliser HlsInterstitialsAdsLoader.AdsMediaSourceFactory
directement pour créer un MediaSource
et le fournir à ExoPlayer à l'aide de l'API de playlist basée sur la source multimédia :
Kotlin
hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context)
// Create a MediaSource.Factory for HLS streams with interstitials.
var hlsMediaSourceFactory =
HlsInterstitialsAdsLoader.AdsMediaSourceFactory(hlsInterstitialsAdsLoader, playerView, context)
// Build player with default media source factory.
player = new 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(Uri.parse("hls://interstitials"))
.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.
player = new ExoPlayer.Builder(context).build();
// Create an 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.
exoPlayer.setMediaSource(mediaSource);
exoPlayer.prepare();
exoPlayer.play();
Écouter les événements publicitaires
Un Listener
peut être ajouté à HlsInterstitialsAdsLoader
pour surveiller les événements concernant les changements d'état de la lecture des interstitiels HLS. Cela permet à une application ou à un SDK de suivre les annonces diffusées, les listes de composants chargées, les sources de contenu publicitaire en cours de préparation ou de détecter les erreurs de chargement de listes de composants et de préparation d'annonces. De plus, les métadonnées émises par les sources de contenu multimédia publicitaire peuvent être reçues pour une vérification précise de la lecture des annonces ou pour suivre la progression de la lecture des annonces.
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
private 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.
}
}
Consultez le JavaDoc de HlsInterstitialsAdsLoader.Listener
pour obtenir la documentation détaillée de tous les rappels disponibles.
L'écouteur peut ensuite être ajouté au chargeur d'annonces :
Kotlin
var 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);
Cycle de vie HlsInterstitialsAdsLoader
Une instance de HlsInterstitialsAdsLoader
ou HlsInterstitialsAdsLoader.AdsMediaSourceFactory
peut être réutilisée pour plusieurs instances de lecteur qui créent plusieurs sources multimédias pour lesquelles des annonces doivent être chargées.
Une instance peut être créée, par exemple, dans la méthode onCreate
d'un Activity
, puis être réutilisée pour plusieurs instances de lecteur. Cela fonctionne tant qu'il est utilisé par une seule instance de lecteur à la fois. Cela est utile dans le cas d'utilisation courant où l'application est mise en arrière-plan, l'instance du lecteur est détruite, puis une nouvelle instance est créée lorsque l'application est à nouveau mise au premier plan.
Kotlin
// Create the ads loader instance (for example onCreate).
hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context);
// Build a player and set it on the ads loader (for example onStart).
player = ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);
// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);
// Build another player and set it on the ads loader (for example onStart).
player = ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);
// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);
// Release the ads loader when not used anymore (for example onDestroy).
hlsInterstitialsAdsLoader.release();
Java
// Create the ads loader instance (for example onCreate).
hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(context);
// Build a player and set it on the ads loader (for example onStart).
player = new ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);
// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);
// Build another player and set it on the ads loader (for example onStart).
player = new ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);
// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);
// Release the ads loader when not used anymore (for example onDestroy).
hlsInterstitialsAdsLoader.release();
En règle générale, veillez à libérer l'ancienne instance de lecteur avant de définir la prochaine instance de lecteur sur le chargeur d'annonces. Une fois le chargeur d'annonces lui-même publié, il ne peut plus être utilisé.
Personnaliser la lecture
ExoPlayer vous permet de personnaliser l'expérience de lecture de plusieurs façons en fonction des besoins de votre application. Pour obtenir des exemples, consultez la page Personnalisation.
Désactiver la préparation sans blocs
Par défaut, ExoPlayer utilise la préparation sans blocs. Cela signifie qu'ExoPlayer n'utilisera que les informations de la playlist multivariante pour préparer le flux, ce qui fonctionne si les balises #EXT-X-STREAM-INF
contiennent l'attribut CODECS
.
Vous devrez peut-être désactiver cette fonctionnalité si vos segments multimédias contiennent des pistes de sous-titres muxées qui ne sont pas déclarées dans la playlist multivariante avec une balise #EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS
. Sinon, ces pistes de sous-titres ne seront pas détectées ni lues. Vous pouvez désactiver la préparation sans blocs dans HlsMediaSource.Factory
, comme indiqué dans l'extrait suivant. Notez que cela augmentera le temps de démarrage, car ExoPlayer doit télécharger un segment multimédia pour découvrir ces pistes supplémentaires. Il est préférable de déclarer les pistes de sous-titres dans la 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));
Créer du contenu HLS de haute qualité
Pour exploiter tout le potentiel d'ExoPlayer, vous pouvez suivre certaines consignes afin d'améliorer votre contenu HLS. Pour en savoir plus, consultez notre article Medium sur la lecture HLS dans ExoPlayer. Voici les principaux points à retenir :
- Utilisez des durées de segments précises.
- Utilisez un flux multimédia continu et évitez les modifications de la structure multimédia entre les segments.
- Utilisez la balise
#EXT-X-INDEPENDENT-SEGMENTS
. - Privilégiez les flux démultiplexés aux fichiers incluant à la fois la vidéo et l'audio.
- Ajoutez toutes les informations possibles dans la playlist multivariante.
Les consignes suivantes s'appliquent spécifiquement aux diffusions en direct :
- Utilisez la balise
#EXT-X-PROGRAM-DATE-TIME
. - Utilisez la balise
#EXT-X-DISCONTINUITY-SEQUENCE
. - Fournissez une longue fenêtre de diffusion en direct. Une minute ou plus, c'est parfait.