剑痴乎

  • 首页
  • 文章分类
    • 音视频
    • WebRTC
    • 编程之美
    • Linux
    • Windows
    • 生活点滴
    • 校园生活
  • 参考
    • API参考
    • 实用工具
    • 测试音视频
    • 文档
  • 留言板
  • 关于
剑痴乎
代码为剑,如痴如醉
  1. 首页
  2. 音视频
  3. 正文

mp4文件elst研究

2016年6月16日 4613点热度 11人点赞 1条评论

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的命令:

C:\Users\MyPc>D:

D:\>cd bin

D:\bin>Mp4Box -info test.mp4

得到:

由此可知test.mp4中,视频的track id 为1,音频track id为2。

3)接着我们敲如下命令:

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信息:

也就是:

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 结构体中:

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函数中有如下代码:

    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

本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可
标签: ffmpeg 音视频
最后更新:2019年11月21日

Jeff

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

打赏 点赞
< 上一篇
下一篇 >

文章评论

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

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理。

文章目录
  • 结构
  • 例子
  • ffmpeg相关代码分析
  • 相关下载
相关文章
  • ffmpeg视频编码YUV与AVFrame对应关系
  • 安防视频播放秒开优化
  • avcodec_decode_video2解码得不到图像
  • 音视频开发入门:视频基础
  • 大话WebRTC

COPYRIGHT © 2025 jianchihu.net. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang