Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ExoPlayerのトラック選択と再生中の解像度制限

Avatar for scache scache
February 12, 2025

 ExoPlayerのトラック選択と再生中の解像度制限

Avatar for scache

scache

February 12, 2025
Tweet

More Decks by scache

Other Decks in Programming

Transcript

  1. トラック3 320x180 トラック2 960x540 動画再生の仕組み 元素材 1920x1080 0 60 尺(秒)

    0 60 尺(秒) トラック1 1920x1080 10 20 30 40 50 解像度別に変換 & 分割
  2. 動画再生の仕組み トラック3 320x180 トラック2 960x540 0 60 尺(秒) トラック1 1920x1080

    10 20 30 40 50 1. 1920x1080 のデータを20秒分貯める(バッファ) 再生位置
  3. 動画再生の仕組み トラック3 320x180 トラック2 960x540 0 60 尺(秒) トラック1 1920x1080

    10 20 30 40 50 1. 1920x1080 のデータを20秒分貯める(バッファ) 再生位置
  4. 動画再生の仕組み トラック3 320x180 トラック2 960x540 0 60 尺(秒) トラック1 1920x1080

    10 20 30 40 50 1. 1920x1080 のデータを20秒分貯める(バッファ) 2. 再生を開始 再生位置
  5. 動画再生の仕組み トラック3 320x180 トラック2 960x540 0 60 尺(秒) トラック1 1920x1080

    10 20 30 40 50 1. 1920x1080 のデータを20秒分貯める(バッファ) 2. 再生を開始 3. 5秒経過 / データを10秒分取得 再生位置
  6. 動画再生の仕組み トラック3 320x180 トラック2 960x540 0 60 尺(秒) トラック1 1920x1080

    10 20 30 40 50 1. 1920x1080 のデータを20秒分貯める(バッファ) 2. 再生を開始 3. 5秒経過 / データを10秒分取得 4. 5秒経過 / 通信速度が悪化したため 960x540 のデータを取得 再生位置
  7. 動画再生の仕組み トラック3 320x180 トラック2 960x540 0 60 尺(秒) トラック1 1920x1080

    10 20 30 40 50 5. 30秒経過 / データの取得失敗 6. データ読み込みの間、再生停止 再生位置
  8. トラック選択ロジック int newSelectedIndex = determineIdealSelectedIndex(nowMs , chunkDurationUs) ; if (newSelectedIndex

    != previousSelectedIndex && !isTrackExcluded(previousSelectedIndex , nowMs)) { Format currentFormat = getFormat(previousSelectedIndex) ; Format selectedFormat = getFormat(newSelectedIndex) ; long minDurationForQualityIncreaseUs = minDurationForQualityIncreaseUs(availableDurationUs , chunkDurationUs) ; if (selectedFormat. bitrate > currentFormat. bitrate && bufferedDurationUs < minDurationForQualityIncreaseUs) { newSelectedIndex = previousSelectedIndex ; } else if (selectedFormat. bitrate < currentFormat. bitrate && bufferedDurationUs >= maxDurationForQualityDecreaseUs ) { newSelectedIndex = previousSelectedIndex ; } } https://github.com/androidx/media/blob/1.5.1/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelection.java#L461-L480
  9. トラック選択ロジック int newSelectedIndex = determineIdealSelectedIndex(nowMs , chunkDurationUs) ; if (newSelectedIndex

    != previousSelectedIndex && !isTrackExcluded(previousSelectedIndex, nowMs)) { Format currentFormat = getFormat(previousSelectedIndex); Format selectedFormat = getFormat(newSelectedIndex); long minDurationForQualityIncreaseUs = minDurationForQualityIncreaseUs(availableDurationUs, chunkDurationUs); if (selectedFormat.bitrate > currentFormat.bitrate && bufferedDurationUs < minDurationForQualityIncreaseUs) { newSelectedIndex = previousSelectedIndex; } else if (selectedFormat.bitrate < currentFormat.bitrate && bufferedDurationUs >= maxDurationForQualityDecreaseUs) { newSelectedIndex = previousSelectedIndex; } } https://github.com/androidx/media/blob/1.5.1/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelection.java#L461-L480
  10. トラック選択ロジック private int determineIdealSelectedIndex (long nowMs, long chunkDurationUs) { long

    effectiveBitrate = getAllocatedBandwidth(chunkDurationUs) ; int lowestBitrateAllowedIndex = 0; for (int i = 0; i < length; i++) { if (nowMs == Long. MIN_VALUE || !isTrackExcluded(i , nowMs)) { Format format = getFormat(i) ; if (canSelectFormat(format , format.bitrate, effectiveBitrate)) { return i; } else { lowestBitrateAllowedIndex = i ; } } } return lowestBitrateAllowedIndex ; } トラックの情報 ビットレートが高い順 https://github.com/androidx/media/blob/1.5.1/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelection.java#L599-L613 通信速度の予測結果
  11. トラック選択ロジック int newSelectedIndex = determineIdealSelectedIndex(nowMs , chunkDurationUs) ; if (newSelectedIndex

    != previousSelectedIndex && !isTrackExcluded(previousSelectedIndex , nowMs)) { Format currentFormat = getFormat(previousSelectedIndex) ; Format selectedFormat = getFormat(newSelectedIndex) ; long minDurationForQualityIncreaseUs = minDurationForQualityIncreaseUs(availableDurationUs, chunkDurationUs); if (selectedFormat.bitrate > currentFormat.bitrate && bufferedDurationUs < minDurationForQualityIncreaseUs) { newSelectedIndex = previousSelectedIndex; } else if (selectedFormat.bitrate < currentFormat.bitrate && bufferedDurationUs >= maxDurationForQualityDecreaseUs) { newSelectedIndex = previousSelectedIndex; } } https://github.com/androidx/media/blob/1.5.1/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelection.java#L461-L480 トラックが切り替わる時 (解像度が変化する )
  12. トラック選択ロジック int newSelectedIndex = determineIdealSelectedIndex(nowMs, chunkDurationUs); if (newSelectedIndex != previousSelectedIndex

    && !isTrackExcluded(previousSelectedIndex, nowMs)) { Format currentFormat = getFormat(previousSelectedIndex); Format selectedFormat = getFormat(newSelectedIndex); long minDurationForQualityIncreaseUs = minDurationForQualityIncreaseUs(availableDurationUs , chunkDurationUs) ; if (selectedFormat. bitrate > currentFormat. bitrate && bufferedDurationUs < minDurationForQualityIncreaseUs) { newSelectedIndex = previousSelectedIndex ; } else if (selectedFormat.bitrate < currentFormat.bitrate && bufferedDurationUs >= maxDurationForQualityDecreaseUs) { newSelectedIndex = previousSelectedIndex; } } https://github.com/androidx/media/blob/1.5.1/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelection.java#L461-L480 ビットレートが上がる場合
  13. トラック選択ロジック int newSelectedIndex = determineIdealSelectedIndex(nowMs, chunkDurationUs); if (newSelectedIndex != previousSelectedIndex

    && !isTrackExcluded(previousSelectedIndex, nowMs)) { Format currentFormat = getFormat(previousSelectedIndex); Format selectedFormat = getFormat(newSelectedIndex); long minDurationForQualityIncreaseUs = minDurationForQualityIncreaseUs(availableDurationUs, chunkDurationUs); if (selectedFormat.bitrate > currentFormat.bitrate && bufferedDurationUs < minDurationForQualityIncreaseUs) { newSelectedIndex = previousSelectedIndex; } else if (selectedFormat. bitrate < currentFormat. bitrate && bufferedDurationUs >= maxDurationForQualityDecreaseUs ) { newSelectedIndex = previousSelectedIndex ; } } https://github.com/androidx/media/blob/1.5.1/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelection.java#L461-L480 ビットレートが下がる場合
  14. 解像度制限の方法: トラック選択ロジックを独自実装 public class MyTrackSelection extends BaseTrackSelection { public long

    maxBitrate = Long.MAX_VALUE; public void setMaxBitrate (long bitrate) { maxBitrate = bitrate; } }
  15. 解像度制限の方法: トラック選択ロジックを独自実装 int newSelectedIndex = determineIdealSelectedIndex(nowMs , chunkDurationUs) ; if

    (newSelectedIndex != previousSelectedIndex && !isTrackExcluded(previousSelectedIndex , nowMs)) { Format currentFormat = getFormat(previousSelectedIndex) ; Format selectedFormat = getFormat(newSelectedIndex) ; long minDurationForQualityIncreaseUs = minDurationForQualityIncreaseUs(availableDurationUs , chunkDurationUs) ; if (selectedFormat. bitrate > currentFormat. bitrate && bufferedDurationUs < minDurationForQualityIncreaseUs) { newSelectedIndex = previousSelectedIndex ; } else if (selectedFormat. bitrate < currentFormat. bitrate && bufferedDurationUs >= maxDurationForQualityDecreaseUs ) { newSelectedIndex = previousSelectedIndex ; } }
  16. 解像度制限の方法: トラック選択ロジックを独自実装 private int determineIdealSelectedIndex (long nowMs, long chunkDurationUs) {

    long effectiveBitrate = getAllocatedBandwidth(chunkDurationUs) ; int lowestBitrateAllowedIndex = 0; for (int i = 0; i < length; i++) { if (nowMs == Long. MIN_VALUE || !isTrackExcluded(i , nowMs)) { Format format = getFormat(i) ; if (canSelectFormat(format , format.bitrate, effectiveBitrate)) { return i; } else { lowestBitrateAllowedIndex = i ; } } } return lowestBitrateAllowedIndex ; }
  17. 解像度制限の方法: トラック選択ロジックを独自実装 private int determineIdealSelectedIndex(long nowMs, long chunkDurationUs) { long

    effectiveBitrate = getAllocatedBandwidth(chunkDurationUs); int lowestBitrateAllowedIndex = 0; for (int i = 0; i < length; i++) { if (nowMs == Long. MIN_VALUE || !isTrackExcluded(i, nowMs)) { Format format = getFormat(i); if (format.bitrate < maxBitrate && canSelectFormat(format , format.bitrate, effectiveBitrate)) { return i; } else { lowestBitrateAllowedIndex = i; } } } return lowestBitrateAllowedIndex; }
  18. 解像度制限の方法: トラック選択ロジックを独自実装 int newSelectedIndex = determineIdealSelectedIndex(nowMs , chunkDurationUs) ; if

    (newSelectedIndex != previousSelectedIndex && !isTrackExcluded(previousSelectedIndex , nowMs)) { Format currentFormat = getFormat(previousSelectedIndex) ; Format selectedFormat = getFormat(newSelectedIndex) ; long minDurationForQualityIncreaseUs = minDurationForQualityIncreaseUs(availableDurationUs , chunkDurationUs) ; if (selectedFormat. bitrate > currentFormat. bitrate && bufferedDurationUs < minDurationForQualityIncreaseUs) { newSelectedIndex = previousSelectedIndex ; } else if (selectedFormat. bitrate < currentFormat. bitrate && bufferedDurationUs >= maxDurationForQualityDecreaseUs ) { newSelectedIndex = previousSelectedIndex ; } }
  19. 解像度制限の方法: トラック選択ロジックを独自実装 int newSelectedIndex = determineIdealSelectedIndex(nowMs, chunkDurationUs); if (newSelectedIndex !=

    previousSelectedIndex && !isTrackExcluded(previousSelectedIndex, nowMs)) { Format currentFormat = getFormat(previousSelectedIndex); Format selectedFormat = getFormat(newSelectedIndex); long minDurationForQualityIncreaseUs = minDurationForQualityIncreaseUs(availableDurationUs, chunkDurationUs); if (selectedFormat.bitrate > currentFormat.bitrate && bufferedDurationUs < minDurationForQualityIncreaseUs) { newSelectedIndex = previousSelectedIndex; } else if (selectedFormat. bitrate < currentFormat. bitrate && currentFormat.bitrate <= maxBitrate && bufferedDurationUs >= maxDurationForQualityDecreaseUs ) { newSelectedIndex = previousSelectedIndex ; } }
  20. 解像度制限の方法: まとめ • TrackSelectionParametersでトラックを無効化 • 再生中のトラックを無効化するとバッファが破棄され て一時的に再生が止まる ライブラリのAPIを使用 トラック選択ロジックを独自実装 •

    AdaptiveTrackSelectionの実装を変更 • バッファの破棄をしないためシームレスな再生が可能 即時にトラックを無効化したい時に有効 再生を止めずにサーバ (CDN)の転送量を削減し たい時に有効