最近突发奇想研究了一下视频播放,听说 AVI 视频文件较简单,找到了一篇古老的英文文档,翻译后供自己看。
原文链接:https://web.archive.org/web/20170411001412/http://www.alexander-noe.com/video/documentation/avi.pdf
1 introduction
1.1 为什么要这一个AVI文件格式文档
尽管AVI文件格式已经存在了10多年,但是没有一个文档不仅描述了格式本身,而且还说明了有缺陷的 demuxer 和 decoder 的问题,以及如何规避这些问题。
本文档的目的不仅仅是解释AVI格式,就像文章中定义的那样,而是在使用有缺陷的 muxers、demuxer、decompressor 时如何使用它。
1.2 基本数据结构
1.2.1 Chunks
1 | typedef struct{ |
1.2.2 Lists
1 | typedef struct{ |
一个 chunk 包含视频、音频或字幕数据,使用 dwFourCC 中包含的 2 个十六进制数字指定流编号,2个字母指定数据类型(dc = 视频,wb = 音频, tx = 文本)。dwFourCC 和 dwSize 在两种结构中的意思相同,dwFourCC 描述了 chunk 的类型(例如 “header list” 是 “hdrl”),dwSize 是 chunk 或 list 的大小,包括了 dwSize 后的第一个字节。在 list 结构中,dwSize 包括了 dwFourCC 的 4 字节。
dwList 的值可以是 “RIFF” (RIFF-List) 或 “LIST” (List)。
注:FourCC : four character code
1.3 AVI 文件类型
通常有三种类型的 AVI 文件:
- AVI 1.0 原始的,老的 AVI 文件
- Open-DML 一个 AVI 文件格式的扩展,在 1996.2.28 制定了 V1.02 版本,最重要的改进是:
- 几乎不限制文件大小(超过了 NTFS 允许的大小)
- 开销减少 33%
- Hybride-Files Open-DML 文件为了兼容性,包含了一个额外的索引。这不是一个官方的词,但是它很好的描述了文件类型。仅包含一个 RIFF List 的 Hybride files 可以视为任意一种文件类型。
本文档描述了 Open-DML 1.02 文件格式特性的子集,以及一些附加功能(“hacks”)。如果安装了适当的(免费可用的)过滤器,这些附加功能将在普通播放器中使用。在 Open-DML 中可用,但在 AVI 1.0 中不可用的特性,将被指示为仅 Open-DML。
2 AVI 文件的布局
当一个 RIFF-List 的 dwFourCC 是 “AVI ” 时被称为 RIFF-AVI-List,当一个 RIFF-List 的 dwFourCC 是 “AVIX” 时被称为 RIFF-AVIX-List。
每一个 AVI 文件都有如下的布局:
1 | RIFF AVI // mandatory |
这些 List 的大小不是 uint32_t 的最大值 4GB,而是:
- 对于 AVI 1.0:size(RIFF-AVI) < 2GB
- 对于 Open-DML:
- size(RIFF-AVI) < 1GB (某些 muxing 软件是 2GB)
- size(RIFF-AVIX) < 2GB
如果没有找到 Legacy index,Windows XP 总是把第一个 RIFF AVI List 整个读取,这个 Legacy index 会导致一定的开销,所以推荐尽可能创建一个小的 RIFF AVI List。
2.1 Header
AVI 文件的 Header 看起来是这样:
接下来说明这些 header 和 chunk 的意思。
2.1.1 MainAVIHeader(avih)
这个结构定义如下:
1 | typedef struct { |
不幸的是,这些值没有它们的名字看上去的那种意思。
-
dwMicroSecPerFrame
包含一个视频帧的时间,以 us 为时间。这个值可以忽略,但是任何 AVI 文件都应该正确的填入该值。重要:一些坏的软件将帧率写入到 stream header 而不是 dwMicroSecPerFrame 。因此 dwMicroSecPerFrame 是不可信的。
-
dwMaxBytesPerSec
文件的最大数据率。这个也不是不重要,只是不应该过高的估计重要性
-
dwPaddingGranularity
文件被填充为这个的倍数
-
dwFlags
see below
-
dwTotalFrames
包含 RIFF-AVI List 中视频的帧数(在 RIFF-AVIX-List 中,不是整个文件的帧数。一些声称可以处理AVI文件的工具甚至会假设这一点,但它肯定违反了opendml文件格式规范。)鉴于一些 AVI 文件 muxer 在这写入错误的值,这个值也变得不可信。
-
dwInitialFrames
忽略这个
-
dwStreams
文件中 stream 的数量
-
dwSuggestedBufferSize
装载这个文件中chunk的buffer大小。这个值的可信度不可高估
-
dwWidth
视频流的宽度
-
dwHeight
视频流的高度
MainAVIHeader 中可用的 flags:
- AVIF_HASINDEX :文件中有一个 index
- AVIF_MUSTUSEINDEX : 视频和音频块播放的顺序必须由 index 确定,并且可能不同于这些块在文件中出现的顺序。
- AVIF_ISINTERLEAVED :这些 stream 适当地交织在一起
- AVIF_WASCAPTUREFILE :The file was captured. The interleave might be weird
- AVIF_COPYRIGHTED : 忽略
- AVIF_TRUSTCKTYPE :只有 Open-DML 才有,这个 flag 表明了在 index 中的关键帧标记是可信的。如果在 Open-DML 文件中没有这个flag,则关键帧标志可能有缺陷,但技术上不会使文件无效。
2.1.2 Stream Header list (strl)
每一个 stream 都有一个 strl 。如果在 hdrl 中 strl 的数量与 MainAVIHeader 中 streams 字段的值不相等,则应该报一个 fatal error。
2.1.3 stream header chunk
1 | typedef struct { |
同样的,这些值的意思也不总是明显的。
-
fccType 可以是
vids
,auds
,txts
依次代表 视频流,音频流, 字幕流 -
fccHandler :使用的编码器
-
dwFlags:
-
AVISF_DISABLED : stream 默认处于未激活状态
-
AVISF_VIDEO_PALCHANGES : stream 是一个使用调色板的视频流,并且调色板在播放时会改变。
-
dwInitialFrames :文件中存在的流的第一个 chunk 的编号。
-
dwRate / dwScale = samples / second(audio) or frames / second(video). dwScale 和 dwRate 应该互为素数,测试表明,使用10000000/400000 而不是 25/1 将导致某些硬件 MPEG4 播放器不工作。
-
dwStart :stream的开始时间。如果是VBR音频,这个值指示了 stream 开始前的静音帧数量
-
dwLength :以dwRate和dwScale中定义的单位表示的流大小
-
dwSuggestedBufferSize :用来存储 stream 块的缓存大小。可以是0(这样应用程序必须靠猜),但是不应该是0,因为 Microsoft 的 AVI splitter 不能正确处理这种情况(例如 Open-DML 文件中的 MP3-CBR)
-
dwQuality :指示 stream 的质量,不重要
-
dwSampleSize :stream 中一个 atom 的字节数(不应进一步拆分)
2.1.4 stream header list (strf)
strf 块的结构依赖于媒体的类型。视频流使用 BITMAPINFOHEADER 结构,音频流使用 WAVEFORMATEX 结构
2.1.5 stream header list (index)
这个块包含更高级别的索引,见第3章
2.1.6 stream header list (strn)
这个块包含流的名字,该名字应该仅使用简单的 ASCII,尤其不能是UTF-8
3 AVI indexes
3.1 旧风格的索引
所描述的索引是你将在 AVI1.0 文件中找到的索引。它放在 RIFF AVI List 的 movi List 之后。idx1块的数据部分具有以下布局:
1 | AVIINDEXENTRY index_entry[n] |
这些值的意思如下:
-
ckid :规定与文件中数据块的 chunkID 相对应的 FourCC
-
dwFlags :
以下flag被定义:
- AVIIF_KEYFRAME :指向的块是一个关键帧
- AVIIF_LIST :指向一个List 而不是块
- AVIIF_FIRSTPART :指示此块需要使用它后面的帧;它不能独立
- AVIIF_LASTPART :指示此块需要使用它前面的帧;它不能独立
- AVIIF_NOTIME :应用于相应区块的持续时间为0
如果 AVIIF_FIRSTPART 和 AVIIF_LASTPART 都没有设置,这个块可以独立,换言之,它是对应流的至少一个分组。这对于在 AVI 文件中存储 VBR 音频流非常重要(参见第5.4章)
- dwChunkOffset :包含相应块的头位置。警告:这可以是文件的绝对位置,也可以是相对 movi 标识符的相对位置,AVI 文件解析器必须能处理这两种情况。
- dwChunkLength : 对应块的字节长度
3.2 Open-DML Index
Open-DML 索引块的通常结构如下:
1 | typedef struct _aviindex_chunk { |