HLS

ExoPlayer obsługuje HLS w wielu formatach kontenerów. Obsługiwane muszą być też formaty próbek audio i wideo (więcej informacji znajdziesz w sekcji formaty próbek). Gorąco zachęcamy producentów treści HLS do generowania wysokiej jakości strumieni HLS, jak opisano w tym poście na blogu.

Funkcja Obsługiwane Komentarze
Kontenery
MPEG-TS TAK
FMP4/CMAF TAK
ADTS (AAC) TAK
MP3 TAK
Napisy
CEA-608 TAK
CEA-708 TAK
WebVTT TAK
Metadane
ID3 TAK
SCTE-35 NIE
Ochrona treści
AES-128 TAK
Przykładowy AES-128 NIE
Widevine TAK API w wersji 19+ (schemat „cenc”) i 25+ (schemat „cbcs”)
PlayReady SL2000 TAK Tylko Android TV
Kontrola serwera
Aktualizacje delta TAK
Blokowanie ponownego wczytywania playlisty TAK
Blokowanie wczytywania wskazówek wstępnego wczytywania TAK z wyjątkiem zakresów bajtów o nieokreślonej długości.
Wstawianie reklam
Wstawianie reklam sterowane przez serwer (reklamy pełnoekranowe) Częściowo Tylko VOD z X-ASSET-URI. Transmisje na żywo i X-ASSET-LIST zostaną dodane później.
Reklamy po stronie serwera i po stronie klienta IMA TAK Przewodnik po wstawianiu reklam
Odtwarzanie na żywo
Odtwarzanie na żywo TAK
HLS o małym opóźnieniu (Apple) TAK
HLS o niskim opóźnieniu (społecznościowy) NIE
Common Media Client Data CMCD TAK Przewodnik po integracji CMCD

Używanie elementu MediaItem

Aby odtworzyć strumień HLS, musisz użyć modułu HLS.

Kotlin

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

Groovy

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

Następnie możesz utworzyć MediaItem dla adresu URI playlisty HLS i przekazać go do odtwarzacza.

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();

Jeśli Twój identyfikator URI nie kończy się znakiem .m3u8, możesz przekazać MimeTypes.APPLICATION_M3U8 do parametru setMimeType funkcji MediaItem.Builder, aby wyraźnie wskazać typ treści.

Identyfikator URI elementu multimedialnego może wskazywać playlistę multimediów lub playlistę z wieloma wariantami. Jeśli identyfikator URI wskazuje listę odtwarzania z wieloma wariantami, która deklaruje wiele tagów #EXT-X-STREAM-INF, ExoPlayer automatycznie dostosuje się do wariantów, biorąc pod uwagę zarówno dostępną przepustowość, jak i możliwości urządzenia.

Korzystanie z HlsMediaSource

Aby uzyskać więcej opcji dostosowywania, możesz utworzyć obiekt HlsMediaSource i przekazać go bezpośrednio do odtwarzacza zamiast obiektu 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();

Dostęp do pliku manifestu

Bieżący plik manifestu możesz pobrać, wywołując funkcję Player.getCurrentManifest. W przypadku HLS zwrócony obiekt należy rzutować na HlsManifest. Wywołanie zwrotne onTimelineChanged funkcji Player.Listener jest też wywoływane za każdym razem, gdy wczytywany jest plik manifestu. W przypadku treści na żądanie zdarzy się to raz, a w przypadku treści na żywo może się to zdarzyć wiele razy. Poniższy fragment kodu pokazuje, jak aplikacja może wykonać działanie po wczytaniu pliku manifestu.

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.
        }
      }
    });

Odtwarzanie strumieni HLS z reklamami pełnoekranowymi

Specyfikacja HLS definiuje reklamy pełnoekranowe HLS, których można używać do umieszczania informacji o reklamach pełnoekranowych na playliście multimedialnej. ExoPlayer domyślnie ignoruje te reklamy pełnoekranowe. Pomoc można dodać za pomocą HlsInterstitialsAdsLoader. Nie obsługujemy wszystkich funkcji specyfikacji od samego początku. Jeśli brakuje obsługi Twojego strumienia, daj nam znać, zgłaszając problem w serwisie GitHub i przesyłając nam identyfikator URI strumienia, abyśmy mogli dodać jego obsługę.

Używanie MediaItem z interfejsem API playlisty

Najwygodniejszym sposobem odtwarzania strumieni HLS z reklamami pełnoekranowymi jest utworzenie instancji ExoPlayera z HlsInterstitialsAdsLoader.AdsMediaSourceFactory. Umożliwia to korzystanie z MediaItem opartego na interfejsie API playlisty Player do odtwarzania reklam pełnoekranowych HLS.

MediaSource.Factory ExoPlayer można wstrzyknąć do konstruktora podczas tworzenia instancji odtwarzacza:

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);

W takiej konfiguracji odtwarzanie reklam pełnoekranowych HLS polega na ustawieniu elementu multimedialnego z AdsConfiguration w odtwarzaczu:

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();

Korzystanie z interfejsu API opartego na źródle multimediów

Możesz też utworzyć instancję ExoPlayera bez zastępowania domyślnej fabryki źródeł multimediów. Aby obsługiwać reklamy pełnoekranowe, aplikacja może użyć bezpośrednio HlsInterstitialsAdsLoader.AdsMediaSourceFactory do utworzenia MediaSource i przekazania go do ExoPlayera za pomocą interfejsu API playlisty opartej na źródle multimediów:

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 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.
player.setMediaSource(mediaSource);
player.prepare();
player.play();

Nasłuchiwanie zdarzeń reklamowych

Do HlsInterstitialsAdsLoader można dodać Listener, aby monitorować zdarzenia dotyczące zmian stanu odtwarzania reklam pełnoekranowych HLS. Umożliwia to aplikacji lub pakietowi SDK śledzenie wyświetlanych reklam, wczytywanych list komponentów, przygotowywanych źródeł multimediów reklamowych oraz wykrywanie błędów wczytywania list komponentów i przygotowywania reklam. Ponadto metadane emitowane przez źródła multimediów reklamowych mogą być odbierane w celu szczegółowej weryfikacji odtwarzania reklam lub śledzenia postępów odtwarzania reklam.

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.
  }
}

Szczegółową dokumentację wszystkich dostępnych wywołań zwrotnych znajdziesz w JavaDoc HlsInterstitialsAdsLoader.Listener.

Następnie można dodać odbiorcę do modułu wczytywania reklam:

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);

HlsInterstitialsAdsLoader cykl życia

Instancję HlsInterstitialsAdsLoader lub HlsInterstitialsAdsLoader.AdsMediaSourceFactory można ponownie wykorzystać w przypadku wielu instancji odtwarzacza, które tworzą wiele źródeł multimediów, do których trzeba wczytać reklamy.

Instancję można utworzyć na przykład w metodzie onCreate elementu Activity, a potem używać jej ponownie w przypadku wielu instancji odtwarzacza. Działa to, o ile jest używane przez jedną instancję odtwarzacza w tym samym czasie. Jest to przydatne w typowym przypadku, gdy aplikacja jest przenoszona w tle, instancja odtwarzacza jest niszczona, a następnie tworzona jest nowa instancja, gdy aplikacja ponownie przechodzi na pierwszy plan.

Wznawianie odtwarzania ze stanem odtwarzania reklamy

Aby uniknąć konieczności ponownego oglądania reklam przez użytkowników, stan odtwarzania reklamy można zapisać i przywrócić po ponownym utworzeniu odtwarzacza. W tym celu wywołaj funkcję getAdsResumptionStates(), gdy gracz ma zostać zwolniony, i zapisz zwrócone obiekty AdsResumptionState. Po ponownym utworzeniu odtwarzacza stan można przywrócić, wywołując metodę addAdResumptionState() w instancji modułu wczytywania reklam. AdsResumptionState można połączyć w pakiet, więc można ją przechowywać w pakiecie onSaveInstanceState usługi Activity. Pamiętaj, że wznawianie reklam jest obsługiwane tylko w przypadku strumieni 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

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Create the ads loader instance.
    hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(this)
    // Restore ad resumption states.
    savedInstanceState?.getParcelableArrayList<Bundle>(ADS_RESUMPTION_STATE_KEY)?.let { bundles ->
      adsResumptionStates =
        bundles.map { HlsInterstitialsAdsLoader.AdsResumptionState.fromBundle(it) }
    }
  }

  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;

  @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 =
          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);
    }
  }
}

Dostosowywanie odtwarzania

ExoPlayer udostępnia wiele sposobów dostosowywania odtwarzania do potrzeb aplikacji. Przykłady znajdziesz na stronie Dostosowywanie.

Wyłączanie przygotowywania bez podziału na części

Domyślnie ExoPlayer używa przygotowania bez podziału na fragmenty. Oznacza to, że ExoPlayer będzie używać informacji z listy odtwarzania z wieloma wariantami tylko do przygotowania strumienia, co działa, jeśli tagi #EXT-X-STREAM-INF zawierają atrybut CODECS.

Może być konieczne wyłączenie tej funkcji, jeśli segmenty multimediów zawierają zmultipleksowane ścieżki napisów, które nie są zadeklarowane na liście odtwarzania z wieloma wariantami za pomocą tagu #EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS. W przeciwnym razie ścieżki napisów nie zostaną wykryte i odtworzone. Przygotowywanie bez podziału na części możesz wyłączyć w HlsMediaSource.Factory, jak w tym przykładzie: Pamiętaj, że wydłuży to czas uruchamiania, ponieważ ExoPlayer musi pobrać segment multimediów, aby wykryć te dodatkowe ścieżki. Zamiast tego lepiej jest zadeklarować ścieżki napisów w wielowariantowej playliście.

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));

Tworzenie wysokiej jakości treści HLS

Aby w pełni wykorzystać możliwości ExoPlayera, możesz postępować zgodnie z pewnymi wytycznymi, które pomogą Ci ulepszyć treści HLS. Pełne wyjaśnienie znajdziesz w naszym poście na Medium o odtwarzaniu HLS w ExoPlayerze. Najważniejsze kwestie:

  • Używaj dokładnych czasów trwania segmentów.
  • Używaj ciągłego strumienia multimediów. Unikaj zmian w strukturze multimediów w poszczególnych segmentach.
  • Używaj tagu #EXT-X-INDEPENDENT-SEGMENTS.
  • Preferuj strumienie zdemuksowane zamiast plików zawierających zarówno wideo, jak i audio.
  • W playlistach z wieloma wariantami podaj wszystkie możliwe informacje.

W przypadku transmisji na żywo obowiązują te wytyczne:

  • Używaj tagu #EXT-X-PROGRAM-DATE-TIME.
  • Używaj tagu #EXT-X-DISCONTINUITY-SEQUENCE.
  • zapewnić długie okno transmisji na żywo; Minuta lub więcej to świetny wynik.