elst全称Edit List Box,mp4文件中不一定都含有这个box。该box作用是使某个track的时间戳产生偏移。
结构
在ISO_IEC_14496-12中,elst结构定义如下:
- segment_duration:表示该edit段的时长,以Movie Header Box(mvhd)中的
timescale
为单位。 - media_time:表示该edit段的起始时间,以track中Media Header Box(mdhd)中的
timescale
为单位。如果值为-1,表示是空edit,一个track中最后一个edit不能为空。 - media_rate:edit段的速率为0的话,edit段相当于一个"dwell",即画面停止。画面会在
media_time
点上停止segment_duration
时间。否则这个值始终为1。
例子
现在我们手里有个mp4文件,我们要让封装的视频延迟10秒才开始显示,封装的音频不变,这个可以通过修改视频的时间戳实现,将视频的所有时间戳都加上10秒,但是一个个改太麻烦了,此时elst就派上用场了,我们要通过它让视频时间戳偏移10秒。
下面我们先动手操作番,了解elst如何起作用。
1)找一个没有elst box的mp4文件:test.mp4,假设我放在D:\\bin目录下。至于mp4有没有包含elst可以用文章末尾链接提供的mp4分析工具Mp4Reader分析下。用mediainfo查看该视频的信息:
该mp4音视频时长都为3分32秒。
2)得到带elst的mp4。到https://gpac.wp.mines-telecom.fr/mp4box/ 下载windows下的mp4box,按提示一步步安装。
打开windows cmd命令行,cd到test.mp4目录,然后敲入Mp4Box的命令:
1 2 3 4 5 |
C:\Users\MyPc>D: D:\>cd bin D:\bin>Mp4Box -info test.mp4 |
得到:
由此可知test.mp4中,视频的track id 为1,音频track id为2。
3)接着我们敲如下命令:
1 |
D:\bin>MP4Box -add test.mp4#1:delay=10000 -add test.mp4#2 delay_10s.mp4 |
由于我们只对视频操作,视频track id是1,所以是#1:delay=10000。得到:
此时在bin目录下会生成一个delay_10s.mp4,该mp4中视频track延迟了10秒,音频track不变。mediainfo查看delay_10s.mp4信息:
可以看到视频的时长多了10秒
4)打开mp4reader查看视频track的elst信息:
也就是:
1 2 3 4 5 6 7 8 |
Entry-count = 2 Segment-duration = 10 秒 Media-Time = -1 Media-Rate = 1 Segment-duration = 3分32秒 Media-Time = 0 Media-Rate = 1 |
可以看到有两个elst entry,第一个为空,Segment-duration
为6000,由于timescale
为600(该timescale
在mvhd中获
得),6000除以timescale
刚好为10秒。由此可知我们要延迟播放某个track,可以在elst中插入一个空的entry,Segment-duration
设置为需要延迟播放的时间,Media-Time
设置为-1,然后在插入一个entry,Segment-duration
设置为正常播放时间,Media-Time
也就是起始时间设置为0。
5)播放器验证。我们使用vlc播放器打开delay_10s.mp4:
前10秒视频没有播放,而声音正常播放,到第10秒时视频才开始播放,等声音播放结束后,视频还会播放10秒,可以看出视频确实是推迟了10秒播放,此时音视频已经不同步了。
不是所有的播放器都支持elst的,我测试了下,vlc与potplayer支持,windows自带播放器就不支持。
ffmpeg相关代码分析
下面结合ffmpeg中相关代码以及上面的视频延迟10秒的例子分析,看ffmpeg中对elst数据如何处理。ffmpeg中elst entry数据存放在MOVElst 结构体中:
1 2 3 4 5 |
typedef struct MOVElst { int64_t duration; int64_t time; float rate; } MOVElst; |
duration
对应mp4标准中的segment_duration
time
对应mp4标准中的media_time
在ffmpeg源码libavformat\mov.c中的mov_build_index
函数中有如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
if (sc->elst_count) { int i, edit_start_index = 0, unsupported = 0; int64_t empty_duration = 0; // empty duration of the first edit list entry int64_t start_time = 0; // start time of the media for (i = 0; i < sc->elst_count; i++) { const MOVElst *e = &sc->elst_data[i]; if (i == 0 && e->time == -1) { /* if empty, the first entry is the start time of the stream * relative to the presentation itself */ empty_duration = e->duration; edit_start_index = 1; } else if (i == edit_start_index && e->time >= 0) { start_time = e->time; } else unsupported = 1; } if (unsupported) av_log(mov->fc, AV_LOG_WARNING, "multiple edit list entries, " "a/v desync might occur, patch welcome\n"); /* adjust first dts according to edit list */ if ((empty_duration || start_time) && mov->time_scale > 0) { if (empty_duration) empty_duration = av_rescale(empty_duration, sc->time_scale, mov->time_scale); sc->time_offset = start_time - empty_duration; current_dts = -sc->time_offset; if (sc->ctts_count>0 && sc->stts_count>0 && sc->ctts_data[0].duration / FFMAX(sc->stts_data[0].duration, 1) > 16) { /* more than 16 frames delay, dts are likely wrong this happens with files created by iMovie */ sc->wrong_dts = 1; st->codec->has_b_frames = 1; } } if (!unsupported && st->codec->codec_id == AV_CODEC_ID_AAC && start_time > 0) sc->start_pad = start_time; } |
第8行代码中可以知道,如果e->time == -1
,也就是第一个elst为空,此时得到empty_duration
,按前面的例
子该值为10*timescale
,下一个for循环得到start_time =e->time
,值为0。
在第26行代码中,可知sc->time_offset =0 -10*timescale = -10timescale
,然后所有dts都要减去该sc->time_offse
,最后结果是都加上10timescale
,与原来时间戳比相当于延迟了10s。所以当mp4存在elst时,dts要按如下计算:
1.参考上述ffmpeg代码得到time_offset
2.解码时间戳dts = sample_delta * n – time_offset
,其中sample_delta
在stts中获得,如果存在B帧,还要从ctts中获得sample_offset
,此时:
显示时间戳pts= dts+ sample_offset
否则pts = dts
。
接下来我们使用ffmpeg验证下,打印出所有viedeo packet的dts与pts:
可以看到所有时间戳都偏移了230000,也就是10timescale
,在video track的mdhd中可知timescale为23000,刚好是10timescale = 23000*10
:
由此可知elst的作用就是使某个track时间戳偏移,达到延迟播放的效果。在我们解析mp4文件时,如果存在elst,一定要解析,然后配合stts与ctts,这样才可以得到正确的时间戳。
相关下载
Mp4Reader:https://pan.baidu.com/s/1cBC_yRR-BUfMGpUnC2LN8g
文章评论