传送门:MP4 文档 P14 页
Moov Box 这个 Box 是我们前文提到的 Container Box,Moov Box 里面的 Data 包含其他的子 Box。在 MP4 文件中必须有但是只有一个的 Moov Box,我们看到上面官方的定义介绍,这个 Box 本身是很简单的,里面没有携带具体 Box 的信息,看下面的结构图,Box Data 区里面的就是各个的 Trak(Video,Audio ......),所以这个 Box 更像是一个分界标识。
名称 | 大小(byte) | 意义 | 说明 |
---|---|---|---|
Box length | 4 | Box 整体的大小 | 包含 Header 和 Data部分 |
Box type | 4 | ”moov”的ASCII码,表明是 moov Box | box 属性值,通常是固定值 |
data | Box length - 8 | 具体数据 | 这个Box是Container Box,里面还有好几层子Bo |
我们发现 Moov Box 在最后面,前面还有 uuid box 和 mdat box,这里 uuid box 不是必须的,也不是重要的,这里就不花篇幅进行解析介绍了。还有就是 mdat box,这个 box 里面放的都是音视频的原数据,这里我们先不解析,等把 moov box解析完成后,再回过头去看 mdat box。
上图黑框标志出来的是 box size,红框标注出来的是 moov 的 type 值,也就是对应的是 “moov” ascii码。我们再看到
箭头指向的偏移地址,这里我们验算一遍这个偏移量:
ftyp 的大小 0x18 ,uuid 的大小 0x28,mdat 的大小 0x03<<24|0x68<<16|0x87<<8|0xCD
这里计算后的大小,正好就是 moov box 的偏移量:
接下来我们根据上图16进制查看器展现出来的值进行分析:
定义 | 实际值(16进制) | 具体值(10进制 / ASCII) | 字段位置 |
---|---|---|---|
Box length(4 byte) | 0x00 0x10 0x8A 0x77 | 1084023 | Header |
Box type(4 byte) | 0x6D 0x6F 0x6F 0x76 | "moov" | Header |
Box data(Box length - 4 byte) | ...... | Data |
这里我们还要再添加一个需求就是解析 Data 数据里面的子box,那么我们便要重构一下Box Header 和 Base Box 。
// BaseBox.h
enum BOX_TYPE{
FTYP,
MOOV,
// 添加3个新的Box类型
MVHD,
TRAK,
LODS,
UUID,
MDAT,
ERROR
};
struct BoxHeader{
// ...
// 其他不变
BOX_TYPE GetType() {
if(_h_type[0]=='f'&&_h_type[1]=='t'&&_h_type[2]=='y'&&_h_type[3]=='p') return FTYP;
else if (_h_type[0]=='m'&&_h_type[1]=='o'&&_h_type[2]=='o'&&_h_type[3]=='v') return MOOV;
else if (_h_type[0]=='m'&&_h_type[1]=='v'&&_h_type[2]=='h'&&_h_type[3]=='d') return MVHD;
else if (_h_type[0]=='t'&&_h_type[1]=='r'&&_h_type[2]=='a'&&_h_type[3]=='k') return TRAK;
return ERROR;
}
};
class BaseBox{
private:
std::vector<BaseBox*> boxs; // 这个用来存放子Box容器
protected:
Timebyte *data = nullptr;
public:
BoxHeader h;
BaseBox(BoxHeader h);
BaseBox(BoxHeader h, Timebyte * d);
BaseBox(BaseBox &baseBox);
~BaseBox();
Timebyte * GetData() const {return data;};
void PushBox(BaseBox *box);
BaseBox* GetBox(BOX_TYPE type);
void Analyze(); // 解析data数据的方法,子类有需求就重写这个方法
virtual size_t GetDataOffset() const {return 0;}; // 这个是获取当前box的偏移量后面会讲解到
virtual void PrintDataInfo() {};
virtual void AnalyzeBoxHeader(BoxHeader header, size_t offset) {}; // 具体的解析方法,子类要解析data就重写这个方法
};
实现
// BaseBox.cpp
// ...
// 其他的还是没变 就添加了下面的方法
BaseBox *BaseBox::GetBox(BOX_TYPE type) {
for (auto &item : boxs) {
if(item->h.GetType() == type) return item;
}
return nullptr;
}
void BaseBox::PushBox(BaseBox *box) {
boxs.push_back(box);
}
void BaseBox::Analyze() {
size_t offset = GetDataOffset();
while (1) {
BoxHeader header;
memcpy(header._h_size, data + offset, 4);
memcpy(header._h_type, data + offset + 4, 4);
AnalyzeBoxHeader(header, offset);
offset += header.GetSize();
if (offset >= h.GetDataSize()) break;
}
}
moov box 的定义
// BaseBox.h
// ... 其他 box 的定义
class TimeMoovBox : public BaseBox{
private:
public:
TimeMoovBox(BoxHeader h);
TimeMoovBox(BoxHeader h, Timebyte * d): BaseBox(h, d){};
~TimeMoovBox();
void AnalyzeBoxHeader(BoxHeader header, size_t offset) override;
};
实现
// TimeMoovBox.cpp
TimeMoovBox::TimeMoovBox(BoxHeader h) : BaseBox(h) {
if (h.GetSize()) {
data = new Timebyte[h.GetSize()];
}
}
void TimeMoovBox::AnalyzeBoxHeader(BoxHeader header, size_t offset) {
if (header.GetType() == MVHD) {
printf("===========================\n");
header.to_string();
}
if (header.GetType() == TRAK) {
printf("===========================\n");
header.to_string();
}
}
TimeMoovBox::~TimeMoovBox() {
}