สตรีมมิงแบบสด

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: ระบบจะเพิ่มค่านี้ลงในออฟเซ็ตของข้อมูลสดเป้าหมายทุกครั้งที่มีการบัฟเฟอร์ใหม่ เพื่อดำเนินการอย่างระมัดระวังมากขึ้น คุณปิดใช้ฟีเจอร์นี้ได้โดยการตั้งค่าเป็น 0
  • minPossibleLiveOffsetSmoothingFactor: ปัจจัยการถอดยอดแบบเอ็กซ์โพเนนเชียลที่ใช้ติดตามออฟเซ็ตแบบสดที่ต่ำที่สุดที่เป็นไปได้ตามสื่อที่บัฟเฟอร์ไว้ในปัจจุบัน ค่าที่ใกล้เคียงกับ 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
  }
}