Android使用MediaCodec硬解码播放H264格式视频文件

来源:互联网 发布:假短信软件 编辑:程序博客网 时间:2024/05/17 01:37
  前些时间,通过各种搜索加请教了好几个同行的朋友,在他们的指点下实现:  RTSP+H264实时视频播放播放及把实时视频流保存到手机SD卡中,再对保存的H264格式文件进行播放等基本功能。  非常感谢这些朋友的无私帮忙,在实现功能的同时,我也把他们提供的一些代码加自己的修改简单记录下来,希望能给有需要的朋友一点点帮助。  这篇博客就是简单记录用MediaCodec +SurfaceView解码播放本地H264文件。MediaCodec硬解码实现RTSP+H264实时视频播放完整功能可以参考:https://github.com/ldm520/ANDROID_MEDIACODEC_RTSP_H264
public class ParseH264FileActivity extends Activity {    private SurfaceView mSurface = null;    private SurfaceHolder mSurfaceHolder;    private Thread mDecodeThread;    private MediaCodec mCodec;    private boolean mStopFlag = false;    private DataInputStream mInputStream;    private String FileName = "test.h264";    private int Video_Width = 1920;    private int Video_Height = 1080;    private int FrameRate = 15;    private Boolean isUsePpsAndSps = false;    private String filePath = Environment.getExternalStorageDirectory() + "/" + FileName;    private Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            Toast.makeText(ParseH264FileActivity.this, "播放结束!", Toast.LENGTH_LONG).show();        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        //保持屏幕常亮        getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);        setContentView(R.layout.activity_main);        File f = new File(filePath);        if (null == f || !f.exists() || f.length() == 0) {            Toast.makeText(this, "指定文件不存在", Toast.LENGTH_LONG).show();            return;        }        try {            //获取文件输入流            mInputStream = new DataInputStream(new FileInputStream(new File(filePath)));        } catch (FileNotFoundException e) {            e.printStackTrace();        }        mSurface = (SurfaceView) findViewById(R.id.preview);        mSurfaceHolder = mSurface.getHolder();        mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {            @Override            public void surfaceCreated(SurfaceHolder holder) {                try                {                    //通过多媒体格式名创建一个可用的解码器                    mCodec = MediaCodec.createDecoderByType("video/avc");                } catch (IOException e) {                    e.printStackTrace();                }                //初始化编码器                final MediaFormat mediaformat = MediaFormat.createVideoFormat("video/avc", Video_Width, Video_Height);                //获取h264中的pps及sps数据                if (isUsePpsAndSps) {                    byte[] header_sps = {0, 0, 0, 1, 103, 66, 0, 42, (byte) 149, (byte) 168, 30, 0, (byte) 137, (byte) 249, 102, (byte) 224, 32, 32, 32, 64};                    byte[] header_pps = {0, 0, 0, 1, 104, (byte) 206, 60, (byte) 128, 0, 0, 0, 1, 6, (byte) 229, 1, (byte) 151, (byte) 128};                    mediaformat.setByteBuffer("csd-0", ByteBuffer.wrap(header_sps));                    mediaformat.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));                }                //设置帧率                mediaformat.setInteger(MediaFormat.KEY_FRAME_RATE, FrameRate);                //https://developer.android.com/reference/android/media/MediaFormat.html#KEY_MAX_INPUT_SIZE                //设置配置参数,参数介绍 :                // format   如果为解码器,此处表示输入数据的格式;如果为编码器,此处表示输出数据的格式。                //surface   指定一个surface,可用作decode的输出渲染。                //crypto    如果需要给媒体数据加密,此处指定一个crypto类.                //   flags  如果正在配置的对象是用作编码器,此处加上CONFIGURE_FLAG_ENCODE 标签。                mCodec.configure(mediaformat, holder.getSurface(), null, 0);                startDecodingThread();            }            @Override            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {            }            @Override            public void surfaceDestroyed(SurfaceHolder holder) {            }        });    }    private void startDecodingThread() {        mCodec.start();        mDecodeThread = new Thread(new decodeH264Thread());        mDecodeThread.start();    }    /**     * @author ldm     * @description 解码线程     * @time 2016/12/19 16:36     */    private class decodeH264Thread implements Runnable {        @Override        public void run() {            try {                decodeLoop();            } catch (Exception e) {            }        }        private void decodeLoop() {            //存放目标文件的数据            ByteBuffer[] inputBuffers = mCodec.getInputBuffers();            //解码后的数据,包含每一个buffer的元数据信息,例如偏差,在相关解码器中有效的数据大小            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();            long startMs = System.currentTimeMillis();            long timeoutUs = 10000;            byte[] marker0 = new byte[]{0, 0, 0, 1};            byte[] dummyFrame = new byte[]{0x00, 0x00, 0x01, 0x20};            byte[] streamBuffer = null;            try {                streamBuffer = getBytes(mInputStream);            } catch (IOException e) {                e.printStackTrace();            }            int bytes_cnt = 0;            while (mStopFlag == false) {                bytes_cnt = streamBuffer.length;                if (bytes_cnt == 0) {                    streamBuffer = dummyFrame;                }                int startIndex = 0;                int remaining = bytes_cnt;                while (true) {                    if (remaining == 0 || startIndex >= remaining) {                        break;                    }                    int nextFrameStart = KMPMatch(marker0, streamBuffer, startIndex + 2, remaining);                    if (nextFrameStart == -1) {                        nextFrameStart = remaining;                    } else {                    }                    int inIndex = mCodec.dequeueInputBuffer(timeoutUs);                    if (inIndex >= 0) {                        ByteBuffer byteBuffer = inputBuffers[inIndex];                        byteBuffer.clear();                        byteBuffer.put(streamBuffer, startIndex, nextFrameStart - startIndex);                        //在给指定Index的inputbuffer[]填充数据后,调用这个函数把数据传给解码器                        mCodec.queueInputBuffer(inIndex, 0, nextFrameStart - startIndex, 0, 0);                        startIndex = nextFrameStart;                    } else {                        Log.e(Constant.LOG_TAG, "aaaaa");                        continue;                    }                    int outIndex = mCodec.dequeueOutputBuffer(info, timeoutUs);                    if (outIndex >= 0) {                        //帧控制是不在这种情况下工作,因为没有PTS H264是可用的                        while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {                            try {                                Thread.sleep(100);                            } catch (InterruptedException e) {                                e.printStackTrace();                            }                        }                        boolean doRender = (info.size != 0);                        //对outputbuffer的处理完后,调用这个函数把buffer重新返回给codec类。                        mCodec.releaseOutputBuffer(outIndex, doRender);                    } else {                        Log.e(Constant.LOG_TAG, "bbbb");                    }                }                mStopFlag = true;                mHandler.sendEmptyMessage(0);            }        }    }    public static byte[] getBytes(InputStream is) throws IOException {        int len;        int size = 1024;        byte[] buf;        if (is instanceof ByteArrayInputStream) {            size = is.available();            buf = new byte[size];            len = is.read(buf, 0, size);        } else {//            BufferedOutputStream bos=new BufferedOutputStream(new ByteArrayOutputStream());            ByteArrayOutputStream bos = new ByteArrayOutputStream();            buf = new byte[size];            while ((len = is.read(buf, 0, size)) != -1)                bos.write(buf, 0, len);            buf = bos.toByteArray();        }        Log.e(Constant.LOG_TAG, "bbbb");        return buf;    }    private int KMPMatch(byte[] pattern, byte[] bytes, int start, int remain) {        try {            Thread.sleep(30);        } catch (InterruptedException e) {            e.printStackTrace();        }        int[] lsp = computeLspTable(pattern);        int j = 0;  // Number of chars matched in pattern        for (int i = start; i < remain; i++) {            while (j > 0 && bytes[i] != pattern[j]) {                // Fall back in the pattern                j = lsp[j - 1];  // Strictly decreasing            }            if (bytes[i] == pattern[j]) {                // Next char matched, increment position                j++;                if (j == pattern.length)                    return i - (j - 1);            }        }        return -1;  // Not found    }    private int[] computeLspTable(byte[] pattern) {        int[] lsp = new int[pattern.length];        lsp[0] = 0;  // Base case        for (int i = 1; i < pattern.length; i++) {            // Start by assuming we're extending the previous LSP            int j = lsp[i - 1];            while (j > 0 && pattern[i] != pattern[j])                j = lsp[j - 1];            if (pattern[i] == pattern[j])                j++;            lsp[i] = j;        }        return lsp;    }}

MediaCodec硬解码实现RTSP+H264实时视频播放完整功能可以参考:
https://github.com/ldm520/ANDROID_MEDIACODEC_RTSP_H264

1 0
原创粉丝点击