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.liveConfiguration
Window.windowStartTimeMs
คือเวลานับจาก Epoch ของ Unix ที่กรอบเวลาเผยแพร่เริ่มขึ้นWindow.getCurrentUnixTimeMs
คือเวลานับตั้งแต่ Epoch ของ Unix ของเวลาจริงปัจจุบัน ค่านี้อาจได้รับการแก้ไขโดยความแตกต่างของนาฬิกาที่ทราบระหว่างเซิร์ฟเวอร์กับไคลเอ็นต์Window.getDefaultPositionMs
คือตําแหน่งในหน้าต่างแบบสดที่โปรแกรมเล่นจะเริ่มเล่นโดยค่าเริ่มต้น
การกรอในสตรีมแบบสด
คุณกรอไปข้างหน้าหรือข้างหลังภายในกรอบเวลาการถ่ายทอดสดได้โดยใช้ Player.seekTo
ตำแหน่งการกรอที่ส่งจะสัมพันธ์กับจุดเริ่มต้นของกรอบเวลาการถ่ายทอดสด เช่น seekTo(0)
จะข้ามไปที่จุดเริ่มต้นของกรอบเวลาการถ่ายทอดสด โปรแกรมเล่นจะพยายามใช้ออฟเซ็ตแบบสดเดียวกับตำแหน่งที่กรอไปหลังจากการกรอ
นอกจากนี้หน้าต่างที่เผยแพร่อยู่ยังมีตำแหน่งเริ่มต้นซึ่งควรเริ่มเล่นด้วย ตำแหน่งนี้มักจะอยู่ใกล้ขอบที่มีการใช้งานจริง คุณสามารถกรอไปที่ตำแหน่งเริ่มต้นได้โดยกด Player.seekToDefaultPosition
UI การเล่นแบบสด
คอมโพเนนต์ UI เริ่มต้นของ ExoPlayer จะแสดงระยะเวลาของหน้าต่างที่เผยแพร่อยู่และตำแหน่งการเล่นปัจจุบันภายในกรอบเวลาดังกล่าว ซึ่งหมายความว่าตำแหน่งจะเลื่อนไปข้างหลังทุกครั้งที่มีการอัปเดตกรอบเวลาที่เผยแพร่อยู่ หากต้องการลักษณะการทำงานที่ต่างออกไป เช่น การแสดงเวลา Unix หรือค่าออฟเซ็ตปัจจุบันของเวลาจริง คุณสามารถแยก 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 } }