H264视频传输、编解码----MediaCodec硬解码


public class RtspDecoder {
    private static final String TAG = RtspDecoder.class.getSimpleName();
    //处理音视频的编解码的类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;
        // use sps to check isVGAorHD?

        byte[] header_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 (header_sps[i] != sps[i]) {
                isVGA = false;
                break;
            }
        }
        if (isVGA) {
            format = MediaFormat.createVideoFormat("video/avc", 1280, 640);
            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, 640 * 360);
        } else {
            format = MediaFormat.createVideoFormat("video/avc", 1280, 640);
            byte[] header_HD_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_HD_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();
    }

   
    boolean flag = true;
    private void runDecodeVideoThread() {
        Thread t = new Thread() {

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

                while (isRuning) {
                    if(flag){
                        Log.i(TAG,"tag 1");
                    }

                    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();
                    }
                    if(flag){
                        Log.i(TAG,"tag 2");
                        flag = false;
                    }
                }
            }
        };

        t.start();
    }

}
public class H264Stream {
    private final static String TAG = "H24Stream";

    private MediaCodec mMeidaCodec;
    private SurfaceView mSurfaceView;
    private ByteBuffer[] inputBuffers;
    private Handler mHandler;
    private final static int TIME_INTERNAL = 30;
    private LinkedBlockingDeque<bufferUnit> bufferQueue = new LinkedBlockingDeque<bufferUnit>();

    private int picWidth = 768;
    private int picHeight = 432;
    private HandlerThread thread;
    private boolean flag = true;
    private int totalcount = 0;
    private boolean initial = true;
    public long lastTimeStamp = 0;

    private long startMill = Calendar.getInstance().getTimeInMillis();

    private final static String MIME_TYPE = "video/avc"; // H.264 Advanced Video   视频格式,  video/avc 指的是 H264格式

    private IVideoFrameListener videoFrameListener;

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)

    public void setVideoFrameListener(IVideoFrameListener videoFrameListener){
        this.videoFrameListener = videoFrameListener;
    }

    public void clearVideoFrameListener(){
        this.videoFrameListener = null;
    }

    public H264Stream(RtspClient.SDPInfo sp) {
//        mSDPinfo = sp;
        thread = new HandlerThread("H264StreamThread");
        thread.start();
        mHandler = new Handler(thread.getLooper());
        flag = true;
    }

    private void configMediaDecoder() {
        if (Build.VERSION.SDK_INT > 15) {
            //创建解码器
            getBestMediaCodeC();
            MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE, picWidth, picHeight);//指定解码类型,数据尺寸
//          mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
//          MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar);
            mMeidaCodec.configure(mediaFormat, mSurfaceView.getHolder().getSurface(), null, 0);//
//          mMeidaCodec.configure(mediaFormat, null, null, 0);//
            mMeidaCodec.start();
        }
    }

    private void getBestMediaCodeC() {
        int numberCodec = MediaCodecList.getCodecCount();
        List<String> codeList = new ArrayList<>();
        for (int i =0;i<numberCodec;i++){
            codeList.add(MediaCodecList.getCodecInfoAt(i).getName());
        }
        try {
            if (codeList.size() > 0){
                if (codeList.contains("OMX.qcom.video.decoder.avc")){
                    mMeidaCodec = MediaCodec.createByCodecName("OMX.qcom.video.decoder.avc");
                    return;
                }else if (codeList.contains("OMX.google.h264.decoder")){
                    mMeidaCodec = MediaCodec.createByCodecName("OMX.google.h264.decoder");
                    return;
                }
            }

            mMeidaCodec = MediaCodec.createDecoderByType(MIME_TYPE);
        } catch (IOException e) {
            try {
                mMeidaCodec = MediaCodec.createDecoderByType(MIME_TYPE);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }


    public void startMediaHardwareDecode() {
        mHandler.post(hardwareDecodeThread);
    }

    private Runnable hardwareDecodeThread = new Runnable() {
        @Override
        public void run() {
            int mCount = 0;
            byte[] tmpByte;
            int framType;
            boolean startKeyFrame = false;

            configMediaDecoder();
            while (flag) {
                try {
                    if (initial){
                        int validFrame = 0;
                        bufferUnit[] objects = new bufferUnit[bufferQueue.size()];
                      
                        objects = bufferQueue.toArray(objects);
                        for(int i=0;i<objects.length;i++){
                            int tempFrameType = (objects[i]).data[4] & 0x1F;
                            if (tempFrameType == 5 || tempFrameType == 1){
                                validFrame++;
                            }
                        }
                        if (validFrame < 4){
//                        initial = false;
                        Thread.sleep(30);
                        continue;
                        }
                     }
                    initial = false;
                    tmpByte = bufferQueue.take().data;


                    framType = tmpByte[4] & 0x1F;

//                    if (framType == 5) startKeyFrame = true;
                    if (framType == 5||framType == 1 ||framType == 6 || framType == 7 || framType == 8) {
					      if(framType == 1||framType == 5 ){
                              if (videoFrameListener != null){
                                  videoFrameListener.onFrameChanged(1);
                              }
                            long endMill = Calendar.getInstance().getTimeInMillis();
                            int totalTime = 33;

                           if (totalTime +startMill- endMill > 0){
                                Thread.sleep(totalTime +startMill- endMill);
                               startMill = totalTime +startMill;
                            }else{
                               startMill = endMill;
                           }
                        }
                    ByteBuffer[] inputBuffers = mMeidaCodec.getInputBuffers();//获取输入缓存队列
                    int inputBufferIndex = mMeidaCodec.dequeueInputBuffer(-1);//请求一个输入缓存,-1表示一直等,如果是具体的时间,如1000,表示等1秒钟;返回整形变量表示获取到的输入缓存的index;
                    if (inputBufferIndex >= 0) {
                        ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];//输入缓存对象
                        inputBuffer.clear();//使用之前clear一下,防止之前缓存数据影响当前的数据
                        inputBuffer.put(tmpByte, 0, tmpByte.length);//将数据添加进缓存中
                        mMeidaCodec.queueInputBuffer(inputBufferIndex, 0, tmpByte.length, 0, 0);//将缓存数据入队,接下来就是解码
                        mCount++;
                    }
                    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                    int outputBufferIndex = mMeidaCodec.dequeueOutputBuffer(bufferInfo, 0);//获取一个输出缓存,传入bufferInfo存放ByteBuffer的信息。
                    while (outputBufferIndex >= 0) {
                        mMeidaCodec.releaseOutputBuffer(outputBufferIndex, true);//输出缓存使用完了释放,true是是否渲染到surfaceView
                        outputBufferIndex = mMeidaCodec.dequeueOutputBuffer(bufferInfo, 0);
                    }

                    }
                } catch (InterruptedException e) {
                    Log.e(TAG, "Wait the buffer come..");
                    e.printStackTrace();
                } catch (Exception e) {
                    Log.e(TAG, "Wait the buffer come..");
                    e.printStackTrace();
                }
            }
            bufferQueue.clear();
            if (mMeidaCodec != null) {
                try {
                    mMeidaCodec.stop();
                    mMeidaCodec.release();
                    mMeidaCodec = null;
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }
    };

    public void stop() {
        //暂停解码
        flag = false;
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //释放资源
        try {
            bufferQueue.clear();
            mHandler.removeCallbacks(hardwareDecodeThread);
            if (mMeidaCodec != null) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    try {
                        mMeidaCodec.stop();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    try {
                        mMeidaCodec.release();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                mMeidaCodec = null;
            }
            thread.quit();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public void setSurfaceView(SurfaceView s) {
        this.mSurfaceView = s;
        if (Build.VERSION.SDK_INT > 15) {
            startMediaHardwareDecode();
        } else {
            Log.e(TAG, "The Platform not support the hardware decode H264!");
        }
    }

    public void decodeH264Stream(byte[] data) {
        try {
            lastTimeStamp = new Date().getTime();
            bufferUnit tmpBuffer = new bufferUnit();
            tmpBuffer.data = data;
            bufferQueue.put(tmpBuffer);
        } catch (InterruptedException e) {
            Log.e(TAG, "The buffer queue is full , wait for the place..");
        }
    }

    //传过来的数据
    private class bufferUnit {
        public byte[] data;
    }
}

注意:

1、如果送来的流的第一帧Frame有pps和sps,可以不需要配置format.setByteBuffer的”csd-0” (sps) 和”csd-1”(pps); 否则必须配置相应的pps和sps,通常情况下sps和pps如下: 

byte[] sps = { 0, 0, 0, 1, 103, 100, 0, 40, -84, 52, -59, 1, -32, 17, 31, 120, 11, 80, 16, 16, 31, 0, 0, 3, 3, -23, 0, 0, -22, 96, -108 }; 

 

byte[] pps = { 0, 0, 0, 1, 104, -18, 60, -128 };。

 

2、需要使用生产者-消费者模式,否则有可能存在:上一帧还没有解码完成,下一帧就传入进来了,解码发生错误。其实也就是MediaCodec状态发生错误的异常。

 

 

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值