ExoPlayer hỗ trợ HLS với nhiều định dạng vùng chứa. Các định dạng mẫu âm thanh và video có trong vùng chứa 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 luồng HLS chất lượng cao như mô tả trong bài đăng trên blog này.
| Tính năng | Được hỗ trợ | Bình luận |
|---|---|---|
| Vùng chứa | ||
| MPEG-TS | CÓ | |
| FMP4/CMAF | CÓ | |
| ADTS (AAC) | CÓ | |
| MP3 | CÓ | |
| Phụ đề chi tiết / phụ đề | ||
| CEA-608 | CÓ | |
| CEA-708 | CÓ | |
| WebVTT | CÓ | |
| Siêu dữ liệu | ||
| ID3 | CÓ | |
| SCTE-35 | KHÔNG | |
| Biện pháp bảo vệ nội dung | ||
| AES-128 | CÓ | |
| Mẫu AES-128 | KHÔNG | |
| Widevine | CÓ | API 19+ (lược đồ "cenc") và 25+ (lược đồ "cbcs") |
| PlayReady SL2000 | CÓ | Chỉ phát hành cho Android TV |
| Kiểm soát máy chủ | ||
| Bản cập nhật Delta | CÓ | |
| Chặn tải lại danh sách phát | CÓ | |
| Chặn tải các gợi ý tải trước | CÓ | Ngoại trừ các dải byte có độ dài không xác định |
| Chèn quảng cáo | ||
| Chèn quảng cáo do máy chủ hướng dẫn (Quảng cáo xen kẽ) | Một phần | Chỉ VOD có X-ASSET-URI.
Luồng phát trực tiếp và X-ASSET-LIST sẽ được thêm sau. |
| Quảng cáo phía máy chủ và phía máy khách IMA | CÓ | Hướng dẫn chèn quảng cáo |
| Phát trực tiếp | ||
| Phát trực tiếp thông thường | CÓ | |
| HLS có độ trễ thấp (Apple) | CÓ | |
| 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 | CÓ | Hướng dẫn tích hợp CMCD |
Sử dụng MediaItem
Để phát một luồng HLS, bạn cần phải phụ thuộc vào mô-đun HLS.
Kotlin
implementation("androidx.media3:media3-exoplayer-hls:1.10.0")
Groovy
implementation "androidx.media3:media3-exoplayer-hls:1.10.0"
Sau đó, bạn có thể tạo 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 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ỉ định 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 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 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. Phương thức 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 trực tiếp. Đoạn mã sau đây cho biết cách một ứng dụng có thể thực hiện một việc 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, @Player.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 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 tính năng 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 quy cách ngay từ đầu. Nếu bạn bỏ lỡ tính năng hỗ trợ cho
luồng của mình, hãy cho chúng tôi biết bằng cách gửi một vấn đề trên GitHub và gửi cho chúng tôi
URI luồng của bạn để chúng tôi có thể thêm tính năng hỗ trợ cho luồng của bạn.
Sử dụng MediaItem với API danh sách phát
Cách thuận tiện nhất để phát 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 MediaItem dựa trên 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 thực thể trình phát:
Kotlin
val hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context) // Create a MediaSource.Factory for HLS streams with interstitials. val hlsMediaSourceFactory = HlsInterstitialsAdsLoader.AdsMediaSourceFactory( hlsInterstitialsAdsLoader, playerView, DefaultMediaSourceFactory(context), ) // Build player with interstitials media source factory val player = ExoPlayer.Builder(context).setMediaSourceFactory(hlsMediaSourceFactory).build() // Set the player on the ads loader. hlsInterstitialsAdsLoader.setPlayer(player) playerView.setPlayer(player)
Java
HlsInterstitialsAdsLoader 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 ExoPlayer player = new ExoPlayer.Builder(context).setMediaSourceFactory(hlsMediaSourceFactory).build(); // Set the player on the ads loader. hlsInterstitialsAdsLoader.setPlayer(player); playerView.setPlayer(player);
Với việc 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 đặt một mục nội dung nghe nhìn có AdsConfiguration trên trình phát:
Kotlin
// Build an HLS media item with ads configuration to be played. player.setMediaItem( MediaItem.Builder() .setUri("https://www.example.com/media.m3u8") .setAdsConfiguration( MediaItem.AdsConfiguration.Builder("hls://interstitials".toUri()) .setAdsId("ad-tag-0") // must be unique within playlist .build() ) .build() ) player.prepare() player.play()
Java
// Build an HLS media item with ads configuration to be played. 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 đè trình tạo nguồn nội dung nghe nhìn mặc định. Để hỗ trợ quảng cáo xen kẽ, ứng dụng có thể sau đó
sử dụng HlsInterstitialsAdsLoader.AdsMediaSourceFactory trực tiếp để 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
val hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context) // Create a MediaSource.Factory for HLS streams with interstitials. val hlsMediaSourceFactory = HlsInterstitialsAdsLoader.AdsMediaSourceFactory( hlsInterstitialsAdsLoader, playerView, context, ) // Build player with default media source factory. val player = 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("hls://interstitials".toUri()) .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. ExoPlayer 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. player.setMediaSource(mediaSource); player.prepare(); player.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 việc thay đổi trạng thái liên quan đến việc 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 tài sản đang được tải, các 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 tài sản và lỗi chuẩn bị quảng cáo. Hơn nữa, bạn có thể nhận 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 chi tiết việc phát quảng cáo 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
@OptIn(markerClass = UnstableApi.class) private static 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. } }
Hãy xem JavaDoc của HlsInterstitialsAdsLoader.Listener để biết tài liệu chi tiết
về tất cả các phương thức 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
val 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ể sử dụng lại thực thể HlsInterstitialsAdsLoader hoặc
HlsInterstitialsAdsLoader.AdsMediaSourceFactory cho nhiều
thực thể 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.
Bạn có thể tạo một thực thể, chẳng hạn như trong phương thức onCreate của Activity, rồi sử dụng lại cho nhiều thực thể trình phát. Điều này hoạt động miễn là thực thể đó đang được một thực thể trình phát sử dụng cùng một lúc. Điều này hữu ích cho trường hợp sử dụng phổ biến khi ứng dụng được đưa vào chế độ nền, thực thể trình phát bị huỷ rồi một thực thể mới được tạo khi ứng dụng được đưa lên nền trước trở lại.
Tiếp tục phát với trạng thái phát quảng cáo
Để tránh việc người dùng phải xem lại quảng cáo, bạn có thể lưu và khôi phục trạng thái phát quảng cáo khi trình phát được tạo lại. Bạn có thể thực hiện việc này bằng cách gọi
getAdsResumptionStates() khi trình phát sắp được phát hành và lưu trữ
các đối tượng AdsResumptionState được trả về. Khi trình phát được tạo lại, bạn có thể khôi phục trạng thái bằng cách gọi addAdResumptionState() trên thực thể trình tải quảng cáo. AdsResumptionState có thể được đóng gói, vì vậy, bạn có thể lưu trữ trong gói onSaveInstanceState của Activity. Xin lưu ý rằng tính năng tiếp tục phát quảng cáo chỉ được hỗ trợ cho luồng VOD
Kotlin
class HlsInterstitialsActivity : Activity() { companion object { const val ADS_RESUMPTION_STATE_KEY = "ads_resumption_state" } private var hlsInterstitialsAdsLoader: HlsInterstitialsAdsLoader? = null private var playerView: PlayerView? = null private var player: ExoPlayer? = null private var adsResumptionStates: List<HlsInterstitialsAdsLoader.AdsResumptionState>? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Create the ads loader instance. hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(this) // Restore ad resumption states. savedInstanceState?.getParcelableArrayList<Bundle>(ADS_RESUMPTION_STATE_KEY)?.let { bundles -> adsResumptionStates = bundles.map { HlsInterstitialsAdsLoader.AdsResumptionState.fromBundle(it) } } } override fun onStart() { super.onStart() // Build a player and set it on the ads loader. initializePlayer() hlsInterstitialsAdsLoader?.setPlayer(player) // Add any stored ad resumption states to the ads loader. adsResumptionStates?.forEach { hlsInterstitialsAdsLoader?.addAdResumptionState(it) } adsResumptionStates = null // Consume the states } override fun onStop() { super.onStop() // Get ad resumption states. adsResumptionStates = hlsInterstitialsAdsLoader?.adsResumptionStates releasePlayer() } override fun onDestroy() { // Release the ads loader when not used anymore. hlsInterstitialsAdsLoader?.release() hlsInterstitialsAdsLoader = null super.onDestroy() } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) // Store the ad resumption states. adsResumptionStates?.let { outState.putParcelableArrayList( ADS_RESUMPTION_STATE_KEY, ArrayList(it.map(HlsInterstitialsAdsLoader.AdsResumptionState::toBundle)), ) } } fun initializePlayer() { if (player == null) { // Create a media source factory for HLS streams. val hlsMediaSourceFactory = HlsInterstitialsAdsLoader.AdsMediaSourceFactory( checkNotNull(hlsInterstitialsAdsLoader), playerView!!, /* context= */ this, ) // Build player with interstitials media source player = ExoPlayer.Builder(/* context= */ this) .setMediaSourceFactory(hlsMediaSourceFactory) .build() // Set the player on the ads loader. hlsInterstitialsAdsLoader?.setPlayer(player) playerView?.player = player } // Use a media item with an HLS stream URI, an ad tag URI and ads ID. player?.setMediaItem( MediaItem.Builder() .setUri("https://www.example.com/media.m3u8") .setMimeType(MimeTypes.APPLICATION_M3U8) .setAdsConfiguration( MediaItem.AdsConfiguration.Builder("hls://interstitials".toUri()) .setAdsId("ad-tag-0") // must be unique within ExoPlayer playlist .build() ) .build() ) player?.prepare() player?.play() } fun releasePlayer() { player?.release() player = null hlsInterstitialsAdsLoader?.setPlayer(null) playerView?.player = null } }
Java
@OptIn(markerClass = UnstableApi.class) public static class HlsInterstitialsActivity extends Activity { public static final String ADS_RESUMPTION_STATE_KEY = "ads_resumption_state"; @Nullable private HlsInterstitialsAdsLoader hlsInterstitialsAdsLoader; @Nullable private PlayerView playerView; @Nullable private ExoPlayer player; private List<HlsInterstitialsAdsLoader.AdsResumptionState> adsResumptionStates; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create the ads loader instance. hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(this); // Restore ad resumption states. if (savedInstanceState != null) { ArrayList<Bundle> bundles = savedInstanceState.getParcelableArrayList(ADS_RESUMPTION_STATE_KEY); if (bundles != null) { adsResumptionStates = new ArrayList<>(); for (Bundle bundle : bundles) { adsResumptionStates.add( HlsInterstitialsAdsLoader.AdsResumptionState.fromBundle(bundle)); } } } } @Override protected void onStart() { super.onStart(); // Build a player and set it on the ads loader. initializePlayer(); // Add any stored ad resumption states to the ads loader. if (adsResumptionStates != null) { for (HlsInterstitialsAdsLoader.AdsResumptionState state : adsResumptionStates) { hlsInterstitialsAdsLoader.addAdResumptionState(state); } adsResumptionStates = null; // Consume the states } } @Override protected void onStop() { super.onStop(); // Get ad resumption states before releasing the player. if (hlsInterstitialsAdsLoader != null) { adsResumptionStates = hlsInterstitialsAdsLoader.getAdsResumptionStates(); } releasePlayer(); } @Override protected void onDestroy() { // Release the ads loader when not used anymore. if (hlsInterstitialsAdsLoader != null) { hlsInterstitialsAdsLoader.release(); hlsInterstitialsAdsLoader = null; } super.onDestroy(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // Store the ad resumption states. if (adsResumptionStates != null) { ArrayList<Bundle> bundles = new ArrayList<>(); for (HlsInterstitialsAdsLoader.AdsResumptionState state : adsResumptionStates) { bundles.add(state.toBundle()); } outState.putParcelableArrayList(ADS_RESUMPTION_STATE_KEY, bundles); } } private void initializePlayer() { if (player == null) { // Create a media source factory for HLS streams. MediaSource.Factory hlsMediaSourceFactory = new HlsInterstitialsAdsLoader.AdsMediaSourceFactory( checkNotNull(hlsInterstitialsAdsLoader), playerView, /* context= */ this); // Build player with interstitials media source player = new ExoPlayer.Builder(/* context= */ this) .setMediaSourceFactory(hlsMediaSourceFactory) .build(); // Set the player on the ads loader. hlsInterstitialsAdsLoader.setPlayer(player); playerView.setPlayer(player); } // Use a media item with an HLS stream URI, an ad tag URI and ads ID. player.setMediaItem( new MediaItem.Builder() .setUri("https://www.example.com/media.m3u8") .setMimeType(MimeTypes.APPLICATION_M3U8) .setAdsConfiguration( new MediaItem.AdsConfiguration.Builder(Uri.parse("hls://interstitials")) .setAdsId("ad-tag-0") // must be unique within ExoPlayer playlist .build()) .build()); player.prepare(); player.play(); } private void releasePlayer() { if (player != null) { player.release(); player = null; } if (hlsInterstitialsAdsLoader != null) { hlsInterstitialsAdsLoader.setPlayer(null); } if (playerView != null) { playerView.setPlayer(null); } } }
Tuỳ chỉnh việc 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. Xem trang Tuỳ chỉnh để biết các ví dụ.
Tắt tính năng chuẩn bị không có phân đoạn
Theo mặc định, ExoPlayer sẽ sử dụng tính năng chuẩn bị không có phân đoạn. Điều này có nghĩa là ExoPlayer sẽ chỉ sử dụng thông tin trong danh sách phát nhiều biến thể để chuẩn bị luồng. Điều này hoạt động nếu các 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 chứa các bản nhạc phụ đề chi tiết được ghép kênh không được khai báo trong danh sách phát nhiều biến thể bằng thẻ #EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS. Nếu không, các bản nhạc phụ đề chi tiết này sẽ không được phát hiện và phát. Bạn có thể tắt tính năng chuẩn bị không có phân đoạn trong HlsMediaSource.Factory như minh hoạ trong đoạn mã sau. Xin lưu ý rằng điều này sẽ làm tăng thời gian khởi động vì ExoPlayer cần tải một phân đoạn nội dung nghe nhìn xuống để phát hiện các bản nhạc bổ sung này và bạn nên khai báo các bản nhạc phụ đề chi tiết trong danh sách phát 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
Để tận dụng tối đa ExoPlayer, bạn có thể tuân 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 trên Medium của chúng tôi về việc phát HLS trong ExoPlayer để biết giải thích đầy đủ. Các điểm chính là:
- 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.
- Sử dụng thẻ
#EXT-X-INDEPENDENT-SEGMENTS. - Ưu tiên luồng được tách kênh, thay vì các tệp bao gồm cả video và âm thanh.
- Đưa tất cả thông tin bạn có thể vào 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:
- Sử dụng thẻ
#EXT-X-PROGRAM-DATE-TIME. - Sử dụng thẻ
#EXT-X-DISCONTINUITY-SEQUENCE. - Cung cấp một cửa sổ trực tiếp dài. Một phút trở lên là tốt nhất.