传送门:MP4 文档 P30页
上面我们介绍了 Stsz Stco 这2个 Box,分别可以知道 Chunk 的偏移量,又可以知道在 Chunk 里面的各个 Sample 的大小,但是现在还缺一样,就是不知道一个 Chunk 里面包含了多少个 Sample,这个 Box 提供的就是 Chunk 包含 Sample 的信息。综上所述媒体数据被分为若干个 Chunk, Chunk 可以有不同的大小,同一个 Chunk 中的样点 Sample 也允许有不同的大小。
这里解释一下上面各个的意思:
First chunk: 代表第几个 Chunk,可以理解为 Chunk index。
Samples per chunk: 当前 Chunk 里面的 Sample 个数。
Sample description index:Sample 的描述。
我们发现上面红色箭头指向12后面不是13,这里说明第12个和第13个 Chunk 里面的 Sample 数量是一样的。当然这里 Sample 的数量加起来是等于 Sttz Box 里面的 Sample count 就是总的 Sample 数量。
其中该 Box 是 Full Box,后面有四字节的 version 和 flag 字段,其中 flag 默认值用0,这里分析略过,这里就直接解析 Data 的字段。
字段 | 大小 | 实际值(16进制) | 含义 |
---|---|---|---|
entry count | 4 | 00 00 00 50 | Chunk 数量 |
first chunk | 4 | Chunk Index | |
sample per chunk | 4 | 每一个 chunk 里面包含多少的 sample | |
sample description index | 4 | 每一个 sample 的描述。一般可以默认设置为 1。 |
// BaseBox.h
// ...
// 其他 Box 的定义
class TimeStscBox : public BaseBox {
public:
Timebyte version = 0;
Timebyte flags = 0;
unsigned int entry_count = 0;
unsigned int first_chunk = 0;
unsigned int sample_per_chunk = 0;
unsigned int sample_description_index = 0;
TimeStscBox(BoxHeader h, Timebyte * d): BaseBox(h, d){};
void PrintDataInfo() override;
};
实现
// TimeStscBox.cpp
void TimeStscBox::PrintDataInfo() {
TimeBufferStream timeBufferStream(data, h.GetDataSize());
version = timeBufferStream.GetUChar();
timeBufferStream.GetLenData(&flags, 3);
entry_count = timeBufferStream.GetUInt();
unsigned int upNumb = timeBufferStream.GetUInt();
unsigned int allSample = timeBufferStream.GetUInt();
unsigned int upCount = allSample;
timeBufferStream.GetUInt();
for (int i = 0; i < entry_count - 1; ++i) {
unsigned int numb = timeBufferStream.GetUInt();
unsigned int count = timeBufferStream.GetUInt();
timeBufferStream.GetUInt();
if(numb - upNumb == 1) allSample += count;
else allSample += (numb - upNumb - 1) * upCount + count;
upNumb = numb;
upCount = count;
}
printf("===========================\n");
h.to_string();
printf("allSample: %d\n",allSample);
}
最后再修改一下Stbl Box
// TimeStblBox.cpp
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) {
// Analyze
printf("===========================\n");
header.to_string();
TimeStsdBox stsdBox(header, data + offset + 8);
stsdBox.Analyze();
}
if (header.GetType() == STTS) {
// 这个可以给出解码时间和Sample的关系
TimeSttsBox* sttsBox = new TimeSttsBox(header, data + offset + 8);
sttsBox->PrintDataInfo();
PushBox(sttsBox);
}
if (header.GetType() == CTTS) {
// 这个可以通过Composition Time就可以计算出Sample的PTS
// 没有B帧的话就没有这个BOX 此时 dts=pts
TimeCttsBox* cttsBox = new TimeCttsBox(header, data + offset + 8);
cttsBox->PrintDataInfo();
PushBox(cttsBox);
}
if (header.GetType() == STSC) {
// 这个可以找到对应的chunk里面有多少个sample
TimeStscBox* stscBox = new TimeStscBox(header, data+offset+8);
stscBox->PrintDataInfo();
PushBox(stscBox);
}
if (header.GetType() == STSZ) {
// 这个可以获取到每一个的sample的size
TimeStszBox* stszBox = new TimeStszBox(header, data + offset + 8);
PushBox(stszBox);
}
if (header.GetType() == STCO) {
// 这个可以通过chunk的偏移量找到每个Chunk在文件中的位置
TimeStcoBox* stcoBox = new TimeStcoBox(header, data+offset+8);
PushBox(stcoBox);
}
if (header.GetType() == STSS) {
// 找到存储了那些Sample是I关键帧
TimeStssBox* stssBox = new TimeStssBox(header, data + offset + 8);
stssBox->CheckIFrame();
PushBox(stssBox);
}
PrintDtsAndPts();
}
void TimeStblBox::PrintDtsAndPts() {
TimeSttsBox* sttsBox = (TimeSttsBox*)GetBox(STTS);
TimeCttsBox* cttsBox = (TimeCttsBox*)GetBox(CTTS);
TimeStssBox* stssBox = (TimeStssBox*)GetBox(STSS);
TimeStscBox* stscBox = (TimeStscBox*)GetBox(STSC);
TimeStszBox* stszBox = (TimeStszBox*)GetBox(STSZ);
TimeStcoBox* stcoBox = (TimeStcoBox*)GetBox(STCO);
if (sttsBox && cttsBox && stssBox && stszBox && stscBox && stcoBox) {
printf("===========================\n");
unsigned int *dts = new unsigned int[sttsBox->allSample];
int d_index = 0;
unsigned int *pts = new unsigned int[cttsBox->allSample];
int p_index = 0;
TimeBufferStream stts(sttsBox->GetData() + 4, sttsBox->h.GetDataSize() - 4);
TimeBufferStream ctts(cttsBox->GetData() + 4, cttsBox->h.GetDataSize() - 4);
TimeBufferStream stsz(stszBox->GetData() + 4, stszBox->h.GetDataSize() - 4);
TimeBufferStream stsc(stscBox->GetData() + 4, stscBox->h.GetDataSize() - 4);
unsigned int c1 = stts.GetUInt();
unsigned int c2 = ctts.GetUInt();
unsigned int sample_size = stsz.GetUInt();
unsigned int d_sample_count = 0;
unsigned int d_sample_delta = 0;
unsigned int p_sample_count = 0;
unsigned int p_sample_delta = 0;
unsigned int sample_count = stsz.GetUInt();;
unsigned int entry_size = stsz.GetUInt();
printf("sample_count:%d\n",sample_count);
for (int i = 0; i < c1; ++i) {
d_sample_count = stts.GetUInt();
d_sample_delta = stts.GetUInt();
for (int j = 0; j < d_sample_count; ++j) {
dts[d_index++] = d_sample_delta;
}
}
for (int i = 0; i < c2; ++i) {
p_sample_count = ctts.GetUInt();
p_sample_delta = ctts.GetUInt();
for (int j = 0; j < p_sample_count; ++j) {
pts[p_index++] = p_sample_delta;
}
}
unsigned int d = 0;
unsigned int p = dts[0];
unsigned int ii = 0;
for (unsigned int i = 0; i < sttsBox->allSample; ++i) {
p = d + pts[i];
d += dts[i];
if (stssBox->IFrames[ii] - 1 == i) {
ii++;
printf("numb:%d, dts: %ud, pts: %ud, size: %ud (I frame) \n",
i, d, p, entry_size);
} else {
printf("numb:%d, dts: %ud, pts:%ud, size: %ud\n",
i, d, p, entry_size);
}
if(!sample_size) entry_size = stsz.GetUInt();
}
} else if (sttsBox) {
// 没有ctts 不含B帧 PTS是等于DTS
}
}