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
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
160
[紹介]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
150
KDoc
sckm
1
930
Other Decks in Programming
See All in Programming
How I Stole PSI from Android Studio - DroidKaigi2026
worker8
0
130
マイコン向けの軽量Ruby「PicoRuby」で各種デバイスを制御するネイティブアプリの実現手法
bash0c7
0
410
『寄り添うラジオ』をAIで作る 体験価値から逆算した、会話しないUXと品質設計
theoriatec2024
3
180
Webの地図
yosuke_furukawa
PRO
6
4.6k
AHC070解法紹介
eijirou
0
120
モデルのリファクタリングが難しいと思ったら、そもそも複雑だったのはビジネス仕様だった ? / is-the-business-domain-the-real-complexity
hatsu38
0
290
GraphRAGのKnowledge Graphを 直接!見る/View-GraphRAG's-KnowledgeGraph-directly!
tyumugi1113
1
330
大喜利で理解するLLM as a Judge / Understanding LLM-as-a-Judge through Ogiri
rockname
0
120
Jetpack Compose メカニズム
skydoves
0
430
Vue Fes Japan 2026 タイムテーブル徹底解説
448jp
1
220
AI に Inclusive UI を書かせよう — Design Rules Skill で Compose UI を作り直す
theoriatec2024
1
490
巨大モノリシックアプリ モダン化大作戦
ktcryomm
1
1k
Featured
See All Featured
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
540
ラッコキーワード サービス紹介資料
rakko
1
4.9M
Prompt Engineering for Job Search
mfonobong
0
450
New Earth Scene 8
popppiees
4
2.6k
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
500
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
540
sira's awesome portfolio website redesign presentation
elsirapls
0
410
Ethics towards AI in product and experience design
skipperchong
2
380
Accessibility Awareness
sabderemane
1
210
The untapped power of vector embeddings
frankvandijk
2
1.9k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.3k
Utilizing Notion as your number one productivity tool
mfonobong
4
590
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