之前的博文我们讲了如何通过抓包RTMP协议保存视频流为H264文件,那么如果用户需要直接推流本地H264文件应该如何实现?这里可以借助TSINGSEE青犀视频研发的EasyRTMP推流组件来进行,本文我们就和大家分享一下如何通过EasyRTMP安卓版推流本地H264文件。
首先需要解析出H264文件,解析出每一帧视频,包括关键帧和非关键帧,之后再调用mEasyPusher.push推流出去。H264视频帧结构:
1、判断I帧或者P帧
private boolean isVideoFrameHeadType(byte head) {
return head == (byte) 0x65 || head == (byte) 0x61 || head == (byte) 0x41;
}
2、判断是否是I帧/P帧头
private boolean isHead(byte[] data, int offset) {
boolean result = false;
// 00 00 00 01 x
if (data[offset] == 0x00 && data[offset + 1] == 0x00
&& data[offset + 2] == 0x00 && data[3] == 0x01
&& isVideoFrameHeadType(data[offset + 4])) {
result = true;
}
// 00 00 01 x
if (data[offset] == 0x00 && data[offset + 1] == 0x00
&& data[offset + 2] == 0x01
&& isVideoFrameHeadType(data[offset + 3])) {
result = true;
}
return result;
}
3、寻找指定buffer中h264头的开始位置
private int findHead(byte[] data, int offset, int max) {
int i;
for (i = offset; i <= max; i++) {
//发现帧头
if (isHead(data, i))
break;
}
//检测到最大值,未发现帧头
if (i == max) {
i = -1;
}
return i;
}
4、启动线程,不停读取文件,读出的每一帧数据,就推流出去:
if (file.exists()) {
try {
FileInputStream fis = new FileInputStream(file);
//保存完整数据帧
byte[] frame = new byte[FRAME_MAX_LEN];
//当前帧长度
int frameLen = 0;
//每次从文件读取的数据
byte[] readData = new byte[10 * 1024];
//开始时间
long startTime = System.currentTimeMillis();
//循环读取数据
while (!isFinish) {
if (fis.available() > 0) {
int readLen = fis.read(readData);
//当前长度小于最大值
if (frameLen + readLen < FRAME_MAX_LEN) {
//将readData拷贝到frame
System.arraycopy(readData, 0, frame, frameLen, readLen);
//修改frameLen
frameLen += readLen;
//寻找第一个帧头
int headFirstIndex = findHead(frame, 0, frameLen);
while (headFirstIndex >= 0 && isHead(frame, headFirstIndex)) {
//寻找第二个帧头
int headSecondIndex = findHead(frame, headFirstIndex + FRAME_MIN_LEN, frameLen);
//如果第二个帧头存在,则两个帧头之间的就是一帧完整的数据
if (headSecondIndex > 0 && isHead(frame, headSecondIndex)) {
Log.e(TAG, "headSecondIndex:" + headSecondIndex);
onFrame(frame,
headFirstIndex,
headSecondIndex - headFirstIndex,
isIHead(frame, headSecondIndex));
byte[] temp = Arrays.copyOfRange(frame,
headSecondIndex, frameLen);
System.arraycopy(temp, 0, frame, 0, temp.length);
//修改frameLen的值
frameLen = temp.length;
//线程休眠
sleepThread(startTime, System.currentTimeMillis());
//重置开始时间
startTime = System.currentTimeMillis();
//继续寻找数据帧
headFirstIndex = findHead(frame, 0, frameLen);
} else {
//找不到第二个帧头
headFirstIndex = -1;
}
}
} else {
//如果长度超过最大值,frameLen置0
frameLen = 0;
}
} else {
//文件读取结束
isFinish = true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}