Android 硬解前后臺切換黑屏
首先遇到這個問題的是項目開發(fā)Android 定制播放器UI的時候,需要列表顯示視頻且需要小窗口切換到全屏。遇到了小窗口到全屏的時候有黑屏一段時間,且時間不定。有時候飛快有時候很慢。其實這個問題和硬解前后臺切換黑屏問題是同一個問題。
我們知道Android 進行視頻渲染的View 有2個,一個是SurfaceView 另一個是TextureView。至于它們之間的區(qū)別這里不再進行描述。請查看SurfaceView文檔 。
首先這個問題應(yīng)當(dāng)如何進行描述?其實就是正常播放視頻的過程中從一個舊的切換到了新的SurfaceView 或者TextureView。然后導(dǎo)致這個現(xiàn)象。
當(dāng)我遇到這個問題的時候,第一反應(yīng)肯定是ijkplayer 硬解的時候切換Surface 的時候清空了硬件解碼器中的緩存幀導(dǎo)致新的視頻幀送到硬件解碼器中無法解碼。需要等待下一個GOP IDR 幀序列的到來,才能正常解碼。
ijkplayer的硬解實現(xiàn)是在MediaCodec configure 的時候可以設(shè)置一個Surface 這樣硬解后圖像不需要內(nèi)存拷貝可以直接渲染到Surface 上面,這樣性能更加優(yōu)秀。當(dāng)然也可以configure 的時候不設(shè)置Surface 這樣可以直接拿到解碼后的數(shù)據(jù),然后自己用OpenGL ES 渲染。
資料參考:MediaCodec 文檔。
??經(jīng)過閱讀ijkplayer硬解實現(xiàn)的過程,因為Android MediaCodec 不支持動態(tài)設(shè)置Surface,ijkplayer 硬解實現(xiàn)是直接每當(dāng)設(shè)置display 不是同一個Surface 的時候直接重新創(chuàng)建了新的MediaCodec.
可以直接查看下來的函數(shù)實現(xiàn):
static int reconfigure_codec_l(JNIEnv *env, IJKFF_Pipenode *node, jobject new_surface)
ijkplayer 硬解實現(xiàn)相關(guān)代碼
然本篇并非講解[MediaCodec]相關(guān)文章(https://developer.android.com/reference/android/media/MediaCodec.html),所以這里就僅僅簡單的說明一下。
??Android 6.0 更新了新的API 可以支持直接設(shè)置Surface。
void [setOutputSurface]((https://developer.android.com/reference/android/view/Surface.html) surface)(Surface)
不過并不支持先設(shè)置NULL 然后再設(shè)置一個Surface.這樣前后臺切換的問題應(yīng)該是暫不能解決。
原因已經(jīng)查找到了,那該如何解決這個問題呢?首先 SurfaceView 無法用于解決這個問題,前后臺切換的時候會有銷毀創(chuàng)建的過程。上面提到Android 上顯示視頻還有一個是用TextureView, 然后自然想到了會創(chuàng)建一個SurfaceTexture 。我們可以用SurfaceTexture創(chuàng)建一個Surface ,然后MediaCodec configure 時候用這Surface。構(gòu)造函數(shù)如下:
Surface(android.graphics.SurfaceTexture))([SurfaceTexture]surfaceTexture)
下面這個2個事件大家應(yīng)該很熟悉:
[onSurfaceTextureAvailable](https://developer.android.com/reference/android/view/TextureView.SurfaceTextureListener.html#onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int))(SurfaceTexture surface, int width, int height)
boolean onSurfaceTextureDestroyed (SurfaceTexture surface)
然后怎么操作呢?當(dāng)我們進入后臺的時候 onSurfaceTextureDestroyed 事件設(shè)置 return fale 并保存 SurfaceTexture,這樣將手動接管SurfaceTexture 生命周期。然后當(dāng)重現(xiàn)創(chuàng)建TextureView的時候調(diào)用成員函數(shù):
void setSurfaceTexture (SurfaceTexture surfaceTexture)
這樣就解決了有關(guān)MediaCodec 設(shè)置Surface 的問題了。解決的方案是不切換。
其實ijkplayer 官方的demo 中有相關(guān)示例代碼。不過很多人不了解其中的緣由而已。