视频文件格式分析(1):avi格式

近日某网盘对用户保存其中的部分私人视频进行篡改,使得这部分视频无论是在线或者下载后均无法播放。我们借着研究对应方法,修复被非法篡改的视频数据,恢复正常使用的机会,研究一下avi的数据格式。

avi视频的格式分析

avi是“音视频交错(Audio-Video Interlance)"的缩写,是非常常见的视频文件封装格式。avi是一种适用于采集、编辑、播放的RIFF格式,对不同的编码标准和播放工具具有很强的适应性。

1、文件主体结构

RIFF文件的组成方式由多个chuck组成,组成方式为:

  1. FOURCC字符:表示当前chuck的名称;
  2. chuck大小:一个uint32类型整数,使用little-endian保存;该值仅表示下一部分“文件内容”的字节数大小;
  3. chuck数据:表示当前chuck中实际包含的信息内容;

每一个文件有且仅有一个RIFF chuck,它可以包含多个子chuck,其中的list可以再包含下一级子chuck。相比普通chuck,在RIFF和LIST的chuck大小和数据之间多了一个表示“Form Type/List Type”4个FOURCC字符,如“AVI[ ]”或“WAVE”等,即组成为:

  1. FOURCC字符:表示当前chuck的名称,对于最上层的chuck,固定为“RIFF”;
  2. chuck大小:一个uint32类型整数,使用little-endian保存;该值仅表示下一部分“文件内容”的字节数大小,不包含riff字符和文件大小本身;
  3. 4字节的形式类型或者列表类型:在此为“AVI[ ]”;
  4. chuck数据:表示当前chuck中实际包含的信息内容;

下一部分数据是AVI的LIST子块。AVI数据块一般包含3个LIST,分别是hdrl、movi和idxl三个,分别表示头信息、音视频数据和索引信息。

每一个list的结构如下:

  1. FOURCC字符:“LIST”
  2. LIST数据块的大小;
  3. LIST类型;
  4. LIST数据;

首先研究第一个LIST,即hdrl list。首先包括了一个数据结构表示当前数据的AVI Main Header结构,用AVIMAINHEADER表示。这个数据结构包含了当前AVI文件的整体信息,包括视频分辨率、视频中的流数目等。AVIMAINHEADER实现方式如下:

typedef struct _avimainheader {
    FOURCC fcc;<span style="white-space:pre">			</span>//fourcc字符“avih”
    DWORD  cb;<span style="white-space:pre">			</span>//当前结构占据多少个字节
    DWORD  dwMicroSecPerFrame;<span style="white-space:pre">	</span>//显示相邻两帧的间隔,以毫秒为单位
    DWORD  dwMaxBytesPerSec;<span style="white-space:pre">	</span>//每秒钟传输的最大数据量的估计值
    DWORD  dwPaddingGranularity;//字节对齐单位,数据块长度必须是该值的倍数
    DWORD  dwFlags;<span style="white-space:pre">		</span>//一些标志位
    DWORD  dwTotalFrames;<span style="white-space:pre">	</span>//数据帧的总数
    DWORD  dwInitialFrames;<span style="white-space:pre">	</span>//第一个视频帧开始播放之前需预先准备的帧数
    DWORD  dwStreams;<span style="white-space:pre">		</span>//文件中流的个数(如一个视频流+一个音频流=2个流)
    DWORD  dwSuggestedBufferSize;//读取数据的缓存区的建议大小
    DWORD  dwWidth;<span style="white-space:pre">		</span>//视频像素宽度
    DWORD  dwHeight;<span style="white-space:pre">		</span>//视频像素高度
    DWORD  dwReserved[4];<span style="white-space:pre">	</span>//保留位
} AVIMAINHEADER;

在AVIMAINHEADER之后是hdrl的子list——strl,包括strh和strf,分别表示stream header和stream format信息。另外,还可能包括strd和strn等部分分别表示header data和stream name等数据。

以下为strh包含的 AVISTREAMHEADER结构:

typedef struct _avistreamheader {
     FOURCC fcc;//“strh”四字符
     DWORD  cb;//当前结构大小
     FOURCC fccType;//表示当前stream所包含的数据类型
     FOURCC fccHandler;//对于音频、视频数据,该部分表示所采用的解码器
     DWORD  dwFlags;
     WORD   wPriority;//表示当前流的优先级
     WORD   wLanguage;//语言
     DWORD  dwInitialFrames;
     DWORD  dwScale;//与下一个元素一起表示sample的频率,如视频帧率等。
     DWORD  dwRate;
     DWORD  dwStart;//表示当前流的起始时间
     DWORD  dwLength;//该stream的长度
     DWORD  dwSuggestedBufferSize;//建议缓存区大小
     DWORD  dwQuality;//表示数据质量;在视频流中表示编码的QP
     DWORD  dwSampleSize;//sample的大小
     struct {
         short int left;
         short int top;
         short int right;
         short int bottom;
     }  rcFrame;//数据显示的目标区域
} AVISTREAMHEADER;

紧跟着strh之后是一个strf数据块,该数据块表示当前流中的数据格式。对于视频,以BITMAPINFO表示;对于音频,以WAVEFORMATEX表示。数据结构如下:

typedef struct tagBITMAPINFO { 
  BITMAPINFOHEADER bmiHeader; 
  RGBQUAD bmiColors[1]; 
} BITMAPINFO; 
typedef struct tagBITMAPINFOHEADER { 
  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 tagRGBQUAD { 
  BYTE rgbBlue;
  BYTE rgbGreen;
  BYTE rgbRed;
  BYTE rgbReserved;
} RGBQUAD;
在实验中,BITMAPINFOHEADER中的biCompression成员为一个fourCC字符“avc1”,且不包含后面的RGBQUAD部分。

对于音频部分,WAVEFORMATEX的实现如下:

typedef struct { 
  WORD  wFormatTag; 
  WORD  nChannels; 
  DWORD nSamplesPerSec; 
  DWORD nAvgBytesPerSec; 
  WORD  nBlockAlign; 
  WORD  wBitsPerSample; 
  WORD  cbSize; 
} WAVEFORMATEX; 

这部分的后面还存在一个odml的list,包含一个dmlh的部分,用来表示avi扩展头部序列块。

以上部分即avi文件的文件信息hdrl部分,紧接着便是保存实际音视频数据的movi list。一个fourcc字符“movi”之后的fourcc字符可能含有一下双字符组合中的一个,表示chuck中包含的数据的种类:

字符组合 含义
db 未压缩视频帧
dc 压缩的视频数据
pc 调色信息
wd 音频信息

在文件的最后,包含一个可选的chuck——索引数据“idx1”。索引chuck包含了各个数据chuck在文件中的位置,其实现方式如下:

typedef struct _avioldindex {
   FOURCC  fcc;
   DWORD   cb;
   struct _avioldindex_entry {
      DWORD   dwChunkId;
      DWORD   dwFlags;
      DWORD   dwOffset;
      DWORD   dwSize;
  } aIndex[];
} AVIOLDINDEX;

由此可以看出,avi格式在文件末尾包含了索引信息,所以播放需要获得从开始到结束的完整文件数据。由于这种特性,avi格式并不适合应用在流视频传输和播放的场合。


上一篇:微信、qq时间格式模板


下一篇:如何在Excel 单元格中另起一行?