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
Customize & Debug ExoPlayer @droidkaigi 2020
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
TakuSemba
March 17, 2020
Technology
2.1k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Customize & Debug ExoPlayer @droidkaigi 2020
TakuSemba
March 17, 2020
More Decks by TakuSemba
See All by TakuSemba
Jetpack Compose
takusemba
3
3.7k
Protobuf in Kotlin
takusemba
2
2k
Single Activity with MVVM
takusemba
4
1.4k
KotlinConf Report @ca.kt#7
takusemba
2
500
Request in a QUIC way @shibuya.apk#28
takusemba
2
1.1k
Lint for Kotlin @R.kt#3
takusemba
3
1.6k
Auto Release @potatochips#48
takusemba
1
1.3k
Media streaming on Android @droidkaigi 2018
takusemba
6
8.3k
gRPC on Android @DroidconSF Report
takusemba
1
640
Other Decks in Technology
See All in Technology
時期が悪い!それでもRaspberry Piを買って遊んで活用するには / 20260627-osc26do-rpi-jikigawarui
akkiesoft
0
840
GitHub Copilot 最新アップデート – 「一歩先」の実践活用術
moulongzhang
5
1.9k
【セミナー資料】Claude Code をセキュアに使うための考え方と設定の勘どころ / Claude Code Webinar 20260616
masahirokawahara
2
470
AIペネトレーションテスト・ セキュリティ検証「AgenticSec」紹介資料
laysakura
2
7.6k
GitHub Copilot app最速の発信の裏側
tomokusaba
1
260
從開發到部署全都交給 AI:實作 AI 驅動的自動化流程
appleboy
0
170
MUSUBI 田中裕一『AIと共に行う「しごとのリデザイン」- スモールバックオフィス編』AI Ops Lab #4
musubi
0
320
When Platform Engineering Meets GenAI
sucitw
0
180
From Prompt Engineering to Loop Engineering
shibuiwilliam
1
240
40代で“やっとエンジニアになれた”――閉じた学びを開き、空の青さを知る / 20260628 Naoki Takahashi
shift_evolve
PRO
4
890
2026-06-24_人とAIの責務分離に基づく開発プロセスの提案.pdf
takahiromatsui
0
190
AIチャットの改善から見えた、良いAI体験とは / What Constitutes a Good AI Experience: Insights from Improving AI Chat
kubode
0
120
Featured
See All Featured
Un-Boring Meetings
codingconduct
0
320
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
180
So, you think you're a good person
axbom
PRO
2
2.1k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.2k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.5k
Designing for humans not robots
tammielis
254
26k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.5k
Skip the Path - Find Your Career Trail
mkilby
1
150
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
750
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
490
Transcript
None
@takusemba https://github.com/TakuSemba
Media Streaming
None
None
None
None
None
Streaming Protocol
Streaming Protocol
None
None
None
None
None
None
None
None
None
None
...
Adaptive Bitrate
None
None
None
…
None
None
None
…
None
10s ~
~ 25s
val trackSelectionFactory = AdaptiveTrackSelection.Factory( MIN_DURATION_FOR_QUALITY_INCREASE_MS, MAX_DURATION_FOR_QUALITY_DECREASE_MS, ... )
val trackSelectionFactory = AdaptiveTrackSelection.Factory( MIN_DURATION_FOR_QUALITY_INCREASE_MS, MAX_DURATION_FOR_QUALITY_DECREASE_MS, ... ) AdaptiveTrackSelection.Factory( MIN_DURATION_FOR_QUALITY_INCREASE_MS,
)
val trackSelectionFactory = AdaptiveTrackSelection.Factory( MIN_DURATION_FOR_QUALITY_INCREASE_MS, MAX_DURATION_FOR_QUALITY_DECREASE_MS, ... ) AdaptiveTrackSelection.Factory( MAX_DURATION_FOR_QUALITY_DECREASE_MS,
)
val trackSelectionFactory = AdaptiveTrackSelection.Factory( MIN_DURATION_FOR_QUALITY_INCREASE_MS, MAX_DURATION_FOR_QUALITY_DECREASE_MS, BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE, ... ) AdaptiveTrackSelection.Factory(
BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE,
val trackSelectionFactory = AdaptiveTrackSelection.Factory( MIN_DURATION_FOR_QUALITY_INCREASE_MS, MAX_DURATION_FOR_QUALITY_DECREASE_MS, BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE, ... ) AdaptiveTrackSelection.Factory(
BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE, Edge Current Position Buffered Position 0.75
val bandwidthMeter = DefaultBandwidthMeter.Builder(context).build()
Bandwidth Time 10Mbps
Bandwidth Time 10Mbps
Bandwidth Time 10Mbps
Bandwidth Time 10Mbps
Bandwidth Time 10Mbps
Bandwidth Time 10Mbps
Bandwidth Time 10Mbps
Bandwidth Time 10Mbps
Bandwidth Time 10Mbps
Bandwidth Time 10Mbps
Bandwidth Time 10Mbps 6Mbps
Bandwidth Time 10Mbps 6Mbps SlidingWindowMaxWeight
Time 6Mbps SlidingWindowMaxWeight
Time SlidingWindowMaxWeight 6Mbps * 0.75 = 4.5Mbps
Time 6Mbps * 0.75 = 4.5Mbps SlidingWindowMaxWeight BandwidthFraction
val trackSelectionFactory = AdaptiveTrackSelection.Factory( MIN_DURATION_FOR_QUALITY_INCREASE_MS, MAX_DURATION_FOR_QUALITY_DECREASE_MS, BANDWIDTH_FRACTION ... ) val
bandwidthMeter = DefaultBandwidthMeter.Builder(context) .setSlidingWindowMaxWeight(SLIDING_WINDOW_MAX_WEIGHT) .build()
val trackSelectionFactory = AdaptiveTrackSelection.Factory( MIN_DURATION_FOR_QUALITY_INCREASE_MS, MAX_DURATION_FOR_QUALITY_DECREASE_MS, BANDWIDTH_FRACTION ... ) AdaptiveTrackSelection.Factory(
BANDWIDTH_FRACTION val bandwidthMeter = DefaultBandwidthMeter.Builder(context) .setSlidingWindowMaxWeight(SLIDING_WINDOW_MAX_WEIGHT) .build()
val trackSelectionFactory = AdaptiveTrackSelection.Factory( MIN_DURATION_FOR_QUALITY_INCREASE_MS, MAX_DURATION_FOR_QUALITY_DECREASE_MS, BANDWIDTH_FRACTION ... ) val
bandwidthMeter = DefaultBandwidthMeter.Builder(context) .setSlidingWindowMaxWeight(SLIDING_WINDOW_MAX_WEIGHT) .build() .setSlidingWindowMaxWeight(SLIDING_WINDOW_MAX_WEIGHT)
Initial Bitrate
None
???
5.7Mbps Wifi
5.7Mbps Wifi
2.2Mbps 3G
2.2Mbps 3G
2.0Mbps Wifi
2.0Mbps Wifi
5.7Mbps Wifi
5.7Mbps Wifi
val bandwidthMeter = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(INITIAL_BITRATE_ESTIMATE) .build()
val bandwidthMeter = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(INITIAL_BITRATE_ESTIMATE) .build() .setInitialBitrateEstimate(INITIAL_BITRATE_ESTIMATE)
val bandwidthMeter = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .build() 180p 360p 720p Session
Scope
Session Scope 720p val bandwidthMeter = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .build()
Session Scope 180p 360p 720p val bandwidthMeter = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE)
.build() 720p
Session Scope val bandwidthMeter = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .build() 180p 360p
720p
Session Scope val bandwidthMeter = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .build() 720p
Session Scope val bandwidthMeter = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .build() 180p 360p
720p 720p
fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if (singletonInstance == null) {
singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .build() } return singletonInstance } Application Scope 180p 360p 720p
Application Scope 720p fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if (singletonInstance
== null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .build() } return singletonInstance }
Application Scope 720p 720p 720p fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter {
if (singletonInstance == null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .build() } return singletonInstance } 720p
Application Scope fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if (singletonInstance ==
null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .build() } return singletonInstance } 720p 720p 720p
Application Scope fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if (singletonInstance ==
null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .build() } return singletonInstance } 720p Launch App
Application Scope Launch App fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if
(singletonInstance == null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .build() } return singletonInstance } 720p 180p 360p 720p
Application Scope fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if (singletonInstance ==
null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .build() } return singletonInstance } 180p 360p 720p
Application Scope fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if (singletonInstance ==
null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .build() } return singletonInstance } 720p
Application Scope Wifi -> 4G fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter {
if (singletonInstance == null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .build() } return singletonInstance } 720p 720p
Application Scope fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if (singletonInstance ==
null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .setResetOnNetworkTypeChange(true) .build() } return singletonInstance } 720p 720p Wifi -> 4G
Application Scope fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if (singletonInstance ==
null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .setResetOnNetworkTypeChange(true) .build() } return singletonInstance } 720p Wifi -> 4G .setResetOnNetworkTypeChange(true) 720p
Application Scope Wifi -> 4G 720p 180p 360p fun getSingletonBandwidthMeter(context:
Context): DefaultBandwidthMeter { if (singletonInstance == null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(LOWEST_RESOLUTION_BITRATE) .setResetOnNetworkTypeChange(true) .build() } return singletonInstance } 720p
Lifetime Scope 180p 360p 720p fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter {
if (singletonInstance == null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(prefs.getLastEstimatedBitrate()) .setResetOnNetworkTypeChange(true) .build() } return singletonInstance }
Lifetime Scope fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if (singletonInstance ==
null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(prefs.getLastEstimatedBitrate()) .setResetOnNetworkTypeChange(true) .build() } return singletonInstance } .setInitialBitrateEstimate(prefs.getLastEstimatedBitrate()) 180p 360p 720p
Lifetime Scope fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if (singletonInstance ==
null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(prefs.getLastEstimatedBitrate()) .setResetOnNetworkTypeChange(true) .build() } return singletonInstance } .setResetOnNetworkTypeChange(true) 180p 360p 720p
Lifetime Scope fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if (singletonInstance ==
null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(prefs.getLastEstimatedBitrate()) .setResetOnNetworkTypeChange(true) .build() } return singletonInstance } 180p 360p 720p
Lifetime Scope fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if (singletonInstance ==
null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(prefs.getLastEstimatedBitrate()) .setResetOnNetworkTypeChange(true) .build() } return singletonInstance } 720p
720p 720p 720p Lifetime Scope fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter {
if (singletonInstance == null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(prefs.getLastEstimatedBitrate()) .setResetOnNetworkTypeChange(true) .build() } return singletonInstance } 720p
Lifetime Scope fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if (singletonInstance ==
null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(prefs.getLastEstimatedBitrate()) .setResetOnNetworkTypeChange(true) .build() } return singletonInstance } 720p 720p 720p
Lifetime Scope fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if (singletonInstance ==
null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(prefs.getLastEstimatedBitrate()) .setResetOnNetworkTypeChange(true) .build() } return singletonInstance } 720p Launch App
Launch App Lifetime Scope fun getSingletonBandwidthMeter(context: Context): DefaultBandwidthMeter { if
(singletonInstance == null) { singletonInstance = DefaultBandwidthMeter.Builder(context) .setInitialBitrateEstimate(prefs.getLastEstimatedBitrate()) .setResetOnNetworkTypeChange(true) .build() } return singletonInstance } 720p 720p 720p 720p
Limit Bitrate
None
None
val trackSelectionFactory = AdaptiveTrackSelection.Factory(...) val parameter = DefaultTrackSelector.ParametersBuilder(context) .setMaxVideoBitrate(MAX_VIDEO_BITRARE) .build()
val trackSelector = DefaultTrackSelector(parameter, trackSelectionFactory)
val trackSelectionFactory = AdaptiveTrackSelection.Factory(...) val parameter = DefaultTrackSelector.ParametersBuilder(context) .setMaxVideoBitrate(MAX_VIDEO_BITRARE) .build()
val trackSelector = DefaultTrackSelector(parameter, trackSelectionFactory) .setMaxVideoBitrate(MAX_VIDEO_BITRARE)
val trackSelectionFactory = AdaptiveTrackSelection.Factory(...) val parameter = DefaultTrackSelector.ParametersBuilder(context) .setMaxVideoSize(640, 360)
.build() val trackSelector = DefaultTrackSelector(parameter, trackSelectionFactory)
val trackSelectionFactory = AdaptiveTrackSelection.Factory(...) val parameter = DefaultTrackSelector.ParametersBuilder(context) .setMaxVideoSize(640, 360)
.build() val trackSelector = DefaultTrackSelector(parameter, trackSelectionFactory) .setMaxVideoSize(640, 360)
None
val newParameter = DefaultTrackSelector.ParametersBuilder(context) .setMaxVideoBitrate(NEW_MAX_VIDEO_BITRARE) .build() trackSelector.parameters = newParameter
val newParameter = DefaultTrackSelector.ParametersBuilder(context) .setMaxVideoBitrate(NEW_MAX_VIDEO_BITRARE) .build() trackSelector.parameters = newParameter trackSelector.parameters
= newParameter
None
None
None
None
None
class LimitTrackSelection(...) : AdaptiveTrackSelection(...) { private var maxVideoBitrate = Int.MAX_VALUE
fun setMaxVideoBitrate(maxVideoBitrate: Int) { this.maxVideoBitrate = maxVideoBitrate } override fun canSelectFormat( format: Format, trackBitrate: Int, playbackSpeed: Float, effectiveBitrate: Long ): Boolean { return trackBitrate <= maxVideoBitrate && super.canSelectFormat(...) } }
class LimitTrackSelection(...) : AdaptiveTrackSelection(...) { private var maxVideoBitrate = Int.MAX_VALUE
fun setMaxVideoBitrate(maxVideoBitrate: Int) { this.maxVideoBitrate = maxVideoBitrate } override fun canSelectFormat( format: Format, trackBitrate: Int, playbackSpeed: Float, effectiveBitrate: Long ): Boolean { return trackBitrate <= maxVideoBitrate && super.canSelectFormat(...) } } AdaptiveTrackSelection(...) { override fun canSelectFormat( format: Format, trackBitrate: Int, playbackSpeed: Float, effectiveBitrate: Long ): Boolean }
class LimitTrackSelection(...) : AdaptiveTrackSelection(...) { private var maxVideoBitrate = Int.MAX_VALUE
fun setMaxVideoBitrate(maxVideoBitrate: Int) { this.maxVideoBitrate = maxVideoBitrate } override fun canSelectFormat( format: Format, trackBitrate: Int, playbackSpeed: Float, effectiveBitrate: Long ): Boolean { return trackBitrate <= maxVideoBitrate && super.canSelectFormat(...) } } private var maxVideoBitrate = Int.MAX_VALUE fun setMaxVideoBitrate(maxVideoBitrate: Int) { this.maxVideoBitrate = maxVideoBitrate } override fun canSelectFormat( { return trackBitrate <= maxVideoBitrate } }
class LimitTrackSelection(...) : AdaptiveTrackSelection(...) { private var maxVideoBitrate = Int.MAX_VALUE
fun setMaxVideoBitrate(maxVideoBitrate: Int) { this.maxVideoBitrate = maxVideoBitrate } override fun canSelectFormat( format: Format, trackBitrate: Int, playbackSpeed: Float, effectiveBitrate: Long ): Boolean { return trackBitrate <= maxVideoBitrate && super.canSelectFormat(...) } } https://github.com/google/ExoPlayer/issues/2250
Chunkless Preparation
m3u8 Playlist
ts First Chunk Playlist
First Chunk Playlist Prepare Decoder
First Chunk Playlist Prepare Decoder Start Decodeing
#EXTM3U #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=200000,CODECS="mp4a.40.2, avc1.4d4015" index1.m3u8 #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=600000,CODECS=“mp4a.40.2, avc1.4d401e" index2.m3u8 …
#EXTM3U #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=200000,CODECS="mp4a.40.2, avc1.4d4015" index1.m3u8 #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=600000,CODECS=“mp4a.40.2, avc1.4d401e" index2.m3u8 … CODECS="mp4a.40.2, avc1.4d4015"
CODECS=“mp4a.40.2, avc1.4d401e"
#EXTM3U #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=200000,CODECS="mp4a.40.2, avc1.4d4015" index1.m3u8 #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=600000,CODECS=“mp4a.40.2, avc1.4d401e" index2.m3u8 … CODECS="mp4a.40.2, avc1.4d4015"
CODECS=“mp4a.40.2, avc1.4d401e" val hlsMediaSource = HlsMediaSource.Factory(dataSourceFactory) .setAllowChunklessPreparation(true) .createMediaSource(uri)
#EXTM3U #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=200000,CODECS="mp4a.40.2, avc1.4d4015" index1.m3u8 #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=600000,CODECS=“mp4a.40.2, avc1.4d401e" index2.m3u8 … CODECS="mp4a.40.2, avc1.4d4015"
CODECS=“mp4a.40.2, avc1.4d401e" val hlsMediaSource = HlsMediaSource.Factory(dataSourceFactory) .setAllowChunklessPreparation(true) .createMediaSource(uri) .setAllowChunklessPreparation(true)
m3u8 Playlist
ts First Chunk Playlist Prepare Decoder
First Chunk Playlist Prepare Decoder Start Decodeing
Reuse Decoder
Renderer
None
Disabled Enabled Started
Disabled Enabled Started No Streams No Decoders Have Streams Might
Have Decoders Decoding
Disabled Enabled Started No Streams No Decoders Have Streams Might
Have Decoders Decoding Prepare Player
Disabled Enabled Started ~ v2.10.0 No Streams No Decoders Have
Streams Might Have Decoders Decoding Prepare Player
Disabled Enabled Started ~ v2.10.0 No Streams No Decoders Have
Streams Might Have Decoders Decoding Prepare Player
Disabled Enabled Started ~ v2.10.0
Disabled Enabled Started ~ v2.10.0 v2.10.0 ~ Disabled Enabled Started
v2.10.0 ~ Disabled Enabled Started
v2.10.0 ~ Disabled Enabled Started
v2.10.0 ~ Disabled Enabled Started Prepare Decoder Content A Content
B Content C
Tunneled Video Playback
Media Source Renderer (Video) Renderer (Audio) Media Codec (Video) Media
Codec (Audio) Audio Track Surface A/V Sync
Media Source Renderer (Video) Renderer (Audio) Media Codec (Video) Media
Codec (Audio) Audio Track Surface A/V Sync
Media Source Renderer (Video) Renderer (Audio) Media Codec (Video) Media
Codec (Audio) Audio Track Surface A/V Sync
Media Source Renderer (Video) Renderer (Audio) Media Codec (Video) Media
Codec (Audio) Audio Track Surface A/V Sync
Media Source Renderer (Video) Renderer (Audio) Media Codec (Video) Media
Codec (Audio) Audio Track Surface A/V Sync
Media Source Renderer (Video) Renderer (Audio) Media Codec (Video) Media
Codec (Audio) Audio Track Surface A/V Sync
val parameter = DefaultTrackSelector.ParametersBuilder() .setTunnelingAudioSessionId(C.generateAudioSessionIdV21(this)) .build()
val parameter = DefaultTrackSelector.ParametersBuilder() .setTunnelingAudioSessionId(C.generateAudioSessionIdV21(this)) .build() .setTunnelingAudioSessionId(C.generateAudioSessionIdV21(this))
Systrace
thread(name = "example-thread") { Trace.beginSection("do something") // do something Trace.endSection()
}
thread(name = "example-thread") { Trace.beginSection("do something") // do something Trace.endSection()
} python systrace.py --app package-name --time=10 -o ~/Downloads/example.html
None
python systrace.py --app "com.google.android.exoplayer2.demo" --time=10 -o ~/Downloads/trace.html
Load Master Playlist
Load Media Playlist
Load First Chunk
Prepare Decoder
None
Initialize Video Decoder
Initialize Audio Decoder
None
Render Video Output Buffer
Traditional Preparation Chunkless Preparation
Traditional Preparation Chunkless Preparation
Video Decoder Audio Decoder Decoder Initialization (150ms)
~ v2.10.0 (reuse decoder) v2.10.0 ~ (create recoders)
~ v2.10.0 (reuse decoder) v2.10.0 ~ (create recoders)
with tunneling without tunneling
Reference https://medium.com/google-exoplayer/faster-hls-preparation-f6611aa15ea6 https://medium.com/google-exoplayer/tunneled-video-playback-in-exoplayer-84f084a8094d https://medium.com/google-exoplayer/improved-decoder-reuse-in-exoplayer-ef4c6d99591d https://exoplayer.dev