AVI 文件格式解析

AVI 文件格式解析

前言

    AVI(Audio Video Interleaved 音频视频交错格式)是一种音视频的封装格式,于1992年由微软公司推出。它用RIFF(Resource Interchange File Format 资源交换文件格式)描述,包括ChunkLIST两种结构。Chunk用来描述(音频/视频/字幕)的数据,包含在movi LIST中:

movi中Chunk标志是4字节的ASCII码标识符dwFourCC("four-character code") :

    除了dc,wb,tx之后 ,还需要在其前面添加00,01,02这些数字,表示流的编号,如00dc,01wb,02txChunk长度指出了Chunk内容的长度,这个长度不包括前面4字节的标识符和4字节的Chunk长度,它以小端的方式呈现。 如果Chunk的长度不是偶数,有1个字节的padding。AVI中其他常用FourCC及文件结构列举如下:

LIST其实用来包含Chunks下一级LIST,一个AVI文件中可能有多个RIFF LIST,RIFF LIST紧接着后面的FourCC如果是"AVI ",表示标准的AVI文件,如果是"AVIX",则表示AVI文件的扩展部分,用来保存超过1G的movi数据,而其他信息则不应该保存在扩展部分的LIST中        

由于AVI格式的历史重要性,常常需要对其进行分析,本文旨在列出要点,以备查预阅需要。

格式分析

AVI的分析工具有:AVIMux_GUI,RIFFspot等。一个AVI格式的视频举例:

MainAVIHeader结构体

 AVIStreamHeader结构体

BITMAPINFOHEADER结构体

WAVEFORMAT结构体

可能是WAVEFORMAT, PCMWAVEFORMAT or WAVEFORMATEX结构体中的一个,以下举例:

索引表

   AVI格式的索引表不仅是关键帧的索引,它的索引是针对每一条流的每一个Chunk的,也就是说有多少个数据Chunk,就有多少个索引项,因此开销也特别的大。AVI的索引表存在几个版本:

它的FourCC是"idx1",并且放在于movi之后 ,idx1后的长度size,表示索引数组的个数。索引指向的位置其实是真实数据所在的FourCC开始的位置位置,用dwChunkLength来描述这个Chunk的长度。受限于DWORD的字节数,AVI1.0的索引表实际上只能索引到4G范围内,于是有了Open-DML Index的索引表,一种通用的Open-DML Index结构定义:

各字段含义说明:

 实际应用中Open-DML Index包含了Upper Level Index(Super Index)、Standard Index和Field Index。

Super Index:

    Super Index其实是索引表的索引,它的FourCC是"indx",bIndexSubType是Super index指向的索引类型,0表示指向标准索引,1表示指向场索引Super Index不是放在movi之后 ,而是放在strf Chunk之后 ,也就是放在文件头部的每条流的描述信息中,它既可以指向索引,也可以指向真实的数据块。指向的索引应位于movi之后,并且它的FourCC应该是"ix01"这样的,这儿的01表示流的编号和dc前面的字符一致。

Standard Index:

    单个Standard index只能索引到4G的范围内,wLongsPerEntry取值为2,bIndexSubType设置为0;bIndexType设置为AVI_INDEX_OF_CHUNKS,dwOffset指向数据的起点,绝对位置是qwBaseOffset + dwOffset, dwSize的BIT31位是1表示非关键帧这个index可以放在movi之后,也可以放在文件头部。

Field Index:

     单个Field index只能索引到4G的范围内,wLongsPerEntry取值3,dwOffsetField值为2表示第二个场的偏移量,dwSize的BIT31位是1表示非关键帧。这个index可以放在movi之后,也可以放在文件头部。

AVI时间戳

    说了这么多,还没有列出AVI的时间戳计算方式,AVI的时间戳与索引表有关,下面给出一种实现方式。FFMPEG中索引表调用顺序如下: 

avformat_open_input->avi_read_header->avi_load_index->avi_read_idx1->av_add_index_entry->ff_add_index_entry

    每一个Chunk都有一个索引项,因此,在遍历索引表的时候,就将索引表的时间戳根据前一个索引表的时候戳和dwSampleSize算出来,dwSampleSize异常的时候,每次将索引表的时间戳加1,ffmpeg在AVI中算的是DTS,这个值需要乘以wScale/dwRate,才能得出PTS。ffmpeg在读的时候,去查了每一个表的索引项,索引项的时间戳就等于要读取的Chunk的时间戳。因此没有索引表的AVI播放也是比较麻烦的。总结起来就是:

关于FFMPEG对AVI索引的处理

        FFMPEG顺序读取AVI包,读取之初并没有目前也不能指定需要读取的stream_index,avi->stream_index最初是为-1。需要进入avi_sync函数重新查找下一个能够读取的包,这儿有几点值得注意:
        1. AVI的索引表并不包含每个每个包的时间戳,时间戳是用累加的方式得来的,如帧率fps为50的码流(50/1),那第0个索引表项的时间戳是0,第1个索引表项的时间戳是1,第3232个索引表项的时间戳就是3232,对应真实时间就是3232/50
=64.64秒。所以AVI的码流每个包都需要一个索引项,否则不能计算出时间戳,这也是AVI视频索引表可能比较大的原因。
        2.FFMPEG的AVI seek之初,调用av_find_default_stream_index匹配默认最佳的第一次搜索码注,一般为视频。SEEK完成之后,会将SEEK到的时间作为基准,去搜索其他流的索引表,这个时候可能改变第一次搜索过的码流的位置,如果此时 不加处理顺序读取码流,那么第一条流的输出结果可能会是错的,已经配置好的流的frame_offset其实已经改变,需要重新匹配。
        3.每一条流都对应单独的AVIStream和AVStream

static int avi_read_packet(AVFormatContext *s, AVPacket *pkt)
{
……
resync:
    if (avi->stream_index >= 0) {
        ……
    }

    if ((err = avi_sync(s, 0)) < 0)
        return err;
    goto resync;
}

参考网页:

https://en.wikipedia.org/wiki/Resource_Interchange_File_Format
http://www.daubnet.com/en/file-format-riff

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值