라이브 스트리밍

ExoPlayer는 특별한 구성 없이 대부분의 적응형 라이브 스트림을 즉시 재생합니다. 자세한 내용은 지원되는 형식 페이지를 참고하세요.

적응형 라이브 스트림은 현재 실시간과 함께 이동하도록 정기적으로 업데이트되는 사용 가능한 미디어 창을 제공합니다. 즉, 재생 위치는 항상 이 기간 내에 있으며 대부분의 경우 스트림이 생성되는 현재 실시간과 가깝습니다. 현재 실시간과 재생 위치의 차이를 라이브 오프셋이라고 합니다.

실시간 재생 감지 및 모니터링

라이브 기간이 업데이트될 때마다 등록된 Player.Listener 인스턴스가 onTimelineChanged 이벤트를 수신합니다. 아래에 나열되어 있고 다음 그림에 표시된 다양한 PlayerTimeline.Window 메서드를 쿼리하여 현재 실시간 재생에 관한 세부정보를 가져올 수 있습니다.

실시간 창

  • Player.isCurrentWindowLive는 현재 재생 중인 미디어 항목이 라이브 스트림인지 여부를 나타냅니다. 이 값은 라이브 스트림이 종료되었더라도 계속 유지됩니다.
  • Player.isCurrentWindowDynamic는 현재 재생 중인 미디어 항목이 아직 업데이트 중인지 나타냅니다. 이는 일반적으로 아직 종료되지 않은 라이브 스트림에 해당합니다. 경우에 따라 이 플래그는 라이브 스트림이 아닌 경우에도 적용됩니다.
  • Player.getCurrentLiveOffset는 현재 실시간과 재생 위치 간의 오프셋을 반환합니다 (가능한 경우).
  • Player.getDuration는 현재 실시간 창의 길이를 반환합니다.
  • Player.getCurrentPosition는 실시간 창의 시작을 기준으로 한 재생 위치를 반환합니다.
  • Player.getCurrentMediaItem는 현재 미디어 항목을 반환합니다. 여기서 MediaItem.liveConfiguration에는 앱에서 제공하는 타겟 실시간 오프셋 및 실시간 오프셋 조정 매개변수의 재정의가 포함됩니다.
  • Player.getCurrentTimelineTimeline로 현재 미디어 구조를 반환합니다. 현재 Timeline.WindowPlayer.getCurrentMediaItemIndexTimeline.getWindow를 사용하여 Timeline에서 가져올 수 있습니다. Window 내에서 다음을 실행합니다.
    • Window.liveConfiguration에는 타겟 실시간 오프셋과 실시간 오프셋 조정 매개변수가 포함됩니다. 이러한 값은 미디어의 정보와 MediaItem.liveConfiguration에 설정된 앱 제공 재정의를 기반으로 합니다.
    • Window.windowStartTimeMs은 라이브 기간이 시작되는 Unix 에포크 이후의 시간입니다.
    • Window.getCurrentUnixTimeMs는 현재 실시간의 Unix epoch 이후 경과 시간입니다. 이 값은 서버와 클라이언트 간의 알려진 시계 차이로 수정될 수 있습니다.
    • Window.getDefaultPositionMs는 라이브 창에서 플레이어가 기본적으로 재생을 시작하는 위치입니다.

라이브 스트림에서 탐색하기

Player.seekTo를 사용하여 실시간 창 내 어디로든 이동할 수 있습니다. 전달된 탐색 위치는 실시간 기간의 시작을 기준으로 합니다. 예를 들어 seekTo(0)는 라이브 기간의 시작 부분으로 탐색합니다. 플레이어는 탐색 후 탐색된 위치와 동일한 실시간 오프셋을 유지하려고 시도합니다.

라이브 창에는 재생이 시작되어야 하는 기본 위치도 있습니다. 이 위치는 일반적으로 라이브 에지와 가까운 곳에 있습니다. Player.seekToDefaultPosition를 호출하여 기본 위치를 탐색할 수 있습니다.

실시간 재생 UI

ExoPlayer의 기본 UI 구성요소는 라이브 창의 길이와 그 내의 현재 재생 위치를 표시합니다. 즉, 실시간 창이 업데이트될 때마다 위치가 뒤로 이동하는 것처럼 보입니다. 유닉스 시간 또는 현재 실시간 오프셋 표시와 같이 다른 동작이 필요한 경우 PlayerControlView를 포크하고 필요에 맞게 수정할 수 있습니다.

실시간 재생 매개변수 구성

ExoPlayer는 일부 매개변수를 사용하여 실시간 가장자리에서 재생 위치의 오프셋과 이 오프셋을 조정하는 데 사용할 수 있는 재생 속도 범위를 제어합니다.

ExoPlayer는 세 곳에서 이러한 매개변수의 값을 우선순위 내림차순으로 가져옵니다 (처음 찾은 값이 사용됨).

  • MediaItem.Builder.setLiveConfiguration에 전달된 MediaItem 값별로
  • 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)

자바

// 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는 재생 속도를 약간 변경하여 실시간 오프셋을 조정합니다. 플레이어는 미디어 또는 앱에서 제공하는 타겟 실시간 오프셋을 일치시키려고 하지만 네트워크 상태가 변경될 때도 반응하려고 합니다. 예를 들어 재생 중에 리버퍼링이 발생하면 플레이어는 재생 속도를 약간 느리게 하여 실시간 가장자리에서 더 멀리 이동합니다. 네트워크가 라이브 에지에서 다시 더 가까이서 재생할 수 있을 만큼 충분히 안정적이 되면 플레이어는 재생 속도를 높여 타겟 라이브 오프셋으로 다시 이동합니다.

자동 재생 속도 조정을 사용하지 않으려면 minPlaybackSpeedmaxPlaybackSpeed 속성을 1.0f로 설정하여 사용 중지할 수 있습니다. 마찬가지로 지연 시간이 짧지 않은 라이브 스트림의 경우 이를 명시적으로 1.0f가 아닌 값으로 설정하여 사용 설정할 수 있습니다. 이러한 속성을 설정하는 방법에 관한 자세한 내용은 위의 구성 섹션을 참고하세요.

재생 속도 조정 알고리즘 맞춤설정

속도 조정이 사용 설정된 경우 LivePlaybackSpeedControl는 어떤 조정이 이루어질지 정의합니다. 맞춤 LivePlaybackSpeedControl를 구현하거나 기본 구현인 DefaultLivePlaybackSpeedControl를 맞춤설정할 수 있습니다. 두 경우 모두 플레이어를 빌드할 때 인스턴스를 설정할 수 있습니다.

Kotlin

val player =
  ExoPlayer.Builder(context)
    .setLivePlaybackSpeedControl(
      DefaultLivePlaybackSpeedControl.Builder().setFallbackMaxPlaybackSpeed(1.04f).build()
    )
    .build()

자바

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setLivePlaybackSpeedControl(
            new DefaultLivePlaybackSpeedControl.Builder()
                .setFallbackMaxPlaybackSpeed(1.04f)
                .build())
        .build();

DefaultLivePlaybackSpeedControl의 관련 맞춤설정 매개변수는 다음과 같습니다.

  • fallbackMinPlaybackSpeedfallbackMaxPlaybackSpeed: 미디어나 앱 제공 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
  }
}