HLS (Phát trực tuyến dựa trên HTTP)

ExoPlayer hỗ trợ HLS với nhiều định dạng vùng chứa. Định dạng mẫu âm thanh và video có trong tệp cũng phải được hỗ trợ (xem phần định dạng mẫu để biết thông tin chi tiết). Các nhà sản xuất nội dung HLS nên tạo các luồng HLS chất lượng cao, như mô tả trong bài đăng này trên blog.

Tính năng Được hỗ trợ Bình luận
Vùng chứa
MPEG-TS
FMP4/CMAF
ADTS (AAC)
MP3
Phụ đề / phụ đề chi tiết
CEA-608
CEA-708
WebVTT
Siêu dữ liệu
ID3
SCTE-35 KHÔNG
Bảo vệ nội dung
AES-128
Mẫu AES-128 KHÔNG
Widevine API 19+ ("cenc" scheme) và 25+ ("cbcs" scheme)
PlayReady SL2000 Chỉ phát hành cho Android TV
Quyền kiểm soát máy chủ
Bản cập nhật gia tăng
Chặn tải lại danh sách phát
Chặn tải các gợi ý tải trước Ngoại trừ byterange có độ dài không xác định
Chèn quảng cáo
Chèn quảng cáo có hướng dẫn của máy chủ (Quảng cáo xen kẽ) Một phần Chỉ VOD có X-ASSET-URI. Chúng tôi sẽ bổ sung sự kiện phát trực tiếp và X-ASSET-LIST sau.
Quảng cáo phía máy chủ và phía máy khách của IMA Hướng dẫn chèn quảng cáo
Phát trực tiếp
Phát trực tiếp theo cách thông thường
HLS có độ trễ thấp (Apple)
HLS có độ trễ thấp (Cộng đồng) KHÔNG
Dữ liệu ứng dụng nội dung nghe nhìn phổ biến CMCD Hướng dẫn tích hợp CMCD

Sử dụng MediaItem

Để phát một sự kiện phát trực tiếp HLS, bạn cần phải phụ thuộc vào mô-đun HLS.

Kotlin

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

Groovy

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

Sau đó, bạn có thể tạo một MediaItem cho URI danh sách phát HLS và truyền URI đó đến trình phát.

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

Nếu URI của bạn không kết thúc bằng .m3u8, bạn có thể truyền MimeTypes.APPLICATION_M3U8 đến setMimeType của MediaItem.Builder để chỉ ra rõ ràng loại nội dung.

URI của mục nội dung nghe nhìn có thể trỏ đến danh sách phát nội dung nghe nhìn hoặc danh sách phát nhiều biến thể. Nếu URI trỏ đến một danh sách phát có nhiều biến thể khai báo nhiều thẻ #EXT-X-STREAM-INF, thì ExoPlayer sẽ tự động điều chỉnh giữa các biến thể, có tính đến cả băng thông hiện có và khả năng của thiết bị.

Sử dụng HlsMediaSource

Để có thêm các lựa chọn tuỳ chỉnh, bạn có thể tạo một HlsMediaSource và truyền trực tiếp đến trình phát thay vì 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();

Truy cập vào tệp kê khai

Bạn có thể truy xuất tệp kê khai hiện tại bằng cách gọi Player.getCurrentManifest. Đối với HLS, bạn nên truyền đối tượng được trả về đến HlsManifest. Lệnh gọi lại onTimelineChanged của Player.Listener cũng được gọi bất cứ khi nào tệp kê khai được tải. Điều này sẽ xảy ra một lần đối với nội dung theo yêu cầu và có thể nhiều lần đối với nội dung phát trực tiếp. Đoạn mã sau đây cho biết cách một ứng dụng có thể làm điều gì đó bất cứ khi nào tệp kê khai được tải.

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

Phát luồng HLS có quảng cáo xen kẽ

Quy cách HLS xác định các quảng cáo xen kẽ HLS có thể dùng để đưa thông tin quảng cáo xen kẽ vào danh sách phát nội dung nghe nhìn. Theo mặc định, ExoPlayer sẽ bỏ qua những quảng cáo xen kẽ này. Bạn có thể thêm nội dung hỗ trợ bằng cách sử dụng HlsInterstitialsAdsLoader. Chúng tôi không hỗ trợ tất cả các tính năng của thông số kỹ thuật ngay từ đầu. Nếu bạn không thấy dịch vụ hỗ trợ cho luồng phát của mình, hãy cho chúng tôi biết bằng cách gửi báo cáo vấn đề trên GitHub và gửi cho chúng tôi URI luồng phát của bạn để chúng tôi có thể thêm dịch vụ hỗ trợ cho luồng phát đó.

Sử dụng MediaItem với Playlist API

Cách thuận tiện nhất để phát các luồng HLS có quảng cáo xen kẽ là tạo một thực thể ExoPlayer bằng HlsInterstitialsAdsLoader.AdsMediaSourceFactory. Điều này cho phép sử dụng API danh sách phát dựa trên MediaItem của giao diện Player để phát quảng cáo xen kẽ HLS.

MediaSource.Factory của ExoPlayer có thể được chèn vào trình tạo khi tạo phiên bản trình phát:

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

Với chế độ thiết lập trình phát như vậy, việc phát quảng cáo xen kẽ HLS chỉ là việc thiết lập một mục nội dung nghe nhìn có AdsConfiguration trên trình phát:

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

Sử dụng API dựa trên nguồn nội dung nghe nhìn

Ngoài ra, bạn có thể tạo thực thể ExoPlayer mà không cần ghi đè nhà máy nguồn nội dung nghe nhìn mặc định. Để hỗ trợ quảng cáo xen kẽ, ứng dụng có thể sử dụng trực tiếp HlsInterstitialsAdsLoader.AdsMediaSourceFactory để tạo MediaSource và cung cấp cho ExoPlayer bằng API danh sách phát dựa trên nguồn nội dung nghe nhìn:

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

Theo dõi các sự kiện quảng cáo

Bạn có thể thêm Listener vào HlsInterstitialsAdsLoader để theo dõi các sự kiện liên quan đến những thay đổi về trạng thái phát quảng cáo xen kẽ HLS. Điều này cho phép ứng dụng hoặc SDK theo dõi các quảng cáo đã phát, danh sách thành phần đang được tải, nguồn nội dung nghe nhìn quảng cáo đang được chuẩn bị hoặc phát hiện lỗi tải danh sách thành phần và lỗi chuẩn bị quảng cáo. Ngoài ra, bạn có thể nhận được siêu dữ liệu do các nguồn nội dung nghe nhìn quảng cáo phát ra để xác minh quá trình phát quảng cáo một cách chi tiết hoặc theo dõi tiến trình phát quảng cáo.

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

Xem JavaDoc của HlsInterstitialsAdsLoader.Listener để biết tài liệu chi tiết về tất cả các lệnh gọi lại có sẵn.

Sau đó, bạn có thể thêm trình nghe vào trình tải quảng cáo:

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

Vòng đời HlsInterstitialsAdsLoader

Bạn có thể dùng lại một phiên bản HlsInterstitialsAdsLoader hoặc HlsInterstitialsAdsLoader.AdsMediaSourceFactory cho nhiều phiên bản trình phát tạo nhiều nguồn nội dung nghe nhìn mà quảng cáo phải được tải.

Ví dụ: bạn có thể tạo một phiên bản trong phương thức onCreate của Activity, sau đó sử dụng lại cho nhiều phiên bản trình phát. Điều này sẽ hoạt động miễn là chỉ có một phiên bản trình phát đang sử dụng tại cùng một thời điểm. Điều này hữu ích cho trường hợp sử dụng phổ biến khi ứng dụng chuyển sang chạy ở chế độ nền, thực thể trình phát bị huỷ và sau đó một thực thể mới được tạo khi ứng dụng chuyển sang chạy ở chế độ nền một lần nữa.

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

Nói chung, hãy nhớ phát hành phiên bản trình phát cũ trước khi đặt phiên bản trình phát tiếp theo trên trình tải quảng cáo. Sau khi trình tải quảng cáo được phát hành, bạn sẽ không thể sử dụng trình tải quảng cáo nữa.

Tuỳ chỉnh chế độ phát

ExoPlayer cung cấp nhiều cách để bạn điều chỉnh trải nghiệm phát cho phù hợp với nhu cầu của ứng dụng. Hãy xem trang Tuỳ chỉnh để tham khảo các ví dụ.

Tắt tính năng chuẩn bị không phân đoạn

Theo mặc định, ExoPlayer sẽ sử dụng thông tin đa biến trong danh sách phát cho việc phát nội dung đa phương tiện (chunkless preparation). Điều này có nghĩa là ExoPlayer sẽ chỉ sử dụng thông tin trong danh sách phát có nhiều biến thể để chuẩn bị luồng phát. Điều này sẽ hoạt động nếu thẻ #EXT-X-STREAM-INF chứa thuộc tính CODECS.

Bạn có thể cần tắt tính năng này nếu các phân đoạn nội dung nghe nhìn của bạn có chứa các bản phụ đề được ghép kênh không được khai báo trong danh sách phát có nhiều biến thể bằng thẻ #EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS. Nếu không, các tệp phụ đề này sẽ không được phát hiện và phát. Bạn có thể tắt chế độ chuẩn bị không phân đoạn trong HlsMediaSource.Factory như minh hoạ trong đoạn mã sau. Lưu ý rằng thao tác này sẽ làm tăng thời gian khởi động vì ExoPlayer cần tải một đoạn nội dung nghe nhìn xuống để khám phá các bản phụ đề bổ sung này. Thay vào đó, bạn nên khai báo các bản phụ đề trong danh sách phát có nhiều biến thể.

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

Tạo nội dung HLS chất lượng cao

Để khai thác tối đa ExoPlayer, bạn có thể làm theo một số nguyên tắc nhất định để cải thiện nội dung HLS. Hãy đọc bài đăng của chúng tôi trên Medium về tính năng phát HLS trong ExoPlayer để biết nội dung giải thích đầy đủ. Sau đây là những điểm chính:

  • Sử dụng thời lượng phân đoạn chính xác.
  • Sử dụng luồng nội dung nghe nhìn liên tục; tránh thay đổi cấu trúc nội dung nghe nhìn trên các phân đoạn.
  • Dùng thẻ #EXT-X-INDEPENDENT-SEGMENTS.
  • Ưu tiên các luồng được tách kênh, thay vì các tệp có cả video và âm thanh.
  • Hãy cung cấp tất cả thông tin có thể trong Danh sách phát nhiều biến thể.

Các nguyên tắc sau đây áp dụng riêng cho luồng phát trực tiếp:

  • Dùng thẻ #EXT-X-PROGRAM-DATE-TIME.
  • Dùng thẻ #EXT-X-DISCONTINUITY-SEQUENCE.
  • Cung cấp một cửa sổ phát trực tiếp dài. Một phút trở lên là lý tưởng.