ライブ配信

ExoPlayer は、特別な設定をしなくても、ほとんどのアダプティブ ライブ配信をすぐに再生できます。詳細については、サポートされている形式のページをご覧ください。

アダプティブ ライブ ストリームでは、利用可能なメディアのウィンドウが定期的に更新され、現在のリアルタイムに合わせて移動します。つまり、再生位置は常にこのウィンドウ内のどこかにあり、ほとんどの場合、ストリームが生成されている現在のリアルタイムに近い位置になります。現在のリアルタイムと再生位置の差をライブ オフセットと呼びます。

ライブ再生の検出とモニタリング

ライブ ウィンドウが更新されるたびに、登録された Player.Listener インスタンスは onTimelineChanged イベントを受け取ります。現在のライブ再生に関する詳細を取得するには、以下のリストと図に示すように、さまざまな Player メソッドと Timeline.Window メソッドをクエリします。

ライブ ウィンドウ

  • Player.isCurrentWindowLive は、現在再生中のメディア アイテムがライブ ストリームかどうかを示します。ライブ配信が終了した場合でも、この値は true のままです。
  • Player.isCurrentWindowDynamic は、現在再生中のメディア アイテムがまだ更新中かどうかを示します。通常、これはまだ終了していないライブ配信に当てはまります。このフラグは、ライブ配信以外の配信でも true になる場合があります。
  • Player.getCurrentLiveOffset は、現在の実時間と再生位置のオフセットを返します(利用可能な場合)。
  • Player.getDuration は、現在のライブ ウィンドウの長さを返します。
  • Player.getCurrentPosition は、ライブ ウィンドウの開始位置を基準とした再生位置を返します。
  • Player.getCurrentMediaItem は現在のメディア アイテムを返します。ここで、MediaItem.liveConfiguration には、ターゲットのライブ オフセットとライブ オフセット調整パラメータに対するアプリ提供のオーバーライドが含まれます。
  • Player.getCurrentTimeline は、Timeline で現在のメディア構造を返します。現在の Timeline.Window は、Player.getCurrentMediaItemIndexTimeline.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.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)

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

再生位置がライブ ウィンドウよりも遅れることがあります。たとえば、プレーヤーが一時停止されたり、バッファリングが長時間行われたりした場合です。この場合、再生は失敗し、エラーコード 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
  }
}