HLS

ExoPlayer est compatible avec HLS avec plusieurs formats de conteneur. Les formats d'échantillons audio et vidéo contenus doivent également être compatibles (pour en savoir plus, consultez la section Formats d'échantillons). Nous encourageons vivement les producteurs de contenus 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 /sous-titres en version étrangère
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 et versions ultérieures ("cenc") et 25 et versions ultérieures ("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 indices de préchargement OUI À l'exception des plages d'octets dont la longueur n'est pas définie
Insertion d'annonces
Insertion d'annonces guidée par le serveur (annonces interstitielles) En partie Uniquement la VOD avec X-ASSET-URI. Les diffusions en direct et 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 régulière OUI
HLS à faible latence (Apple) OUI
HLS à faible latence (communauté) NON
Données client multimédias communes CMCD OUI Guide d'intégration CMMC

Utiliser MediaItem

Pour lire un flux HLS, vous devez dépendre du module HLS.

Kotlin

implementation("androidx.media3:media3-exoplayer-hls:1.6.0")

Groovy

implementation "androidx.media3:media3-exoplayer-hls:1.6.0"

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 multivariée. Si l'URI pointe vers une playlist multivariante qui déclare plusieurs balises #EXT-X-STREAM-INF, ExoPlayer s'adapte automatiquement entre les variantes, en tenant compte à la fois de la bande passante disponible et des fonctionnalités de l'appareil.

Utiliser HlsMediaSource

Pour 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é en HlsManifest. Le rappel onTimelineChanged de Player.Listener est également appelé chaque fois que le fichier manifeste est chargé. Cela se produit une fois pour les contenus à la demande et peut se produire 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 une prise en charge à l'aide de HlsInterstitialsAdsLoader. Nous ne prenons pas en charge toutes les fonctionnalités de la spécification dès le départ. Si votre flux n'est pas compatible, veuillez nous en informer en signalant un problème sur GitHub et en nous envoyant votre URI de flux afin que nous puissions le prendre en charge.

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 de 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 d'annonces interstitielles 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 multimé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 liés aux changements d'état concernant la lecture des interstitiels HLS. Cela permet à une application ou à un SDK de suivre les annonces diffusées, les listes d'assets en cours de chargement, les sources multimédias d'annonces en cours de préparation ou de détecter les erreurs de chargement de la liste d'assets et de préparation des annonces. De plus, les métadonnées émises par les sources multimédias d'annonces peuvent être reçues pour vérifier la lecture des annonces de manière précise 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 of HlsInterstitialsAdsLoader.Listener.

  @Override
  public void onStop(MediaItem mediaItem, Object adsId, AdPlaybackState adPlaybackState) {
    // Do something with the resulting ad playback state when stopped.
  }
}

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 réutilisée pour plusieurs instances de joueurs. Cela fonctionne tant qu'il est utilisé par une seule instance de joueur en même temps. Cela est utile pour le cas d'utilisation courant lorsque l'application est mise en arrière-plan, que l'instance du lecteur est détruite, puis qu'une nouvelle instance est créée lorsque l'application est de nouveau 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 l'instance de lecteur suivante 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 en fonction des besoins de votre application de plusieurs façons. Pour en savoir plus, consultez la page Personnalisation.

Désactiver la préparation sans bloc

Par défaut, ExoPlayer utilise la préparation sans blocs. Cela signifie qu'ExoPlayer n'utilisera que les informations de la playlist multivariée 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 multiplexé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 diffusées. Vous pouvez désactiver la préparation sans bloc 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 tirer le meilleur parti d'ExoPlayer, vous pouvez suivre certaines consignes pour améliorer votre contenu HLS. Pour en savoir plus, consultez notre post sur Medium concernant la lecture HLS dans ExoPlayer. Voici les points principaux:

  • Utilisez des durées de segment précises.
  • Utilisez un flux multimédia continu. É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 plutôt que les fichiers contenant à la fois de la vidéo et de l'audio.
  • Incluez toutes les informations possibles dans la playlist multivariée.

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 période de diffusion en direct. Une minute ou plus est idéal.