读取h.265视频文件的每一帧送Mediacodec 解码/Surface 显示

H265FileDecodeActivity.java

public class H265FileDecodeActivity extends Activity      implements SurfaceHolder.Callback, OnClickListener, TextureView.SurfaceTextureListener, OnFrameAvailableListener {    private SurfaceView testSurfaceView;    private TextureView textureView;    private SurfaceHolder holder;    //文件路径    private String path0 = Environment.getExternalStorageDirectory() + "/H265_22f.h265";    private String path = Environment.getExternalStorageDirectory() + "/mediacodec_1.264";    private String TAG = "H264FileDecodeActivity";    private int width, height;    //解码器    private MediaCodec mCodec0;    private boolean isFirst = true;    private boolean isFirst0 = true;    // 需要解码的类型    private final static String MIME_TYPE = "video/avc"; // H.264 Advanced Video    private final static int TIME_INTERNAL = 5;    //文件读取完成标识    private boolean isFinish = false;    private boolean isFinish0 = false;    //这个值用于找到第一个帧头后,继续寻找第二个帧头,如果解码失败可以尝试缩小这个值    private int FRAME_MIN_LEN = 8; //1024;    //一般H264帧大小不超过200k,如果解码失败可以尝试增大这个值    private static final int FRAME_MAX_LEN = 300 * 1024;    //根据帧率获取的解码每帧需要休眠的时间,根据实际帧率进行操作    private int PRE_FRAME_TIME = 1000 / 25;    //保存完整数据帧    byte[] frame = new byte[FRAME_MAX_LEN];    //每次从文件读取的数据    byte[] readData = new byte[10 * 1024];    //当前帧长度    int frameLen = 0;    int headFirstIndex;    int headSecondIndex;    int frameNum;    //读取文件解码线程    UpdateDecoder updateDecoderT;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_h264);        testSurfaceView = (SurfaceView) findViewById(R.id.surfaceview);        holder = testSurfaceView.getHolder();        holder.addCallback(this);        textureView = (TextureView) findViewById(R.id.textureview);        textureView.setSurfaceTextureListener(this);        Button btn = (Button) findViewById(R.id.takePhoto);        btn.setOnClickListener(this);        nalu = new NaluUnit();        isFinish = false;        isFinish0 = false;    }    Handler myHandler = new Handler() {          public void handleMessage(Message msg) {                switch (msg.what) {                     case 2:                      byte[] sendBuf = mQueue0.poll();                      onFrame(sendBuf, 0, sendBuf.length);                      break;                   case 1:                      Log.d(TAG,"  onFrame 1  ");                      onFrame((byte[])msg.obj, 0, msg.arg1);                        break;                  case 0:                      Log.d(TAG,"  onFrame 0  ");                      onFrame((byte[])msg.obj, 0, msg.arg1);                         break;                   default :                      break;             }                super.handleMessage(msg);           }       };     private  LinkedBlockingQueue mQueue;    private  LinkedBlockingQueue<byte[]> mQueue0;    int waitC = 0;    class UpdateDecoder extends Thread{        private boolean runFlag;        public void init(){             //mList = new ArrayList(); //new ArrayList(8)            mQueue = new LinkedBlockingQueue();             mQueue0 = new LinkedBlockingQueue<byte[]>();         }        public void pushBuf(byte[] buf, int offset, int len)        {            //ByteBuffer buffer = ByteBuffer.allocate(len);            //buffer.put(buf,offset,len);            //mQueue.offer(buffer);            byte[] frameD = new byte[len];            System.arraycopy(frameD, 0, buf, offset, len);            mQueue0.offer(frameD);        }        public void updataStop()        {            runFlag = false;            mQueue0.clear();        }        @Override          public void run() {            runFlag = true;            Log.i("UpdateDecoder", " UpdateDecoder  mQueue.size() = "+mQueue0.size());            while(runFlag){                     if(mQueue0.size() <= 0){                    try {                        Thread.sleep(40);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }                while(mQueue0.size() > 0){                    //ByteBuffer sendBuf = mQueue.poll();                    //onFrame(sendBuf.array(), 0, sendBuf.capacity());                    //byte[] sendBuf = mQueue0.poll();                    try {                           //onFrame(sendBuf, 0, sendBuf.length);                          Message message = new Message();                           message.what = 2;                             myHandler.sendMessage(message);                        try {                            Thread.sleep(40);                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                    } catch (Throwable t) {                        t.printStackTrace();                    }                }            }        }      }    @Override    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {        this.width  = width;        this.height = height;        Log.i(TAG, "onSurfaceTextureAvailable:  width = " + width + ", height = " + height);        //startCodec0();    }    @Override    public void onFrameAvailable(SurfaceTexture surfaceTexture) {        // TODO Auto-generated method stub    }    @Override    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {        Log.i(TAG, "onSurfaceTextureSizeChanged: width = " + width + ", height = " + height);    }    @Override    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {        //stopCodec0();        return false;    }    @Override    public void onSurfaceTextureUpdated(SurfaceTexture surface) {    }    @Override    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {    }    @Override    public void surfaceCreated(SurfaceHolder holder) {        width  = holder.getSurfaceFrame().width();        height = holder.getSurfaceFrame().height();        Log.i(TAG, "surfaceCreated:  width = " + width + ", height = " + height);        startCodec0();        //updateDecoderT = new UpdateDecoder();        //updateDecoderT.init();        //updateDecoderT.start();    }    @Override    public void surfaceDestroyed(SurfaceHolder holder) {        stopCodec0();        //updateDecoderT.updataStop();    }    @Override    public void onClick(View v) {        // TODO Auto-generated method stub        if(!isFinish0) {           doDecoder();           //doDecodec();        }    }    public void startCodec0() {        if (isFirst0) {            initDecoder0();        }    }    private class DecoderCallback extends MediaCodec.Callback{        @Override        public void onInputBufferAvailable(MediaCodec codec, int index) {             if(nalu.size != 0) {                 ByteBuffer inputBuffer = mCodec0.getInputBuffer(index);                 long timestamp = mCount0++ * 1000000 / 25;                 //Log.i(TAG," nalu type = "+ nalu.type+", nalu.size = "+nalu.size);                  inputBuffer.clear();                 inputBuffer.put(nalu.data, 0, nalu.size);                 mCodec0.queueInputBuffer(index, 0, nalu.size, timestamp, 0);                }            }        @Override        public void onOutputBufferAvailable(MediaCodec codec, int index, MediaCodec.BufferInfo info) {            //ByteBuffer encodedData = codec.getOutputBuffer(index);            //encodedData.position(info.offset);            //encodedData.limit(info.offset + info.size);            codec.releaseOutputBuffer(index, true);           }        @Override        public void onError(MediaCodec codec, MediaCodec.CodecException e) {            Log.d(TAG, "Error: " + e);        }        @Override        public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {            Log.d(TAG, "encoder output format changed: " + format);        }    }    //h265的nal unit header有两个字节构成    //hevc的nal包结构与h264有明显的不同,hevc加入了nal所在的时间层的ID,取去除了nal_ref_idc,此信息合并到了naltype中,    //通常情况下F为0,layerid为0,  TID为1    //| F(1bit) | Type(6bits) | LayerId(6bits) | TID (3bits)|    //H265的NALU类型    //00 00 00 01 40 01  的nuh_unit_type的值为 32, 语义为视频参数集        VPS    //00 00 00 01 42 01  的nuh_unit_type的值为 33, 语义为序列参数集         SPS    //00 00 00 01 44 01  的nuh_unit_type的值为 34, 语义为图像参数集         PPS    //00 00 00 01 4E 01  的nuh_unit_type的值为 39, 语义为补充增强信息       SEI    //00 00 00 01 26 01  的nuh_unit_type的值为 19, 语义为可能有RADL图像的IDR图像的SS编码数据   IDR    //00 00 00 01 02 01  的nuh_unit_type的值为1, 语义为被参考的后置图像,且非TSA、非STSA的SS编码数据    //常用Nalu Type的定义    //NAL_UNIT_CODED_SLICE_TRAIL_N= 0,// 0      //NAL_UNIT_CODED_SLICE_TRAIL_R,   // 1          //NAL_UNIT_CODED_SLICE_TSA_N,     // 2      //NAL_UNIT_CODED_SLICE_TLA,       // 3   Current name in the spec: TSA_R          //NAL_UNIT_CODED_SLICE_STSA_N,    // 4      //NAL_UNIT_CODED_SLICE_STSA_R,    // 5      //NAL_UNIT_CODED_SLICE_RADL_N,    // 6      //NAL_UNIT_CODED_SLICE_DLP,       // 7   Current name in the spec: RADL_R      //NAL_UNIT_CODED_SLICE_RASL_N,    // 8      //NAL_UNIT_CODED_SLICE_TFD,       // 9   Current name in the spec: RASL_R    //NAL_UNIT_CODED_SLICE_BLA,       // 16  Current name in the spec: BLA_W_LP      //NAL_UNIT_CODED_SLICE_BLANT,     // 17  Current name in the spec: BLA_W_DLP      //NAL_UNIT_CODED_SLICE_BLA_N_LP,  // 18      //NAL_UNIT_CODED_SLICE_IDR,       // 19  Current name in the spec: IDR_W_DLP      //NAL_UNIT_CODED_SLICE_IDR_N_LP,  // 20      //NAL_UNIT_CODED_SLICE_CRA,       // 21      //以下NAL_BLA_N_LP到NAL_IDR_W_RADL都算I帧    //NAL_BLA_N_LP:    //NAL_BLA_W_LP:    //NAL_BLA_W_RADL:    //NAL_CRA_NUT:    //NAL_IDR_N_LP:    //NAL_IDR_W_RADL    /*    ffmpeg 中 hevc.h定义:     NAL_TRAIL_N = 0,     NAL_TRAIL_R = 1,     NAL_TSA_N = 2,     NAL_TSA_R = 3,     NAL_STSA_N = 4,     NAL_STSA_R = 5,     NAL_RADL_N = 6,     NAL_RADL_R = 7,     NAL_RASL_N = 8,     NAL_RASL_R = 9,     NAL_BLA_W_LP = 16,     NAL_BLA_W_RADL = 17,     NAL_BLA_N_LP = 18,     NAL_IDR_W_RADL = 19,     NAL_IDR_N_LP = 20,     NAL_CRA_NUT = 21,     NAL_VPS = 32,     NAL_SPS = 33,     NAL_PPS = 34,     NAL_AUD = 35,     NAL_EOS_NUT = 36,     NAL_EOB_NUT = 37,     NAL_FD_NUT = 38,     NAL_SEI_PREFIX = 39,     NAL_SEI_SUFFIX = 40,    */    //NALU类型为vps, sps, pps, 或者解码顺序为第一个AU的第一个NALU, 起始码前面再加一个0x00    private static final int NAL_VPS = 32; // 码流中对应字节值 0x40    private static final int NAL_SPS = 33; // 0x42    private static final int NAL_PPS = 34; // 0x44    private static byte[] getvps_sps_pps(byte[] data, int offset, int length) {        int i = 0;        int vps = -1, sps = -1, pps = -1;        do {            if (vps == -1) {                for (i = offset; i < length - 4; i++) {                    if ((0x00 == data[i]) && (0x00 == data[i + 1]) && (0x00 == data[i + 2])) {                        byte nal_spec = data[i + 3];                        //int type = (code & 0x7E)>>1;                        int nal_type = (nal_spec >> 1) & 0x03f;                        if (nal_type == NAL_VPS) {                            // vps found.                            if (data[i - 1] == 0x00) {  // vps sps pps start with 00 00 00 01 ordinary                                vps = i - 1;                            } else {                    // start with 00 00 01                                vps = i;                            }                            break;                        }                    }                }            }            if (sps == -1) {                for (i = vps; i < length - 4; i++) {                    if ((0x00 == data[i]) && (0x00 == data[i + 1]) && (0x01 == data[i + 2])) {                        byte nal_spec = data[i + 3];                        int nal_type = (nal_spec >> 1) & 0x03f;                        if (nal_type == NAL_SPS) {                            // vps found.                            if (data[i - 1] == 0x00) {  // start with 00 00 00 01                                sps = i - 1;                            } else {                      // start with 00 00 01                                sps = i;                            }                            break;                        }                    }                }            }            if (pps == -1) {                for (i = sps; i < length - 4; i++) {                    if ((0x00 == data[i]) && (0x00 == data[i + 1]) && (0x01 == data[i + 2])) {                        byte nal_spec = data[i + 3];                        int nal_type = (nal_spec >> 1) & 0x03f;                        if (nal_type == NAL_PPS) {                            // vps found.                            if (data[i - 1] == 0x00) {  // start with 00 00 00 01                                pps = i - 1;                            } else {                    // start with 00 00 01                                pps = i;                            }                            break;                        }                    }                }            }        } while (vps == -1 || sps == -1 || pps == -1);        if (vps == -1 || sps == -1 || pps == -1) {// 没有获取成功。            return null;        }        // 计算csd buffer的长度。即从vps的开始到pps的结束的一段数据        int begin = vps;        int end = -1;        for (i = pps; i < length - 4; i++) {            if ((0x00 == data[i]) && (0x00 == data[i + 1]) && (0x01 == data[i + 2])) {                if (data[i - 1] == 0x00) {  // start with 00 00 00 01                    end = i - 1;                } else {                    // start with 00 00 01                    end = i;                }                break;            }        }        if (end == -1 || end < begin) {            return null;        }        // 拷贝并返回        byte[] buf = new byte[end - begin];        System.arraycopy(data, begin, buf, 0, buf.length);        return buf;    }    private void initDecoder0() {        try {            //根据需要解码的类型创建解码器            mCodec0 = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_HEVC); //"video/hevc"        } catch (IOException e) {            e.printStackTrace();        }        MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_HEVC, width, height);        //CSD Codec-specific Data -- 是指跟特定编码算法相关的一些参数,比如AAC的ADTS、H.264的SPS/PPS等        //H.265,CSD只需要“csd-0”参数,把VPS、SPS、PPS拼接到一起即可   IDR CRA        //mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);        /*        MediaFormat mediaFormat = new MediaFormat();            int rlen = 0;        File file = new File(path0);         if(file.exists()){            FileInputStream fis = null;            try {               fis = new FileInputStream(file);                rlen = fis.read(frame, 0, frame.length);            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }         }               byte[] csd0 = getvps_sps_pps(frame, 0, Math.min(rlen, 200));//Math.min(length, 200));        if (csd0 == null) {            try {                throw new IOException("parse vps sps pps error...");            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }        ByteBuffer csd0bf = ByteBuffer.allocate(csd0.length);        csd0bf.put(csd0);        csd0bf.clear();        mediaFormat.setByteBuffer("csd-0", csd0bf);        mediaFormat.setInteger(MediaFormat.KEY_WIDTH, width);        mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, height);        mediaFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_HEVC);        */        //SurfaceView        mCodec0.configure(mediaFormat, holder.getSurface(), null, 0); //直接解码送surface显示        //开始解码        mCodec0.start();        isFirst0 = false;    }    public void stopCodec0() {        try {            mCodec0.stop();            mCodec0.release();            mCodec0 = null;            isFirst0  = true;            isFinish0 = true;        } catch (Exception e) {            e.printStackTrace();            mCodec0 = null;        }    }    int mCount0 = 0;    public void onFrame(byte[] buf, int offset, int length) {        //-1表示一直等待;0表示不等待;其他大于0的参数表示等待毫秒数        //Log.e(TAG,"        onFrame start       ");        int inputBufferIndex = mCodec0.dequeueInputBuffer(-1);        if (inputBufferIndex >= 0) {            ByteBuffer inputBuffer =  mCodec0.getInputBuffer(inputBufferIndex);            inputBuffer.clear();            inputBuffer.put(buf, offset, length);             //解码            long timestamp = mCount0 * 1000000 / 25;            mCodec0.queueInputBuffer(inputBufferIndex, 0, length,  timestamp, 0);             mCount0++;        }        //Log.e(TAG,"        onFrame middle      ");        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();        int outputBufferIndex = mCodec0.dequeueOutputBuffer(bufferInfo, 0); //10        //循环解码,直到数据全部解码完成        while (outputBufferIndex >= 0) {            //logger.d("outputBufferIndex = " + outputBufferIndex);            mCodec0.releaseOutputBuffer(outputBufferIndex, true);            outputBufferIndex = mCodec0.dequeueOutputBuffer(bufferInfo, 0);        }    }    NaluUnit nalu;    public class NaluUnit {         byte[] data;         int size;         int type;         public NaluUnit() {             data = new byte[20*1024];             size = 0;          }    }    public  void  doDecoder()     {        final File file = new File(path0);         if(!file.exists() || !file.canRead()){            Log.e(TAG,"failed to open h264 file.");            return;        }        Log.e(TAG," path0 = "+path0);        Log.e(TAG,"        readH264FromFile       ");        new Thread()         {            public void run() {              int readlen = 0;              int writelen = 0;              int i = 0;              int pos = 0;              int index = 0;              int index0 = 0;              boolean findFlag = false;              FileInputStream fis = null;              try {                 fis = new FileInputStream(file);              } catch (FileNotFoundException e1) {                // TODO Auto-generated catch block                e1.printStackTrace();              }              //每次从文件读取的数据              frameNum = 0;              long startTime = System.currentTimeMillis();              while (!isFinish0)               {                 try {                    if(fis.available() > 0) {                        readlen = fis.read(frame, pos, frame.length-pos);                        if(readlen<=0) {                           break;                        }                        readlen += pos;                        i   = 0;                        pos = 0;                        writelen = readlen;                        //while(i < readlen-4) {                        for (i=0; i 4; i++) {                            findFlag = false;                            index  = 0;                            index0 = 0;                            if(frame[i] == 0x00 && frame[i+1] == 0x00 && frame[i+2] == 0x01)                            {                                pos = i;                                if(i>0) {                                   if(frame[i-1] == 0x00) { //start with 0x00 0x00 0x00 0x01                                                                  index = 1;                                   }                                }                                //while (pos < readlen-4) {                                for (pos=i+3; pos4; pos++) {                                    if(frame[pos] == 0x00 && frame[pos+1] == 0x00 && frame[pos+2] == 0x01) {                                        findFlag = true;                                        if(frame[pos-1] == 0x00) {//start with 0x00 0x00 0x00 0x01                                           index0 = 1;                                        }                                        break;                                    }                                }                                if(findFlag){                                    nalu.type = (frame[i+3]&0x7E)>>1;                                       if(index==1) {                                       i = i - 1;                                    }                                    if(index0==1) {                                       pos = pos - 1;                                    }                                    onFrame(frame, i, pos-i); // start code + nalu 送解码器                                    i = pos;                                    writelen = i;                                    Log.i(TAG," nalu type = "+ nalu.type+", nalu.size = "+i);                                     frameNum++;                                    //Log.i(TAG," frameNum = "+frameNum);                                     long time = PRE_FRAME_TIME - (System.currentTimeMillis() - startTime);                                    if (time > 0) {                                        try {                                            Thread.sleep(time);                                        } catch (InterruptedException e) {                                            e.printStackTrace();                                        }                                    }                                    startTime = System.currentTimeMillis();                                }else {                                    writelen = i;                                    break;                                }                            }                        }                        if(writelen>0 && writelen0, readlen-writelen);                            //Log.i(TAG, " readlen = "+readlen+", writelen = "+writelen);                        }                        pos = readlen-writelen;                    }else {                        isFinish0 = true;                    }                 } catch (IOException e) {                    // TODO Auto-generated catch block                    Log.e(TAG, "  error =  "+e.getMessage());                    e.printStackTrace();                 }              }               isFinish0 = false;              Log.i(TAG, "         frameNum     "+frameNum);           }        }.start();    }}

布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent" >    <SurfaceView        android:id="@+id/surfaceview"        android:layout_width="640px"        android:layout_height="480px" />    <TextureView        android:id="@+id/textureview"        android:layout_below="@id/surfaceview"        android:layout_width="640px"        android:layout_height="480px"         android:layout_marginTop="20dp"/>    <Button        android:id="@+id/takePhoto"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:background="@drawable/btn_camera_all_click"        android:layout_alignParentBottom="true"        android:layout_centerHorizontal="true"         android:layout_marginBottom="10dip"/>RelativeLayout>

效果图:

更多相关文章

  1. Android自动播放图片功能实现
  2. Android数据缓存
  3. Android(安卓)数据存储(数据库、文件、参数)操作实例
  4. Android(安卓)SqlLite的简单实用
  5. Android(安卓)Studio学习:中级控件:使用Intent进行页面之间数据传
  6. Android的数据存储方式
  7. Android将发送的短信插入数据库
  8. android极简原创系列:最简单的listview数据绑定
  9. mybatisplus的坑 insert标签insert into select无参数问题的解决

随机推荐

  1. [Android]【安卓】开发小结
  2. 诡异特殊的EditText 弹出软键盘遮挡BUG
  3. 2011年,Android星光不再闪烁
  4. Android安全加密:非对称加密
  5. Android 动画(anim)详解
  6. Android ListView控件基本用法
  7. android 布局详解
  8. android开发系列文章集合
  9. 短视频源码,实现文字横向移动效果(跑马灯
  10. Android(安卓)打包成APK