Android平台對H264視頻硬解碼


  本文講述如何使用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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM