視頻渲染用什么顯卡,Android視頻解碼及渲染
視頻渲染用什么顯卡,Android視頻解碼及渲染
今天給大家說說在android
上如何做視頻解碼及渲染。 視頻解碼有多種方法,今天給大家介紹的是用android
自帶的MediaCodec
進行硬解碼,所謂硬解碼就是利用硬件進行解碼,速度快,與之相對就是軟解碼,速度慢,但兼容性好。 MediaCodec
視頻解碼是基于生產者/消費者模式,里面會有一些buffer
,需要解碼一幀時從里面拿出一個buffer
,給buffer
填充好數據,然后再送進去解碼,然后再拿出來解碼好的buffer
,用完之后再還回去,如下圖所示:
下面我們來看看如何一步步實現視頻硬解碼及渲染:
1. 創建一塊surface
這個surface
的作用是讓MediaCodec
解碼到上面,如果你是用SurfaceView
,那么它自帶了一個surface
,直接解碼到上面就會自動顯示出來,本文中因為還涉及到渲染,所以我是解碼到一個自己創建的surface
上,這個surface
又是通過surface texture
創建的,而surface texture
又是通過一個oes texture
創建的,所以最終會解碼到一個紋理上,接下來就可以用OpenGL
進行渲染處理。
2. 初始化MediaExtractor及MediaCodec
MediaExtractor
的作用是從視頻文件中提取數據,前面說的給buffer
填充的數據就來源于此,初始化工作主要是給它設置視頻文件路徑,以及選擇軌道,本文講解的是視頻解碼,因為只關心視頻軌道,聲音就不管了。
mediaExtractor = MediaExtractor()
mediaExtractor.setDataSource(filePath)
val trackCount = mediaExtractor.getTrackCount()
for (i in 0 until trackCount) {val trackFormat = mediaExtractor.getTrackFormat(i)val mime = trackFormat.getString(MediaFormat.KEY_MIME)if (mime.contains("video")) {videoTrackIndex = ibreak}
}
if (videoTrackIndex == -1) {mediaExtractor.release()return
}
mediaExtractor.selectTrack(videoTrackIndex)
視頻渲染用什么顯卡。然后是初始化MediaCodec
,可以看到我們會向它傳遞一個surface
,初始化好之后,就讓它開始工作:
mediaCodec = MediaCodec.createDecoderByType(videoMime)
mediaCodec.configure(videoFormat, surface, null, 0)
mediaCodec.start()
3. 讀取數據并解碼
這一步稍微復雜些,前面提到MediaCodec
視頻解碼是基于生產者/消費者模式,我們首先通過dequeueInputBuffer
向它去要一個buffer
用于承載要解碼的數據,可以指定超時時間,因為里面不一定有空閑buffer
了:
val inputBufferIndex = mediaCodec.dequeueInputBuffer(10000)
if (inputBufferIndex >= 0) {val buffer = mediaCodec.getInputBuffers()[inputBufferIndex]
}
然后從視頻文件中讀取數據,如果讀取到的數據長度小于0,說明已經讀完了,此時給buffer
置一個標記BUFFER_FLAG_END_OF_STREAM
,否則就是讀到了數據填充到了剛剛拿到的buffer
,此時再將這個buffer
通過queueInputBuffer
送回MediaCodec
,并讓MediaExtractor
的讀取位置往前走:
val sampleSize = mediaExtractor.readSampleData(buffer, 0)
if (sampleSize < 0) {mediaCodec.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM)eos = true
} else {mediaCodec.queueInputBuffer(inputBufferIndex, 0, sampleSize, mediaExtractor.sampleTime, 0)mediaExtractor.advance()
}
接下來就是用dequeueOutputBuffer
獲取解碼的結果,同樣也可以設置超時間,如果獲取到的buffer
有BUFFER_FLAG_END_OF_STREAM
標記,那說明解碼全部完成了:
val outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 10000)
if ((bufferInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {return false
}
對于從dequeueOutputBuffer
獲取到的結果,有一些是未解碼好的情況,對于解碼好了的情況,就通過releaseOutputBuffer
將buffer
歸還回去:
when (outputBufferIndex) {MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED, MediaCodec.INFO_OUTPUT_FORMAT_CHANGED, MediaCodec.INFO_TRY_AGAIN_LATER -> {}else -> {mediaCodec.releaseOutputBuffer(outputBufferIndex, true)return true}
}
releaseOutputBuffer
第二個參數如果傳true
,就表示會渲染到surface
上,此時用于構造這個surface
的surface texture
就會收到onFrameAvailable()
回調,這樣我們就知道一幀解碼好了,這時調用surface texture
的updateTexImage()
方法將解碼數據更新到texture
上,有了這個texture
,就可以用OpenGL
做渲染了,渲染方法和之前的OpenGL
教程里是一樣的,使用完了記得將MediaCodec
停止及釋放相關資源。
最后
android opencv,如果你看到了這里,覺得文章寫得不錯就給個贊唄!歡迎大家評論討論!如果你覺得那里值得改進的,請給我留言。一定會認真查詢,修正不足,定期免費分享技術干貨。謝謝!