Android使用MediaCodec解码H264视频解码器

本文介绍了一个用于实时解码H264格式视频流的Android应用案例。通过MediaCodec类实现硬解码,并利用SurfaceView展示解码后的视频帧。代码示例展示了如何初始化解码器、配置输入输出缓冲区以及处理解码过程。
摘要由CSDN通过智能技术生成
前些日子有写了一篇博客[Android使用MediaCodec硬解码播放H264格式视频文件](http://blog.csdn.net/true100/article/details/53992939)。
 有些朋友加我好友让告诉下解码的相关代码,我今天就贴出来了。今天是农历2016年上班最后一天,明年继续写博客,
 和朋友们一起学习,一起进步。提前祝所有朋友春节愉快!
public class RtspDecoder {
    //处理音视频的编解码的类MediaCodec
    private MediaCodec video_decoder;
    //显示画面的Surface
    private Surface surface;
    // 0: live, 1: playback, 2: local file
    private int state = 0;
    //视频数据
    private BlockingQueue<byte[]> video_data_Queue = new ArrayBlockingQueue<byte[]>(10000);
    //音频数据
    private BlockingQueue<byte[]> audio_data_Queue = new ArrayBlockingQueue<byte[]>(10000);

    private boolean isReady = false;
    private int fps = 0;

    private ByteBuffer[] inputBuffers;
    private ByteBuffer[] outputBuffers;
    private MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
    private int frameCount = 0;
    private long deltaTime = 0;
    private long counterTime = System.currentTimeMillis();
    private boolean isRuning = false;

    public RtspDecoder(Surface surface, int playerState) {
        this.surface = surface;
        this.state = playerState;

    }

    public void stopRunning() {
        video_data_Queue.clear();
        audio_data_Queue.clear();
    }

    //添加视频数据
    public void setVideoData(byte[] data) {

        try {
            video_data_Queue.put(data);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //添加音频数据
    public void setAudioData(byte[] data) {
        try {
            audio_data_Queue.put(data);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public int getFPS() {
        return fps;
    }


    public void initial(byte[] sps) throws IOException {
        MediaFormat format = null;
        boolean isVGA = true;
        //使用sps数据格式判断是否是VGA
        byte[] video_sps = {0, 0, 0, 1, 103, 100, 64, 41, -84, 44, -88, 10, 2, -1, -107};
        for (int i = 0; i < sps.length; i++) {
            if (video_sps[i] != sps[i]) {
                //判断是否是VGA视频传输标准
                isVGA = false;
                break;
            }
        }
        if (isVGA) {
            format = MediaFormat.createVideoFormat("video/avc", 1920, 1088);
            byte[] header_pps = {0, 0, 0, 1, 104, -18, 56, -128};
            format.setByteBuffer("csd-0", ByteBuffer.wrap(video_sps));
            format.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
            format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 640 * 360);
        } else {
            format = MediaFormat.createVideoFormat("video/avc", 1920, 1088);
            byte[] header_sps = {0, 0, 0, 1, 103, 100, 64, 41, -84, 44, -88, 5, 0, 91, -112};
            byte[] header_pps = {0, 0, 0, 1, 104, -18, 56, -128};
            format.setByteBuffer("csd-0", ByteBuffer.wrap(header_sps));
            format.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
            //		format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1280 * 720);
        }
        if (video_decoder != null) {
            video_decoder.stop();
            video_decoder.release();
            video_decoder = null;
        }
        video_decoder = MediaCodec.createDecoderByType("video/avc");
        if (video_decoder == null) {
            return;
        }

        video_decoder.configure(format, surface, null, 0);
        video_decoder.start();
        inputBuffers = video_decoder.getInputBuffers();
        outputBuffers = video_decoder.getOutputBuffers();
        frameCount = 0;
        deltaTime = 0;
        isRuning = true;
        runDecodeVideoThread();
    }

    /**
     * @description 解码视频流数据
     * @author ldm
     * @time 2016/12/20
     */
    private void runDecodeVideoThread() {

        Thread t = new Thread() {

            @SuppressLint("NewApi")
            public void run() {

                while (isRuning) {

                    int inIndex = -1;
                    try {
                        inIndex = video_decoder.dequeueInputBuffer(-1);
                    } catch (Exception e) {
                        return;
                    }
                    try {

                        if (inIndex >= 0) {
                            ByteBuffer buffer = inputBuffers[inIndex];
                            buffer.clear();

                            if (!video_data_Queue.isEmpty()) {
                                byte[] data;
                                data = video_data_Queue.take();
                                buffer.put(data);
                                if (state == 0) {
                                    video_decoder.queueInputBuffer(inIndex, 0, data.length, 66, 0);
                                } else {
                                    video_decoder.queueInputBuffer(inIndex, 0, data.length, 33, 0);
                                }
                            } else {
                                if (state == 0) {
                                    video_decoder.queueInputBuffer(inIndex, 0, 0, 66, 0);
                                } else {
                                    video_decoder.queueInputBuffer(inIndex, 0, 0, 33, 0);
                                }
                            }
                        } else {
                            video_decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                        }

                        int outIndex = video_decoder.dequeueOutputBuffer(info, 0);
                        switch (outIndex) {
                            case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                                outputBuffers = video_decoder.getOutputBuffers();
                                break;
                            case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                                isReady = true;
                                break;
                            case MediaCodec.INFO_TRY_AGAIN_LATER:
                                break;
                            default:

                                video_decoder.releaseOutputBuffer(outIndex, true);
                                frameCount++;
                                deltaTime = System.currentTimeMillis() - counterTime;
                                if (deltaTime > 1000) {
                                    fps = (int) (((float) frameCount / (float) deltaTime) * 1000);
                                    counterTime = System.currentTimeMillis();
                                    frameCount = 0;
                                }
                                break;
                        }

                        //所有流数据解码完成,可以进行关闭等操作
                        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                            Log.e(Constant.LOG_TAG, "BUFFER_FLAG_END_OF_STREAM");
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }

            }
        };

        t.start();
    }
}

---------在Activity中使用--------------------

private RtspDecoder mPlayer = null;
......
mPlayer = new RtspDecoder(holder.getSurface(), 0);
........
//收到byte[] video格式H264实时流后,调用
 private void onReceiveVideoData(byte[] video) {
            //0x67是SPS的NAL头,0x68是PPS的NAL头
            if (video[4] == 0x67) {
                byte[] tmp = new byte[15];
                //把video中索引0开始的15个数字复制到tmp中索引为0的位置上
                System.arraycopy(video, 0, tmp, 0, 15);
                try {
                    mPlayer.initial(tmp);
                } catch (Exception e) {
                    return;
                }
            } else {
                return;
            }
         
        if (mPlayer != null)
        //设置数据
            mPlayer.setVideoData(video);

    }

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值