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
2.1k
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.2k
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
650
Other Decks in Technology
See All in Technology
マイナンバーカード本人確認の実装比較(OAuth/OIDC Numa (Immersion) Workshop 2026) / 20260825 numa-12
oidfj
PRO
0
360
「面白い!」を信じ抜け。激動の時代を貫く、オンリーワン・エンジニアの条件
kizawa2020
2
450
Eight Engineering Unit 紹介資料
sansan33
PRO
3
8.3k
あなたの知らないバージョン命名規則
sat
PRO
2
870
パスキーでドライブする アカウント統合(OAuth/OIDC Numa (Immersion) Workshop 2026)
oidfj
PRO
0
390
Kiro Crew で始める マルチエージェント開発
miu_crescent
PRO
0
210
わたしが知り合いゼロの勉強会に 行けるようになるまで
r5ni4
2
840
現場の暗黙知を継承するAIエージェント — 対話から生まれる長期記憶と Skills
atsukish
0
170
AIレビュー時代に必要なのは、SLOで引く撤退ライン
nobuoooo
0
150
どんな手を使っても絶対間に合わせるスケジューラ
asari194617
0
1.5k
最新技術に積極チャレンジ!EKS共通基盤のこれまでとこれから
daitak
0
260
会計事務所と顧問先の契約関係をOIDC・OAuthで表現する
terara
0
520
Featured
See All Featured
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
260
AI Search: Where Are We & What Can We Do About It?
aleyda
0
7.8k
Google's AI Overviews - The New Search
badams
0
1.6k
Exploring anti-patterns in Rails
aemeredith
3
480
Testing 201, or: Great Expectations
jmmastey
46
8.3k
YesSQL, Process and Tooling at Scale
rocio
174
15k
The Invisible Side of Design
smashingmag
301
52k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.4k
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
6
1.2k
The Power of CSS Pseudo Elements
geoffreycrofte
82
6.5k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6.1k
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
68
57k
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