MP4⽂件中⾳视频时间戳的计算
MP4⽂件的组成
MP4⽂件的格式遵循ISO/IEC14496-12标准,即ISObamediafileformat。所有数据都封装在被称为Box的数据结构中,⼀个MP4⽂件,
是由多个Box组成的。
MP4⽂件的最外层Box
如上图所⽰,该MP4⽂件由ftype、free、mdat和moov四个Box组成。
其中moovBox属于containerbox,它⼜可以包含有其他的Box。它⾥⾯保存的数据如下图所⽰
moovbox
在这⾥moovbox及其⼦box包含了该MP4⽂件的元数据,⽤于指定⾳视频数据的存储位置,数据类型,时间戳之类的信息。
mdatbox为长度最⼤的box,该⽂件中的⾳视频数据都包含在该box中,可以通过解析moovbox来获取每帧⾳视频数据具体保存的位置。
moovbox包含有每帧⾳视频数据在⽂件中的偏移量信息,所以⼀般都是位于⽂件尾,⽤于⽅便保存⽂件时记录偏移量信息。但是也可以通过
其他⽅式将其移动到⽂件前⾯的位置(MP4box和ffmpeg都可以做到),这样做的好处是播放器在播放⽹络上的MP4⽂件时,可以直接读取
到⽂件的索引信息,使得开播更快。
Box结构的定义
box
fullbox
size字段表⽰该Box的长度,如果size值为1,则表⽰box的长度超过了32位的表⽰范围,需要由type之后的64位⽤于表⽰实际的长度。
type字段表⽰该Box的类型,⼀般使⽤4个可打印的字符组合表⽰,也称为FOURCC,如ftyp、moov、meta、mdat等。
⼤部分box除了包含有size和type字段外,还包含有version和flag字段,⽤于处理在标准升级时产⽣的box内容定义不⼀致的问题。
除去以上数据后box剩余的数据为该box的实际数据,根据type不同,表⽰的含义也各不相同。
moovbox
如上图所⽰,moovbox中会包含有⼀个mvhdbox和⼀个或多个trakbox,每个trakbox表⽰⼀个⾳视频的流。
mvhdbox
定义如下:
mvhdbox
mvhdbox中的duration和timescale字段⽤来指定该⽂件的播放时长,duration/timescale的值即为单位为秒的时长。如果⽂件中多个流的
时长不⼀致,该位置为最⼤时长。如下图所⽰,该⽂件的播放时长为189167/1000=189.167秒。
mvhdbox
trakbox
每个trakbox表⽰⼀路单独的流,可能是⾳频也可能是视频。
mdiabox下的hdlrbox⽤来指定该流是⾳频还是视频
stsdbox的⼦box⽤于保存该流的编码类型
avcC
上图中avcCbox指定了该流的编码类型为H264,且存储了解码所需的SPS、PPS信息。
stscstszstco三个box⽤于保存没帧视频或⾳频数据在⽂件中的保存位置。
sttsstssctts三个box⽤于保存媒体数据和时间戳的对应关系。
Sample(⾳视频帧)保存位置的计算
stsz(SampleSizeBox)⽤于保存每个sample对应的⼤⼩
stsz
sample_count字段指明sample的个数;
如果每个sample⼤⼩都相等的话,则sample_size字段为sample的⼤⼩。否则sample_size设置为0,每个sample的⼤⼩由后续的⼀个数组
来指定。
stsc(SampleToChunkBox)
多个sample组成⼀个chunk,stscbox保存了sample和chunk之间的对应关系。
stsc
每个chunk可以有⼀个或多个sample,如果相邻的chunk含有相同的sample数量,则first_count字段⽤于指明第⼀个chunk的索
引,sample_per_chunk指明该组chunk中每个chunk中sample的数量。
sample
如上图
index为1的chunk含有3个sample;
index为2的chunk含有1个sample;
然后下⼀个first_chunk值为4,则表明index为3的chunk含有和2相同数量的sample,也是1个;
继续,index为4的chunk含有2个sample;
index为5和6的chunk含有1个sample;7有2个sample;8有1个sample;9含有2个sample;
stco(ChunkOfftBox)
stcobox指明了每个chunk在⽂件中的存储位置
stco
entry_count指明了总的chunk的数量
chunk_offt指明了该chunk在⽂件中的偏移量
以上三个box结合起来,即可计算每个sample在⽂件中保存的位置和⼤⼩
voidmp4Parr::GetSamplePosition(Stream*s)
{
intsample_count=s->stsz_count;
intchunk_count=s->stco_count;
if(sample_count>0)
{
s->sample_position=newuint64_t[sample_count];
}
intremain_chunk_count=chunk_count;
intsample_index=0;
for(inti=0;i
{
intc_count=0;
if(i!=s->stsc_count-1)
{
c_count=s->stsc_data[i+1].first_chunk-s->stsc_data[i].first_chunk;
remain_chunk_count-=c_count;
}
el
{
c_count=remain_chunk_count;
}
for(intj=0;j
{
intchunk_index=s->stsc_data[i].first_chunk+j;
uint64_tofft=s->stco_data[chunk_index-1];
for(intk=0;k
{
s->sample_position[sample_index]=offt;
offt+=s->stsz_data[sample_index];
sample_index++;
if(sample_index>sample_count)
return;
}
}
}
}
PTS和DTS的计算
IPB帧的概念
在视频压缩中,为了提⾼压缩率,会将每帧画⾯压缩为不同类型的视频帧数据。
I帧表⽰关键帧,包含有⼀帧画⾯的完整信息,解码时只需要本帧数据就可以解码出完整的⼀帧画⾯。
P帧表⽰前向参考帧,它保存了本帧与上⼀帧的差异信息,它不能单独解码,需要根据上⼀帧的画⾯加上本帧保存的差值来获取本帧的完整画
⾯。
B帧为双向参考帧,它解码时需要依赖它之前和之后的帧来获取最终的画⾯
因为B帧需要依赖它后⾯的帧来进⾏解码,所以它的解码顺序就必然和显⽰顺序不能保持⼀致,这是就需要解码时间戳(DTS)和显⽰时间戳
(PTS)来共同决定⼀帧视频数据何时解码,然后何时显⽰了。
stts(TimeToSampleBox)
stts
根据sttsbox可以计算出每个sample的dts,其中sample_delta为该sample的dts相对于上⼀个smaple的差值,⽐
如
entry_count=1,sample_count=5,sample_delta=1024
时,5个sample的dts将依次为
24096
。
ctts(CompositionOfftBox)
ctts
cttsbox保存了每个sample的compositiontime和decodetime之间的差值,这⾥CompositionTime就直接理解成PTS吧。
如果不存在cttsbox,则代表该流不存在B帧,那么PTS就直接等于DTS,例如⾳频数据就不存在cttsbox。
根据stts和ctts两个box可以计算出sample的DTS和PTS
stss(SyncSampleBox)
stss
stssbox保存了哪些帧是关键帧(即I帧),做ek跳转时,视频需要从关键帧开始解码,否则解码会出现异常。
⽰例
这⾥我们选择⼀个只有5帧画⾯的MP4⽂件进⾏分析
stsz内容:
sample_count=5
index=1,size=919
index=2,size=39
index=3,size=36
index=4,size=36
index=5,size=36
stsc内容:
entry_count=2
first_chunk=1,samples_per_chunk=3,sample_description_index=1
first_chunk=2,samples_per_chunk=1,sample_description_index=1
stco内容
entry_count=3
index=1,chunk_offt=48
index=2,chunk_offt=1051
index=3,chunk_offt=1096
index为1、2、3的三帧组成为chunk1
chunk1的起始地址为48,则sample1的起始地址为48,sample2的起始地址为48+919=967(919为sample1的⼤⼩),sample3的起始地
址为967+39=1006(39为sample2的⼤⼩)。
chunk2和chunk3只包含有1个sample,分别为sample4和sample5
chunk2的起始地址为1051,则sample4的起始地址为1051
chunk3的起始地址为1096,则sample5的起始地址为1096
stts内容:
stts_count=1
count:5,delte:512
ctts内容:
ctts_count=5
count:1,offt:1024
count:1,offt:2560
count:1,offt:1024
count:1,offt:0
count:1,offt:512
根据stts可知,5个sample的DTS分别为0、512、1024、1536、2048
与ctts内容相加,可得PTS分别为1024、3072、2048、1536、2560
即实际显⽰的顺序应该是按照PTS从⼩到⼤的顺序(1、4、3、5、2)
DTS和PTS值转换为时间
以上计算出来的DTS和PTS为⼀个整形的数值,但是他们如何转换为以秒为单位的实际时间呢?
参看上⾯第⼆幅图,moov/trak/mdia/mdhd这个顺序下的mdhdbox
mdhd
此box中有和mvhd中同样的timesacle和duration字段,两处并不⼀定⼀致,mdhdbox中的timescale和duration表⽰当前流的时
长,duration/timescale的值即为当前流的时长。
同样,PTS和DTS除以timescale即为相应的以秒为单位的时间
上⾯那个例⼦中,视频流的timescale=15360,则相应的DTS和PTS应该为(0、0.033、0.067、0.1、0.133)(0.067、0.2、0.133、
0.1、0.167)。
elst(EditListBox)
moov/trak/edts/elstbox同样对PTS会产⽣影响,它可以是实际时间戳产⽣偏移
elst
gment_duration:表⽰该edit段的时长,以MovieHeaderBox(mvhd)中的timescale为单位。
media_time:表⽰该edit段的起始时间,以track中MediaHeaderBox(mdhd)中的timescale为单位。如果值为-1,表⽰是空edit,⼀个
track中最后⼀个edit不能为空。
media_rate:edit段的速率为0的话,edit段相当于⼀个”dwell”,即画⾯停⽌。画⾯会在media_time点上停⽌gment_duration时间。
否则这个值始终为1。
为使PTS从0开始,media_time字段⼀般设置为第⼀个CTTS的值,计算PTS和DTS的时候,他们分别都减去media_time字段的值就可以将PTS
调整为从0开始的值
如果media_time是从⼀个⽐较⼤的值,则表⽰要求PTS值⼤于该值时画⾯才进⾏显⽰,这时应该将第⼀个⼤于或等于该值的PTS设置为0,其他
的PTS和DTS也相应做调整
如果elstbox中有多个设置,表⽰会有多段的显⽰,具体⽤法这⾥不再说明,可以查询elstbox⽤法。
本文发布于:2023-03-07 06:51:06,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/1678143067168788.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:mp4视频.doc
本文 PDF 下载地址:mp4视频.pdf
留言与评论(共有 0 条评论) |