ข่าวสารผลิตภัณฑ์

ยกระดับการเล่นสื่อ: ขอแนะนำการโหลดล่วงหน้าด้วย Media3 - ตอนที่ 1

ใช้เวลาอ่าน 8 นาที
Mayuri Khinvasara Khabya
วิศวกรนักพัฒนาซอฟต์แวร์สัมพันธ์

ในแอปที่เน้นสื่อเป็นหลักในปัจจุบัน การมอบประสบการณ์การเล่นที่ราบรื่นและไม่ขาดตอนเป็นกุญแจสำคัญในการสร้างประสบการณ์ที่น่าพึงพอใจให้แก่ผู้ใช้ ผู้ใช้คาดหวังว่าวิดีโอจะเริ่มเล่นทันทีและเล่นได้อย่างราบรื่นโดยไม่มีการหยุดชั่วคราว

ความท้าทายหลักคือเวลาในการตอบสนอง โดยปกติแล้ว วิดีโอเพลเยอร์จะเริ่มทำงาน (เชื่อมต่อ ดาวน์โหลด แยกวิเคราะห์ บัฟเฟอร์) หลังจากที่ผู้ใช้เลือกรายการที่จะเล่นแล้วเท่านั้น แนวทางที่ต้องรอให้เกิดเหตุการณ์ก่อนนี้ช้าเกินไปสำหรับบริบทวิดีโอแบบสั้นในปัจจุบัน วิธีแก้ปัญหาคือการป้องกันไว้ล่วงหน้า เราต้องคาดการณ์ว่าผู้ใช้จะดูอะไรต่อไปและเตรียมเนื้อหาไว้ล่วงหน้า นี่คือหัวใจสำคัญของการโหลดล่วงหน้า

ประโยชน์หลักของการโหลดล่วงหน้ามีดังนี้

  • 🚀 เริ่มเล่นได้เร็วขึ้น: วิดีโอพร้อมเล่นแล้ว ทำให้เปลี่ยนระหว่างรายการต่างๆ ได้เร็วขึ้นและเริ่มเล่นได้ทันที
  • 📉 ลดการบัฟเฟอร์: การโหลดข้อมูลล่วงหน้าจะช่วยลดโอกาสที่การเล่นจะหยุดชะงัก เช่น เนื่องจากเครือข่ายขัดข้อง
  • ✨ ประสบการณ์ของผู้ใช้ที่ราบรื่นยิ่งขึ้น: การเริ่มต้นที่เร็วขึ้นและการบัฟเฟอร์ที่น้อยลงจะช่วยให้ผู้ใช้ได้รับประสบการณ์การโต้ตอบที่ราบรื่นและลื่นไหลยิ่งขึ้น

ในซีรีส์ 3 ตอนนี้ เราจะแนะนำและเจาะลึกยูทิลิตีที่มีประสิทธิภาพของ Media3 สำหรับการโหลด (ล่วงหน้า) คอมโพเนนต์

  • ในส่วนที่ 1 เราจะพูดถึงพื้นฐาน ได้แก่ การทำความเข้าใจกลยุทธ์การโหลดล่วงหน้าต่างๆ ที่มีใน Media3, การเปิดใช้ PreloadConfiguration และการตั้งค่า DefaultPreloadManager เพื่อให้แอปโหลดรายการล่วงหน้าได้ เมื่ออ่านบล็อกนี้จบ คุณจะสามารถโหลดรายการสื่อล่วงหน้าและเล่นรายการเหล่านั้นได้โดยใช้การจัดอันดับและระยะเวลาที่คุณกำหนดค่าไว้
  • ในส่วนที่ 2 เราจะเจาะลึกหัวข้อขั้นสูงเพิ่มเติมของ DefaultPreloadManager ได้แก่ การใช้ Listener สำหรับข้อมูลวิเคราะห์ การสำรวจแนวทางปฏิบัติแนะนำที่พร้อมใช้งานจริง เช่น รูปแบบหน้าต่างเลื่อน และคอมโพเนนต์ที่แชร์ที่กำหนดเองของ DefaultPreloadManager และ ExoPlayer
  • ในส่วนที่ 3 เราจะเจาะลึกการแคชดิสก์ด้วย DefaultPreloadManager

การโหลดล่วงหน้าช่วยได้ 🦸‍♀️

แนวคิดหลักเบื้องหลังการโหลดล่วงหน้านั้นเรียบง่าย นั่นคือโหลดเนื้อหาสื่อก่อนที่คุณจะต้องการใช้ เมื่อผู้ใช้ปัดไปที่วิดีโอถัดไป ระบบจะดาวน์โหลดและพร้อมใช้งานส่วนแรกของวิดีโอแล้ว ทำให้พร้อมเล่นได้ทันที

เปรียบเหมือนร้านอาหาร ห้องครัวที่วุ่นวายจะไม่รอให้มีออเดอร์ก่อนแล้วค่อยเริ่มหั่นหัวหอม 🧅 เตรียมงานล่วงหน้า การโหลดล่วงหน้าคือการเตรียมการสำหรับวิดีโอเพลเยอร์

เมื่อเปิดใช้ การโหลดล่วงหน้าจะช่วยลดเวลาในการตอบสนองเมื่อผู้ใช้ข้ามไปยังรายการถัดไปก่อนที่บัฟเฟอร์การเล่นจะไปถึงรายการถัดไป ระบบจะเตรียมช่วงแรกของหน้าต่างถัดไปและบัฟเฟอร์ตัวอย่างวิดีโอ เสียง และข้อความ จากนั้นระบบจะจัดคิวช่วงที่โหลดล่วงหน้าลงในเพลเยอร์ โดยมีตัวอย่างที่บัฟเฟอร์พร้อมใช้งานทันทีและพร้อมที่จะส่งไปยังตัวแปลงรหัสสำหรับการแสดงผล

ใน Media3 มี API หลัก 2 รายการสำหรับการโหลดล่วงหน้า ซึ่งแต่ละรายการเหมาะกับกรณีการใช้งานที่แตกต่างกัน การเลือก API ที่เหมาะสมเป็นขั้นตอนแรก

1. การโหลดรายการในเพลย์ลิสต์ล่วงหน้าด้วย PreloadConfiguration

นี่เป็นแนวทางที่เรียบง่ายและมีประโยชน์สำหรับสื่อเชิงเส้นแบบลำดับ เช่น เพลย์ลิสต์ที่คาดการณ์ลำดับการเล่นได้ (เช่น ตอนต่างๆ ของซีรีส์) คุณให้รายการไอเทมสื่อทั้งหมดแก่ผู้เล่นโดยใช้ API เพลย์ลิสต์ของ ExoPlayer และตั้งค่า PreloadConfiguration สำหรับเพลเยอร์ จากนั้นเพลเยอร์จะโหลดไอเทมถัดไปในลำดับตามที่กำหนดค่าไว้ล่วงหน้าโดยอัตโนมัติ API นี้พยายามเพิ่มประสิทธิภาพเวลาในการตอบสนองในการเข้าร่วมเมื่อผู้ใช้ข้ามไปยังรายการถัดไปก่อนที่บัฟเฟอร์การเล่นจะทับซ้อนกับรายการถัดไป

การโหลดล่วงหน้าจะเริ่มขึ้นเมื่อไม่มีการโหลดสื่อสำหรับการเล่นที่กำลังดำเนินอยู่เท่านั้น ซึ่งจะช่วยป้องกันไม่ให้การโหลดล่วงหน้าแย่งแบนด์วิดท์กับการเล่นหลัก

หากยังไม่แน่ใจว่าต้องใช้การโหลดล่วงหน้าหรือไม่ API นี้เป็นตัวเลือกที่ยอดเยี่ยมและง่ายต่อการลองใช้

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

เมื่อใช้ PreloadConfiguration ด้านบน เพลเยอร์จะพยายามโหลดสื่อล่วงหน้า 5 วินาทีสำหรับรายการถัดไปในเพลย์ลิสต์

เมื่อเลือกใช้แล้ว คุณจะปิดการโหลดเพลย์ลิสต์ล่วงหน้าได้อีกครั้งโดยใช้ PreloadConfiguration.DEFAULT เพื่อปิดใช้การโหลดเพลย์ลิสต์ล่วงหน้า

player.preloadConfiguration = PreloadConfiguration.DEFAULT

2. การโหลดรายการแบบไดนามิกล่วงหน้าด้วย PreloadManager

สำหรับ UI แบบไดนามิก เช่น ฟีดแนวตั้งหรือภาพสไลด์ ซึ่งระบบจะกำหนดรายการ "ถัดไป" ตามการโต้ตอบของผู้ใช้ API ของ PreloadManager จะเหมาะสม นี่คือคอมโพเนนต์แบบสแตนด์อโลนใหม่ที่มีประสิทธิภาพภายในไลบรารี ExoPlayer ของ Media3 ซึ่งออกแบบมาโดยเฉพาะเพื่อโหลดล่วงหน้าอย่างรวดเร็ว โดยจะจัดการคอลเล็กชัน MediaSource ที่เป็นไปได้ โดยจัดลำดับความสำคัญตามระยะใกล้กับตำแหน่งปัจจุบันของผู้ใช้ และให้การควบคุมแบบละเอียดว่าจะโหลดล่วงหน้าอะไร เหมาะสำหรับสถานการณ์ที่ซับซ้อน เช่น ฟีดวิดีโอแบบสั้นแบบไดนามิก

การตั้งค่า PreloadManager

DefaultPreloadManager คือการใช้งานที่ชัดเจนสำหรับ PreloadManager

เครื่องมือสร้างของ DefaultPreloadManager สามารถสร้างทั้ง DefaultPreloadManager และอินสแตนซ์ ExoPlayer ใดก็ได้ที่จะเล่นเนื้อหาที่โหลดล่วงหน้า หากต้องการสร้าง DefaultPreloadManager คุณจะต้องส่ง TargetPreloadStatusControl ซึ่งเครื่องมือจัดการการโหลดล่วงหน้าจะค้นหาเพื่อดูว่าควรโหลดรายการมากน้อยเพียงใด เราจะอธิบายและกำหนดตัวอย่างของ TargetPreloadStatusControl ในส่วนด้านล่าง

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

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

คุณต้องใช้เครื่องมือสร้างเดียวกันสำหรับทั้ง ExoPlayer และ DefaultPreloadManager เพื่อให้มั่นใจว่าคอมโพเนนต์ที่อยู่เบื้องหลังจะได้รับการแชร์อย่างถูกต้อง

เท่านี้ก็เรียบร้อย! ตอนนี้คุณมีผู้จัดการที่พร้อมรับคำสั่งแล้ว

กำหนดค่าระยะเวลาและการจัดอันดับด้วย TargetPreloadStatusControl

จะเกิดอะไรขึ้นหากคุณต้องการโหลดวิดีโอล่วงหน้า เช่น 10 วินาที คุณระบุตำแหน่งของรายการสื่อในภาพสไลด์ได้ และ DefaultPreloadManager จะจัดลำดับความสำคัญในการโหลดรายการตามระยะห่างจากรายการที่ผู้ใช้กำลังเล่น

หากต้องการควบคุมระยะเวลาของรายการที่จะโหลดล่วงหน้า คุณสามารถระบุได้ด้วย DefaultPreloadManager.PreloadStatus ที่คุณส่งคืน

ตัวอย่างเช่น

  • รายการ "A" มีลำดับความสำคัญสูงสุด โหลดวิดีโอ 5 วินาที
  • รายการ "ข" มีลำดับความสำคัญปานกลาง แต่เมื่อถึงรายการนี้ ให้โหลดวิดีโอ 3 วินาที
  • รายการ "C" มีลำดับความสำคัญน้อยกว่า โหลดเฉพาะแทร็ก
  • รายการ "ง" มีลำดับความสำคัญน้อยกว่านั้นอีก เพียงแค่เตรียมไว้
  • หากมีรายการอื่นๆ อยู่ไกลออกไป ให้ไม่ต้องโหลดล่วงหน้า

การควบคุมแบบละเอียดนี้จะช่วยเพิ่มประสิทธิภาพการใช้ทรัพยากร ซึ่งเราขอแนะนำเพื่อให้การเล่นราบรื่น

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
            }
}

เคล็ดลับ: PreloadManager สามารถโหลดล่วงหน้าทั้งรายการก่อนหน้าและรายการถัดไปได้ ในขณะที่ PreloadConfiguration จะโหลดล่วงหน้าเฉพาะรายการถัดไป

การจัดการรายการที่โหลดล่วงหน้า

เมื่อสร้างบัญชีดูแลจัดการแล้ว คุณก็เริ่มบอกได้ว่าจะให้บัญชีดูแลจัดการทำอะไร ขณะที่ผู้ใช้เลื่อนดูฟีด คุณจะเห็นวิดีโอที่กำลังจะเผยแพร่และเพิ่มวิดีโอเหล่านั้นลงในเครื่องมือจัดการ การโต้ตอบกับ PreloadManager คือการสนทนาที่ขับเคลื่อนด้วยสถานะระหว่าง UI กับเครื่องมือโหลดล่วงหน้า

1. เพิ่มรายการสื่อ

เมื่อสร้างฟีด คุณต้องแจ้งให้ผู้จัดการทราบเกี่ยวกับสื่อที่ต้องติดตาม หากเพิ่งเริ่มต้น คุณสามารถเพิ่มทั้งรายการที่ต้องการโหลดล่วงหน้าได้ หลังจากนั้น คุณจะเพิ่มรายการเดียวลงในรายการได้เรื่อยๆ ตามต้องการ คุณสามารถควบคุมได้อย่างเต็มที่ว่าจะให้มีรายการใดอยู่ในรายการการโหลดล่วงหน้า ซึ่งหมายความว่าคุณต้องจัดการสิ่งที่เพิ่มและนำออกจากเครื่องมือจัดการด้วย

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

ตอนนี้ผู้จัดการจะเริ่มดึงข้อมูลสำหรับ MediaItem นี้ในเบื้องหลัง

หลังจากเพิ่มแล้ว ให้บอกผู้จัดการให้ประเมินรายการใหม่ (บอกเป็นนัยว่ามีการเปลี่ยนแปลง เช่น การเพิ่ม/ นำรายการออก หรือผู้ใช้เปลี่ยนไปเล่นรายการใหม่)

preloadManager.invalidate()

2. ดึงข้อมูลและเล่นรายการ

มาถึงตรรกะการเล่นหลักแล้ว เมื่อผู้ใช้ตัดสินใจที่จะเล่นวิดีโอนั้น คุณไม่จำเป็นต้องสร้าง MediaSource ใหม่ แต่คุณจะขอPreloadManagerสำหรับรายการที่เตรียมไว้แล้วแทน คุณสามารถดึงข้อมูล MediaSource จาก Preload Manager โดยใช้ MediaItem ได้

หากรายการที่ดึงมาจาก PreloadManager เป็น null แสดงว่ายังไม่ได้โหลด MediaItem ล่วงหน้าหรือเพิ่มลงใน PreloadMamager ดังนั้นคุณจึงเลือกตั้งค่า MediaItem โดยตรงได้

// 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()

การเตรียม MediaSource ที่ดึงมาจาก PreloadManager จะช่วยให้คุณเปลี่ยนจากการโหลดล่วงหน้าเป็นการเล่นได้อย่างราบรื่นโดยใช้ข้อมูลที่อยู่ในหน่วยความจำอยู่แล้ว ซึ่งจะช่วยให้เวลาเริ่มต้นเร็วขึ้น

3. ซิงค์ดัชนีปัจจุบันกับ UI

เนื่องจากฟีด / รายการอาจมีการเปลี่ยนแปลง คุณจึงควรแจ้งให้ PreloadManager ทราบถึงดัชนีการเล่นปัจจุบันเพื่อให้ PreloadManager จัดลำดับความสำคัญของรายการที่อยู่ใกล้กับดัชนีปัจจุบันมากที่สุดสำหรับการโหลดล่วงหน้าได้เสมอ

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

4. นำรายการออก

คุณควรนำรายการที่เครื่องมือจัดการไม่จำเป็นต้องติดตามออกเพื่อให้เครื่องมือจัดการทำงานได้อย่างมีประสิทธิภาพ เช่น รายการที่อยู่ไกลจากตำแหน่งปัจจุบันของผู้ใช้

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

หากต้องการล้างรายการทั้งหมดในครั้งเดียว คุณสามารถโทรหา preloadManager.reset() ได้

5. ปล่อยตัวจัดการ

เมื่อไม่ต้องการใช้ PreloadManager อีกต่อไป (เช่น เมื่อ UI ถูกทำลาย) คุณต้องปล่อย PreloadManager เพื่อเพิ่มพื้นที่ว่างสำหรับทรัพยากร คุณควรทำในที่ที่คุณเผยแพร่ทรัพยากรของเพลเยอร์อยู่แล้ว เราขอแนะนำให้ปล่อยเครื่องมือก่อนเพลเยอร์ เนื่องจากเพลเยอร์จะเล่นต่อไปได้หากคุณไม่ต้องการโหลดล่วงหน้าอีก

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

ช่วงเวลาสาธิตการใช้งาน

ดูการใช้งานจริงได้เลย 👍

ในเดโมด้านล่าง เราจะเห็นผลกระทบของ PreloadManager ทางด้านขวาซึ่งมีเวลาในการโหลดที่เร็วกว่า ในขณะที่ด้านซ้ายแสดงประสบการณ์การใช้งานที่มีอยู่ นอกจากนี้ คุณยังดูตัวอย่างโค้ดสำหรับเดโมได้ด้วย (โบนัส: ยังแสดงเวลาในการเริ่มต้นสำหรับวิดีโอทุกรายการด้วย)

Demo-PreloadManager_2.webp

ขั้นตอนต่อไปคือ

และนี่คือทั้งหมดของส่วนที่ 1 ตอนนี้คุณมีเครื่องมือในการสร้างระบบการโหลดล่วงหน้าแบบไดนามิกแล้ว คุณสามารถใช้ PreloadConfiguration เพื่อโหลดรายการถัดไปของเพลย์ลิสต์ใน ExoPlayer ล่วงหน้า หรือตั้งค่า DefaultPreloadManager เพิ่มและนำรายการออกได้ทันที กำหนดค่าสถานะการโหลดล่วงหน้าเป้าหมาย และดึงเนื้อหาที่โหลดล่วงหน้ามาเล่นได้อย่างถูกต้อง

ในส่วนที่ 2 เราจะเจาะลึกเกี่ยวกับ DefaultPreloadManager เราจะมาดูวิธีฟังเหตุการณ์การโหลดล่วงหน้า อภิปรายแนวทางปฏิบัติแนะนำ เช่น การใช้หน้าต่างเลื่อนเพื่อหลีกเลี่ยงปัญหาเกี่ยวกับหน่วยความจำ และดูรายละเอียดเกี่ยวกับคอมโพเนนต์ที่แชร์ที่กำหนดเองของ ExoPlayer และ DefaultPreloadManager

หากมีความคิดเห็นที่ต้องการแชร์ เรายินดีรับฟังความคิดเห็นจากคุณ

โปรดติดตามข้อมูลเพิ่มเติม และไปทำให้แอปของคุณเร็วขึ้นกันเลย 🚀

เขียนโดย

อ่านต่อ