Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
ExoPlayerのトラック選択と再生中の解像度制限
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
scache
February 12, 2025
Programming
230
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
170
[紹介]Writing Your First Kotlin Compiler Plugin by Kevin Most
sckm
0
380
ChangeLogを読もう(1.2.70編)
sckm
1
420
3分でわかるSequence
sckm
1
740
Property + Getter
sckm
0
1.6k
略解reified
sckm
0
160
KDoc
sckm
1
940
Other Decks in Programming
See All in Programming
Secure Your Coding Agent Like It’s Malware (v1.5) 🇬🇧 @Code.talks 2026
richargh
0
110
Enforcing Your Architecture with Unit Tests in the AI Era (Swift Connection X)
steliosf
PRO
1
150
Long Living Tokens, Die, Die, Die! (or: The One Ring is Banned in Modern)
sylph01
0
420
Spec-Driven Development - Why Vaadin Is the Perfect Fit
simas
PRO
0
240
作れるエンジニアから、価値を届けるエンジニアへ
ryunakayama
1
250
AI時代のTerminal環境を UNIX哲学で設計・運用する
yasunori0418
1
350
社内AIエージェント「Askワンバン」における コストチューニングの取り組み
t0yohei
0
170
AI時代のFlutter品質設計 — アプリ品質を守るQuality Layers
omatty198
0
370
LLM 101
dalinaum
0
480
flutter_test のその先へ — Flutter アプリの LLM 機能に「評価」という品質保証を
kosukesaigusa
0
200
動画プレイヤーはなぜカクつくのか 〜Flutterのレンダリングとネイティブの境界を解剖する〜
naruogram
0
330
AI-Powered Domain-Driven Design
ziobrando
0
420
Featured
See All Featured
Reality Check: Gamification 10 Years Later
codingconduct
0
2.4k
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
48k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
203
77k
Git: the NoSQL Database
bkeepers
PRO
433
67k
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
610
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
350
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.6k
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
680
Public Speaking Without Barfing On Your Shoes - THAT 2023
reverentgeek
1
660
Abbi's Birthday
coloredviolet
4
11k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
2.2k
brightonSEO & MeasureFest 2025 - Winning Strategies for Black Friday CRO & PPC - Christian Goodrich
cargoodrich
3
890
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