Giao diện Player
là cốt lõi của thư viện ExoPlayer. Player
hiển thị chức năng trình phát nội dung đa phương tiện cấp cao truyền thống, chẳng hạn như khả năng lưu nội dung đa phương tiện vào bộ đệm, phát, tạm dừng và tua. Phương thức triển khai mặc định ExoPlayer
được thiết kế để đưa ra một vài giả định về (từ đó áp dụng một số hạn chế về) loại nội dung nghe nhìn đang phát, cách thức và vị trí lưu trữ nội dung nghe nhìn, cũng như cách hiển thị nội dung nghe nhìn. Thay vì triển khai trực tiếp việc tải và hiển thị nội dung đa phương tiện, các hoạt động triển khai ExoPlayer
sẽ uỷ quyền công việc này cho các thành phần được chèn khi tạo trình phát hoặc khi truyền nguồn nội dung đa phương tiện mới đến trình phát.
Các thành phần phổ biến cho tất cả các cách triển khai ExoPlayer
là:
- Các thực thể
MediaSource
xác định nội dung đa phương tiện sẽ phát, tải nội dung đa phương tiện và nơi có thể đọc nội dung đa phương tiện đã tải. Một thực thểMediaSource
được tạo từMediaItem
bằngMediaSource.Factory
bên trong trình phát. Bạn cũng có thể chuyển trực tiếp các danh sách phát này đến trình phát bằng cách sử dụng 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 đổiMediaItem
thànhMediaSource
.MediaSource.Factory
được chèn khi trình phát được tạo. - Các thực thể
Renderer
hiển thị từng thành phần riêng lẻ của nội dung nghe nhìn. Các mã này được chèn khi tạo trình phát. - Một
TrackSelector
chọn các bản nhạc doMediaSource
cung cấp để mỗiRenderer
có sẵn có thể sử dụng.TrackSelector
được chèn vào khi tạo trình phát. LoadControl
kiểm soát thời điểmMediaSource
lưu nội dung nghe nhìn vào bộ đệm và lượng nội dung nghe nhìn được lưu vào bộ đệm.LoadControl
được chèn khi tạo trình phát.LivePlaybackSpeedControl
kiểm soát tốc độ phát trong quá trình phát trực tiếp để cho phép trình phát ở gần với khoảng thời gian bù trừ trực tiếp đã định cấu hình.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 phần chức năng của người chơi có trong toàn bộ thư viện. Phương thức triển khai mặc định của một số thành phần sẽ 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 thành phần phụ được thay thế riêng lẻ bằng các phương thức triển khai được định cấu hình theo cách tuỳ chỉnh.
Tuỳ chỉnh trình phát
Dưới đây là một số ví dụ phổ biến về cách tuỳ chỉnh trình phát bằng cách chèn các thành phần.
Đị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 được tải từ mạng vào bộ nhớ đệm
Xem hướng dẫn về cách lưu tạm thời vào bộ nhớ đệm một cách nhanh chóng và tả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 yêu cầu và phản hồi HTTP. Bạn có thể muố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 URI của yêu cầu, 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 dưới dạng tiêu đề khi yêu cầu các phân đoạn nội dung nghe nhìn.
Ví dụ sau đây minh hoạ cách triển khai các hành vi này bằng cách 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 cố định cho mọi lượt tương tác với một nguồn HTTP.
Để có phương pháp chi tiết hơn, bạn có thể chèn hành vi đúng lúc bằng cách sử dụng ResolvingDataSource
. Đoạn mã sau đây cho biết cách chèn tiêu đề 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ể sử dụng ResolvingDataSource
để thực hiện việc sửa đổi URI kịp thời, 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 LoadErrorHandlingPolicy
tuỳ chỉnh cho phép các ứng dụng tuỳ chỉnh cách ExoPlayer phản ứng với lỗi tải. Ví dụ: ứng dụng có thể muốn nhanh chóng báo lỗi 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 đây 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ề lượt tải không thành công để
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ờ trình trích xuất
Bạn có thể sử dụng cờ trình trích xuất để tuỳ chỉnh cách trích xuất từng định dạng từ nội dung nghe nhìn tăng tiến. Bạn có thể đặt các giá trị này trên DefaultExtractorsFactory
được cung cấp cho DefaultMediaSourceFactory
. Ví dụ sau đây 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 không đổi
Đối với các luồng MP3, ADTS và AMR, bạn có thể bật chế độ 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 các cờ này cho từng trình trích xuất bằng cách sử dụng các phương thức DefaultExtractorsFactory.setXyzExtractorFlags
riêng lẻ như mô tả ở trê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 này, 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
như mô tả để tuỳ chỉnh cờ của trình trích xuất ở trên.
Bật tính năng xếp hàng bộ đệm không đồng bộ
Việc xếp hàng vùng đệm không đồng bộ là một điểm cải tiến trong quy trình kết xuất của ExoPlayer. Quy trình này hoạt động các thực thể MediaCodec
ở chế độ không đồng bộ và sử dụng các luồng bổ sung để lên lịch giải mã và kết xuất dữ liệu. Việc bật tính năng này có thể giảm tình trạng bị rớt khung hình và âm thanh bị thiếu.
Tính năng xếp hàng 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ể được bật theo cách thủ công kể từ Android 6.0 (API cấp 23). Hãy cân nhắc bật tính năng này cho các thiết bị cụ thể mà bạn nhận thấy bị rớt khung hình hoặc âm thanh bị thiếu, đặc biệt là khi phát nội dung được bảo vệ bằng DRM hoặc nội dung có tốc độ khung hình cao.
Trong trường hợp đơn giản nhất, bạn cần chèn DefaultRenderersFactory
cho người chơi 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 bản sao trực tiếp cho trình kết xuất, hãy truyền AsynchronousMediaCodecAdapter.Factory
đến hàm khởi tạo MediaCodecVideoRenderer
và 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ể đó trong một lớp con của ForwardingPlayer
và ghi đè các phương thức để thực hiện bất kỳ thao tác nào sau đây:
- 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ề giá trị đó. - Triển khai lại hoàn toàn phương thức.
Khi ghi đè các phương thức ForwardingPlayer
, điều quan trọng là phải đảm bảo việc triển khai vẫn nhất quán và tuân thủ giao diện Player
, đặc biệt là khi xử lý các phương thức dự định có hành vi giống hệt hoặc có liên quan. Ví dụ:
- Nếu muốn ghi đè mọi thao tác "phát", bạn cần ghi đè cả
ForwardingPlayer.play
vàForwardingPlayer.setPlayWhenReady
, vì phương thức gọi sẽ mong đợi hành vi của các phương thức này giống hệt nhau khiplayWhenReady = true
. - Nếu muốn thay đổi gia số tua đi, bạn cần ghi đè cả
ForwardingPlayer.seekForward
để thực hiện thao tác tua đi bằng gia số tuỳ chỉnh vàForwardingPlayer.getSeekForwardIncrement
để báo cáo gia số tuỳ chỉnh chính xác cho phương thức gọi. - Nếu muốn kiểm soát
Player.Commands
được quảng cáo bởi một thực thể trình phát, bạn phải ghi đè cảPlayer.getAvailableCommands()
vàPlayer.isCommandAvailable()
, đồng thời nghe lệnh gọi lạiPlayer.Listener.onAvailableCommandsChanged()
để nhận thông báo về các thay đổi 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 của tất cả các đối tượng MediaItem
được truyền đến trình phát. Khi yêu cầu tuỳ chỉnh chi tiết, bạn cũng có thể chèn các thành phần tuỳ chỉnh vào các thực thể MediaSource
riêng lẻ. Các thực thể này 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
, ExtractorsFactory
và LoadErrorHandlingPolicy
tuỳ chỉnh:
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 này cung cấp các phương thức triển khai mặc định của các thành phần được liệt kê ở đầu trang này cho các trường hợp sử dụng phổ biến. Một ExoPlayer
có thể 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 thức triển khai tuỳ chỉnh nếu bắt buộc phải có các hành vi không chuẩn. Sau đây là một số trường hợp sử dụng cho cách triển khai tuỳ chỉnh:
Renderer
– Bạn nên triển khaiRenderer
tuỳ chỉnh để xử lý loại nội dung nghe nhìn không được hỗ trợ theo các phương thức triển khai mặc định do thư viện cung cấp.TrackSelector
– Việc triển khaiTrackSelector
tuỳ chỉnh cho phép nhà phát triển ứng dụng thay đổi cách các kênh doMediaSource
hiển thị được chọn để sử dụng cho từngRenderer
có sẵn.LoadControl
– Việc triển khaiLoadControl
tuỳ chỉnh cho phép nhà phát triển ứng dụng thay đổi chính sách lưu vào bộ đệm của người chơi.Extractor
– Nếu bạn cần hỗ trợ một định dạng vùng chứa hiện chưa được thư viện hỗ trợ, hãy cân nhắc triển khai một lớpExtractor
tuỳ chỉnh.MediaSource
– Bạn nên triển khai lớpMediaSource
tuỳ chỉnh nếu muốn lấy các mẫu nội dung nghe nhìn để cung cấp cho trình kết xuất theo cách tuỳ chỉnh hoặc nếu muốn triển khai hành vi kết hợpMediaSource
tuỳ chỉnh.MediaSource.Factory
– Việc triển khaiMediaSource.Factory
tuỳ chỉnh cho phép ứng dụng tuỳ chỉnh cách tạoMediaSource
từMediaItem
.DataSource
– Gói cấp trên của ExoPlayer đã chứa một số cách triển khaiDataSource
cho nhiều trường hợp sử dụng. Bạn nên triển khai lớpDataSource
của riêng mình để 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 bộ nhớ đệm cố định tuỳ chỉnh.
Khi xây dựng thành phần tuỳ chỉnh, bạn nên làm như sau:
- Nếu một thành phần tuỳ chỉnh cần báo cáo sự kiện trở lại ứng dụng, bạn nên thực hiện việc này bằng cách sử dụng cùng một mô hình với các thành phần ExoPlayer hiện có, chẳng hạn như sử dụng các lớp
EventDispatcher
hoặc truyềnHandler
cùng với trình nghe đến hàm khởi tạo của thành phần. - Bạn nên sử dụng cùng một mô hình cho các thành phần tuỳ chỉnh như các thành phần ExoPlayer hiện có để cho phép ứng dụng định cấu hình lại trong khi phát. Để làm việc này, các thành phần tuỳ chỉnh phải triển khai
PlayerMessage.Target
và nhận các thay đổi về cấu hình trong phương thứchandleMessage
. Mã ứng dụng phải truyền các thay đổi về cấu hình bằng cách gọi phương thứccreateMessage
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ằngPlayerMessage.send
. Việc gửi thông báo để phân phối trên luồng phát đảm bảo rằng các thông báo đó được thực thi theo thứ tự với mọi thao tác khác đang được thực hiện trên trình phát.