Androidマンになりたいおじさん

自分の備忘録的なのです。Androidを普及できたらいいなと思ったりしながら書きます

ExoPlayerのBufferingについて

はじめに

こんにちは!!!!いよいよ花粉が今週過ぎ去れば、平穏な日々がやってく....????

ハッッッッッックショイ!!!!!!!!!!!!!!

今日はタイトルの通りの疑問を持ったので、DefaultLoadControl classを少し覗いて紹介したいと思います

ExoPlayerってなんやねん!!!って方は先にこっち

gumiossan.hatenablog.com

DefaultLoadControl



まず、ExoPlayerを生成する際に自分でカスタマイズできる1つにLoadControlがあります

何も指定しなければ、DefaultLoadControlが使用されます



こいつって何してるの???
名前から察するにLoad周りの処理を制御する何かやろ〜くらいの気持ちでみていきましょう!

今回のkeyになってくるのが、以下の4つ

public Builder setBufferDurationsMs(
        int minBufferMs,
        int maxBufferMs,
        int bufferForPlaybackMs,
        int bufferForPlaybackAfterRebufferMs) { ・・・ }
  • minBufferMs
    • Exoが追加のデータを読み込む前のbufferされた最小量 = 最小でこんなけは持ちたいよ!
  • maxBufferMs
    • Exoが再生開始してから、更に停止する前に読み込む最大量 = 最大でこんなけは持てるよ!
  • bufferForPlaybackMs
    • 再生開始する前にどのくらいbufferするかを表す (minBuffer <= bufferForPlayback)
    • これはセグメントを完全にbufferしなくても最低限bufferForPlaybackMsを確保したら、再生開始する
  • bufferForPlaybackAfterRebufferMs
    • 再生後にbufferがなくなって、rebufferingが発生し、その際にbufferするか(minBuffer <= bufferForPlaybackAfterreBuffer)

これらのdefault値は以下のとおりです

  • minBufferMs: 15000
  • maxBufferMs: 50000
  • bufferForPlaybackMs: 2500
  • bufferForPlaybackAfterRebufferMs: 5000

ここで最低限確保したいminBufferMsが、bufferFor~の2つより小さいと意味不明って言われるわけですね minBufferMsが自分が2秒確保してるよ〜って保障したいのに、bufferFor~らが再生開始までに10秒必要やわ〜って言われてると矛盾を感じますよね...(いや、2秒ちゃうんかーいって)

再生開始するまでに10秒もbufferしたら、最低でもminBufferは10以上は確保されてないとおかしいわけです

実際、minBufferMsをbufferFor~より小さくしてみると

minBufferMs cannot be less than bufferForPlaybackMs

.........怒られました。

ここまで言いたいこと

ここまでで分かることをAkamaiさんが紹介してくれてるので、それを見ながら書いていきます!

blogs.akamai.com

単純にstart up time(再生開始時間)を速くしたかったら、bufferForPlaybackMsを限りなく0に近づければいいと!

ただ、それをすると最初に充分なbufferingが行われず、rebufferingがめちゃくちゃ発生するというわけですね…

VODコンテンツなどの終わりが分かっているものなら、maxBufferMsをたくさん伸ばしてあげれば、rebufferingが減って快適な視聴体験を届けれる...?

届けれるけど、maxBufferの値が大きいほど、Exoのメモリが消費されるので、世の中のデバイスの平均的な積まれてるメモリなどは把握しといた方がいいかも…。 更に途中で再生停止して離脱されたら、帯域幅をたくさん無駄に食べてしまい、通信量こんなに使ってないぞー!なんて怒られたり…

以下はExoPlayerのチームが行った実験の紹介なのですが

github.com

Exoで常に大きなbufferを確保しておけば、rebufferingは起きにくくなるので、minBufferとmaxBufferは同じ値にしてあげたらよいと言っています。rebufferingが起きにくい、快適な視聴体験を届けることができて、電池消費量がちょっと増えたくらいって言ってますね。これは今の値じゃ、burst bufferというのが発生し、rebufferingになりやすいから、それが起きないようにmin, max値を一緒にすることを→点滴供給方式って言っていますね(どゆこと???)

まぁ、この辺の詳細はあまり理解できていないので、要勉強します!!!!!!!!!!!!!!!!!!むしろ教えてください!!!!!!!!!

以上から、これらの設定できる4つの値はトレードオフな関係なのが分かりますね….

ExoPlayerはその辺りを全て考慮して、現在のdefault値を定めてるんでしょうね(すばらしい)

あくまでケースバイケースになるので、ユーザに値をいじれるようになってるのもさすがという感じです(すばらしい)

皆さんのプロダクトに合わせた、用法用量を守り、値を調整しましょう(すばらry)

実装覗き見

最後にこの辺りをDefaultLoadControlは結局どういう風にBufferingしようとか、Rebufferingだなとか判断しているのかを見て終わりにしたいと思います!

keyとなりそうなのは、以下の2つ

@Override
public boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed) {
  boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferSize;
  long minBufferUs = hasVideo ? minBufferVideoUs : minBufferAudioUs;
  if (playbackSpeed > 1) {
    // The playback speed is faster than real time, so scale up the minimum required media
    // duration to keep enough media buffered for a playout duration of minBufferUs.
    long mediaDurationMinBufferUs =
        Util.getMediaDurationForPlayoutDuration(minBufferUs, playbackSpeed);
    minBufferUs = Math.min(mediaDurationMinBufferUs, maxBufferUs);
  }
  if (bufferedDurationUs < minBufferUs) {
    isBuffering = prioritizeTimeOverSizeThresholds || !targetBufferSizeReached;
  } else if (bufferedDurationUs >= maxBufferUs || targetBufferSizeReached) {
    isBuffering = false;
  } // Else don't change the buffering state
  return isBuffering;
}

@Override
public boolean shouldStartPlayback(long bufferedDurationUs, float playbackSpeed, boolean rebuffering) {
  bufferedDurationUs = Util.getPlayoutDurationForMediaDuration(bufferedDurationUs, playbackSpeed);
  long minBufferDurationUs = rebuffering ? bufferForPlaybackAfterRebufferUs : bufferForPlaybackUs;
  return minBufferDurationUs <= 0
      || bufferedDurationUs >= minBufferDurationUs
      || (!prioritizeTimeOverSizeThresholds
          && allocator.getTotalBytesAllocated() >= targetBufferSize);
}

shouldContinueLoading

これは単純に渡されたbuffere済なdurationがminBufferを満たしていなかったら、bufferingする必要ありますねーくらいの判断をしてます

一応ここで、videoかどうかでminVideo, minAudioってminBufferの分解が行われているのが分かります

shouldStartPlayback

playbackStateがBUFFEINGのときにstateをREADYにして、再生開始していいかを判断してますね

その際にrebufferingかどうかがExoPlayerInternal classのdoSomeWorkでstate READYだけど、renderersReadyOrEndedがfalseになっていると(次のrendererの準備ができていないよ〜って状態)、rebufferingがtrueになり、渡ってきてますね

Rebufferingかどうかで、最小限確保したいdurationを最初に紹介したbufferForPlaybackAfterRebufferにするかどうかが見られてます。 bufferedDurationがminBufferDurationを満たしてたら、OK判定っぽいです。なるほどど

最後に

如何だったでしょうか??? 案外あっさりしているようで、深いな〜って僕は思いました。

もし、mobile/tvごとに帯域環境が異なったり、動画プレイヤー/音楽プレイヤーなど分かれている場合はこの辺りの値をカスタマイズすると効率よくできるかも????

それでは皆さん、よいExo lifeを!!!!!!