JCHub

  • Home
  • Category
    • A/V
    • WebRTC
    • Beauty of Programming
    • Linux
    • Windows
    • Moments of Life
    • Campus Life
  • Reference
    • API Reference
    • Utilities
    • AV Test
    • Doc
  • Message Board
  • About
JCHub
Code as My Sword, Lost in Obsession
  1. Main page
  2. A/V
  3. Main content

mp4文件elst研究

2016年6月16日 5319hotness 11likes 1comments

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

This article is licensed with Creative Commons Attribution-NonCommercial-No Derivatives 4.0 International License
Tag: ffmpeg 音视频
Last updated:2019年11月21日

Jeff

管理员——代码为剑,如痴如醉

Tip the author Like
< Last article
Next article >

Comments

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
Cancel

This site uses Akismet to reduce spam. Learn how your comment data is processed.

文章目录
  • 结构
  • 例子
  • ffmpeg相关代码分析
  • 相关下载
Related Posts
  • ffmpeg视频编码YUV与AVFrame对应关系
  • 安防视频播放秒开优化
  • avcodec_decode_video2解码得不到图像
  • 告别CapCut收费!开源视频剪辑工具OpenCut 来了
  • 音视频开发入门:视频基础
Categories

COPYRIGHT © 2026 jianchihu.net. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang