HLS

ExoPlayer mendukung HLS dengan beberapa format penampung. Format sampel audio dan video yang disertakan juga harus didukung (lihat bagian format sampel untuk mengetahui detailnya). Sebaiknya produser konten HLS membuat streaming HLS berkualitas tinggi, seperti yang dijelaskan dalam postingan blog ini.

Fitur Didukung Komentar
Container
MPEG-TS YA
FMP4/CMAF YA
ADTS (AAC) YA
MP3 YA
Teks tertutup / subtitel
CEA-608 YA
CEA-708 YA
WebVTT YA
Metadata
ID3 YA
SCTE-35 TIDAK
Perlindungan konten
AES-128 YA
AES-128 Sampel TIDAK
Widevine YA API 19+ (skema "cenc") dan 25+ (skema "cbcs")
PlayReady SL2000 YA Khusus Android TV
Kontrol server
Update delta YA
Memblokir pemuatan ulang playlist YA
Memblokir pemuatan petunjuk pramuat YA Kecuali untuk rentang byte dengan panjang yang tidak ditentukan
Penyisipan iklan
Penyisipan iklan yang dipandu server (Interstisial) Sebagian Hanya VOD dengan X-ASSET-URI. Live stream dan X-ASSET-LIST akan ditambahkan nanti.
Iklan sisi server dan sisi klien IMA YA Panduan penyisipan iklan
Pemutaran live
Pemutaran live reguler YA
HLS latensi rendah (Apple) YA
HLS latensi rendah (Komunitas) TIDAK
Data Klien Media Umum CMCD YA Panduan integrasi CMCD

Menggunakan MediaItem

Untuk memutar streaming HLS, Anda harus bergantung pada modul HLS.

Kotlin

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

Groovy

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

Kemudian, Anda dapat membuat MediaItem untuk URI playlist HLS dan meneruskannya ke pemutar.

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

Jika URI Anda tidak diakhiri dengan .m3u8, Anda dapat meneruskan MimeTypes.APPLICATION_M3U8 ke setMimeType dari MediaItem.Builder untuk menunjukkan jenis konten secara eksplisit.

URI item media dapat mengarah ke playlist media atau playlist multivariasi. Jika URI mengarah ke playlist multivarian yang mendeklarasikan beberapa tag #EXT-X-STREAM-INF, ExoPlayer akan otomatis beradaptasi antar-varian, dengan mempertimbangkan bandwidth yang tersedia dan kemampuan perangkat.

Menggunakan HlsMediaSource

Untuk opsi penyesuaian lainnya, Anda dapat membuat HlsMediaSource dan meneruskannya langsung ke pemutar, bukan 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();

Mengakses manifes

Anda dapat mengambil manifes saat ini dengan memanggil Player.getCurrentManifest. Untuk HLS, Anda harus melakukan transmisi objek yang ditampilkan ke HlsManifest. Callback onTimelineChanged dari Player.Listener juga dipanggil setiap kali manifes dimuat. Hal ini akan terjadi satu kali untuk konten on-demand dan mungkin berkali-kali untuk konten live. Cuplikan kode berikut menunjukkan cara aplikasi dapat melakukan sesuatu setiap kali manifes dimuat.

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

Memutar streaming HLS dengan iklan interstisial

Spesifikasi HLS menentukan iklan interstisial HLS yang dapat digunakan untuk menyertakan informasi iklan interstisial dalam playlist media. Secara default, ExoPlayer mengabaikan iklan interstitial ini. Dukungan dapat ditambahkan menggunakan HlsInterstitialsAdsLoader. Kami tidak mendukung semua fitur spesifikasi sejak awal. Jika Anda tidak mendapatkan dukungan untuk streaming Anda, beri tahu kami dengan melaporkan masalah di GitHub dan kirimkan URI streaming Anda, agar kami dapat menambahkan dukungan untuk streaming Anda.

Menggunakan MediaItem dengan playlist API

Cara paling mudah untuk memutar streaming HLS dengan iklan interstisial adalah dengan membuat instance ExoPlayer dengan HlsInterstitialsAdsLoader.AdsMediaSourceFactory. Hal ini memungkinkan penggunaan playlist API berbasis MediaItem dari antarmuka Player untuk memutar interstisial HLS.

MediaSource.Factory dari ExoPlayer dapat disuntikkan ke builder saat membangun instance pemutar:

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

Dengan penyiapan pemutar seperti itu, memutar iklan interstisial HLS hanya perlu menyetel item media dengan AdsConfiguration di pemutar:

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

Menggunakan API berbasis sumber media

Atau, instance ExoPlayer dapat dibuat tanpa mengganti factory sumber media default. Untuk mendukung iklan interstisial, aplikasi kemudian dapat menggunakan HlsInterstitialsAdsLoader.AdsMediaSourceFactory secara langsung untuk membuat MediaSource dan memberikannya ke ExoPlayer menggunakan API playlist berbasis sumber media:

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

Memproses peristiwa iklan

Listener dapat ditambahkan ke HlsInterstitialsAdsLoader untuk memantau peristiwa mengenai perubahan status terkait pemutaran iklan interstisial HLS. Hal ini memungkinkan aplikasi atau SDK melacak iklan yang diputar, daftar aset yang dimuat, sumber media iklan yang disiapkan, atau mendeteksi error pemuatan daftar aset dan penyiapan iklan. Selain itu, metadata yang dikeluarkan oleh sumber media iklan dapat diterima untuk verifikasi pemutaran iklan yang lebih mendetail atau untuk melacak progres pemutaran iklan.

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

Lihat JavaDoc HlsInterstitialsAdsLoader.Listener untuk mengetahui dokumentasi mendetail semua callback yang tersedia.

Kemudian, pemroses dapat ditambahkan ke pemuat iklan:

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

Siklus proses HlsInterstitialsAdsLoader

Instance HlsInterstitialsAdsLoader atau HlsInterstitialsAdsLoader.AdsMediaSourceFactory dapat digunakan kembali untuk beberapa instance pemutar yang membuat beberapa sumber media yang iklannya harus dimuat.

Misalnya, instance dapat dibuat dalam metode onCreate dari Activity lalu digunakan kembali untuk beberapa instance pemain. Hal ini berfungsi selama digunakan oleh satu instance pemain pada waktu yang sama. Hal ini berguna untuk kasus penggunaan umum saat aplikasi dibawa ke latar belakang, instance pemutar dihancurkan, lalu instance baru dibuat saat aplikasi dibawa ke latar depan lagi.

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

Secara umum, pastikan untuk melepaskan instance pemutar lama sebelum menyetel instance pemutar berikutnya di pemuat iklan. Setelah pemuat iklan itu sendiri dirilis, pemuat iklan tidak dapat digunakan lagi.

Menyesuaikan pemutaran

ExoPlayer menyediakan beberapa cara bagi Anda untuk menyesuaikan pengalaman pemutaran dengan kebutuhan aplikasi Anda. Lihat Halaman penyesuaian untuk melihat contoh.

Menonaktifkan persiapan tanpa potongan

Secara default, ExoPlayer akan menggunakan persiapan tanpa potongan. Artinya, ExoPlayer hanya akan menggunakan informasi dalam playlist multivariasi untuk menyiapkan streaming, yang berfungsi jika tag #EXT-X-STREAM-INF berisi atribut CODECS.

Anda mungkin perlu menonaktifkan fitur ini jika segmen media Anda berisi trek teks tertutup yang di-mux dan tidak dideklarasikan dalam playlist multivarian dengan tag #EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS. Jika tidak, trek teks tertutup ini tidak akan terdeteksi dan diputar. Anda dapat menonaktifkan penyiapan tanpa potongan di HlsMediaSource.Factory seperti yang ditunjukkan dalam cuplikan berikut. Perhatikan bahwa hal ini akan meningkatkan waktu mulai karena ExoPlayer perlu mendownload segmen media untuk menemukan trek tambahan ini dan lebih baik mendeklarasikan trek teks tertutup dalam playlist multivarian.

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

Membuat konten HLS berkualitas tinggi

Untuk mengoptimalkan ExoPlayer, ada pedoman tertentu yang dapat Anda ikuti untuk meningkatkan kualitas konten HLS Anda. Baca postingan Medium kami tentang pemutaran HLS di ExoPlayer untuk mengetahui penjelasan selengkapnya. Poin utamanya adalah:

  • Gunakan durasi segmen yang presisi.
  • Gunakan aliran media berkelanjutan; hindari perubahan struktur media di seluruh segmen.
  • Gunakan tag #EXT-X-INDEPENDENT-SEGMENTS.
  • Lebih memilih aliran yang sudah di-demux, bukan file yang menyertakan video dan audio.
  • Sertakan semua informasi yang dapat Anda berikan di Playlist Multivarian.

Panduan berikut berlaku khusus untuk live stream:

  • Gunakan tag #EXT-X-PROGRAM-DATE-TIME.
  • Gunakan tag #EXT-X-DISCONTINUITY-SEQUENCE.
  • Menyediakan jendela live yang panjang. Satu menit atau lebih sudah cukup.