ライブ配信

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

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

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

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

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

  • fallbackMinPlaybackSpeed と fallbackMaxPlaybackSpeed: メディアとアプリ提供の 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
  }
}