本文講述如何使用Android標准的API (MediaCodec)實現H264的硬件解碼。
原本我們是用JNI調用平台提供的硬件解碼接口得到YUV幀,再放入opengl腳本里處理渲染的。可是換了新平台之后,沒有拿到底層的接口,所以這兩天找在Android上的H264解碼方案。前天在友人的提示下找到了MediaCodec這個類,Android developer上面有MediaCodec的描述和用法,還算詳細可以慢慢摸索。但是在網上關於這個類的用法是比較少。
那在這里貼代碼介紹一下。
1 // Video Constants 2 private final static String MIME_TYPE = "video/avc"; // H.264 Advanced Video 3 private final static int VIDEO_WIDTH = 1280; 4 private final static int VIDEO_HEIGHT = 720; 5 private final static int TIME_INTERNAL = 30; 6 7 public void initDecoder() { 8 9 mCodec = MediaCodec.createDecoderByType(MIME_TYPE); 10 MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE, 11 VIDEO_WIDTH, VIDEO_HEIGHT); 12 mCodec.configure(mediaFormat, mSurfaceView.getHolder().getSurface(), 13 null, 0); 14 mCodec.start(); 15 }
這是初始化解碼器操作,具體要設置解碼類型,高度,寬度,還有一個用於顯示視頻的surface。
1 public boolean onFrame(byte[] buf, int offset, int length) { 2 Log.e("Media", "onFrame start"); 3 Log.e("Media", "onFrame Thread:" + Thread.currentThread().getId()); 4 // Get input buffer index 5 ByteBuffer[] inputBuffers = mCodec.getInputBuffers(); 6 int inputBufferIndex = mCodec.dequeueInputBuffer(100); 7 8 Log.e("Media", "onFrame index:" + inputBufferIndex); 9 if (inputBufferIndex >= 0) { 10 ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; 11 inputBuffer.clear(); 12 inputBuffer.put(buf, offset, length); 13 mCodec.queueInputBuffer(inputBufferIndex, 0, length, mCount 14 * TIME_INTERNAL, 0); 15 mCount++; 16 } else { 17 return false; 18 } 19 20 // Get output buffer index 21 MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); 22 int outputBufferIndex = mCodec.dequeueOutputBuffer(bufferInfo, 100); 23 while (outputBufferIndex >= 0) { 24 mCodec.releaseOutputBuffer(outputBufferIndex, true); 25 outputBufferIndex = mCodec.dequeueOutputBuffer(bufferInfo, 0); 26 } 27 Log.e("Media", "onFrame end"); 28 return true; 29 }
具體流程:
1.獲取一個可用的inputBuffer的索引(出列)
2.將一幀數據放入inputBuffer
3.將inputBuffer入列進行解碼
4.獲得一個outputBuffer的索引(出列)
5.釋放outputBuffer
6.在4,5間循環直到沒有outputBuffer可出列為止
這里解碼器有多個輸入緩沖區(我測試是有3個),實現不卡頓。
常見的問題:
dequeueInputBuffer和dequeueOutputBuffer經常會獲取不了緩沖區(跟機器的性能有關),如果參數為-1,則會一直等待;如果參數為正數,則等待相應的微秒后返回,沒有可用緩沖區就會返回-1。
結論:
測試程序是讀取一個H264裸流文件,識別每一幀然后將其放入解碼器解碼以及渲染。讀取和識別的代碼在Demo中。經測試,不同平台的解碼效果還挺大。我播放的是720p的H264文件。最出色的居然是RK(瑞芯微)平台,緩沖區獲取很少失敗,但是會因為過熱重啟機器;高通平台試了幾台機器(錘子、小米4、還有個別定制機),經常有拿不到緩沖區的情況。MTK的只有一台(TCL么么噠),直接初始化不了解碼器。。。
這里跟GPU性能有關,我們解的是720p8m碼率的視頻,換成較低碼率較低分辨率的,大部分機器還是可以正常運行的(除了么么噠)。當然或許有些平台的實現是軟解碼(使用cpu,ffmpeg方案),這我暫時還沒遇到。
Demo鏈接如下(注意裸流文件要放置的路徑):
http://files.cnblogs.com/files/superping/H264Demo.zip