ExoPlayer จะเล่นไลฟ์สดแบบปรับอัตราการส่งข้อมูลส่วนใหญ่ได้ทันทีโดยไม่ต้องมีการกำหนดค่าพิเศษ ดูรายละเอียดเพิ่มเติมได้ที่หน้ารูปแบบที่รองรับ
ไลฟ์สดแบบปรับเปลี่ยนได้จะแสดงหน้าต่างของสื่อที่พร้อมใช้งานซึ่งจะอัปเดตเป็น ระยะๆ เพื่อให้สอดคล้องกับเวลาจริงในปัจจุบัน ซึ่งหมายความว่าตำแหน่งการเล่นจะอยู่ในกรอบเวลานี้เสมอ โดยในกรณีส่วนใหญ่จะอยู่ใกล้กับเวลาจริงปัจจุบันที่สตรีมกำลังออกอากาศ ความแตกต่างระหว่าง ตำแหน่งเรียลไทม์ปัจจุบันกับตำแหน่งการเล่นเรียกว่าออฟเซ็ตสด
การตรวจหาและการตรวจสอบการเล่นแบบสด
ทุกครั้งที่มีการอัปเดตช่วงเวลาถ่ายทอดสด Player.Listenerอินสแตนซ์
ที่ลงทะเบียนไว้จะได้รับเหตุการณ์ onTimelineChanged คุณสามารถดึงรายละเอียดเกี่ยวกับการ
เล่นสดปัจจุบันได้โดยการค้นหาเมธอด Player และ Timeline.Window
ต่างๆ ตามที่ระบุไว้ด้านล่างและแสดงในรูปภาพต่อไปนี้

Player.isCurrentWindowLiveระบุว่ารายการสื่อที่กำลังเล่นอยู่เป็นไลฟ์สดหรือไม่ ค่านี้จะยังคงเป็นจริงแม้ว่าไลฟ์สดจะ จบไปแล้วก็ตามPlayer.isCurrentWindowDynamicระบุว่าระบบยังอัปเดตรายการสื่อที่กำลังเล่นอยู่หรือไม่ โดยปกติแล้วจะเป็นจริงสำหรับไลฟ์สดที่ยังไม่จบ โปรดทราบว่าในบางกรณี ฟีเจอร์นี้จะใช้ได้กับวิดีโอที่ไม่ใช่ไลฟ์สดด้วยPlayer.getCurrentLiveOffsetจะแสดงออฟเซ็ตระหว่างเวลาจริงปัจจุบันกับตำแหน่งการเล่น (หากมี)Player.getDurationจะแสดงผลความยาวของหน้าต่างไลฟ์สดปัจจุบันPlayer.getCurrentPositionจะแสดงตำแหน่งการเล่นที่สัมพันธ์กับ จุดเริ่มต้นของหน้าต่างไลฟ์สดPlayer.getCurrentMediaItemจะแสดงผลรายการสื่อปัจจุบัน ซึ่งMediaItem.liveConfigurationมีการลบล้างที่แอประบุสำหรับพารามิเตอร์ออฟเซ็ตสดเป้าหมายและการปรับออฟเซ็ตสดPlayer.getCurrentTimelineแสดงผลโครงสร้างสื่อปัจจุบันในTimelineคุณสามารถเรียกดูTimeline.Windowปัจจุบันได้จากTimelineโดยใช้Player.getCurrentMediaItemIndexและTimeline.getWindowภายในWindow:Window.liveConfigurationมีพารามิเตอร์ออฟเซ็ตสดเป้าหมายและการปรับออฟเซ็ตสด ค่าเหล่านี้อิงตามข้อมูลในสื่อ และการลบล้างที่แอประบุซึ่งตั้งค่าไว้ในMediaItem.liveConfigurationWindow.windowStartTimeMsคือเวลาตั้งแต่ Unix Epoch ที่ หน้าต่างไลฟ์เริ่มWindow.getCurrentUnixTimeMsคือเวลาตั้งแต่ Unix Epoch ของ เรียลไทม์ปัจจุบัน ค่านี้อาจได้รับการแก้ไขโดยความแตกต่างของนาฬิกาที่ทราบ ระหว่างเซิร์ฟเวอร์กับไคลเอ็นต์Window.getDefaultPositionMsคือตำแหน่งในหน้าต่างสดที่ เพลเยอร์จะเริ่มเล่นโดยค่าเริ่มต้น
การกรอในไลฟ์สด
คุณสามารถข้ามไปยังส่วนใดก็ได้ภายในหน้าต่างการถ่ายทอดสดโดยใช้ Player.seekTo ตำแหน่ง
ที่ส่งผ่านจะสัมพันธ์กับจุดเริ่มต้นของหน้าต่างไลฟ์สด เช่น
seekTo(0) จะข้ามไปยังจุดเริ่มต้นของหน้าต่างไลฟ์สด เพลเยอร์จะพยายาม
รักษาออฟเซ็ตแบบสดให้เหมือนกับตำแหน่งที่ค้นหาหลังจากค้นหา
หน้าต่างสดจะมีตำแหน่งเริ่มต้นที่ควรเริ่มเล่นด้วย โดยปกติแล้วตำแหน่งนี้จะอยู่ใกล้กับขอบของไลฟ์สด คุณสามารถไปยังตำแหน่งเริ่มต้นได้โดยโทรหา Player.seekToDefaultPosition
UI การเล่นไลฟ์สด
คอมโพเนนต์ UI เริ่มต้นของ ExoPlayer จะแสดงระยะเวลาของหน้าต่างสดและ
ตำแหน่งการเล่นปัจจุบันภายในหน้าต่างนั้น ซึ่งหมายความว่าตำแหน่งจะดูเหมือน
ย้อนกลับทุกครั้งที่อัปเดตหน้าต่างการถ่ายทอดสด หากต้องการลักษณะการทำงานที่แตกต่างออกไป เช่น แสดงเวลา Unix หรือออฟเซ็ตสดปัจจุบัน คุณสามารถ Fork PlayerControlView และแก้ไขให้เหมาะกับความต้องการของคุณได้
การกำหนดค่าพารามิเตอร์การเล่นสด
ExoPlayer ใช้พารามิเตอร์บางอย่างเพื่อควบคุมออฟเซ็ตของตำแหน่งการเล่น จากขอบแบบเรียลไทม์ และช่วงของความเร็วในการเล่นที่ใช้เพื่อ ปรับออฟเซ็ตนี้ได้
ExoPlayer จะรับค่าสำหรับพารามิเตอร์เหล่านี้จาก 3 ที่ตามลำดับความสำคัญจากมากไปน้อย (ระบบจะใช้ค่าแรกที่พบ) ดังนี้
- ค่าต่อ
MediaItemที่ส่งไปยังMediaItem.Builder.setLiveConfiguration - ค่าเริ่มต้นส่วนกลางที่ตั้งค่าไว้ใน
DefaultMediaSourceFactory - ค่าที่อ่านจากสื่อโดยตรง
Kotlin
// Global settings. val player = ExoPlayer.Builder(context) .setMediaSourceFactory(DefaultMediaSourceFactory(context).setLiveTargetOffsetMs(5000)) .build() // Per MediaItem settings. val mediaItem = MediaItem.Builder() .setUri(mediaUri) .setLiveConfiguration( MediaItem.LiveConfiguration.Builder().setMaxPlaybackSpeed(1.02f).build() ) .build() player.setMediaItem(mediaItem)
Java
// Global settings. ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context).setLiveTargetOffsetMs(5000)) .build(); // Per MediaItem settings. MediaItem mediaItem = new MediaItem.Builder() .setUri(mediaUri) .setLiveConfiguration( new MediaItem.LiveConfiguration.Builder().setMaxPlaybackSpeed(1.02f).build()) .build(); player.setMediaItem(mediaItem);
ค่าการกำหนดค่าที่ใช้ได้มีดังนี้
targetOffsetMs: ออฟเซ็ตไลฟ์สดเป้าหมาย โดยเพลเยอร์จะพยายามเข้าใกล้ค่าออฟเซ็ตสดนี้ในระหว่างการเล่นหากเป็นไปได้minOffsetMs: ออฟเซ็ตสดขั้นต่ำที่อนุญาต แม้จะปรับ ออฟเซ็ตให้สอดคล้องกับสภาพเครือข่ายปัจจุบันแล้ว แต่เพลเยอร์จะไม่พยายามลดออฟเซ็ตให้ต่ำกว่า นี้ในระหว่างการเล่นmaxOffsetMs: ออฟเซ็ตสดสูงสุดที่อนุญาต แม้จะปรับ ออฟเซ็ตให้สอดคล้องกับสภาพเครือข่ายปัจจุบัน แต่เพลเยอร์จะไม่พยายามรับออฟเซ็ตที่สูงกว่า นี้ในระหว่างการเล่นminPlaybackSpeed: ความเร็วในการเล่นขั้นต่ำที่เพลเยอร์ใช้เป็นตัวเลือกสำรองได้ เมื่อพยายามเข้าถึงออฟเซ็ตสดเป้าหมายmaxPlaybackSpeed: ความเร็วในการเล่นสูงสุดที่เพลเยอร์ใช้เพื่อตามให้ทัน เมื่อพยายามเข้าถึงออฟเซ็ตสดเป้าหมาย
การปรับความเร็วในการเล่น
เมื่อเล่นไลฟ์สดที่มีเวลาในการตอบสนองที่รวดเร็ว ExoPlayer จะปรับออฟเซ็ตสดโดย เปลี่ยนความเร็วในการเล่นเล็กน้อย โดยเพลเยอร์จะพยายามจับคู่กับออฟเซ็ตสดเป้าหมายที่สื่อหรือแอประบุไว้ แต่ก็จะพยายามตอบสนองต่อ สภาพเครือข่ายที่เปลี่ยนแปลงไปด้วย ตัวอย่างเช่น หากเกิดการบัฟเฟอร์ซ้ำระหว่างการเล่น เพลเยอร์จะลดความเร็วในการเล่นลงเล็กน้อยเพื่อเลื่อนออกห่างจาก ขอบแบบสด หากเครือข่ายมีความเสถียรมากพอที่จะรองรับการเล่นที่ใกล้กับ ขอบไลฟ์สดอีกครั้ง เพลเยอร์จะเร่งความเร็วในการเล่นเพื่อกลับไปยัง ออฟเซ็ตไลฟ์สดเป้าหมาย
หากไม่ต้องการให้ปรับความเร็วในการเล่นโดยอัตโนมัติ คุณสามารถปิดใช้ได้โดยตั้งค่าพร็อพเพอร์ตี้ minPlaybackSpeed และ maxPlaybackSpeed เป็น 1.0f
ในทำนองเดียวกัน คุณก็เปิดใช้ฟีเจอร์นี้กับไลฟ์สดที่ไม่ใช่แบบมีความหน่วงต่ำได้โดยตั้งค่าเหล่านี้
อย่างชัดเจนเป็นค่าอื่นที่ไม่ใช่ 1.0f ดูรายละเอียดเพิ่มเติมเกี่ยวกับวิธีตั้งค่าพร็อพเพอร์ตี้เหล่านี้ได้ที่ส่วนการกำหนดค่าด้านบน
การปรับแต่งอัลกอริทึมการปรับความเร็วในการเล่น
หากเปิดใช้การปรับความเร็ว LivePlaybackSpeedControl จะกำหนดว่ามีการปรับอะไรบ้าง คุณสามารถใช้การติดตั้งใช้งานที่กำหนดเอง
LivePlaybackSpeedControl หรือปรับแต่งการติดตั้งใช้งานเริ่มต้น ซึ่งก็คือ
DefaultLivePlaybackSpeedControl ในทั้ง 2 กรณี คุณสามารถตั้งค่าอินสแตนซ์เมื่อ
สร้างเพลเยอร์ได้โดยทำดังนี้
Kotlin
val player = ExoPlayer.Builder(context) .setLivePlaybackSpeedControl( DefaultLivePlaybackSpeedControl.Builder().setFallbackMaxPlaybackSpeed(1.04f).build() ) .build()
Java
ExoPlayer player = new ExoPlayer.Builder(context) .setLivePlaybackSpeedControl( new DefaultLivePlaybackSpeedControl.Builder() .setFallbackMaxPlaybackSpeed(1.04f) .build()) .build();
พารามิเตอร์การปรับแต่งที่เกี่ยวข้องของ DefaultLivePlaybackSpeedControl มีดังนี้
fallbackMinPlaybackSpeedและfallbackMaxPlaybackSpeed: ความเร็วในการเล่นขั้นต่ำและสูงสุดที่ใช้ปรับได้ หากทั้งสื่อและMediaItemที่แอปให้มาไม่ได้กำหนดขีดจำกัดไว้proportionalControlFactor: ควบคุมความราบรื่นของการปรับความเร็ว ค่า สูงจะทำให้การปรับเปลี่ยนเกิดขึ้นอย่างฉับพลันและตอบสนองได้ดีขึ้น แต่ก็มีแนวโน้มที่จะ ได้ยินเสียงมากขึ้นด้วย ค่าที่น้อยลงจะทำให้การเปลี่ยนความเร็วราบรื่นขึ้น แต่จะช้าลงtargetLiveOffsetIncrementOnRebufferMs: ค่านี้จะเพิ่มลงในออฟเซ็ตสดเป้าหมาย ทุกครั้งที่เกิดการบัฟเฟอร์ใหม่ เพื่อให้ดำเนินการอย่างระมัดระวังมากขึ้น คุณปิดใช้ฟีเจอร์นี้ได้โดยตั้งค่าเป็น 0minPossibleLiveOffsetSmoothingFactor: ปัจจัยการปรับให้เรียบแบบเอ็กซ์โพเนนเชียลที่ใช้เพื่อติดตามออฟเซ็ตไลฟ์สดที่เป็นไปได้ขั้นต่ำตามสื่อที่บัฟเฟอร์อยู่ในปัจจุบัน ค่าที่ใกล้เคียงกับ 1 มากหมายความว่าการประมาณจะระมัดระวังมากขึ้นและอาจใช้เวลานานขึ้นในการปรับให้เข้ากับสภาพเครือข่ายที่ดีขึ้น ในขณะที่ค่าที่ต่ำกว่าหมายความว่าการประมาณจะปรับเร็วขึ้นโดยมีความเสี่ยงสูงขึ้นที่จะเกิดการบัฟเฟอร์ซ้ำ
BehindLiveWindowException และ ERROR_CODE_BEHIND_LIVE_WINDOW
ตำแหน่งการเล่นอาจช้ากว่าหน้าต่างสด เช่น หากผู้เล่น
หยุดชั่วคราวหรือบัฟเฟอร์เป็นระยะเวลานานพอ หากเกิดกรณีเช่นนี้ การเล่นจะล้มเหลวและระบบจะรายงานข้อยกเว้นพร้อมรหัสข้อผิดพลาด
ERROR_CODE_BEHIND_LIVE_WINDOW ผ่าน
Player.Listener.onPlayerError โค้ดแอปพลิเคชันอาจต้องการจัดการข้อผิดพลาดดังกล่าว
โดยการเล่นต่อที่ตำแหน่งเริ่มต้น PlayerActivity ของ
แอปเดโมแสดงให้เห็นถึงแนวทางนี้
Kotlin
override fun onPlayerError(error: PlaybackException) { if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) { // Re-initialize player at the live edge. player.seekToDefaultPosition() player.prepare() } else { // Handle other errors } }
Java
@Override public void onPlayerError(PlaybackException error) { if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) { // Re-initialize player at the live edge. player.seekToDefaultPosition(); player.prepare(); } else { // Handle other errors } }