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
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
MIERUNE JCT 発表資料「宇宙から伊能忠敬ごっこ」
syuchimu
0
210
AIはどのように 組織のアジリティを変えるのか?
junki
1
500
「エンジニア進化論」2028年の開発完全自動化、エンジニアはどう進化するか
cyberagentdevelopers
PRO
6
4.6k
失敗を資産に変えるClaude Code
shinyasaita
0
540
AI-DLCを活用した高品質・安全なAI駆動開発実践 / AI Driven Development with AI-DLC
yoshidashingo
0
170
2026TECHFRESH畢業分享會 - 原生還是跨平台? App 開發踩坑實錄
line_developers_tw
PRO
0
880
作って終わりにしない タイミーのセマンティックレイヤー育成の現在地
chanyou0311
4
2.2k
なぜ Platform Engineering の土台に Kubernetes を選ぶのか
r4ynode
2
590
攻撃者視点で考えるDetection Engineering
cryptopeg
2
1.3k
NAB Show 2026 動画技術関連レポート / NAB Show 2026 Report
cyberagentdevelopers
PRO
0
170
2026TECHFRESH畢業分享會 - AI 時代的人生存檔點
line_developers_tw
PRO
0
870
FinOps × AIエージェントで実現する コストインシデントの自動調査
oasis1994liveforever
0
130
Featured
See All Featured
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
390
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
840
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.7k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
310
Ethics towards AI in product and experience design
skipperchong
2
310
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.5k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.9k
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
4k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
Building Applications with DynamoDB
mza
96
7.1k
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