传送门:MP4 文档 P24页
这里终于接触到了可以说是 Mp4 Box 里面的核心内容了,最关键的 Stbl Box,这个 Box 主要核心作用就是把音视频的媒体数据和时间进行映射,可以根据这 Box 里面的信息,实时定位到视频各个时间段的数据。这个 Box 通常包含以下子 Box:
采样时间容器: Time To Sample 即 Stts Box
播放时间容器: (composition)Time To Sample 即 Ctts Box
Chunk 采样容器: Sample To Chunk 即 Stsc
采样大小容器: Sample Size 即 Stsz
Chunk 偏移容器:Chunk Offest 即 Stco
采样同步容器: Sync Sample 即 Stss
采样描述容器: Sample Description 即 Stsd Box
具体如下:
全部子Box的声明
Stbl Box 同样是 Container Box,所以这里也仅仅是8字节的分解符,这里Box Header部分不再分析。
这里我们就把所有的 Box 定义完全
// BaseBox.h
enum BOX_TYPE{
FTYP,
MOOV,
MVHD,
TRAK,
LODS,
TKHD,
MDIA,
MDHD,
HDLR,
MINF,
VMHD,
SMHD,
DINF,
STBL,
STSD,
AVC1,
AVCC,
MP4A,
ESDS,
STTS,
CTTS,
STSC,
STSZ,
STZ2,
STSS,
STCO,
CO64,
UUID,
MDAT,
ERROR
};
struct BoxHeader{
Timebyte _h_size[4] = {0};
Timebyte _h_type[5] = {0};
virtual unsigned int GetSize() {
return _h_size[0] << 24 | _h_size[1] << 16 | _h_size[2] << 8 | _h_size[3];
}
unsigned int GetDataSize() {
return GetSize() - 8;
}
virtual void to_string() {
printf("box_size: %ud\n", GetSize());
printf("box_type: %s\n", _h_type);
}
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]=='d'&&_h_type[2]=='a'&&_h_type[3]=='t') return MDAT;
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;
else if (_h_type[0]=='t'&&_h_type[1]=='k'&&_h_type[2]=='h'&&_h_type[3]=='d') return TKHD;
else if (_h_type[0]=='m'&&_h_type[1]=='d'&&_h_type[2]=='i'&&_h_type[3]=='a') return MDIA;
else if (_h_type[0]=='m'&&_h_type[1]=='d'&&_h_type[2]=='h'&&_h_type[3]=='d') return MDHD;
else if (_h_type[0]=='h'&&_h_type[1]=='d'&&_h_type[2]=='l'&&_h_type[3]=='r') return HDLR;
else if (_h_type[0]=='m'&&_h_type[1]=='i'&&_h_type[2]=='n'&&_h_type[3]=='f') return MINF;
else if (_h_type[0]=='s'&&_h_type[1]=='t'&&_h_type[2]=='b'&&_h_type[3]=='l') return STBL;
else if (_h_type[0]=='s'&&_h_type[1]=='t'&&_h_type[2]=='s'&&_h_type[3]=='d') return STSD;
else if (_h_type[0]=='a'&&_h_type[1]=='v'&&_h_type[2]=='c'&&_h_type[3]=='1') return AVC1;
else if (_h_type[0]=='a'&&_h_type[1]=='v'&&_h_type[2]=='c'&&_h_type[3]=='C') return AVCC;
else if (_h_type[0]=='m'&&_h_type[1]=='p'&&_h_type[2]=='4'&&_h_type[3]=='a') return MP4A;
else if (_h_type[0]=='e'&&_h_type[1]=='s'&&_h_type[2]=='d'&&_h_type[3]=='s') return ESDS;
else if (_h_type[0]=='s'&&_h_type[1]=='t'&&_h_type[2]=='t'&&_h_type[3]=='s') return STTS;
else if (_h_type[0]=='c'&&_h_type[1]=='t'&&_h_type[2]=='t'&&_h_type[3]=='s') return CTTS;
else if (_h_type[0]=='s'&&_h_type[1]=='t'&&_h_type[2]=='s'&&_h_type[3]=='s') return STSS;
else if (_h_type[0]=='s'&&_h_type[1]=='t'&&_h_type[2]=='s'&&_h_type[3]=='z') return STSZ;
else if (_h_type[0]=='s'&&_h_type[1]=='t'&&_h_type[2]=='c'&&_h_type[3]=='o') return STCO;
else if (_h_type[0]=='s'&&_h_type[1]=='t'&&_h_type[2]=='s'&&_h_type[3]=='c') return STSC;
return ERROR;
}
};
// ...
// 其他 Box 的定义
class TimeStblBox : public BaseBox {
public:
TimeStblBox(BoxHeader h);
TimeStblBox(BoxHeader h, Timebyte * d): BaseBox(h, d){};
~TimeStblBox();
void AnalyzeBoxHeader(BoxHeader header, size_t offset) override;
void PrintDtsAndPts(); // 这个将来会用到我们这里先声明一个空实现 后面分析到对应Box来写这个函数
};
实现
//
// Created by Time on 2021/12/14.
//
#include "TimeMP4Box.h"
TimeStblBox::TimeStblBox(BoxHeader h) : BaseBox(h) {
if (h.GetDataSize()) {
data = new Timebyte[h.GetDataSize()];
}
}
TimeStblBox::~TimeStblBox() {
}
void TimeStblBox::AnalyzeBoxHeader(BoxHeader header, size_t offset) {
if (header.GetType() == STSD) {
}
if (header.GetType() == STTS) {
}
if (header.GetType() == CTTS) {
}
if (header.GetType() == STSC) {
}
if (header.GetType() == STSZ) {
}
if (header.GetType() == STCO) {
}
if (header.GetType() == STSS) {
}
PrintDtsAndPts();
}
void TimeStblBox::PrintDtsAndPts() {
}