ライブ配信

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 は、Player.getCurrentWindowIndexTimeline.getWindow を使用して Timeline から取得できます。Window 内:
    • Window.liveConfiguration には、ターゲットのライブ オフセットとライブ オフセットの調整のパラメータが含まれます。これらの値は、メディアの情報と、MediaItem.liveConfiguration で設定されたアプリ提供のオーバーライドに基づきます。
    • Window.windowStartTimeMs は、ライブ ウィンドウが開始する Unix エポックからの経過時間です。
    • Window.getCurrentUnixTimeMs は、現在のリアルタイムの Unix エポックからの経過時間です。この値は、サーバーとクライアントの既知のクロック差によって修正される場合があります。
    • Window.getDefaultPositionMs は、ライブ ウィンドウ内で、プレーヤーがデフォルトで再生を開始する位置です。

ライブ配信内の移動

Player.seekTo を使用すると、ライブ ウィンドウ内の任意の場所にシークできます。渡されるシーク位置は、ライブ ウィンドウの開始からの相対位置になります。たとえば、seekTo(0) はライブ ウィンドウの先頭までシークします。プレーヤーは、シーク後にシークされた位置と同じライブ オフセットを維持しようとします。

ライブ ウィンドウには、再生を開始するデフォルトの位置もあります。この位置は通常、ライブエッジに近い場所です。デフォルトの位置までシークするには、Player.seekToDefaultPosition を呼び出します。

ライブ再生 UI

ExoPlayer のデフォルトの UI コンポーネントは、ライブ ウィンドウの継続時間と、その中の現在の再生位置を表示します。つまり、ライブ ウィンドウが更新されるたびに、位置が後方にジャンプしているように見えます。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)をカスタマイズすることもできます。いずれの場合も、プレーヤーをビルドするときにインスタンスを設定できます。

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 の関連するカスタマイズ パラメータは次のとおりです。

  • fallbackMinPlaybackSpeedfallbackMaxPlaybackSpeed: メディアとアプリ提供の MediaItem のどちらも制限を定義していない場合に調整に使用できる最小再生速度と最大再生速度。
  • proportionalControlFactor: 速度調整の滑らかさを制御します。値が大きいほど、調整はより自然に、また反応的になりますが、聞こえる可能性も高くなります。値を小さくすると速度間の遷移がスムーズになりますが、速度は遅くなります。
  • targetLiveOffsetIncrementOnRebufferMs: この値は、慎重に進めるために、再バッファが発生するたびにターゲット ライブ オフセットに追加されます。この機能は、値を 0 に設定すると無効にできます。
  • minPossibleLiveOffsetSmoothingFactor: 現在バッファされているメディアに基づいて可能な最小ライブ オフセットをトラッキングするために使用される指数平滑化係数。値が 1 に非常に近いほど、推定がより慎重に行われ、改善されたネットワーク状態に調整に時間がかかることがあります。一方、値が小さいほど、再バッファが発生するリスクが高く、推定がより速く調整されます。

BehindLiveWindowException と ERROR_CODE_BEHIND_LIVE_WINDOW

再生位置がライブ ウィンドウより遅れることがあります(たとえば、プレーヤーが一時停止またはバッファリングしているときに十分な時間が経つ場合)。その場合、再生は失敗し、Player.Listener.onPlayerError によってエラーコード ERROR_CODE_BEHIND_LIVE_WINDOW の例外が報告されます。アプリコードでは、デフォルトの位置で再生を再開することにより、このようなエラーを処理したい場合があります。デモアプリの 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
  }
}