参考文献 : 视音频数据处理入门:H.264视频码流解析
测试文件:H264文件
链接:https://pan.baidu.com/s/1eRTJwTsXTgHf2Ez8Inab1A
提取码:1c7q
原理
H.264原始码流(又称为“裸流”)是由一个一个的NALU组成的。他们的结构如下图所示。
其中每个NALU之间通过startcode(起始码)进行分隔,起始码分成两种:0x000001(3Byte)或者0x00000001(4Byte)。如果NALU对应的Slice为一帧的开始就用0x00000001,否则就用0x000001。
H.264码流解析的步骤就是首先从码流中搜索0x000001和0x00000001,分离出NALU;然后再分析NALU的各个字段。本文的程序即实现了上述的两个步骤。
代码
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
public class Test {
//NALU结构
static class NALU
{
int startcodeprefix_len; //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested)
int len; //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU)
int max_size; //! Nal Unit Buffer size
int forbidden_bit; //! should be always FALSE
int nal_reference_idc; //! NALU_PRIORITY_xxxx
int nal_unit_type; //! NALU_TYPE_xxxx
@Override
public String toString() {
return "NALU [len=" + len + ", forbidden_bit=" + forbidden_bit + ", nal_reference_idc=" + nal_reference_idc
+ ", nal_unit_type=" + nal_unit_type + "]";
}
}
private static RandomAccessFile in;
private static List<NALU> list = new ArrayList<NALU>();
private static List<Long> NALUIndexs = new ArrayList<>() ;
public static void main(String[] args) throws Exception {
String fileName = "F:/testFile/video/vid.h264";
if(args.length>0) {
fileName = args[0];
}
in = new RandomAccessFile(fileName, "r");
parseIndexs();
System.out.println(NALUIndexs);
getNALU();
System.out.println(list);
in.close();
}
public static int parseNALU() throws IOException {
int head = in.readInt();
if(head==1) {//0x00000001?
return 4;
}else if(head>>1 == 1) {//0x000001?
in.seek(in.getFilePointer()-1);
return 3;
}
return -1;
}
/*
* 获取每一帧NALU 并存入集合
*/
public static void getNALU() throws IOException, InterruptedException {
for(int i=0;i<NALUIndexs.size();i++) {
NALU n = new NALU();
if(i!=NALUIndexs.size()-1) {
n.len = (int) (NALUIndexs.get(i+1)-NALUIndexs.get(i));
}else {
n.len = (int) (in.length() - NALUIndexs.get(i));
}
in.seek(NALUIndexs.get(i));
int head = in.read();
n.forbidden_bit = head >> 7 & 1;
n.nal_reference_idc = head >> 5 & 3;
n.nal_unit_type = head & 0x1f;
list.add(n);
}
}
/*
* 获取所有NAUL的起始位置
*/
public static void parseIndexs() throws IOException {
while(true) {
if(parseNALU()>0) {
NALUIndexs.add(in.getFilePointer());
}
if(in.getFilePointer()==in.length()) {
break;
}
in.seek(in.getFilePointer()-4);
in.readByte();
}
}
}