Tin tức về sản phẩm

Nâng cao trải nghiệm phát nội dung đa phương tiện: Giới thiệu tính năng tải trước bằng Media3 – Phần 1

Đọc trong 8 phút
Mayuri Khinvasara Khabya
Kỹ sư quan hệ với nhà phát triển

Trong các ứng dụng tập trung vào nội dung đa phương tiện ngày nay, việc mang đến trải nghiệm phát mượt mà, không bị gián đoạn là yếu tố then chốt để tạo ra trải nghiệm người dùng thú vị. Người dùng mong muốn video của họ bắt đầu ngay lập tức và phát liền mạch mà không bị tạm dừng.

Thử thách cốt lõi là độ trễ. Theo truyền thống, trình phát video chỉ bắt đầu hoạt động (kết nối, tải xuống, phân tích cú pháp, đệm) sau khi người dùng chọn một mục để phát. Phương pháp phản ứng này chậm đối với bối cảnh video dạng ngắn ngày nay. Giải pháp là chủ động. Chúng ta cần dự đoán nội dung mà người dùng sẽ xem tiếp theo và chuẩn bị nội dung trước. Đây là bản chất của tính năng tải trước.

Các lợi ích chính của tính năng tải trước bao gồm:

  • 🚀 Bắt đầu phát nhanh hơn: Video đã sẵn sàng phát, giúp chuyển đổi nhanh hơn giữa các mục và bắt đầu phát ngay lập tức.
  • 📉 Giảm tình trạng đệm: Bằng cách chủ động tải dữ liệu, khả năng phát bị gián đoạn sẽ giảm đi đáng kể, chẳng hạn như do sự cố mạng.
  • ✨ Mang lại trải nghiệm người dùng mượt mà hơn: Việc kết hợp giữa thời gian bắt đầu nhanh hơn và ít bị đệm hơn sẽ tạo ra trải nghiệm tương tác mượt mà, liền mạch hơn cho người dùng.

Trong loạt bài gồm 3 phần này, chúng ta sẽ giới thiệu và nghiên cứu chuyên sâu về các tiện ích mạnh mẽ của Media3 để (tải) các thành phần trước.

  • Trong Phần 1, chúng ta sẽ đề cập đến những kiến thức cơ bản: tìm hiểu các chiến lược tải trước khác nhau có trong Media3, bật PreloadConfiguration và thiết lập DefaultPreloadManager, cho phép ứng dụng của bạn tải trước các mục. Khi đọc xong blog này, bạn sẽ có thể tải trước và phát các mục nội dung đa phương tiện với thứ hạng và thời lượng đã định cấu hình.
  • Trong Phần 2, chúng ta sẽ tìm hiểu các chủ đề nâng cao hơn về DefaultPreloadManager: sử dụng trình nghe cho Analytics, khám phá các phương pháp hay nhất sẵn sàng cho quá trình phát hành công khai như mẫu cửa sổ trượt và các thành phần dùng chung tuỳ chỉnh của DefaultPreloadManager và ExoPlayer.
  • Trong Phần 3, chúng ta sẽ tìm hiểu sâu về tính năng lưu vào bộ nhớ đệm trên đĩa bằng DefaultPreloadManager.

Tính năng tải trước giúp giải quyết vấn đề! 🦸‍♀️

Ý tưởng cốt lõi đằng sau tính năng tải trước rất đơn giản: tải nội dung đa phương tiện trước khi bạn cần. Khi người dùng vuốt sang video tiếp theo, các phân đoạn đầu tiên của video đã được tải xuống và có sẵn, sẵn sàng phát ngay lập tức.

Hãy hình dung như một nhà hàng. Một nhà bếp bận rộn không chờ đơn đặt hàng mới bắt đầu thái hành. 🧅 Họ chuẩn bị trước. Tính năng tải trước là công việc chuẩn bị cho trình phát video.

Khi được bật, tính năng tải trước có thể giúp giảm thiểu độ trễ khi người dùng chuyển sang mục tiếp theo trước khi bộ đệm phát đạt đến mục tiếp theo. Khoảng thời gian đầu tiên của cửa sổ tiếp theo được chuẩn bị và các mẫu video, âm thanh và văn bản được đệm. Khoảng thời gian được tải trước sẽ được xếp hàng đợi vào trình phát sau đó với các mẫu được đệm có sẵn ngay lập tức và sẵn sàng được đưa vào codec để kết xuất.

Trong Media3, có 2 API chính để tải trước, mỗi API phù hợp với các trường hợp sử dụng khác nhau. Việc chọn đúng API là bước đầu tiên.

1. Tải trước các mục trong danh sách phát bằng PreloadConfiguration

Đây là phương pháp đơn giản, hữu ích cho nội dung đa phương tiện tuyến tính, tuần tự như danh sách phát, trong đó thứ tự phát có thể dự đoán được (như một loạt tập). Bạn cung cấp cho trình phát danh sách đầy đủ các mục nội dung đa phương tiện bằng API danh sách phát của ExoPlayer và đặt PreloadConfiguration cho trình phát, sau đó, trình phát sẽ tự động tải trước các mục tiếp theo trong chuỗi theo cấu hình. API này cố gắng tối ưu hoá độ trễ khi tham gia khi người dùng chuyển sang mục tiếp theo trước khi bộ đệm phát đã chồng lên mục tiếp theo.

Tính năng tải trước chỉ bắt đầu khi không có nội dung đa phương tiện nào đang được tải cho quá trình phát đang diễn ra, điều này giúp ngăn tính năng này cạnh tranh băng thông với quá trình phát chính.

Nếu bạn vẫn không chắc có cần tính năng tải trước hay không, thì API này là một lựa chọn tuyệt vời để thử!

player.preloadConfiguration =
    PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L)

Với PreloadConfiguration ở trên, trình phát sẽ cố gắng tải trước 5 giây nội dung đa phương tiện cho mục tiếp theo trong danh sách phát.

Sau khi chọn tham gia, bạn có thể tắt tính năng tải trước danh sách phát bằng cách sử dụng PreloadConfiguration.DEFAULT để tắt tính năng tải trước danh sách phát:

player.preloadConfiguration = PreloadConfiguration.DEFAULT

2. Tải trước danh sách động bằng PreloadManager

Đối với các giao diện người dùng động như nguồn cấp dữ liệu dọc hoặc băng chuyền, trong đó mục "tiếp theo" được xác định bằng lượt tương tác của người dùng, thì API PreloadManager là phù hợp. Đây là một thành phần độc lập, mạnh mẽ mới trong thư viện Media3 ExoPlayer được thiết kế đặc biệt để chủ động tải trước. Thành phần này quản lý một tập hợp các MediaSource tiềm năng, ưu tiên các MediaSource này dựa trên khoảng cách đến vị trí hiện tại của người dùng và cung cấp quyền kiểm soát chi tiết đối với nội dung cần tải trước, phù hợp với các tình huống phức tạp như nguồn cấp dữ liệu động của video dạng ngắn.

Thiết lập PreloadManager

DefaultPreloadManager là phương thức triển khai chính tắc cho PreloadManager.

Trình tạo DefaultPreloadManager có thể tạo cả DefaultPreloadManager và mọi thực thể ExoPlayer sẽ phát nội dung được tải trước. Để tạo DefaultPreloadManager, bạn cần truyền TargetPreloadStatusControl. Trình quản lý tải trước có thể truy vấn TargetPreloadStatusControl để tìm hiểu số lượng cần tải cho một mục. Chúng ta sẽ giải thích và xác định một ví dụ về TargetPreloadStatusControl trong phần bên dưới.

val preloadManagerBuilder =
DefaultPreloadManager.Builder(context, targetPreloadStatusControl)
val preloadManager = val preloadManagerBuilder.build()

// Build ExoPlayer with DefaultPreloadManager.Builder
val player = preloadManagerBuilder.buildExoPlayer()

Bạn cần sử dụng cùng một trình tạo cho cả ExoPlayer và DefaultPreloadManager. Điều này đảm bảo rằng các thành phần bên dưới của chúng được chia sẻ chính xác.

Chỉ vậy thôi! Giờ đây, bạn đã có một trình quản lý sẵn sàng nhận hướng dẫn.

Định cấu hình thời lượng và thứ hạng bằng TargetPreloadStatusControl

Điều gì sẽ xảy ra nếu bạn muốn tải trước, chẳng hạn như 10 giây video? Bạn có thể cung cấp vị trí của các mục nội dung đa phương tiện trong băng chuyền và DefaultPreloadManager sẽ ưu tiên tải các mục dựa trên mức độ gần với mục mà người dùng hiện đang phát.

Nếu muốn kiểm soát thời lượng của mục cần tải trước, bạn có thể cho biết thời lượng đó bằng DefaultPreloadManager.PreloadStatus mà bạn trả về.

Ví dụ:

  • Mục "A" có mức độ ưu tiên cao nhất, tải 5 giây video.
  • Mục 'B' có mức độ ưu tiên trung bình nhưng khi bạn chuyển đến mục này, hãy tải 3 giây video.
  • Mục "C" có mức độ ưu tiên thấp hơn, chỉ tải các bản nhạc.
  • Mục "D" có mức độ ưu tiên thấp hơn nữa, chỉ cần chuẩn bị.
  • Mọi mục khác đều ở xa, không tải trước bất kỳ mục nào.

Quyền kiểm soát chi tiết này có thể giúp bạn tối ưu hoá việc sử dụng tài nguyên, đây là điều nên làm để phát liền mạch.

import androidx.media3.exoplayer.DefaultPreloadManager.PreloadStatus


class MyTargetPreloadStatusControl(
    currentPlayingIndex: Int = C.INDEX_UNSET
) : TargetPreloadStatusControl<Int,PreloadStatus> {


    // The app is responsible for updating this based on UI state
    override fun getTargetPreloadStatus(index: Int): PreloadStatus? {

        val distance = index - currentPlayingIndex

        // Adjacent items (Next): preload 5 seconds
        if (distance == 1) { 
        // Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED and suggest loading // 5000ms from the default start position
                    return PreloadStatus.specifiedRangeLoaded(5000L)
                } 

        // Adjacent items (Previous): preload 3 seconds
        else if (distance == -1) { 
        // Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED //and suggest loading 3000ms from the default start position
                    return PreloadStatus.specifiedRangeLoaded(3000L)
                } 

        // Items two positions away: just select tracks
        else if (distance) == 2) {
        // Return a PreloadStatus that is labelled by STAGE_TRACKS_SELECTED
                    return PreloadStatus.TRACKS_SELECTED
                } 

        // Items four positions away: just select prepare
        else if (abs(distance) <= 4) {
        // Return a PreloadStatus that is labelled by STAGE_SOURCE_PREPARED
                    return PreloadStatus.SOURCE_PREPARED
                }

             // All other items are too far away
             return null
            }
}

Mẹo: PreloadManager có thể giữ cả mục trước và mục tiếp theo được tải trước, trong khi PreloadConfiguration chỉ xem trước các mục tiếp theo.

Quản lý các mục tải trước

Sau khi tạo trình quản lý, bạn có thể bắt đầu cho trình quản lý biết nội dung cần thực hiện. Khi người dùng cuộn qua nguồn cấp dữ liệu, bạn sẽ xác định các video sắp tới và thêm các video đó vào trình quản lý. Hoạt động tương tác với PreloadManager là một cuộc trò chuyện dựa trên trạng thái giữa giao diện người dùng và công cụ tải trước.

1. Thêm mục nội dung đa phương tiện

Khi điền nguồn cấp dữ liệu, bạn phải thông báo cho trình quản lý về nội dung đa phương tiện mà trình quản lý cần theo dõi. Nếu mới bắt đầu, bạn có thể thêm toàn bộ danh sách mà bạn muốn tải trước. Sau đó, bạn có thể tiếp tục thêm một mục vào danh sách khi cần. Bạn có toàn quyền kiểm soát những mục có trong danh sách tải trước, nghĩa là bạn cũng phải quản lý những mục được thêm và xoá khỏi trình quản lý.

val initialMediaItems = pullMediaItemsFromService(/* count= */ 20)
for (index in 0 until initialMediaItems.size) {
    preloadManager.add(
        initialMediaItems.get(index),index)
    )
}

Giờ đây, trình quản lý sẽ bắt đầu tìm nạp dữ liệu cho MediaItem này ở chế độ nền.

Sau khi thêm, hãy yêu cầu trình quản lý đánh giá lại danh sách mới (cho biết rằng đã có thay đổi, chẳng hạn như thêm/ xoá một mục hoặc người dùng chuyển sang phát một mục mới).

preloadManager.invalidate()

2. Truy xuất và phát một mục

Đây là logic phát chính. Khi người dùng quyết định phát video đó, bạn không cần tạo MediaSource mới. Thay vào đó, bạn yêu cầu PreloadManager cung cấp video mà trình quản lý đã chuẩn bị. Bạn có thể truy xuất MediaSource từ Trình quản lý tải trước bằng MediaItem.

Nếu mục được truy xuất từ PreloadManager là null, nghĩa là mediaItem chưa được tải trước hoặc thêm vào PreloadMamager, vì vậy, bạn chọn đặt mediaItem trực tiếp.

// When a media item is about to displ​​ay on the screen
val mediaSource = preloadManager.getMediaSource(mediaItem)
if (mediaSource!= null) {
  player.setMediaSource(mediaSource)
} else {
  // If mediaSource is null, that mediaItem hasn't been added yet.
  // So, send it directly to the player.
  player.setMediaItem(mediaItem)
}
player.prepare()
// When the media item is displaying at the center of the screen
player.play()

Bằng cách chuẩn bị MediaSource được truy xuất từ PreloadManager, bạn có thể chuyển đổi liền mạch từ tính năng tải trước sang phát, sử dụng dữ liệu đã có trong bộ nhớ. Đây là yếu tố giúp thời gian bắt đầu nhanh hơn.

3. Đồng bộ hoá chỉ mục hiện tại với giao diện người dùng

Vì nguồn cấp dữ liệu / danh sách của chúng ta có thể là động, nên bạn cần thông báo cho PreloadManager về chỉ mục phát hiện tại để trình quản lý luôn có thể ưu tiên các mục gần nhất với chỉ mục hiện tại của bạn để tải trước.

preloadManager.setCurrentPlayingIndex(currentIndex)
// Need to call invalidate() to update the priorities
preloadManager.invalidate()

4. Xoá một mục

Để duy trì hiệu quả của trình quản lý, bạn nên xoá những mục mà trình quản lý không còn cần theo dõi, chẳng hạn như những mục ở xa vị trí hiện tại của người dùng.

// When an item is too far from the current playing index
preloadManager.remove(mediaItem)

Nếu cần xoá tất cả các mục cùng một lúc, bạn có thể gọi preloadManager.reset().

5. Giải phóng trình quản lý

Khi không cần PreloadManager nữa (ví dụ: khi giao diện người dùng bị huỷ), bạn phải giải phóng trình quản lý này để giải phóng tài nguyên của trình quản lý. Một vị trí tốt để thực hiện việc này là nơi bạn đã giải phóng tài nguyên của Trình phát. Bạn nên giải phóng trình quản lý trước trình phát vì trình phát có thể tiếp tục phát nếu bạn không cần tải trước nữa.

// In your Activity's onDestroy() or Composable's onDispose
preloadManager.release()

Phần minh hoạ

Kiểm tra trực tiếp trong thực tế 👍

Trong bản minh hoạ bên dưới , chúng ta thấy tác động của PreloadManager ở bên phải có thời gian tải nhanh hơn, trong khi bên trái cho thấy trải nghiệm hiện có. Bạn cũng có thể xem mã mẫu cho bản minh hoạ. (Phần thưởng: Bản minh hoạ cũng hiển thị độ trễ khi khởi động cho mỗi video)

Demo-PreloadManager_2.webp

Tiếp theo là gì?

Vậy là hết Phần 1! Giờ đây, bạn đã có các công cụ để xây dựng hệ thống tải trước động. Bạn có thể sử dụng PreloadConfiguration để tải trước mục tiếp theo của danh sách phát trong ExoPlayer hoặc thiết lập DefaultPreloadManager, thêm và xoá các mục ngay lập tức, định cấu hình trạng thái tải trước mục tiêu và truy xuất chính xác nội dung được tải trước để phát.

Trong Phần 2, chúng ta sẽ tìm hiểu sâu hơn về DefaultPreloadManager. Chúng ta sẽ khám phá cách nghe các sự kiện tải trước, thảo luận về các phương pháp hay nhất như sử dụng cửa sổ trượt để tránh các vấn đề về bộ nhớ và xem xét các thành phần dùng chung tuỳ chỉnh của ExoPlayer và DefaultPreloadManager.

Bạn có ý kiến phản hồi nào muốn chia sẻ không? Chúng tôi rất mong nhận được ý kiến của bạn.

Hãy theo dõi và bắt đầu tăng tốc ứng dụng của bạn! 🚀

Tác giả:

Tiếp tục đọc