Android(安卓)MediaCodec 解码H264码流播放
16lz
2021-01-24
视频编解码,编的是什么码?解的又是什么码?有没有想过?现在主流的就是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
更多相关文章
- 微信ANDROID客户端-会话速度提升70%的背后
- Android游戏Graphics绘图之图像像素操作
- android 笔记 --- Android大TXT文本文档读取
- android 性能分析(优化)-利用AOP技术,字节码方法插桩,实现 android
- android 使用post方法请求网址
- Android(安卓)压缩工具类
- Android(安卓)获取uri的正确文件路径的办法
- Android(安卓)-- 零散记录[短信发送,系统拨号,单元测试]
- Android将byte数组写入文件