Tuỳ chỉnh

Cốt lõi của thư viện ExoPlayer là giao diện Player. Một Player cung cấp chức năng trình phát đa phương tiện truyền thống cấp cao, chẳng hạn như khả năng lưu vào bộ đệm nội dung nghe nhìn, phát, tạm dừng và tua. Phương thức triển khai mặc định ExoPlayer là được thiết kế nhằm đưa ra ít giả định (và do đó áp dụng ít hạn chế đối với) loại nội dung đa phương tiện đang được phát, cách thức và vị trí lưu trữ nội dung đa phương tiện cũng như cách thức kết xuất. Thay vì triển khai trực tiếp việc tải và kết xuất nội dung nghe nhìn, Quá trình triển khai ExoPlayer uỷ quyền công việc này cho các thành phần được chèn khi một trình phát được tạo hoặc khi các nguồn nội dung nghe nhìn mới được chuyển đến trình phát. Các thành phần phổ biến cho tất cả quá trình triển khai ExoPlayer là:

  • Các thực thể MediaSource xác định nội dung nghe nhìn sẽ phát, tải nội dung nghe nhìn và mà từ đó có thể đọc nội dung nghe nhìn đã tải. Đã tạo một thực thể MediaSource từ MediaItem của MediaSource.Factory bên trong trình phát. Họ cũng có thể được chuyển trực tiếp đến trình phát thông qua API danh sách phát dựa trên nguồn nội dung nghe nhìn.
  • Một thực thể MediaSource.Factory chuyển đổi MediaItem thành MediaSource. Chiến lược phát hành đĩa đơn MediaSource.Factory được chèn khi tạo trình phát.
  • Các thực thể Renderer kết xuất các thành phần riêng lẻ của nội dung nghe nhìn. Đây là được chèn khi tạo trình phát.
  • Một TrackSelector chọn các bản nhạc do MediaSource cung cấp sẽ là được tiêu thụ bởi mỗi Renderer có sẵn. Một TrackSelector được chèn khi tạo trình phát.
  • LoadControl kiểm soát thời điểm MediaSource lưu vào bộ đệm thêm nội dung nghe nhìn, và lượng nội dung nghe nhìn được lưu vào bộ đệm. LoadControl được chèn khi người chơi đã tạo.
  • Một LivePlaybackSpeedControl kiểm soát tốc độ phát trong khi phát trực tiếp số lần phát để cho phép trình phát luôn ở gần một điểm bù trực tiếp đã định cấu hình. Đáp LivePlaybackSpeedControl được chèn khi tạo trình phát.

Khái niệm chèn các thành phần để triển khai các đoạn của người chơi có sẵn trong toàn bộ thư viện. Các cách triển khai mặc định của một số thành phần uỷ quyền công việc cho các thành phần được chèn thêm. Điều này cho phép nhiều các thành phần phụ cần được thay thế riêng lẻ bằng những thành phần triển khai được định cấu hình theo cách tuỳ chỉnh.

Tuỳ chỉnh trình phát

Một số ví dụ phổ biến về tuỳ chỉnh trình phát bằng cách chèn các thành phần là được mô tả bên dưới.

Định cấu hình ngăn xếp mạng

Chúng tôi có một trang về cách tuỳ chỉnh ngăn xếp mạng mà ExoPlayer sử dụng.

Lưu dữ liệu vào bộ nhớ đệm được tải từ mạng

Xem hướng dẫn về lưu vào bộ nhớ đệm tạm thời một cách nhanh chóngtải nội dung nghe nhìn xuống.

Tuỳ chỉnh hoạt động tương tác với máy chủ

Một số ứng dụng có thể muốn chặn các yêu cầu và phản hồi HTTP. Bạn nên chèn tiêu đề yêu cầu tuỳ chỉnh, đọc tiêu đề phản hồi của máy chủ, sửa đổi các yêu cầu URI, v.v. Ví dụ: ứng dụng của bạn có thể tự xác thực bằng cách chèn mã thông báo làm tiêu đề khi yêu cầu các phân đoạn phương tiện.

Ví dụ sau minh hoạ cách triển khai những hành vi này bằng chèn một DataSource.Factory tuỳ chỉnh vào DefaultMediaSourceFactory:

Kotlin

val dataSourceFactory =
  DataSource.Factory {
    val dataSource = httpDataSourceFactory.createDataSource()
    // Set a custom authentication request header.
    dataSource.setRequestProperty("Header", "Value")
    dataSource
  }
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)
    )
    .build()

Java

DataSource.Factory dataSourceFactory =
    () -> {
      HttpDataSource dataSource = httpDataSourceFactory.createDataSource();
      // Set a custom authentication request header.
      dataSource.setRequestProperty("Header", "Value");
      return dataSource;
    };

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory))
        .build();

Trong đoạn mã trên, HttpDataSource được chèn bao gồm tiêu đề "Header: Value" trong mỗi yêu cầu HTTP. Hành vi này được khắc phục cho mỗi tương tác với một nguồn HTTP.

Để có cách tiếp cận chi tiết hơn, bạn có thể chèn hành vi đúng thời điểm bằng cách sử dụng ResolvingDataSource. Đoạn mã sau đây minh hoạ cách chèn của yêu cầu ngay trước khi tương tác với nguồn HTTP:

Kotlin

val dataSourceFactory: DataSource.Factory =
  ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec ->
    // Provide just-in-time request headers.
    dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri))
  }

Java

    DataSource.Factory dataSourceFactory =
        new ResolvingDataSource.Factory(
            httpDataSourceFactory,
            // Provide just-in-time request headers.
            dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)));

Bạn cũng có thể dùng ResolvingDataSource để thực hiện các sửa đổi kịp thời của URI, như minh hoạ trong đoạn mã sau:

Kotlin

val dataSourceFactory: DataSource.Factory =
  ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec ->
    // Provide just-in-time URI resolution logic.
    dataSpec.withUri(resolveUri(dataSpec.uri))
  }

Java

DataSource.Factory dataSourceFactory =
    new ResolvingDataSource.Factory(
        httpDataSourceFactory,
        // Provide just-in-time URI resolution logic.
        dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri)));

Tuỳ chỉnh cách xử lý lỗi

Việc triển khai một LoadErrorHandlingPolicy tuỳ chỉnh cho phép các ứng dụng tuỳ chỉnh cách ExoPlayer phản ứng với các lỗi tải. Ví dụ: một ứng dụng có thể muốn gặp lỗi nhanh thay vì thử lại nhiều lần hoặc có thể muốn tuỳ chỉnh logic thời gian đợi kiểm soát khoảng thời gian người chơi chờ giữa mỗi lần thử lại. Đoạn mã sau cho biết cách triển khai logic thời gian đợi tuỳ chỉnh:

Kotlin

val loadErrorHandlingPolicy: LoadErrorHandlingPolicy =
  object : DefaultLoadErrorHandlingPolicy() {
    override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorInfo): Long {
      // Implement custom back-off logic here.
      return 0
    }
  }
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)
    )
    .build()

Java

LoadErrorHandlingPolicy loadErrorHandlingPolicy =
    new DefaultLoadErrorHandlingPolicy() {
      @Override
      public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) {
        // Implement custom back-off logic here.
        return 0;
      }
    };

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context)
                .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy))
        .build();

Đối số LoadErrorInfo chứa thêm thông tin về việc không tải được tuỳ chỉnh logic dựa trên loại lỗi hoặc yêu cầu không thành công.

Tuỳ chỉnh cờ của trình trích xuất

Bạn có thể sử dụng cờ của trình trích xuất để tuỳ chỉnh cách trích xuất từng định dạng từ các phương tiện truyền thông tiến bộ. Bạn có thể đặt chúng trên DefaultExtractorsFactory được cung cấp cho DefaultMediaSourceFactory. Ví dụ sau truyền một cờ cho phép tìm kiếm dựa trên chỉ mục cho luồng MP3.

Kotlin

val extractorsFactory =
  DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING)
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(DefaultMediaSourceFactory(context, extractorsFactory))
    .build()

Java

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING);

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(new DefaultMediaSourceFactory(context, extractorsFactory))
        .build();

Bật tính năng tìm kiếm tốc độ bit liên tục

Đối với các luồng MP3, ADTS và AMR, bạn có thể bật tua gần đúng bằng cách sử dụng giả định tốc độ bit không đổi với cờ FLAG_ENABLE_CONSTANT_BITRATE_SEEKING. Bạn có thể đặt những cờ này cho từng trình trích xuất bằng cách sử dụng DefaultExtractorsFactory.setXyzExtractorFlags như mô tả ở trên. Người nhận bật tính năng tìm kiếm tốc độ bit không đổi cho tất cả các trình trích xuất hỗ trợ tốc độ bit, hãy sử dụng DefaultExtractorsFactory.setConstantBitrateSeekingEnabled.

Kotlin

val extractorsFactory = DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)

Java

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);

Sau đó, ExtractorsFactory có thể được chèn thông qua DefaultMediaSourceFactory dưới dạng được mô tả để tuỳ chỉnh cờ trình trích xuất ở trên.

Bật hàng đợi bộ đệm không đồng bộ

Hàng đợi bộ đệm không đồng bộ là một tính năng nâng cao trong quá trình kết xuất của ExoPlayer quy trình vận hành các thực thể MediaCodecchế độ không đồng bộ và sử dụng các luồng bổ sung để lên lịch giải mã và hiển thị dữ liệu. Bật tính năng này có thể giảm tình trạng rớt khung hình và tình trạng âm thanh chạy ngầm.

Tính năng xếp hàng đợi bộ đệm không đồng bộ được bật theo mặc định trên các thiết bị chạy Android 12 (API cấp 31) trở lên và có thể bật theo cách thủ công kể từ Android 6.0 (API cấp 23). Hãy cân nhắc việc bật tính năng này cho những thiết bị cụ thể mà bạn thấy là đã bị bỏ qua tình trạng chạy dưới khung hình hoặc âm thanh, đặc biệt là khi phát video được bảo vệ bằng DRM hoặc tốc độ khung hình cao nội dung.

Trong trường hợp đơn giản nhất, bạn cần chèn DefaultRenderersFactory vào trình phát như sau:

Kotlin

val renderersFactory = 
  DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing()
val exoPlayer = ExoPlayer.Builder(context, renderersFactory).build()

Java

DefaultRenderersFactory renderersFactory =
    new DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing();
ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory).build();

Nếu bạn đang tạo thực thể của trình kết xuất trực tiếp, hãy truyền AsynchronousMediaCodecAdapter.Factory đến MediaCodecVideoRenderer và Hàm khởi tạo MediaCodecAudioRenderer.

Chặn lệnh gọi phương thức bằng ForwardingPlayer

Bạn có thể tuỳ chỉnh một số hành vi của thực thể Player bằng cách gói thực thể đó vào một lớp con của ForwardingPlayer và các phương thức ghi đè để thực hiện bất kỳ như sau:

  • Truy cập các tham số trước khi truyền các tham số đó đến Player uỷ quyền.
  • Truy cập vào giá trị trả về từ Player uỷ quyền trước khi trả về.
  • Triển khai lại hoàn toàn phương thức.

Khi ghi đè phương thức ForwardingPlayer, bạn phải đảm bảo phương thức phương pháp triển khai vẫn tự nhất quán và tuân thủ Player giao diện, đặc biệt là khi xử lý các phương thức dùng để hành vi giống hệt hoặc có liên quan. Ví dụ:

  • Nếu bạn muốn ghi đè mọi lượt "phát" , bạn cần ghi đè cả hai ForwardingPlayer.playForwardingPlayer.setPlayWhenReady vì một phương thức gọi sẽ dự kiến hành vi của các phương thức này giống hệt nhau khi playWhenReady = true.
  • Nếu muốn thay đổi gia số tua đi, bạn cần phải ghi đè cả hai giá trị ForwardingPlayer.seekForward để thực hiện tua bằng và ForwardingPlayer.getSeekForwardIncrement để báo cáo. mức tăng đã tuỳ chỉnh chính xác trở lại phương thức gọi.
  • Nếu bạn muốn kiểm soát nội dung mà trình phát quảng cáo về Player.Commands Bạn phải ghi đè cả Player.getAvailableCommands()Player.isCommandAvailable() cũng như nghe Gọi lại Player.Listener.onAvailableCommandsChanged() để nhận thông báo về những thay đổi đến từ trình phát cơ bản.

Tuỳ chỉnh MediaSource

Các ví dụ ở trên chèn các thành phần tuỳ chỉnh để sử dụng trong quá trình phát lại tất cả Các đối tượng MediaItem được truyền đến người chơi. Trong trường hợp tuỳ chỉnh chi tiết bắt buộc, bạn cũng có thể chèn các thành phần tuỳ chỉnh vào từng Các thực thể MediaSource có thể được truyền trực tiếp đến người chơi. Ví dụ bên dưới cho biết cách tuỳ chỉnh ProgressiveMediaSource để sử dụng DataSource.Factory, ExtractorsFactoryLoadErrorHandlingPolicy:

Kotlin

val mediaSource =
  ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory)
    .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy)
    .createMediaSource(MediaItem.fromUri(streamUri))

Java

ProgressiveMediaSource mediaSource =
    new ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory)
        .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy)
        .createMediaSource(MediaItem.fromUri(streamUri));

Tạo thành phần tuỳ chỉnh

Thư viện cung cấp phương thức triển khai mặc định cho các thành phần được liệt kê ở trên cùng của trang này cho các trường hợp sử dụng phổ biến. ExoPlayer có thể sử dụng các thành phần này, nhưng cũng có thể được xây dựng để sử dụng các phương pháp triển khai tuỳ chỉnh nếu các hành vi không theo chuẩn là là bắt buộc. Một số trường hợp sử dụng triển khai tuỳ chỉnh là:

  • Renderer – Bạn có thể cần triển khai một Renderer tuỳ chỉnh để xử lý một loại phương tiện không được hỗ trợ bởi cách triển khai mặc định được cung cấp bởi thư viện của bạn.
  • TrackSelector – Việc triển khai TrackSelector tuỳ chỉnh cho phép ứng dụng thay đổi cách các kênh mà MediaSource hiển thị được chọn để tiêu thụ bởi từng Renderer hiện có.
  • LoadControl – Việc triển khai LoadControl tuỳ chỉnh cho phép ứng dụng thay đổi chính sách lưu vào bộ đệm của trình phát.
  • Extractor – Nếu bạn cần hỗ trợ một định dạng vùng chứa hiện không được hỗ trợ được thư viện hỗ trợ, hãy cân nhắc triển khai một lớp Extractor tuỳ chỉnh.
  • MediaSource – Việc triển khai một lớp MediaSource tuỳ chỉnh có thể thích hợp nếu bạn muốn lấy mẫu nội dung đa phương tiện để cấp dữ liệu cho trình kết xuất hoặc nếu bạn muốn triển khai tính năng tổng hợp MediaSource tuỳ chỉnh hành vi.
  • MediaSource.Factory – Triển khai một MediaSource.Factory tuỳ chỉnh cho phép ứng dụng tuỳ chỉnh cách tạo MediaSource từ MediaItem.
  • DataSource – Gói ngược dòng của ExoPlayer đã chứa một số Quá trình triển khai DataSource cho nhiều trường hợp sử dụng. Bạn nên triển khai lớp DataSource của riêng bạn để tải dữ liệu theo cách khác, chẳng hạn như qua một giao thức tuỳ chỉnh, sử dụng ngăn xếp HTTP tuỳ chỉnh hoặc từ một giao thức cố định tuỳ chỉnh bộ nhớ đệm.

Khi tạo thành phần tuỳ chỉnh, bạn nên làm những việc sau:

  • Nếu một thành phần tuỳ chỉnh cần báo cáo sự kiện cho ứng dụng, bạn nên bạn làm như vậy bằng cách sử dụng cùng một mô hình như các thành phần ExoPlayer hiện có, để ví dụ về cách sử dụng các lớp EventDispatcher hoặc truyền một Handler cùng với một trình nghe đối với hàm khởi tạo của thành phần.
  • Các thành phần tuỳ chỉnh nên sử dụng cùng một mô hình như ExoPlayer hiện có để cho phép ứng dụng định cấu hình lại trong khi phát. Để thực hiện việc này, các thành phần tuỳ chỉnh nên triển khai PlayerMessage.Target và nhận các thay đổi về cấu hình trong phương thức handleMessage. Mã xử lý ứng dụng nên chuyển các thay đổi về cấu hình bằng cách gọi phương thức createMessage của ExoPlayer, định cấu hình thông báo và gửi thông báo đến thành phần bằng cách sử dụng PlayerMessage.send Đang gửi tin nhắn để gửi trên chuỗi phát sẽ đảm bảo rằng chúng được thực thi theo thứ tự cùng với bất kỳ thao tác nào khác đang được thực hiện trên trình phát.