Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ExoPlayerのトラック選択と再生中の解像度制限
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
scache
February 12, 2025
Programming
210
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
ExoPlayerのトラック選択と再生中の解像度制限
scache
February 12, 2025
More Decks by scache
See All by scache
Hyperion Item Nameplate
sckm
0
160
[紹介]Writing Your First Kotlin Compiler Plugin by Kevin Most
sckm
0
370
ChangeLogを読もう(1.2.70編)
sckm
1
400
3分でわかるSequence
sckm
1
730
Property + Getter
sckm
0
1.5k
略解reified
sckm
0
140
KDoc
sckm
1
910
Other Decks in Programming
See All in Programming
Observability in Practice:Grafana 與 Edge Device SRE 的那些事
blueswen
0
190
Even G2とAWSで推しのエージェントを召喚しよう!
har1101
1
140
AI駆動開発を妨げる技術的負債の解消アプローチ / ai-refactoring-approach
minodriven
17
8.5k
ローカルLLMでどこまでコードが書けるか -縮小版 / How much code can be written on a local LLM Shortened
kishida
2
170
Webフレームワークの ベンチマークについて
yusukebe
0
190
LLM本来の能力を解き放つサンドボックス技術とAI民主化への適用
yukukotani
3
4.8k
Vite+ Unified Toolchain for the Web
naokihaba
0
420
決定論的オーケストレーションの設計と実装 / Design and Implementation of Deterministic Orchestration
nrslib
4
1.6k
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.7k
これからAgentCoreを触る方へトレンドはGatewayです
har1101
6
460
Contextとはなにか
chiroruxx
1
390
なぜ型を書くのか? TSKaigi2026で改めて考える #tskaigi_smarthr
kajitack
0
190
Featured
See All Featured
New Earth Scene 8
popppiees
3
2.4k
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.3k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
960
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
480
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
19k
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
0
340
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
2
290
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
170
[SF Ruby Conf 2025] Rails X
palkan
2
1.1k
A Modern Web Designer's Workflow
chriscoyier
698
190k
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
340
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.8k
Transcript
ExoPlayerの トラック選択と 再生中の解像度制限 2025/02/12 株式会社AbemaTV > Streaming Client Sotoyama Ryo
CA.aab #5
1. 動画再生の仕組み 2. ExoPlayerのトラック選択ロジック 3. ExoPlayerの再生中の解像度制限の方法 この発表で話すこと
• 大規模イベント配信時のサーバ(CDN)負荷・転送量の削減 再生中に解像度制限をするモチベーション
• HTTP通信でサーバから動画を取得しながら再生を行う • 再生中にシームレスに解像度の変化が可能な動画再生 ◦ Adaptive Streaming • 解像度が良い ≒
ビットレートが高い 対象の動画
この発表で話すこと 1. 動画再生の仕組み 2. ExoPlayerのトラック選択ロジック 3. ExoPlayerの再生中の解像度制限の方法
動画再生の仕組み トラック1 1920x1080 トラック3 320x180 0 尺(秒) トラック2 960x540 元素材
1920x1080 0 60 尺(秒) 0 60 尺(秒) 解像度別に変換
トラック3 320x180 トラック2 960x540 動画再生の仕組み 元素材 1920x1080 0 60 尺(秒)
0 60 尺(秒) トラック1 1920x1080 10 20 30 40 50 解像度別に変換 & 分割
動画再生の仕組み トラック3 320x180 トラック2 960x540 0 60 尺(秒) トラック1 1920x1080
10 20 30 40 50 1. 1920x1080 のデータを20秒分貯める(バッファ) 再生位置
動画再生の仕組み トラック3 320x180 トラック2 960x540 0 60 尺(秒) トラック1 1920x1080
10 20 30 40 50 1. 1920x1080 のデータを20秒分貯める(バッファ) 再生位置
動画再生の仕組み トラック3 320x180 トラック2 960x540 0 60 尺(秒) トラック1 1920x1080
10 20 30 40 50 1. 1920x1080 のデータを20秒分貯める(バッファ) 2. 再生を開始 再生位置
動画再生の仕組み トラック3 320x180 トラック2 960x540 0 60 尺(秒) トラック1 1920x1080
10 20 30 40 50 1. 1920x1080 のデータを20秒分貯める(バッファ) 2. 再生を開始 3. 5秒経過 / データを10秒分取得 再生位置
動画再生の仕組み トラック3 320x180 トラック2 960x540 0 60 尺(秒) トラック1 1920x1080
10 20 30 40 50 1. 1920x1080 のデータを20秒分貯める(バッファ) 2. 再生を開始 3. 5秒経過 / データを10秒分取得 4. 5秒経過 / 通信速度が悪化したため 960x540 のデータを取得 再生位置
動画再生の仕組み トラック3 320x180 トラック2 960x540 0 60 尺(秒) トラック1 1920x1080
10 20 30 40 50 5. 30秒経過 / データの取得失敗 6. データ読み込みの間、再生停止 再生位置
動画再生の仕組み 1. 再生したい区間のデータを取得 a. 再生開始前に多めにバッファを貯めると再生が安定する 2. 再生しながら後続のデータを取得 a. データの取得が間に合わなくなった場合は再生が止まる b.
通信速度が悪化した時には低い解像度のデータを取得することで再生 が止まりにくくなる
この発表で話すこと 1. 動画再生の仕組み 2. ExoPlayerのトラック選択ロジック 3. ExoPlayerの再生中の解像度制限の方法
トラック選択ロジック 再生可能なトラックでビットレートが最も高いトラックを選択す る ファイルのダウンロード速度から通信速度を見積もり、トラッ ク選択の判断に使う 基本的な考え方
トラック選択ロジック 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
トラック選択ロジック 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
トラック選択ロジック 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 通信速度の予測結果
トラック選択ロジック 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 トラックが切り替わる時 (解像度が変化する )
トラック選択ロジック 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 ビットレートが上がる場合
トラック選択ロジック 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 ビットレートが下がる場合
トラック選択ロジック バッファが一定量 ある場合は切り替えを行わない バッファを消化している間にデータの取得が期待できる 低解像度への切り替え バッファが一定量 無い場合は切り替えを行わない バッファが少ない時に高解像度へ切り替えると、 バッファの枯渇による一時停止が発生しやすい 高解像度への切り替え
この発表で話すこと 1. 動画再生の仕組み 2. ExoPlayerのトラック選択ロジック 3. ExoPlayerの再生中の解像度制限の方法
• 大規模イベント配信時のサーバ(CDN)負荷・転送量の削減 再生中に解像度制限をするモチベーション
解像度制限の方法 • TrackSelectionParametersでトラックを無効化 ライブラリのAPIを使用
解像度制限の方法: ライブラリのAPIを使用 player.setTrackSelectionParameters( player .getTrackSelectionParameters() .buildUpon() .addOverride( new TrackSelectionOverride( mediaTrackGroup
, trackIndices ) .build() ); トラック1 1920x1080 トラック3 320x180 トラック2 960x540
解像度制限の方法: ライブラリのAPIを使用 • TrackSelectionParametersでトラックを無効化 • 再生中のトラックを無効化するとバッファが破棄され て一時的に再生が止まる ライブラリのAPIを使用 即時にトラックを無効化したい時に有効
解像度制限の方法 • TrackSelectionParametersでトラックを無効化 • 再生中のトラックを無効化するとバッファが破棄され て一時的に再生が止まる ライブラリのAPIを使用 トラック選択ロジックを独自実装 • AdaptiveTrackSelectionの実装を変更
• バッファの破棄をしないためシームレスな再生が可能 即時にトラックを無効化したい時に有効
解像度制限の方法: トラック選択ロジックを独自実装 public class AdaptiveTrackSelection extends BaseTrackSelection { ... }
解像度制限の方法: トラック選択ロジックを独自実装 public class MyTrackSelection extends BaseTrackSelection { public long
maxBitrate = Long.MAX_VALUE; public void setMaxBitrate (long bitrate) { maxBitrate = bitrate; } }
解像度制限の方法: トラック選択ロジックを独自実装 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 ; } }
解像度制限の方法: トラック選択ロジックを独自実装 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 ; }
解像度制限の方法: トラック選択ロジックを独自実装 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; }
解像度制限の方法: トラック選択ロジックを独自実装 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 ; } }
解像度制限の方法: トラック選択ロジックを独自実装 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 ; } }
解像度制限の方法: トラック選択ロジックを独自実装 new ExoPlayer.Builder(context) .setTrackSelector( new DefaultTrackSelector(context , new MyTrackSelection.Factory())
) ... .build();
解像度制限の方法: まとめ • TrackSelectionParametersでトラックを無効化 • 再生中のトラックを無効化するとバッファが破棄され て一時的に再生が止まる ライブラリのAPIを使用 トラック選択ロジックを独自実装 •
AdaptiveTrackSelectionの実装を変更 • バッファの破棄をしないためシームレスな再生が可能 即時にトラックを無効化したい時に有効 再生を止めずにサーバ (CDN)の転送量を削減し たい時に有効
None