之前的一个Android项目中需要根据设备生成的视频中保存的信息,与手机视频做一些视频融合处理,因此,需要对MP4文件进行解析获取其中保存的信息。
mp4是由一个个“box”组成的,大box中存放小box,一级嵌套一级来存放媒体信息。box的基本结构是:
其中,size指明了整个box所占用的大小,包括header部分。如果box很大(例如存放具体视频数据的mdat box),超过了uint32的最大数值,size就被设置为1,并用接下来的8位uint64来存放大小。
项目中的mp4视频保存的信息也是在box中的,对应的box type是 @SK3 ,保存的数据内容对应为:
float playspeed;
float fps;
uint16 pretime;
在MP4 Reader软件中查看具体mp4文件显示的box结构如下:
在图中右侧区域可以看到具体信息是以二进制格式的数字形式显示的:
00 00 00 12 对应box size,转成十进制正好就是18
40 53 4B 33 对应box type,通过查找ASCII码表可知是字符串 @SK3
其他数字对应box的具体内容。
通过一些软件工具可以查看视频格式的内容,那么如何通过编码从MP4文件中解析数据呢?
下面我们以读取指定视频的 @SK3 box为例,实现java代码对MP4文件的数据解析。
已知需要解析的数据格式如下:
class Mp4BoxSK3 {
float playSpeed;
float fps;
int preTime;
}
具体解析算法:
public Mp4BoxSK3 getSK3(String filePath) {
Mp4BoxSK3 sk3 = new Mp4BoxSK3();
BufferedInputStream reader = null;
try {
reader = new BufferedInputStream(new FileInputStream(filePath));
long count;
int headerNormalLen = 8;
int headerLargeSizeLen = 8;
byte[] buffer = new byte[headerNormalLen];
while (readFully(buffer, reader)) { //读取box header
//转换成大端模式处理
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer).order(ByteOrder.BIG_ENDIAN);
int size = byteBuffer.getInt(); //获取box size
char type1 = (char) byteBuffer.get();
char type2 = (char) byteBuffer.get();
char type3 = (char) byteBuffer.get();
char type4 = (char) byteBuffer.get();
String type = String.valueOf(new char[]{type1, type2, type3, type4}); //获取box type
long largeSize = 0;
if (size == 1) { //如果box size等于1,则具体大小存放在largeSize中
byte[] buf8 = new byte[headerLargeSizeLen];
readFully(buf8, reader);
ByteBuffer byteBuffer8 = ByteBuffer.wrap(buf8).order(ByteOrder.BIG_ENDIAN);
largeSize = byteBuffer8.getLong(); //获取box largeSize
}
Log.d(TAG, "type: " + type + " size: " + size + " largeSize: " + largeSize);
if ("@SK3".equals(type)) { //判断是否是想要解析的box
byte[] buf10 = new byte[10];
readFully(buf10, reader);
ByteBuffer byteBuffer10 = ByteBuffer.wrap(buf10).order(ByteOrder.LITTLE_ENDIAN); //????
sk3.playSpeed = byteBuffer10.getFloat();
sk3.fps = byteBuffer10.getFloat();
sk3.preTime = byteBuffer10.getShort();
break;
}
if ("moov".equals(type) || "udta".equals(type)) {
continue;
}
if (size == 1) {
count = largeSize - headerNormalLen - headerLargeSizeLen;
} else {
count = size - headerNormalLen;
}
reader.skip(count);
}
Log.d(TAG, "sk3: " + sk3);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return sk3;
}
private boolean readFully(byte[] buf, BufferedInputStream reader) throws IOException {
int n = 0;
int len = buf.length;
while (n < len) {
int count = reader.read(buf, n, len - n);
if (count < 0) {
return false;
}
n += count;
}
return true;
}
查看解析过程的Log信息,可以得到我们需要的信息:
参考:https://www.cnblogs.com/haibindev/archive/2011/10/17/2214518.html