一个视频从录制到生成MP4文件的过程和需要用到的工具类及作用:
原始数据:yuv(视频),pcm(音频)
编码格式:
视频流:h264,h265,mpeg2
音频流:aac,mp3,wmv
封装格式:
mp4,avi,rmvb,flv...
MediaCodec:
用来编解码H264视频流
编码:YUV->H264
解码:H264->YUV
主要代码及作用:
public class MyRunnable implements Runnable {
@Override
public void run() {
try {
//1、IO流方式读取h264文件【太大的视频分批加载】
byte[] bytes = null;
bytes = getBytes(inputStream);
Log.e(TAG, "bytes size " + bytes.length);
//2、拿到 mediaCodec 所有队列buffer[]
//开始位置
int startIndex = 0;
//h264总字节数
int totalSize = bytes.length;
//3、解析
while (true) {
//判断是否符合
if (totalSize == 0 || startIndex >= totalSize) {
break;
}
//寻找索引
int nextFrameStart = findByFrame(bytes, startIndex + 2, totalSize);
if (nextFrameStart == -1) {
break;
}
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
Log.i(TAG, "run: infosize = "+ info.size);
// 查询10000毫秒后,如果dSP芯片的buffer全部被占用,返回-1;存在则大于0
int inIndex = mediaCodec.dequeueInputBuffer(10000);
if (inIndex >= 0) {
//根据返回的index拿到可以用的buffer
ByteBuffer byteBuffer = mediaCodec.getInputBuffer(inIndex);
//清空缓存
byteBuffer.clear();
//开始为buffer填充数据
//byte数据,startIndex从哪里开始,nextFrameStart - startIndex到下一个分割符的长度
byteBuffer.put(bytes, startIndex, nextFrameStart - startIndex);
//填充数据后通知mediacodec查询inIndex索引的这个buffer,
//index告诉mediaCodec(dsp)用了那个容器
mediaCodec.queueInputBuffer(inIndex, 0, nextFrameStart - startIndex, 0, 0);
//到此数据从cpu传到了dsp
//为下一帧做准备,下一帧首就是前一帧的尾。
startIndex = nextFrameStart;
} else {
//等待查询空的buffer
continue;
}
//mediaCodec 查询 "mediaCodec的输出方队列"得到索引
//解码比较耗时,并不是传一帧数据输出一帧数据
int outIndex = mediaCodec.dequeueOutputBuffer(info, 10000);
Log.e(TAG, "outIndex " + outIndex);
//大于0已经解码完成
if (outIndex >= 0) {
try {
//暂时以休眠线程方式放慢播放速度
Thread.sleep(33);
} catch (InterruptedException e) {
e.printStackTrace();
}
//如果surface绑定了,则直接输入到surface渲染并释放,没有Surface则为false
mediaCodec.releaseOutputBuffer(outIndex, true);
} else {
Log.e(TAG, "没有解码成功");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}