專案記錄: Dynamic playlists with ExoPlayer
專案記錄: Dynamic playlists with ExoPlayer
翻譯一下 Dynamic playlists with ExoPlayer 2017.08.25
介紹
現在支援 Exoplayer media playlists
. 使用者可以在播放器執行的過程中,任意新增和移除 playlist item
.
從 Exoplayer 2.8.0 版本,通過更新 ConcatenatingMediaSource
與 dynamic playlist
的功能. 新的媒體資源可以通過以下簡單和直接的介面:
-
addMediaSource(mediaSource)
-
addMediaSource(index,mediaSource)
: 在 index 位置插入一個新的媒體資源; -
addMediaSource(Collection<MediaSource>)
: 新增一整個媒體資源到 playlist 的末尾; -
addMediaSources(index, Collection<MediaSource>)
: 在 index 位置插入一整個媒體資源; -
removeMediaSource(index)
: 溢位 index 位置的 媒體資源; -
moveMediaSource(fromIndex, toIndex)
: 將 fromindex位置的媒體資源移動至 toindex 位置處;可以不需要再建立新的
MediaSource
. 而且可以不打亂播放,移動當前正在播放的item
; -
getMediaSource(index)
: 獲得index位置的媒體資源 -
getSize()
: 返回當前 playlist 的長度
以上的方法,可以在播放器播放之前或者播放開始之後呼叫,而且無論哪個 item
正在播放.這些方法都是 thread-safe
執行緒安全的;
可以通過預先快取下一個 playlist
gapless playback
無間隔播放.
而且, 在播放到一個新的元素,將會收到 eventListener.onPositionDiscontinuity
的通知.
如果你想要開始使用這個 playlist
的新功能,就去下載 新的 release
版本的ExoPlayer,開始碼程式碼吧.
如果你對實現細節感興趣,可以繼續看下去.
實現細節
為了理解 支援 dynamiclly changing media source
其中涉及到的困難.
先一看 How ExoPlayer works with media sources internally
,Exoplayer和MediaSource在內部是如何一起工作的. 其中,涉及到5個類和5個執行緒:
APP
: 執行在app thread
. 這個執行緒呼叫了 如prepare media source
的命令來呼叫player
Player
: 建立一個例項,在PlayBack thread
播放執行緒中.Media Source
: 在app thread
中建立,是接觸到真正的媒體資源的. 在loader threads
載入資源,而且與playback thread
的player
通訊.timeline
:timelines
是一成不變的? 可以在所有執行緒訪問.media period
: 負責buffering
(快取資料)和playback of the media data
(播放媒體資料). 在playback thread
中被media source
建立, 但是真正載入資料的呢:是在loader thread
;
在這樣的多執行緒環境下,一個很中藥的問題是: 多個執行緒可能在時間上會出現 不同的 player state
(播放器狀態). 因此, 當一個命令在一個執行緒釋出而在另一個執行緒處理,第二個執行緒必須必須處在和第一個執行緒釋出命令的同一狀態,二者要保持一致這是必須的.
當實現 dynamic playlist change into the process
會帶來以下兩個:
- 立即反應所有執行緒的狀態變化 和 2) 等待一個非同步的過程恰當地結束 是兩個競爭的期望. 會造成以下三個難題:
-
Lazily updating the master playlist on the playback thread while having a instantly updated playlist on the app thread
當
app thread
更新了一個playlist
,playback thread
更新playlist
延時了.(懶洋洋Lazily) -
Lazily waiting for new timeline information while keeping the timeline consistent with the playlist.
緩慢地等待新的 timeline資訊 當 和 playlist 保持一致
-
Being able to create new periods based on the playlist while waiting for the lazy timeline information
根據 playlist 建立新的
period
當等待 timeline緩慢更新
總的來說,解決方法是非常相似的: 我們使用 mocking instances of the objects we are waiting for
(模擬的等待物件,相等於建立了臨時的假的?)
這些 mocking instances
被立即建立,來儘可能地欺騙那些延遲操作. 因此呢. 整個系統的狀態會得到迅速地更新,防止進入模糊不清的情況. 一旦真實的結果變得可用,這些 mocking instances
將會停止模擬,而是開始正式地呼叫真正的例項化物件.
Update the playlist
當 playback thread
執行緒的master
playlist 作為我們想要實現所有操作的 playlist
. 會有兩個缺陷:
- 我們希望能夠隨時改變播放列表(即在
playback thread
這個執行緒起之前) - 我們希望視覺化這播放列表的資訊來立即反應所有我們做的變化.
因此,我們將保持第二個(mocked) playlist
(這個是在 app thread
執行緒上的),這個將會立即得到更新,而且它可以用來查詢 playlist
的資訊. 一旦 master playlist
是可用了,所有作應用在 這個 mocked playlist
的操作將會被使用在 playback thread
執行緒上的 playlist
來進行更新.
keeping the timeline in syn with playlist
無論 playlist 何時變化,我們需要有一個新的 timeline 去反應新的 media structure
. 有些時候,我們需要載入新新增的 media
的開始部分獲取資訊來更新 timeline. 但是呢,timeline立即反應所有 更新到playlist的所有變化是非常重要的.
- 例如, 如果 app 添加了一個新的 source 在 index為 i 的位置, 立即 seek 到這個i位置,期望的結果是這個新的source將會被播放.如果player沒有感知到這些變化的話(由於它還在就的時間資訊上),那麼它將 seek到 另一個
item
上,而非我們剛剛新增的. 這就是為什麼立即更新 timeline是非常關鍵的.
為了解決這個問題, 當 master playlist變化時,我們立即觸發 a timeline update
, 用的是 a new mocked timeline element
. 這個 mocked timeline element
是未知的時長, 它被標誌位 dynamic
去告訴播放器,它應該期待發生在它屬性上的變化. 儘管這個資訊不是非常有幫助,但是,至少它保證了 timeline
和 playlist 保持一致. 當 playlist element
準備好了, 這個 mocked
的 timeline element 將會返回真實的媒體屬性.
Creating periods while waiting for timeline updates
第三個問題是由於第二個問題的解決方案造成的.
想想一下我們立即想要播放這個新插入的 playlist item. 播放器會叫 media source建立一個新的 media period. 但是呢,meidia source 無法完成需求,仍然在等待一個真正的 media information
相同地,我們借用了一個 mocked media period
去為 mocked timeline
建立 一個 media period
. 由於沒有真實的 media, 這個 media period 將會用於保持在 緩衝模式,告訴播放器媒體還沒有做不好播放.一旦呢,timeline獲得更新真的media information ,這個 mocked media period
將建立真的media period, 推進呼叫那包裝的例項.
總結:
- 這個部落格描述了 新的
ConcatenatingMediaSource
特性來新增和移除 播放器上的元素.