视频编解码,编的是什么码?解的又是什么码?有没有想过?现在主流的就是H264码流,Android 采集摄像头原始帧数据
这篇博客讲解的是如何从摄像头从提取YUV画面色值,然后由MediaCodec进行编码压缩,最后生成的就是H264码流,我们先了解下H264码流格式。

可以看到一个个NALU单元组成了H264码流,NALU单元又包含头数据部分和帧数据部分。
每一个头开始都包含0x 00000001或者0x000001.
我选取了上面讲的采集摄像头画面进行编码后的H264文件,打开其字节文件,码流格式数据如下

可以看到手机编码后的码流每个NAL开头起始码为0x00000001
所以我要做的工作就是提取出每一个NAL单元,然后送给MediaCodec进行解码。
提取出NAL 单元的代码函数如下

     private byte[] getNALU() {        try {            int curpos = 0;            //一般NAL不超过100000字节            byte[] bb = new byte[100000];            //先读取4个字节            rf.read(bb, 0, 4);            //判断是否是0x00000001开头            if (findStartCode4(bb, 0)) {                curpos = 4;            } else {                rf.seek(0);                rf.read(bb, 0, 3);                //判断是否是0x000001开头                if (findStartCode3(bb, 0)) {                    curpos = 3;                }            }            //标志是否找到NAL单元开头            boolean findNALStartCode = false;            //下一个NAL单元的开始位置            int nextNalStartPos = 0;            //找到适合标记开头的长度            int reWind = 0;            while (!findNALStartCode) {                int hex = rf.read();                if (curpos >= bb.length) {                    break;                }                bb[curpos++] = (byte) hex;                if (hex == -1) {                    nextNalStartPos = curpos;                }                if (findStartCode4(bb, curpos - 4)) {                    findNALStartCode = true;                    reWind = 4;                    nextNalStartPos = curpos - reWind;                } else if (findStartCode3(bb, curpos - 3)) {                    findNALStartCode = true;                    reWind = 3;                    nextNalStartPos = curpos - reWind;                }            }            byte[] nal = new byte[nextNalStartPos];            System.arraycopy(bb, 0, nal, 0, nextNalStartPos);            long pos = rf.getFilePointer();            long setPos = pos - reWind;            //退回rewind长度字节            rf.seek(setPos);            return nal;        } catch (IOException e) {            e.printStackTrace();        }        return null;    }    //find match "00 00 00 01"    private boolean findStartCode4(byte[] bb, int offSet) {        if (offSet < 0) {            return false;        }        if (bb[offSet] == 0 && bb[offSet + 1] == 0 && bb[offSet + 2] == 0 && bb[offSet + 3] == 1) {            return true;        }        return false;    }    //find match "00 00 01"    private boolean findStartCode3(byte[] bb, int offSet) {        if (offSet <= 0) {            return false;        }        if (bb[offSet] == 0 && bb[offSet + 1] == 0 && bb[offSet + 2] == 1) {            return true;        }        return false;    }

封装其数据,读取每一NAL单元

 /**     * 读取每一帧数据     * @param buffer     * @return     */    public int readSampleData(ByteBuffer buffer) {        byte[] nal = getNALU();        buffer.put(nal);        return nal.length;    }

MediaCodec开启解码线程,和 Android MediaCodec,MediaExtractor解码播放MP4文件中解码一样,将NAL单元数据送给解码器即可。

  /**     *  解析播放H264码流     */    private class DecoderH264Thread extends Thread {        long pts = 0;        @Override        public void run() {            super.run();            while (!isDecodeFinish) {                int inputIndex = mediaCodec.dequeueInputBuffer(-1);                if (inputIndex >= 0) {                    ByteBuffer byteBuffer = mediaCodec.getInputBuffer(inputIndex);                    int sampSize = DecodeH264File.getInstance().readSampleData(byteBuffer);                    long time = computePresentationTime();                    if (sampSize > 0 && time > 0) {                        mediaCodec.queueInputBuffer(inputIndex, 0, sampSize, time, 0);                        try {                            sleep(30);                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                    }                }                BufferInfo bufferInfo = new BufferInfo();                int outIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);                if (outIndex >= 0) {                    mediaCodec.releaseOutputBuffer(outIndex, true);                }            }        }    }

好了,到这里结束了,有什么不明白的,欢迎留言~~
GitHub
https://github.com/zxd1991/AndroidMedia

更多相关文章

  1. 微信ANDROID客户端-会话速度提升70%的背后
  2. Android游戏Graphics绘图之图像像素操作
  3. android 笔记 --- Android大TXT文本文档读取
  4. android 性能分析(优化)-利用AOP技术,字节码方法插桩,实现 android
  5. android 使用post方法请求网址
  6. Android(安卓)压缩工具类
  7. Android(安卓)获取uri的正确文件路径的办法
  8. Android(安卓)-- 零散记录[短信发送,系统拨号,单元测试]
  9. Android将byte数组写入文件

随机推荐

  1. 使用GDB调试Android(安卓)Native Lib
  2. 开发环境搭建
  3. Android——Toast重复显示解决方法
  4. android 获取屏幕分辨率
  5. Ubuntu eclipse下android virtual device
  6. Android应用程序上传错误The package nam
  7. Android 技术用于汇总
  8. 传智播客Android视频教程——第六天
  9. 在Eclipse中导入android sdk源码
  10. android Textview属性细节以及EditText属