1. 程式人生 > >Android 解碼MediaCodec 播放H264 265

Android 解碼MediaCodec 播放H264 265

package io.vec.demo.mediacodec;

import java.io.IOException;
import java.nio.ByteBuffer;

import android.app.Activity;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import
android.os.Bundle; import android.os.Environment; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; public class DecodeActivity extends Activity implements SurfaceHolder.Callback { // private static final String strVideo = // Environment.getExternalStorageDirectory() + "/test/xx.avi";
// private static final String strVideo = // Environment.getExternalStorageDirectory() + "/test/test.wmv"; // private static final String strVideo = // Environment.getExternalStorageDirectory() + // "/netease/cloudmusic/Music/Alborada Del Inka - Vientos suaves.mp3"; // private static final String strVideo =
// Environment.getExternalStorageDirectory() + // "/DCIM/Camera/VID_20161117_175920.mp4"; // private static final String strVideo = // Environment.getExternalStorageDirectory() + "/test/H264_1080.mp4"; // private static final String strVideo = // Environment.getExternalStorageDirectory() + "/test/H265_1080.mp4"; private static final String strVideo = Environment.getExternalStorageDirectory() + "/test/H265_1080.mp4"; private String TAG = "DecodeActivity"; private PlayerThread mPlayerThread = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SurfaceView sv = new SurfaceView(this); sv.getHolder().addCallback(this); setContentView(sv); } protected void onDestroy() { super.onDestroy(); } @Override public void surfaceCreated(SurfaceHolder holder) { } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { if (mPlayerThread == null) { mPlayerThread = new PlayerThread(holder.getSurface()); mPlayerThread.start(); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { if (mPlayerThread != null) { mPlayerThread.interrupt(); } } // ***************************************************************** private class PlayerThread extends Thread { private MediaExtractor mediaExtractor; /** 用來讀取音視訊檔案 提取器 */ private MediaCodec mediaCodec; /** 用來解碼 解碼器 */ private Surface surface; public PlayerThread(Surface surface) { this.surface = surface; } @Override public void run() { mediaExtractor = new MediaExtractor(); try { mediaExtractor.setDataSource(strVideo); // 設定資料來源 } catch (IOException e1) { e1.printStackTrace(); } String mimeType = null; for (int i = 0; i < mediaExtractor.getTrackCount(); i++) { // 通道總數 MediaFormat format = mediaExtractor.getTrackFormat(i); // 音訊檔案資訊 mimeType = format.getString(MediaFormat.KEY_MIME); // Video/AVC // H 264 // Video/HEVC // H265 // video/hevc if (mimeType.startsWith("video/")) { // 視訊通道 mediaExtractor.selectTrack(i); // 切換到視訊通道 mediaCodec = MediaCodec.createDecoderByType(mimeType); // 建立解碼器,提供資料輸出 mediaCodec.configure(format, surface, null, 0); break; } if (mimeType.startsWith("audio/")) { // 音訊通道 } } if (mediaCodec == null) { Log.e("DecodeActivity", "Can't find video info!"); return; } mediaCodec.start(); // 啟動MediaCodec ,等待傳入資料 ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); // 用來存放目標檔案的資料 // 輸入 ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers(); // 解碼後的資料 // 輸出 BufferInfo info = new BufferInfo(); // 用於描述解碼得到的byte[]資料的相關資訊 boolean bIsEos = false; long startMs = System.currentTimeMillis(); // ==========開始解碼============= while (!Thread.interrupted()) { if (!bIsEos) { int inIndex = mediaCodec.dequeueInputBuffer(10000); if (inIndex >= 0) { ByteBuffer buffer = inputBuffers[inIndex]; int nSampleSize = mediaExtractor.readSampleData(buffer, 0); // 讀取一幀資料至buffer中 if (nSampleSize < 0) { Log.d("DecodeActivity", "InputBuffer BUFFER_FLAG_END_OF_STREAM"); mediaCodec.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); bIsEos = true; } else { // 填資料 mediaCodec.queueInputBuffer(inIndex, 0, nSampleSize, mediaExtractor.getSampleTime(), 0); // 通知MediaDecode解碼剛剛傳入的資料 mediaExtractor.advance(); // 繼續下一取樣 } } } int outIndex = mediaCodec.dequeueOutputBuffer(info, 10000); switch (outIndex) { case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: Log.d("DecodeActivity", "INFO_OUTPUT_BUFFERS_CHANGED"); outputBuffers = mediaCodec.getOutputBuffers(); break; case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: Log.d("DecodeActivity", "New format " + mediaCodec.getOutputFormat()); break; case MediaCodec.INFO_TRY_AGAIN_LATER: Log.d("DecodeActivity", "dequeueOutputBuffer timed out!"); break; default: ByteBuffer buffer = outputBuffers[outIndex]; Log.v("DecodeActivity", "We can't use this buffer but render it due to the API limit, " + buffer); // We use a very simple clock to keep the video FPS, or the // video // playback will be too fast while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) { try { sleep(10); } catch (InterruptedException e) { e.printStackTrace(); break; } } mediaCodec.releaseOutputBuffer(outIndex, true); break; } // All decoded frames have been rendered, we can stop playing // now if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { Log.d("DecodeActivity", "OutputBuffer BUFFER_FLAG_END_OF_STREAM"); break; } } // end while mediaCodec.stop(); mediaCodec.release(); mediaExtractor.release(); } } }