เหตุการณ์ของผู้เล่น

การฟังเหตุการณ์การเล่น

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

Kotlin

// Add a listener to receive events from the player.
player.addListener(listener)

Java

// Add a listener to receive events from the player.
player.addListener(listener);

Player.Listener มีเมธอดเริ่มต้นที่ว่างเปล่า คุณจึงต้องใช้เฉพาะเมธอดที่คุณสนใจเท่านั้น ดูคำอธิบายแบบเต็มของเมธอดและเวลาที่เรียกใช้ได้ใน Javadoc เราจะอธิบายวิธีการที่สำคัญที่สุดบางส่วนอย่างละเอียดด้านล่าง

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

การเปลี่ยนแปลงสถานะการเล่น

คุณรับการเปลี่ยนแปลงสถานะเพลเยอร์ได้โดยการใช้ onPlaybackStateChanged(@State int state) ใน Player.Listener ที่ลงทะเบียน โดยเพลเยอร์อาจอยู่ในสถานะการเล่น 1 ใน 4 สถานะต่อไปนี้

  • Player.STATE_IDLE: นี่คือสถานะเริ่มต้น สถานะเมื่อเพลเยอร์หยุดเล่น และเมื่อการเล่นล้มเหลว โดยเพลเยอร์จะเก็บเฉพาะ ทรัพยากรที่จำกัดในสถานะนี้
  • Player.STATE_BUFFERING: เพลเยอร์ไม่สามารถเล่นจากตำแหน่งปัจจุบันได้ทันที ปัญหานี้มักเกิดขึ้นเนื่องจากต้องโหลดข้อมูลเพิ่มเติม
  • Player.STATE_READY: ผู้เล่นสามารถเล่นต่อจากตำแหน่งปัจจุบันได้ทันที
  • Player.STATE_ENDED: โปรแกรมเล่นเล่นสื่อทั้งหมดเสร็จแล้ว

นอกจากสถานะเหล่านี้แล้ว ผู้เล่นยังมีplayWhenReadyแฟล็กเพื่อระบุ ความตั้งใจของผู้ใช้ที่จะเล่นด้วย คุณรับการเปลี่ยนแปลงในฟีเจอร์นี้ได้โดยการติดตั้งใช้งาน onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason)

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

  • ผู้เล่นอยู่ในสถานะ Player.STATE_READY
  • playWhenReady คือ true
  • ระบบจะไม่ระงับการเล่นด้วยเหตุผลที่ส่งคืนโดย Player.getPlaybackSuppressionReason

คุณสามารถเรียกใช้ Player.isPlaying แทนที่จะต้องตรวจสอบพร็อพเพอร์ตี้เหล่านี้ทีละรายการ คุณรับการเปลี่ยนแปลงสถานะนี้ได้โดยการติดตั้งใช้งาน onIsPlayingChanged(boolean isPlaying)

Kotlin

player.addListener(
  object : Player.Listener {
    override fun onIsPlayingChanged(isPlaying: Boolean) {
      if (isPlaying) {
        // Active playback.
      } else {
        // Not playing because playback is paused, ended, suppressed, or the player
        // is buffering, stopped or failed. Check player.playWhenReady,
        // player.playbackState, player.playbackSuppressionReason and
        // player.playerError for details.
      }
    }
  }
)

Java

player.addListener(
    new Player.Listener() {
      @Override
      public void onIsPlayingChanged(boolean isPlaying) {
        if (isPlaying) {
          // Active playback.
        } else {
          // Not playing because playback is paused, ended, suppressed, or the player
          // is buffering, stopped or failed. Check player.getPlayWhenReady,
          // player.getPlaybackState, player.getPlaybackSuppressionReason and
          // player.getPlaybackError for details.
        }
      }
    });

ข้อผิดพลาดในการเล่น

ข้อผิดพลาดที่ทำให้การเล่นล้มเหลวจะได้รับโดยการใช้ onPlayerError(PlaybackException error) ใน Player.Listener ที่ลงทะเบียน เมื่อเกิดข้อผิดพลาด ระบบจะเรียกใช้เมธอดนี้ทันทีก่อนที่สถานะการเล่นจะเปลี่ยนเป็น Player.STATE_IDLE การเล่นที่ล้มเหลวหรือหยุดชะงักสามารถ ลองอีกครั้งได้โดยการเรียกใช้ ExoPlayer.prepare

โปรดทราบว่าการติดตั้งใช้งาน Player บางอย่างจะส่งอินสแตนซ์ของคลาสย่อยของ PlaybackException เพื่อให้ข้อมูลเพิ่มเติมเกี่ยวกับความล้มเหลว เช่น ExoPlayer จะส่ง ExoPlaybackException ซึ่งมี type, rendererIndex และฟิลด์อื่นๆ ที่เฉพาะเจาะจงสำหรับ ExoPlayer

ตัวอย่างต่อไปนี้แสดงวิธีตรวจหาเมื่อการเล่นล้มเหลวเนื่องจาก ปัญหาเครือข่าย HTTP

Kotlin

player.addListener(
  object : Player.Listener {
    override fun onPlayerError(error: PlaybackException) {
      val cause = error.cause
      if (cause is HttpDataSourceException) {
        // An HTTP error occurred.
        val httpError = cause
        // It's possible to find out more about the error both by casting and by querying
        // the cause.
        if (httpError is InvalidResponseCodeException) {
          // Cast to InvalidResponseCodeException and retrieve the response code, message
          // and headers.
        } else {
          // Try calling httpError.getCause() to retrieve the underlying cause, although
          // note that it may be null.
        }
      }
    }
  }
)

Java

player.addListener(
    new Player.Listener() {
      @Override
      public void onPlayerError(PlaybackException error) {
        @Nullable Throwable cause = error.getCause();
        if (cause instanceof HttpDataSourceException) {
          // An HTTP error occurred.
          HttpDataSourceException httpError = (HttpDataSourceException) cause;
          // It's possible to find out more about the error both by casting and by querying
          // the cause.
          if (httpError instanceof HttpDataSource.InvalidResponseCodeException) {
            // Cast to InvalidResponseCodeException and retrieve the response code, message
            // and headers.
          } else {
            // Try calling httpError.getCause() to retrieve the underlying cause, although
            // note that it may be null.
          }
        }
      }
    });

การเปลี่ยนเพลย์ลิสต์

เมื่อใดก็ตามที่เพลเยอร์เปลี่ยนไปใช้รายการสื่อใหม่ในเพลย์ลิสต์ ระบบจะเรียกใช้ onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int reason) ในออบเจ็กต์ Player.Listener ที่ลงทะเบียน เหตุผลจะระบุว่าการเปลี่ยนนี้เป็นการเปลี่ยนอัตโนมัติ การกรอ (เช่น หลังจากเรียก player.next()) การเล่นซ้ำรายการเดิม หรือเกิดจากการเปลี่ยนแปลงเพลย์ลิสต์ (เช่น หากมีการนำรายการที่กำลังเล่นอยู่ออก)

ข้อมูลเมตา

ข้อมูลเมตาที่แสดงจาก player.getCurrentMediaMetadata() อาจเปลี่ยนแปลงได้ด้วยเหตุผลหลายประการ เช่น การเปลี่ยนเพลย์ลิสต์ การอัปเดตข้อมูลเมตาในสตรีม หรือการอัปเดต MediaItem ปัจจุบันขณะเล่น

หากสนใจการเปลี่ยนแปลงข้อมูลเมตา เช่น การอัปเดต UI ที่แสดงชื่อปัจจุบัน คุณสามารถฟัง onMediaMetadataChanged ได้

กำลังค้นหา

การเรียกใช้เมธอด Player.seekTo จะทำให้เกิดชุดของ Callback ไปยังอินสแตนซ์ Player.Listener ที่ลงทะเบียนไว้

  1. onPositionDiscontinuity ด้วย reason=DISCONTINUITY_REASON_SEEK ซึ่งเป็นผลโดยตรงจากการเรียกใช้ Player.seekTo โดย Callback มีฟิลด์ PositionInfo สำหรับตำแหน่งก่อนและหลังการกรอ
  2. onPlaybackStateChanged ที่มีการเปลี่ยนแปลงสถานะทันทีที่เกี่ยวข้องกับ seek โปรดทราบว่าอาจไม่มีการเปลี่ยนแปลงดังกล่าว

การโทรกลับแบบรายบุคคลเทียบกับ onEvents

ผู้ฟังสามารถเลือกระหว่างการใช้การเรียกกลับแต่ละรายการ เช่น onIsPlayingChanged(boolean isPlaying) และการเรียกกลับ onEvents(Player player, Events events) ทั่วไป Callback ทั่วไปช่วยให้เข้าถึงออบเจ็กต์ Player และระบุชุด events ที่เกิดขึ้นพร้อมกัน ระบบจะเรียกใช้ การเรียกกลับนี้เสมอหลังจากเรียกใช้การเรียกกลับที่สอดคล้องกับ เหตุการณ์แต่ละรายการ

Kotlin

override fun onEvents(player: Player, events: Player.Events) {
  if (
    events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) ||
      events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)
  ) {
    uiModule.updateUi(player)
  }
}

Java

@Override
public void onEvents(Player player, Events events) {
  if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)
      || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) {
    uiModule.updateUi(player);
  }
}

ควรใช้กิจกรรมแต่ละรายการในกรณีต่อไปนี้

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

ควรใช้ onEvents(Player player, Events events) ทั่วไปในกรณีต่อไปนี้

  • ผู้ฟังต้องการเรียกใช้ตรรกะเดียวกันสำหรับเหตุการณ์หลายรายการ เช่น การอัปเดต UI สำหรับทั้ง onPlaybackStateChanged และ onPlayWhenReadyChanged
  • Listener ต้องเข้าถึงออบเจ็กต์ Player เพื่อทริกเกอร์เหตุการณ์เพิ่มเติม เช่น การกรอหลังจากเปลี่ยนรายการสื่อ
  • ผู้ฟังต้องการใช้ค่าสถานะหลายค่าที่รายงานผ่าน การเรียกกลับแยกกัน หรือใช้ร่วมกับPlayerเมธอด Getter เช่น การใช้ Player.getCurrentWindowIndex() กับ Timeline ที่ระบุไว้ใน onTimelineChanged จะปลอดภัยเมื่อใช้จากภายในแฮนเดิล onEvents เท่านั้น
  • ผู้ฟังสนใจว่าเหตุการณ์เกิดขึ้นพร้อมกันอย่างสมเหตุสมผลหรือไม่ เช่น onPlaybackStateChanged เป็น STATE_BUFFERING เนื่องจาก การเปลี่ยนรายการสื่อ

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

กำลังใช้ AnalyticsListener

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

กำลังใช้ EventLogger

EventLogger คือ AnalyticsListener ที่ห้องสมุดจัดหาให้โดยตรงเพื่อ วัตถุประสงค์ในการบันทึก เพิ่ม EventLogger ใน ExoPlayer เพื่อเปิดใช้การบันทึกเพิ่มเติมที่มีประโยชน์ ด้วยบรรทัดเดียว

Kotlin

player.addAnalyticsListener(EventLogger())

Java

player.addAnalyticsListener(new EventLogger());

ดูรายละเอียดเพิ่มเติมได้ที่หน้าการบันทึกการแก้ไขข้อบกพร่อง

ทริกเกอร์เหตุการณ์ที่ตำแหน่งการเล่นที่ระบุ

Use Case บางอย่างกำหนดให้ต้องทริกเกอร์เหตุการณ์ที่ตำแหน่งการเล่นที่ระบุ ซึ่งรองรับการใช้งานโดยใช้ PlayerMessage คุณสร้าง PlayerMessage ได้โดยใช้ ExoPlayer.createMessage คุณตั้งค่าตำแหน่งการเล่นที่จะเรียกใช้ได้โดยใช้ PlayerMessage.setPosition ระบบจะเรียกใช้ข้อความใน เธรดการเล่นโดยค่าเริ่มต้น แต่คุณปรับแต่งได้โดยใช้ PlayerMessage.setLooper PlayerMessage.setDeleteAfterDelivery ใช้เพื่อควบคุมว่าข้อความจะทำงานทุกครั้งที่พบตำแหน่งการเล่นที่ระบุ (ซึ่งอาจเกิดขึ้นหลายครั้งเนื่องจากโหมดการค้นหาและโหมดเล่นซ้ำ) หรือเฉพาะครั้งแรก เมื่อกำหนดค่า PlayerMessage แล้ว จะกำหนดเวลาได้โดยใช้ PlayerMessage.send

Kotlin

player
  .createMessage { messageType: Int, payload: Any? -> }
  .setLooper(Looper.getMainLooper())
  .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120000)
  .setPayload(customPayloadData)
  .setDeleteAfterDelivery(false)
  .send()

Java

player
    .createMessage(
        (messageType, payload) -> {
          // Do something at the specified playback position.
        })
    .setLooper(Looper.getMainLooper())
    .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120_000)
    .setPayload(customPayloadData)
    .setDeleteAfterDelivery(false)
    .send();