ライブ配信

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
  }
}