플레이어 이벤트

재생 이벤트 수신 대기

상태 변경 및 재생 오류와 같은 이벤트는 등록된 Player.Listener 인스턴스. 리스너를 등록하여 이벤트:

Kotlin

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

자바

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

Player.Listener에는 빈 기본 메서드가 있으므로 구현만 하면 됩니다. 관심 있는 방법을 찾을 수 있습니다. 다음에 대한 자세한 설명은 Javadoc을 참조하세요. 메서드와 메서드가 호출되는 시점에 따라 달라집니다. 가장 중요한 메서드로는 아래에 자세히 설명되어 있습니다.

리스너는 개별 이벤트 콜백을 구현하거나 하나 이상의 이벤트가 발생한 후에 호출되는 일반 onEvents 콜백 있습니다. 다음에 대한 설명은 Individual callbacks vs onEvents를 참조하세요. 여러 사용 사례에 적합해야 합니다

재생 상태 변경

플레이어 상태 변경사항은 onPlaybackStateChanged(@State int state) Player.Listener입니다. 플레이어는 다음 네 가지 재생 상태 중 하나일 수 있습니다.

  • Player.STATE_IDLE: 초기 상태이며 플레이어가 재생 실패 시 표시됩니다. 플레이어는 제한된 자원만을 보유하게 됩니다. 표시됩니다.
  • Player.STATE_BUFFERING: 플레이어가 다음 위치에서 즉시 재생할 수 없습니다. 현재 위치입니다. 이 문제는 더 많은 데이터를 로드해야 하기 때문에 발생합니다.
  • Player.STATE_READY: 플레이어가 현재 재생 중인 동영상에서 즉시 재생할 수 있습니다. 있습니다.
  • Player.STATE_ENDED: 플레이어에서 모든 미디어 재생을 완료했습니다.

이러한 상태 외에도 플레이어에는 playWhenReady 플래그를 사용하여 사용자의 플레이 의도를 파악할 수 있습니다 이 플래그의 변경사항은 onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason)

플레이어가 재생 중입니다. 즉, 플레이어의 위치가 올라가고 미디어가 재생되고 있습니다. 표시됩니다.

  • 플레이어가 Player.STATE_READY 상태입니다.
  • playWhenReadytrue입니다.
  • 다음에서 반환된 이유로 재생이 중지되지 않습니다. 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.
      }
    }
  }
)

자바

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: 실패에 대한 추가 정보를 제공합니다. 대상 예를 들어 ExoPlayertype가 있는 ExoPlaybackException를 전달합니다. 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.
        }
      }
    }
  }
)

자바

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() 호출 후), 반복 재생목록 변경으로 인해 발생한 오류 (예: 현재 삭제됩니다.

Metadata

player.getCurrentMediaMetadata()에서 반환된 메타데이터는 여러 이유로 인해 변경될 수 있습니다. 재생목록 전환, 인스트림 메타데이터 업데이트, 현재 재생 중에 MediaItem입니다.

메타데이터 변경에 관심이 있는 경우(예: UI를 표시하는 UI를 업데이트하려는 경우) onMediaMetadataChanged을 들을 수 있습니다.

탐색 중입니다.

Player.seekTo 메서드를 호출하면 등록된 콜백이 연속으로 발생합니다. 인스턴스 Player.Listener개:

  1. reason=DISCONTINUITY_REASON_SEEKonPositionDiscontinuity 호출 이것은 Player.seekTo 호출의 직접적인 결과입니다. 콜백에는 PositionInfo가 있습니다. 탐색 전후 위치의 필드입니다.
  2. 탐색과 관련된 즉각적인 상태 변경을 포함하는 onPlaybackStateChanged입니다. 이러한 변경은 없을 수도 있습니다.

개별 콜백과 onEvents 비교

리스너는 다음과 같은 개별 콜백을 구현할지 선택할 수 있습니다. onIsPlayingChanged(boolean isPlaying) 및 일반 onEvents(Player player, Events events) 콜백. 일반 콜백은 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)
  }
}

자바

@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에 대해 제공된 이유입니다.
  • 리스너는 콜백 매개변수를 통해 제공된 새 값에만 작동합니다. 콜백 매개변수에 종속되지 않는 다른 무언가를 트리거합니다.
  • 리스너 구현에서는 메서드 이름에서 이벤트를 트리거했습니다.
  • 리스너는 모든 데이터에 대해 알아야 하는 분석 시스템에 보고 개별 이벤트 및 상태 변경을 수행할 수 있습니다

다음에서는 일반 onEvents(Player player, Events events)가 선호되어야 합니다. 다음과 같습니다.

  • 리스너는 여러 이벤트에 대해 동일한 로직을 트리거하려고 합니다. 대상 예를 들어 onPlaybackStateChangedonPlayWhenReadyChanged입니다.
  • 추가 이벤트를 트리거하려면 리스너에서 Player 객체에 액세스해야 합니다. 예를 들어 미디어 항목 전환 후에 찾을 수 있습니다.
  • 리스너는 보고되는 여러 상태 값을 사용하려고 합니다. 별도의 콜백을 함께 사용하거나 Player getter와 함께 사용 메서드를 참조하세요. 예를 들어 Player.getCurrentWindowIndex()onTimelineChanged에 제공된 TimelineonEvents 콜백에 전달합니다.
  • 리스너는 이벤트가 논리적으로 함께 발생했는지 여부를 확인합니다. 대상 예: 미디어 항목으로 인해 onPlaybackStateChanged에서 STATE_BUFFERING로 변경 있습니다.

경우에 따라 리스너가 개별 콜백을 일반 onEvents 콜백(예: 미디어 항목 변경 이유를 기록) onMediaItemTransition를 사용하지만 모든 상태 변경을 사용할 수 있는 경우에만 작동 onEvents에 함께 표시됩니다.

AnalyticsListener 사용

ExoPlayer를 사용하면 AnalyticsListener를 플레이어에 등록할 수 있습니다. addAnalyticsListener를 호출하여 됩니다. AnalyticsListener 구현은 다음을 수행할 수 있습니다. 분석 및 로깅에 유용할 수 있는 자세한 이벤트를 수신 대기 있습니다. 자세한 내용은 분석 페이지를 참조하세요.

EventLogger 사용

EventLogger는 다음 API의 라이브러리에서 직접 제공하는 AnalyticsListener입니다. 로깅 목적으로 사용합니다 ExoPlayerEventLogger를 추가하여 유용한 사용 설정 한 줄로 추가 로깅을 제공합니다.

Kotlin

player.addAnalyticsListener(EventLogger())

자바

player.addAnalyticsListener(new EventLogger());

자세한 내용은 디버그 로깅 페이지를 참고하세요.

지정된 재생 위치에서 이벤트 실행

일부 사용 사례에서는 지정된 재생 위치에서 이벤트를 실행해야 합니다. 이것은 PlayerMessage를 사용하여 지원됩니다. PlayerMessage는 다음을 사용하여 만들 수 있습니다. ExoPlayer.createMessage입니다. 실행되어야 하는 재생 위치 PlayerMessage.setPosition를 사용하여 설정할 수 있습니다. 메시지는 기본적으로 재생 스레드를 감안하여 설정하지만, PlayerMessage.setLooper PlayerMessage.setDeleteAfterDelivery 사용 가능 를 사용하면 지정된 요청이 실행될 때마다 메시지를 실행할지 여부를 제어할 수 있습니다. 재생 위치가 발생했습니다 (탐색으로 인해 여러 번 발생할 수 있음). 반복 모드)에서 실행할 수 있습니다. PlayerMessagePlayerMessage.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();