MediaPlayer(總結)--從對象生命週期理解MediaPlayer狀態

爲了方便先重複貼一下MediaPlayer的狀態圖和MediaPlayer 的基本框架
mediaplayer_state_diagram.gif

MediaPlayerArch.png

總的分爲幾個模塊,爲方便後續文章的書寫,各模塊後續統一用括號裏面的名詞

  • java層MediaPlayer(MediaPlayer)
  • jni層(jni)
  • mediaplayer client端(mediaplayer)
  • MediaPlayer service端 (MediaPlayerService)
  • native mediplayer ,即播放器功能最終實現模塊,不同方案會有不同的實現(NuPlayer)

MediaPlayer的狀態變量是由mediaplayer記錄的

系統初始化

MediaPlayerService是一個native系統服務,在系統初始化階段,具體是init進程解析rc文件,並在後續初始化過程中創建的。該服務同其他服務一樣會在ServiceManager中註冊一個實名binder,這樣後續Android其他模塊就可以通過ServiceManager的getService接口來獲取MediaPlayerService的服務

Idle

應用創建MediaPlayer實例或MediaPlayer實例已經創建reset()進入個狀態。

  1. 創建播放器
  • new mediaplayer實例
  • 設置 mediaplayer --> jni --> MediaPlayer 回調listener
  1. reset()
  • 銷燬MediaPlayerService 跟mediaplayer服務端 binder通訊實例mClient
  • 將NuPlayer的notify回調設置爲0
  • 銷燬NuPlayer實例
  • 銷燬mediaplayer 跟MediaPlayerService服務端 binder通訊實例mPlayer

Initialized

執行完setDataSource()會進入 Initialized,主要做了以下幾件事情

  • 建立mediaplayer 和 MediaPlayerService binder通訊
    mediaplayer和MediaPlayerService 通訊 並不是同MediaPlayerService在ServiceManager中註冊的binder通訊的,而是通過下面兩個binder來通訊的
    IMediaPlayer (mediaplayer --> MediaPlayerService)
    IMediaPlayerClient (MediaPlayerService --> mediaplayer)
    但這兩個爲匿名binder,需要藉助實名的binder建立連接,而這個實名binder即爲MediaPlayerService在ServiceManager中註冊的服務

  • new NuPlayer實例

  • 設置 NuPlayer 回調 MediaPlayerService的回調函數notify
    notify回調函數是在createPlayer時一起作爲參數傳遞過去的。至此 NuPlayer --> MediaPlayerService notify --> mediaplayer notify的回調鏈路就建立,client的notify又會調用Idle狀態設置的listener。所以NuPlayer -> MediaPlayer的回調鏈路就建立了

在Idle以外的其他狀態調用 setDataSource() 都會拋出IllegalStateException,可以理解一個MediaPlayer實例只能有一個NuPlayer實例和對應的回調鏈路

Prepared ,Preparing

解析視頻源,demux, 創建decode,建立視頻播放管道(不同播放器會有不同的實現方式)

Started

開始播放,即音視頻流在播放通路 src -> demux -> decode -> render持續處理

Pause

暫停,即音視頻流會暫停流動

PlaybackCompleted

音視頻流播放完,可通過start()重頭開始播放。可以理解成prepare創建的播放器管道沒有銷燬,只是數據流已經處理完了。

Stoped

MediaPlayer在Started, Paused, Prepared or PlaybackCompleted這個幾個狀態下調用stop()會進到Stop狀態。
處於Stoped狀態需要重新調用prepare()或prepareAsync()才能重新開始播放。
可以理解prepare創建的播放器管道銷燬,需要重新建立才能播放

End

當release()被調用後,所有的資源會被釋放,處於End狀態。

  • 將MediaPlayer 的所有listener置爲null
  • 釋放對surface的引用
  • 將mediaplayer 回調jni的listener置爲null
  • 銷燬MediaPlayerService 跟mediaplayer服務端 binder通訊實例mClient
  • 將NuPlayer的notify回調設置爲0
  • 銷燬NuPlayer實例
  • 銷燬mediaplayer 跟MediaPlayerService服務端 binder通訊實例mPlayer
  • 銷燬mediaplayer實例

Error

由於某些原因,比如無法識別音視頻封裝格式,poorly interleaved audio/video,分辨率過高,流媒體網絡通訊超時等會導致播放操作發生錯誤,會進入Error狀態
不合理的MediaPlayer接口調用也會進入Error狀態
這是底層的播放器即NuPlayer發生了錯誤,需要重新調用reset()方法,才能重新使用,即銷燬NuPlayer,再重新創建。

SDK的文檔裏有一段

在構造函數創建後,立即調用getCurrentPosition(), getDuration(), getVideoHeight(), getVideoWidth(), setAudioAttributes(AudioAttributes), setLooping(boolean), setVolume(float, float), pause(), start(), stop(), seekTo(long, int), prepare() or prepareAsync() 等這些函數,MediaPlayer不會處於Error狀態,不會拋出error的消息。而在reset()之後再調用則會使MediaPlayer轉化爲Error狀態,並拋出異常消息

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章