【编解码:AVI格式解析】

编解码:AVI格式解析

1 音视频及AVI知识

一个完整的音视频文件格式有mp4、mov、flv、avi、rmvb、mkv、ts等,它们是封装数据的容器,包括音频、视频、字幕、基础元信息等,通过一些特定的编码算法,对各种信息进行编码压缩过后得到的。视频文件的封装格式并不影响视频的画质,影响视频画面质量的是视频的编码格式。

H264、HEVC、VP9、AV1等就是视频编码格式,MP3、AAC、AC-3等就是音频编码格式,SRT、SSA等就是字幕编码格式。

例如:将一个视频流以Xvid格式编码、一个音频流以MP3格式编码、一个字幕流以SRT格式进行编码,按AVI封装标准封装以后,就得到一个AVI视频文件。

AVI即Audio Video Interleaved,音视频交错格式,基于RIFF文件结构。多用于音视频捕捉、编辑、回放等应用程序中。通常情况下,一个AVI文件可以包含多个不同类型的媒体流(典型的情况下有一个音频流和一个视频流),不过含有单一音频流或单一视频流的AVI文件也是合法的。AVI可以算是Windows操作系统上最基本的、也是最常用的一种媒体文件格式。

RIFF即Resource Interchange File Format,资源交互文件格式 。是由Microsoft提出的一种多媒体文件存储方式,不同编码的视频、音频文件按照RIFF保存,当提取文件时,可以根据RIFF的规则解析文件。常见的RIFF文件有:WAV、AVI等。

2 最基本的数据单元

//Chunks
typedef struct {
DWORD dwFourCC
DWORD dwSize      //data
BYTE data[dwSize] // contains headers or video/audio data
} CHUNK;
 
//Lists
typedef struct {
DWORD dwList
DWORD dwSize        //dwFourcc + data
DWORD dwFourCC
BYTE data[dwSize-4] // contains Lists and Chunks
} LIST;

Chunks由三部分组成:4字节的数据流格式ID,4字节的数据大小,数据。
Lists由四部分组成:4字节LIST(LIST块中可以再包含一系列的子块),4字节数据加fourCC大小,4字节数据流格式ID,数据。

3 AVI主要结构介绍

一个AVI文件通常有如下几个子块组成:

  • ID为"AVI"的RIFF,文件头
  • ID为"hdrl"的list,信息块,包含了音视频信息,描述媒体流信息
  • ID为"info"的list,包含编码该AVI的程序信息
  • ID为"junk"的chunk,无用的数据,用于字节对齐
  • ID为"movi"的list,数据块,包含了交错排列的音视频数据
  • ID为"idxl"的chunk,索引块,包含音视频排列的索引数据(可选块,不存在时seek会慢很多)

文件信息:
分析工具分别是MediaInfo以及MediaConch。
【编解码:AVI格式解析】
【编解码:AVI格式解析】
【编解码:AVI格式解析】

4 AVI主要结构解析

1.RIFF文件头
【编解码:AVI格式解析】
4个字节的"RIFF",4字节的RIFF文件大小(342872字节),4字节为RIFF文件类型"AVI"

2.hdrl列表
【编解码:AVI格式解析】
1)hdrl列表头
4字节的"LIST"描述信息,4字节list大小(8952字节),4字节list类型"hdrl"

2)avih块
【编解码:AVI格式解析】
用于描述主信息,该块可以用如下结构体表示

typedef struct
{
	DWORD	dwMicroSecPerFrame;     //显示每帧所需的时间ns,定义avi的显示速率
	DWORD	dwMaxBytesPerSec;       // 最大数据传输率
	DWORD	dwPaddingGranularity;   //记录块的长度须为此值的倍数,通常是2048
	DWORD	dwFlags;       // AVI文件的特殊属性,包含文件中的任何标志字。如:有无索引块,是否是interlaced,是否含版权信息等
    DWORD	dwTotalFrames;  	    // 数据帧的总数
    DWORD	dwInitialFrames;     // 在开始播放前需要的帧数
    DWORD	dwStreams;           //文件中包含的数据流种类
	DWORD	dwSuggestedBufferSize;//建议使用的缓冲区的大小,通常为存储一帧图像以及同步声音所需要的数据之和,大于最大的CHUNK的大小
    DWORD	dwWidth;             //图像宽,像素
    DWORD	dwHeight;            //图像高,像素
    DWORD	dwReserved[4];       //保留值dwScale,dwRate,dwStart,dwLength
} MainAVIHeader;

3)strl list头部
【编解码:AVI格式解析】
一个strl list中至少包含一个strh块和一个strf块。文件中有多少个流,就对应有多少个strl list。
上图可知存在两个流,Stream info 0,Stream info 1。

4)strh块
【编解码:AVI格式解析】

 // AVI流头部
typedef struct
{
    FourCC fcc;                 // 必须为 strh
    DWORD cb;                   // 本数据结构的大小,不包括最初的8个字节(fcc和cb两个域)
    FourCC fccType;             // 流的类型: auds(音频流) vids(视频流) mids(MIDI流) txts(文字流)
    FourCC fccHandler;          // 指定流的处理者,对于音视频来说就是解码器
    DWORD dwFlags;              // 标记:是否允许这个流输出?调色板是否变化?
    WORD wPriority;             // 流的优先级(当有多个相同类型的流时优先级最高的为默认流)
    WORD wLanguage;             // 语言
    DWORD dwInitialFrames;      // 为交互格式指定初始帧数
    DWORD dwScale;              // 每帧视频大小或者音频采样大小
    DWORD dwRate;               // dwScale/dwRate,每秒采样率
    DWORD dwStart;              // 流的开始时间
    DWORD dwLength;             // 流的长度(单位与dwScale和dwRate的定义有关)
    DWORD dwSuggestedBufferSize;// 读取这个流数据建议使用的缓存大小
    DWORD dwQuality;            // 流数据的质量指标(0 ~ 10,000)
    DWORD dwSampleSize;         // Sample的大小
    RECT rcFrame;               // 指定这个流(视频流或文字流)在视频主窗口中的显示位置,视频主窗口由AVIMAINHEADER结构中的dwWidth和dwHeight决定
} AVIStreamHeader;

5)strf块

该块用于描述流的具体信息

  • 视频流,fccType = “vids”
    【编解码:AVI格式解析】
// 位图头
typedef struct
{
    DWORD  biSize;
    LONG   biWidth;
    LONG   biHeight;
    WORD   biPlanes;
    WORD   biBitCount;
    DWORD  biCompression;
    DWORD  biSizeImage;
    LONG   biXPelsPerMeter;
    LONG   biYPelsPerMeter;
    DWORD  biClrUsed;
    DWORD  biClrImportant;
} BitmapInfoHeader;
 
// 位图信息
typedef struct
{
    BitmapInfoHeader bmiHeader;   // 位图头
    RGBQUAD bmiColors[1];         // 调色板
} BitmapInfo;
  • 音频流,fccType = “auds”
    【编解码:AVI格式解析】
// 音频波形信息
typedef struct
{
    WORD wFormatTag;
    WORD nChannels;               // 声道数
    DWORD nSamplesPerSec;         // 采样率
    DWORD nAvgBytesPerSec;        // 每秒的数据量
    WORD nBlockAlign;             // 数据块对齐标志
    WORD wBitsPerSample;          // 每次采样的数据量
    WORD cbSize;                  // 大小
} WaveFormatEx;

6)strd块与strn块
strd:可选的额外的头信息数据
strn:可选的流的名字
这两个块是可选的,本AVI文件不包含,故不分析。

3.info列表
【编解码:AVI格式解析】
该list用于描述编码该AVI文件的程序信息,包含一个isft块。

4.junk块
【编解码:AVI格式解析】
一些垃圾填充数据,用于内部数据的队齐(填充),直接跳过。

5.movi列表
【编解码:AVI格式解析】
存储音视频数据,音视频数据在其中以交错方式存放着,视频clip,n音频clip,视频clip,n音频clip…方便seek。
音视频数据子块的种类有:##db,##dc,##pc,##wb。
–##:数据所属的流的序号,视频是00dc或00db,音频是01wb。
–db:未压缩的视频帧
–dc:压缩的视频帧
–wb:音频数据
–pc:改用新的调色板

6.idx1块
【编解码:AVI格式解析】
该块是可选的,描述音视频数据的索引块信息,在AVIMainHeader的dwFlags中指出是否包含索引块。有了索引块可以方便文件快进,如果没有索引块,在对AVI进行快进时需要计算位置,会很耗时。索引块可用如下结构体表示:

// 索引节点信息
typedef struct
{
    DWORD dwChunkId;   // 本数据块的四字符码(00dc 01wb)
    DWORD dwFlags;     // 说明本数据块是不是关键帧、是不是‘rec ’列表等信息
    DWORD dwOffset;    // 本数据块在文件中的偏移量
    DWORD dwSize;      // 本数据块的大小
} AVIIndexEntry;

5 总结

可以编译FFmpeg源码,对AVI文件进行解码,在分离器Demuxer中进一步研究其格式,会有更清晰的认识。

RIFF ('AVI'
     LIST('hdrl'
        'avih'(主 AVI 信息头数据)
        LIST('strl'
                'strh' (流的头信息数据)
                'strf' (流的格式信息数据)
                ['strd' (可选的额外的头信息数据)]
                ['strn' (可选的流的名字) ]
        )
        
        ... // 其他流信息
    )
        
    LIST('movi'
            { 
                // 媒体流数据
                SubChunk | LIST ('rec'
                SubChunk1
                SubChunk2
                ...
            }
    )
    ['idx1' (可选的 AVI 索引块数据) ]
)
上一篇:ffmpeg常用命令


下一篇:使用 C# 获取文件或流的易读大小(KB、MB、GB、TB、KB)