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

一、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字节对齐
#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文件需要做下设置:

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拥抱藍天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值