您的位置:首页 > 编程语言 > C语言/C++

C++解析AVI文件格式学习总结

2017-08-27 18:10 393 查看

一、AVI简介

AVI英文全称为Audio
Video Interleaved,即音频视频交错格式,一种多媒体容器格式。AVI文件将音频和视频

包含在一个文件容器中,允许音视频同步回放。

AVI 1.0 由于索引地址与大小用4字节表示,所以最大支持4G容量,而且与文件系统类型有关; 
AVI 2.0 AVI的扩展格式,解决AVI 1.0大小限制;
本文主要分析AVI 1.0,H264编码视频。

二、AVI格式示意图








三、各部分详细介绍

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

1)”hdrl” list:音视频信息,描述媒体流信息;
2)”info” list:编码该AVI的程序信息;
3)”junk” chunk:无用数据,用于字节对齐;
4)”movi” list:交错排列的音视频数据;
5)”idxl” chunk:音视频排列的索引数据。

1、”hdrl” list



2、”info” list和”junk” chunk




       3、”movi” list




     4、”idxl” chunk



四、解析代码

#include <iostream>
#include <fstream>
using namespace std;
#pragma pack(1)//设定为1
20000
字节对齐
#define AVIF_HASINDEX  0x00000010       //Index at end of file?
typedef unsigned short BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef unsigned long FOURCC;
// MainAVIHeader
typedef struct{
DWORD dwMicroSecPerFrame;//显示每桢所需的时间ns,定义avi的显示速率
DWORD dwMaxBytesPerSec;//最大的数据传输率
DWORD dwPaddingGranularity;//记录块的长度需为此值的倍数,通常是2048
DWORD dwFlages;//AVI文件的特殊属性,如是否包含索引块,音视频数据是否交叉存储
DWORD dwTotalFrame;//文件中的总桢数
DWORD dwInitialFrames;//说明在开始播放前需要多少桢
DWORD dwStreams;//文件中包含的数据流种类
DWORD dwSuggestedBufferSize;       //建议使用的缓冲区的大小,
//通常为存储一桢图像以及同步声音所需要的数据之和
DWORD dwWidth;//图像宽
DWORD dwHeight;//图像高
DWORD dwReserved[4];//保留值
}MainAVIHeader;
typedef struct{
WORD wLeft;
WORD wTop;
WORD wRight;
WORD wBottom;
}RECT;
// strh
typedef struct{
FOURCC fccType;//4字节,表示数据流的种类 vids 表示视频数据流
//auds 音频数据流
FOURCC fccHandler;//4字节 ,表示数据流解压缩的驱动程序代号
DWORD dwFlags;//数据流属性
WORD wPriority;//此数据流的播放优先级
WORD wLanguage;//音频的语言代号
DWORD dwInitalFrames;        //说明在开始播放前需要多少桢
DWORD dwScale;//数据量,视频每桢的大小或者音频的采样大小
DWORD dwRate;//dwScale /dwRate = 每秒的采样数
DWORD dwStart;//数据流开始播放的位置,以dwScale为单位
DWORD dwLength;//数据流的数据量,以dwScale为单位
DWORD dwSuggestedBufferSize;//建议缓冲区的大小
DWORD dwQuality;//解压缩质量参数,值越大,质量越好
DWORD dwSampleSize;//音频的采样大小
RECT rcFrame;//视频图像所占的矩形
}AVIStreamHeader;
// strf:strh子块是视频数据流
typedef struct{
BYTE rgbBlue;// 蓝色分量
BYTE rgbGreen;// 绿色分量
BYTE rgbRed;// 红色分量
BYTE rgbReserved;// 保留字节(用作Alpha通道或忽略)
} RGBQUAD;
typedef struct{
DWORD dwSize;
DWORD dwWidth;
DWORD dwHeight;
WORD wPlanes;
WORD wBitCount;
DWORD dwCompression;
DWORD dwSizeImage;
DWORD dwXPelsPerMeter;
DWORD dwYPelsPerMeter;
DWORD dwClrUsed;
DWORD dwClrImportant;
}BITMAPINFOHEADER;
// strf:strh子块是音频数据流
typedef struct
{
WORD wFormatTag;
WORD wChannels;//声道数
DWORD dwSamplesPerSec;//采样率
DWORD dwAvgBytesPerSec; //WAVE声音中每秒的数据量
WORD wBlockAlign;//数据块的对齐标志
WORD wBitsPerSample;    //每次采样的数据量
WORD wSize;//此结构的大小
}WAVEFORMAT;
typedef struct{
char chunk_id[4];
DWORD is_key;
DWORD pos;
DWORD size;
}AVI_IDX;
void aviListInfo(char *pInfoBuf,int *readPos)
{
char buf[5];
DWORD dwValue=0;
int flag=0;
// list
memset(buf,0,5);
memcpy(buf,pInfoBuf+*readPos,4);
*readPos+=4;
cout<<" list: "<<buf<<endl;
// list len
dwValue=0;
memcpy((char *)&dwValue,pInfoBuf+*readPos,4);
*readPos+=4; 
cout<<" list len: "<<dwValue<<endl;
// strl
memset(buf,0,5);
memcpy(buf,pInfoBuf+*readPos,4);
*readPos+=4; 
cout<<" strl: "<<buf<<endl;
// strh
memset(buf,0,5);
memcpy(buf,pInfoBuf+*readPos,4);
*readPos+=4; 
cout<<" strh: "<<buf<<endl;
// strh len
dwValue=0;
memcpy((char *)&dwValue,pInfoBuf+*readPos,4);
*readPos+=4; 
cout<<" strh len: "<<dwValue<<endl;
// strh info
AVIStreamHeader streamHeader;
memset(&streamHeader,0,sizeof(AVIStreamHeader));
memcpy((char *)&streamHeader, pInfoBuf+*readPos,sizeof(AVIStreamHeader)); 
*readPos+=sizeof(AVIStreamHeader); 
{
cout<<endl;
cout<<" AVIStreamHeader len: "<<sizeof(AVIStreamHeader)<<endl;
memset(buf,0,5);
memcpy(buf,(char *)&streamHeader.fccType,4);
cout<<" fccType: "<<buf<<endl;
string fccType(buf);
if(0 == fccType.compare(0,4,"vids")){  // 文件类型校验
flag=0;
}
else{
flag=1;
}
memset(buf,0,5);
memcpy(buf,(char *)&streamHeader.fccHandler,4);
cout<<" fccHandler: "<<buf<<endl;
cout<<" Flags: "<<streamHeader.dwFlags<<endl;
cout<<" Priority: "<<streamHeader.dwQuality<<endl;
cout<<" Language: "<<streamHeader.wLanguage<<endl;
cout<<" InitalFrames: "<<streamHeader.dwInitalFrames<<endl;
cout<<" Scale: "<<streamHeader.dwScale<<endl;
cout<<" Rate: "<<streamHeader.dwRate<<endl;
cout<<" Start: "<<streamHeader.dwStart<<endl;
cout<<" Length: "<<streamHeader.dwLength<<endl;
cout<<" SuggestedBufferSize: "<<streamHeader.dwSuggestedBufferSize<<endl;
cout<<" Quality: "<<streamHeader.dwQuality<<endl;
cout<<" SampleSize: "<<streamHeader.dwSampleSize<<endl;
cout<<" left: "<<streamHeader.rcFrame.wLeft<<endl;
cout<<" top: "<<streamHeader.rcFrame.wTop<<endl;
cout<<" right: "<<streamHeader.rcFrame.wRight<<endl;
cout<<" bottom: "<<streamHeader.rcFrame.wBottom<<endl<<endl;
}
// strf
memset(buf,0,5);
memcpy(buf,pInfoBuf+*readPos,4);
*readPos+=4;  
cout<<" strf: "<<buf<<endl;
// strf len
dwValue=0;
memcpy((char *)&dwValue,pInfoBuf+*readPos,4);
*readPos+=4; 
cout<<" strf len: "<<dwValue<<endl;
if(0==flag) // 视频信息
{
// strf info:视频流
BITMAPINFOHEADER bmiHeader;
memset(&bmiHeader,0,sizeof(BITMAPINFOHEADER));
memcpy((char *)&bmiHeader,pInfoBuf+*readPos,sizeof(BITMAPINFOHEADER));
*readPos+=sizeof(BITMAPINFOHEADER); 
{
cout<<endl;
cout<<" BITMAPINFOHEADER len: "<<sizeof(BITMAPINFOHEADER)<<endl;
cout<<" Size: "<<bmiHeader.dwSize<<endl;
cout<<" Width: "<<bmiHeader.dwWidth<<endl;
cout<<" Height: "<<bmiHeader.dwHeight<<endl;
cout<<" Planes: "<<bmiHeader.wPlanes<<endl;
cout<<" BitCount: "<<bmiHeader.wBitCount<<endl;
cout<<" Compression: "<<bmiHeader.dwCompression<<endl;
cout<<" SizeImage: "<<bmiHeader.dwSizeImage<<endl;
cout<<" XPelsPerMeter: "<<bmiHeader.dwXPelsPerMeter<<endl;
cout<<" YPelsPerMeter: "<<bmiHeader.dwYPelsPerMeter<<endl;
cout<<" ClrUsed: "<<bmiHeader.dwClrUsed<<endl;
cout<<" ClrImportant: "<<bmiHeader.dwClrImportant<<endl;
}
}
else
{
WAVEFORMAT waveFormat;
memset(&waveFormat,0,sizeof(WAVEFORMAT));
memcpy((char *)&waveFormat,pInfoBuf+*readPos,sizeof(WAVEFORMAT));
*readPos+=sizeof(WAVEFORMAT); 
{
cout<<endl;
cout<<" WAVEFORMAT len: "<<sizeof(WAVEFORMAT)<<endl;
cout<<" FormatTag: "<<waveFormat.wFormatTag<<endl; 
cout<<" Channels: "<<waveFormat.wChannels<<endl; 
cout<<" SamplesPerSec: "<<waveFormat.dwSamplesPerSec<<endl; 
cout<<" AvgBytesPerSec: "<<waveFormat.dwAvgBytesPerSec<<endl; 
cout<<" BlockAlign: "<<waveFormat.wBlockAlign<<endl; 
cout<<" BitsPerSample: "<<waveFormat.wBitsPerSample<<endl; 
cout<<" Size: "<<waveFormat.wSize<<endl; 
}
*readPos+=waveFormat.wSize; 
}
}
int main(int argc, char* argv[])
{
char fileName[32]={0};
//const char *fileName="test.avi";
char buf[5];
DWORD dwValue=0;
DWORD fileLen=0;
DWORD frameCount=0;
int hdrlListLen=0;
char *pInfo=NULL;
int readPos=0;
DWORD moviPos=0;
cout<<"Pleade input file name:";
cin>>fileName;
cout<<" File name:"<<fileName<<endl;
ifstream aviFile;
streampos   pos;
// open
aviFile.open(fileName,ios::in | ios::binary);
if(! aviFile)
{
cout<<fileName<<" open error!"<<endl;
return -1;
}
// RIFF-4字节
memset(buf,0,5); 
aviFile.read(buf, 4); 
cout<<" riffHead: "<<buf<<endl; 
// file Len-4字节:文件总长度-8 
aviFile.read((char *)&dwValue,4); 
cout<<" file len: "<<dwValue<<endl;
// file real len 
pos   =   aviFile.tellg();     // save current position   
aviFile.seekg(0,ios::end);
fileLen =  aviFile.tellg();
    aviFile.seekg(pos);   // restore saved position 
if(fileLen!=(dwValue+8))       // 文件大小校验
{
cout<<" File is damaged!"<<endl;  
return -1;
}
// file Type-4字节
memset(buf,0,5);
aviFile.read(buf, 4); 
cout<<" file type: "<<buf<<endl; 
string aviFormat(buf);
if(0 != aviFormat.compare(0,3,"AVI"))  // 文件类型校验
{
cout<<" Isn't AVI file!"<<endl; 
return -1;
}
// hdrl list-4字节
memset(buf,0,5);
aviFile.read(buf, 4); 
cout<<" list: "<<buf<<endl;
// list Len-4字节
dwValue=0;
aviFile.read((char *)&dwValue,4); 
cout<<" list len: "<<dwValue<<endl;
hdrlListLen=dwValue;
pInfo=new char[hdrlListLen+1];
memset(pInfo,0,hdrlListLen+1);
aviFile.read(pInfo, dwValue); 
{
readPos=0;
 // list Type-4字节
memset(buf,0,5);
memcpy(buf,pInfo+readPos,4);
readPos+=4;
cout<<" list type: "<<buf<<endl; 
// avih-4字节:
memset(buf,0,5);
memcpy(buf,pInfo+readPos,4);
readPos+=4; 
cout<<" avih: "<<buf<<endl;  
// avih Len-4字节
dwValue=0;
memcpy((char *)&dwValue,pInfo+readPos,4);
readPos+=4; 
cout<<" avih len: "<<dwValue<<endl;
// avi header
MainAVIHeader mainAVIHeader;
memset(&mainAVIHeader,0,sizeof(MainAVIHeader));
memcpy((char *)&mainAVIHeader,pInfo+readPos,dwValue);
readPos+=dwValue; 
{
cout<<endl;
cout<<" MainAVIHeader len: "<<sizeof(MainAVIHeader)<<endl;
cout<<" MicroSecPerFrame: "<<mainAVIHeader.dwMicroSecPerFrame<<endl;
cout<<" MaxBytesPerSec: "<<mainAVIHeader.dwMaxBytesPerSec<<endl;
cout<<" PaddingGranularity: "<<mainAVIHeader.dwPaddingGranularity<<endl;
cout<<" Flages: "<<mainAVIHeader.dwFlages<<endl;
cout<<" TotalFrame: "<<mainAVIHeader.dwTotalFrame<<endl;
frameCount=mainAVIHeader.dwTotalFrame;
cout<<" InitialFrames: "<<mainAVIHeader.dwInitialFrames<<endl;
cout<<" Streams: "<<mainAVIHeader.dwStreams<<endl;
cout<<" SuggestedBufferSize: "<<mainAVIHeader.dwSuggestedBufferSize<<endl;
cout<<" Width: "<<mainAVIHeader.dwWidth<<endl;
cout<<" Height: "<<mainAVIHeader.dwHeight<<endl;
}
if(!mainAVIHeader.dwFlages & AVIF_HASINDEX) 
{
cout<< " Hasn't find idx info!"<<endl;
return -1;
}
// list1
cout<<endl<<" ======List1======"<<endl;
aviListInfo(pInfo,&readPos);
// list 2
if(mainAVIHeader.dwStreams>1)
{
int i;
for(i=readPos;i<hdrlListLen;)
{
memset(buf,0,5);
memcpy(buf,pInfo+readPos,4);
cout<<" buf: "<<buf<<endl; 
string listFlag(buf);
if(0==listFlag.compare(0,4,"LIST")) break;
readPos+=4; 
i=readPos;
}
// list2
cout<<endl<<" ======List2======"<<endl;
aviListInfo(pInfo,&readPos);
}
cout<< " readPos="<<readPos<<endl;
}
delete []pInfo;
pInfo=NULL;
// find list
{
int i;
string listFlag;
for(i=readPos+8;i<fileLen;)
{
memset(buf,0,5);
aviFile.read(buf, 4); 
cout<<" buf: "<<buf<<endl; 
listFlag=buf;
if(0==listFlag.compare(0,4,"LIST"))
{
// len
dwValue=0;
aviFile.read((char *)&dwValue,4);
i+=4; 
cout<<" len: "<<dwValue<<endl;
// type
memset(buf,0,5);
aviFile.read(buf,4);
i+=4; 
cout<<" type: "<<buf<<endl; 
listFlag=buf;
aviFile.seekg(dwValue-4,ios::cur);
if(0==listFlag.compare(0,4,"movi"))
{
moviPos=aviFile.tellg();
moviPos=moviPos-dwValue+4;
break;
}
}
else if(0==listFlag.compare(0,4,"JUNK"))
{
// len
dwValue=0;
aviFile.read((char *)&dwValue,4);
cout<<" len: "<<dwValue<<endl;
aviFile.seekg(dwValue,ios::cur);
}
i+=4; 
}
}
if(0==moviPos) return -1;
cout<<endl<< " ========= idxl info ==========="<<endl;
// idxl
memset(buf,0,5);
aviFile.read(buf,4);
cout<<" idxl: "<<buf<<endl; 
// avih Len-4字节
dwValue=0;
aviFile.read((char *)&dwValue,4);
cout<<" idxl len: "<<dwValue<<endl;
// avi file index
AVI_IDX *pIndx=new AVI_IDX[frameCount];
char *pszBuf = new char[1024*1024];
DWORD nBufSize = 1024*1024;
// open video file
ofstream videoFile;
videoFile.open("test.video",ios::out | ios::binary);
if(dwValue>=frameCount*sizeof(AVI_IDX))
dwValue=frameCount*sizeof(AVI_IDX);
aviFile.read((char *)pIndx,dwValue); 
{
int idx;
for(idx=0;idx<frameCount;++idx)
//for(idx=0;idx<50;++idx)
{
//memset(buf,0,5);
//memcpy(buf,pIndx[idx].chunk_id,4);
// cout<< endl<< "  id:" <<  buf << endl;
// cout<< "  key:" <<  pIndx[idx].is_key << endl;
// cout<< "  pos:" <<  pIndx[idx].pos << endl;
// cout<< "  size:" <<  pIndx[idx].size << endl;
aviFile.seekg(moviPos+pIndx[idx].pos,ios::beg);
memset(pszBuf,0,nBufSize);
aviFile.read(pszBuf,pIndx[idx].size); 
if(!strncmp(pIndx[idx].chunk_id,"00dc",4));
videoFile.write(pszBuf,pIndx[idx].size);
}
}
delete []pIndx;
pIndx=NULL;
delete []pszBuf;
pszBuf=NULL;
videoFile.close();
aviFile.close();
return 0;
}

五、打印信息


注:本文用例测试H264编码格式AVI文件正常,解封转后保存的h264视频文件用vlc能够正常播放。
vlc播放h264文件需要做下设置:

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息