Giao diện Player
là cốt lõi của thư viện ExoPlayer. Player
hiển thị chức năng truyền thống của trình phát nội dung đa phương tiện cấp cao, chẳng hạn như khả năng đệm nội dung nghe nhìn, phát, tạm dừng và tìm kiếm. Hoạt động triển khai mặc định ExoPlayer
được thiết kế để đưa ra một số giả định về (và do đó áp đặt một số hạn chế đối với) loại nội dung nghe nhìn đang phát, cách thức và vị trí lưu trữ cũng như cách hiển thị nội dung đó. Thay vì triển khai việc tải và kết xuất nội dung nghe nhìn trực tiếp, 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 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 truyền đến trình phát.
Các thành phần phổ biến cho mọi hoạt động triển khai ExoPlayer
là:
- Các thực thể
MediaSource
xác định nội dung nghe nhìn sẽ được phát, tải nội dung nghe nhìn và từ đó có thể đọc nội dung nghe nhì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ể truyền trực tiếp các tệp này đến trình phát bằ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
kết xuất từng thành phần của nội dung nghe nhìn. Các đối tượng này được chèn khi trình phát được tạo. - Một
TrackSelector
chọn các bản nhạc doMediaSource
cung cấp để mỗiRenderer
có sẵn sử dụng.TrackSelector
sẽ được chèn khi trình phát được tạo. - Một
LoadControl
kiểm soát thời điểmMediaSource
lưu vào bộ nhớ đệm nhiều nội dung nghe nhìn hơn và lượng nội dung nghe nhìn được lưu vào bộ nhớ đệm. MộtLoadControl
sẽ được chèn khi trình phát được tạo. - Mộ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 duy trì gần với độ lệch trực tiếp đã định cấu hình.LivePlaybackSpeedControl
sẽ được chèn khi trình phát được tạo.
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 trình phát có trong toàn bộ thư viện. Các phương thức triển khai mặc định của một số thành phần sẽ uỷ quyền 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ề tuỳ chỉnh ngăn xếp mạng mà ExoPlayer sử dụng.
Lưu vào bộ nhớ đệm dữ liệu được tải từ mạng
Hãy xem hướng dẫn về hoạt động lưu vào bộ nhớ đệm tạm thời tức thì và tải nội dung nghe nhìn xuống.
Tuỳ chỉnh các lượt 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 có thể muốn chèn các 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ột mã thông báo làm 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 hoạt động tương tác với một nguồn HTTP.
Để có một phương pháp chi tiết hơn, bạn có thể chèn hành vi tức thời 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 một 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 các sửa đổi tức thời đối với 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 việc 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ụ: một ứng dụng có thể muốn nhanh chóng thất bại thay vì thử lại nhiều lần hoặc có thể muốn tuỳ chỉnh logic thời gian đợi luỹ thừa để kiểm soát thời gian trình phát đợi giữa mỗi lần thử lại. Đoạn mã sau đây cho biết cách triển khai logic trì hoãn 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ề quá trình 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ờ của trình trích xuất
Bạn có thể 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ừ nội dung nghe nhìn truyền trực tuyế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 sẽ 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 tính năng tìm kiếm tương đối 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ính năng 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 vùng đệm không đồng bộ
Xếp hàng đợi bộ nhớ đệ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, 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 số khung hình bị bỏ qua và tình trạng thiếu hụt âm thanh.
Theo mặc định, tính năng xếp hàng bộ đệm không đồng bộ được bật 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 việc bật tính năng này cho các thiết bị cụ thể mà bạn nhận thấy có khung hình bị rớt 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 một 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 khởi tạo trực tiếp trình kết xuất, hãy truyền new DefaultMediaCodecAdapter.Factory(context).forceEnableAsynchronous()
đến các hàm khởi tạo MediaCodecVideoRenderer
và MediaCodecAudioRenderer
.
Tuỳ chỉnh các thao tác bằng ForwardingSimpleBasePlayer
Bạn có thể tuỳ chỉnh một số hành vi của một thực thể Player
bằng cách bao bọc thực thể đó trong một lớp con của ForwardingSimpleBasePlayer
. Lớp này cho phép bạn chặn các "thao tác" cụ thể, thay vì phải triển khai trực tiếp các phương thức Player
. Điều này đảm bảo hành vi nhất quán của, ví dụ: play()
, pause()
và setPlayWhenReady(boolean)
. Thao tác này cũng đảm bảo tất cả các thay đổi về trạng thái đều được truyền đúng cách đến các thực thể Player.Listener
đã đăng ký. Đối với hầu hết các trường hợp sử dụng tuỳ chỉnh, bạn nên ưu tiên ForwardingSimpleBasePlayer
hơn ForwardingPlayer
dễ xảy ra lỗi hơn do những đảm bảo về tính nhất quán này.
Ví dụ: để thêm một số logic tuỳ chỉnh khi quá trình phát bắt đầu hoặc dừng:
Kotlin
class PlayerWithCustomPlay(player: Player) : ForwardingSimpleBasePlayer(player) { override fun handleSetPlayWhenReady(playWhenReady: Boolean): ListenableFuture<*> { // Add custom logic return super.handleSetPlayWhenReady(playWhenReady) } }
Java
class PlayerWithCustomPlay extends ForwardingSimpleBasePlayer { public PlayerWithCustomPlay(Player player) { super(player); } @Override protected ListenableFuture<?> handleSetPlayWhenReady(boolean playWhenReady) { // Add custom logic return super.handleSetPlayWhenReady(playWhenReady); } }
Hoặc để không cho phép lệnh SEEK_TO_NEXT
(và đảm bảo Player.seekToNext
là một thao tác không có tác dụng):
Kotlin
class PlayerWithoutSeekToNext(player: Player) : ForwardingSimpleBasePlayer(player) { override fun getState(): State { val state = super.getState() return state .buildUpon() .setAvailableCommands( state.availableCommands.buildUpon().remove(COMMAND_SEEK_TO_NEXT).build() ) .build() } // We don't need to override handleSeek, because it is guaranteed not to be called for // COMMAND_SEEK_TO_NEXT since we've marked that command unavailable. }
Java
class PlayerWithoutSeekToNext extends ForwardingSimpleBasePlayer { public PlayerWithoutSeekToNext(Player player) { super(player); } @Override protected State getState() { State state = super.getState(); return state .buildUpon() .setAvailableCommands( state.availableCommands.buildUpon().remove(COMMAND_SEEK_TO_NEXT).build()) .build(); } // We don't need to override handleSeek, because it is guaranteed not to be called for // COMMAND_SEEK_TO_NEXT since we've marked that command unavailable. }
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 tất cả các đối tượng MediaItem
được truyền đến trình phát. Khi cần 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 từng thực thể MediaSource
. Các thành phần này có thể được chuyển trực tiếp đến trình phát. Ví dụ dưới đây 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. ExoPlayer
có thể sử dụng các thành phần này, nhưng cũng có thể được tạo để sử dụng các phương thức triển khai tuỳ chỉnh nếu cần các hành vi không theo tiêu chuẩn. Sau đây là một số trường hợp sử dụng cho các cách triển khai tuỳ chỉnh:
Renderer
– Bạn có thể muốn triển khai mộtRenderer
tuỳ chỉnh để xử lý một loại nội dung nghe nhìn không được các phương thức triển khai mặc định do thư viện cung cấp hỗ trợ.TrackSelector
– Việc triển khai mộtTrackSelector
tuỳ chỉnh cho phép nhà phát triển ứng dụng thay đổi cách chọn các bản nhạc doMediaSource
hiển thị để mỗiRenderer
có sẵn sử dụng.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 đệ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 mà thư viện hiện không hỗ trợ, hãy cân nhắc việc triển khai một lớpExtractor
tuỳ chỉnh.MediaSource
– Việc triển khai một lớpMediaSource
tuỳ chỉnh có thể phù hợp nếu bạn 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 bạn muốn triển khai hành vi kết hợpMediaSource
tuỳ chỉnh.MediaSource.Factory
– Việc triển khai mộtMediaSource.Factory
tuỳ chỉnh cho phép ứng dụng tuỳ chỉnh cách tạoMediaSource
từMediaItem
.DataSource
– Gói nguồn của ExoPlayer đã chứa một số phương thức triển khaiDataSource
cho nhiều trường hợp sử dụng. Bạn có thể muốn triển khai lớpDataSource
của riêng mình để tải dữ liệu theo một cách khác, chẳng hạn như qua giao thức tuỳ chỉnh, bằng cách sử dụng ngăn xếp HTTP tuỳ chỉnh hoặc từ bộ nhớ đệm liên tục tuỳ chỉnh.
Khi tạo các 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 các sự kiện trở lại ứng dụng, bạn nê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ó, chẳng hạn như sử dụng các lớp
EventDispatcher
hoặc truyềnHandler
cùng với một trình nghe đến hàm khởi tạo của thành phần. - Bạn nên 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ó để ứng dụng có thể định cấu hình lại trong quá trình phát. Để thực hiện 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ằng cách sử dụngPlayerMessage.send
. Việc gửi thông báo được phân phối trên luồng phát lại đả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.