传送门:MP4 文档 P26页
该 Box 记录的是每个 sample 的 Composition time 和 Decode time 之间的差值,这里通过 Composition Time 就可以计算出 Sample 的 PTS。所以要先解析完 Stts Box 获取到对应 sample 的 DTS 后解析这个 Box 获取到 Sample 的 PTS。
当视频不存在 B 帧的情况下 PTS 是等于 DTS 的,所以不含 B 帧的视频文件也就没有 Ctts Box,同样音频也没有 Ctts Box。
根据上文解析的 Stts Box 里面的 DTS,只要在对应的 Sample count 的 DTS 加上当前这个 Sample offset 就是PTS了。
上图箭头标注的 Sample count 为2,说明第3个 sample 和第4个 sample 的 Sample offset 是一样的。
其中该 Box 是 Full Box,后面有四字节的 version 和 flag 字段,其中 flag 默认值用0,这里分析略过,这里就直接解析 Data 的字段。
字段 | 大小 | 实际值(16进制) | 含义 |
---|---|---|---|
entry count | 4 | 00 00 04 FB | 描述了下面采样 sample 的信息个数 |
sample count | 4 | 连续相同 offset 的sample个数。 | |
sample offset | 4 | DTS和PTS之间的偏移量 |
我们结合 stts box 进行分析即可以得到前面5个 sample 的 DTS 和 PTS:
Sample num | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
sample delta | 413 | 416 | 418 | 417 | 832 |
sample offset | 1489 | 1492 | 1493 | 1493 | 1908 |
DTS | 0 | 413 | 416+413=829 | 829+418=1247 | 1247+417=1664 |
PTS | 1489 | 1905 | 2322 | 2740 | 3572 |
使用雷神软件分析比对前5个 sample 的 PTS,发现一样。
// BaseBox.h
// ...
// 其他 Box 的定义
class TimeCttsBox : public BaseBox {
public:
Timebyte version = 0;
Timebyte flags = 0;
unsigned int entry_count = 0;
unsigned int sample_count = 0;
unsigned int sample_offset = 0; // CT和DT之间的offset。
///// 这里的数据不在data数据区里面
unsigned int allSample = 0;
unsigned int allOffset = 0;
///// =========================
TimeCttsBox(BoxHeader h, Timebyte * d): BaseBox(h, d){};
void PrintDataInfo() override;
};
实现
// TimeCttsBox.cpp
void TimeCttsBox::PrintDataInfo() {
TimeBufferStream timeBufferStream(data, h.GetDataSize());
version = timeBufferStream.GetUChar();
timeBufferStream.GetLenData(&flags, 3);
entry_count = timeBufferStream.GetUInt();
sample_count = timeBufferStream.GetUInt();
sample_offset = timeBufferStream.GetUInt();
allSample = sample_count;
allOffset = sample_count;
for (int i = 0; i < entry_count; ++i) {
sample_count = timeBufferStream.GetUInt();
sample_offset = timeBufferStream.GetUInt();
allSample += sample_count;
allOffset += sample_offset * sample_count;
}
printf("===========================\n");
h.to_string();
printf("all Sample: %ud\n", allSample);
printf("all Offset: %ud\n", allOffset);
}