视频文件压缩处理
普通的AVI文件,如果不进行压缩,占用的磁盘空间是非常大的。例如,大家视频聊天使用的简易摄像头,录制一分钟的AVI文件,其他大小甚至超过100MB,可以想象,未经压缩的AVI文件多么庞大。下面介绍如何对AVI文件进行压缩,涉及的主要知识是对AVI函数和AVI视频压缩函数的使用。下面对相关函数进行详细介绍。
(1)AVIFileGetStream
该函数返回关联AVI文件的流接口的地址(流接口指针)。
语法:
STDAPI AVIFileGetStream( PAVIFILE pfile, PAVISTREAM * ppavi,DWORD fccType,LONG lParam);
参数说明:
pfile:表示打开的AVI文件句柄。
ppavi:表示返回的流接口指针。
fccType:4个字符的代码,表示视频流的类型。为streamtypeAUDIO,表示音频流,为streamtypeMIDI,表示MIDI流,为streamtypeTEXT,表示文本流,为streamtypeVIDEO,表示视频流。
lParam:表示流类型的数量,即对所标识流类型访问的计数。
(2)AVIStreamStart
该方法返回流的起始帧号。
语法:
STDAPI_(LONG) AVIStreamStart(PAVISTREAM pavi);
参数说明:
pavi:表示流接口指针。
返回值:表示流起始帧号。
(3)AVIStreamLength
该方法返回流的长度,即流中帧的数量。
语法:
STDAPI_(LONG) AVIStreamLength(PAVISTREAM pavi);
参数说明:
pavi:表示流接口指针。
返回值:表示流中帧的数量。
(4)AVIStreamInfo
该方法用于获取流的详细信息。
语法:
STDAPI AVIStreamInfo( PAVISTREAM pavi,AVISTREAMINFO * psi,LONG lSize);
参数说明:
pavi:表示流接口指针。
psi:表示流信息结构的指针,用于接收函数返回的流信息。
lSize:表示AVISTREAMINFO结构的大小。
(5) AVIStreamReadFormat
该方法读取流中指定格式的数据。
语法:
STDAPI AVIStreamReadFormat(PAVISTREAM pavi,LONG lPos, LPVOID lpFormat,LONG * pcbFormat );
参数说明:
pavi:表示流接口指针。
lPos:表示获取数据所在流中的位置,以帧为单位。
lpFormat:表示包含指针格式数据的缓冲区指针。
pcbFormat:表示lpFormat内存区域的大小。
(6)AVIStreamGetFrameOpen
该函数用于从指定的视频流中解压视频帧。
语法:
STDAPI_(PGETFRAME) AVIStreamGetFrameOpen(PAVISTREAM pavi, LPBITMAPINFOHEADER lpbiWanted);
参数说明:
pavi:表示流接口指针。
lpbiWanted:表示视频流格式指针,定义了想要的视频格式,如果为NULL,将采用默认的格式。
(7)ICOpen
该函数用于打开压缩或解压缩管理器。
语法:
HIC ICOpen(DWORD fccType,DWORD fccHandler,UINT wMode);
参数说明:
fccType:为4个字符代码,表示打开的压缩器或解压缩器的类型。对于视频流应为“VIDC”。
fccHandler:表示所标识类型的首选处理者,处理者的类型被存储在AVI文件中流的头部(Header)。
wMode:表示压缩器或解压缩器的标记,可选值如表1所示。
表1 AVI压缩标记
|
值
|
描述
|
|
ICMODE_COMPRESS
|
压缩器进行正常的压缩操作
|
|
ICMODE_DECOMPRESS
|
解压缩器进行正常的解压操作
|
|
ICMODE_DRAW
|
解压缩器解压数据,并将数据直接绘制到硬件设备上
|
|
ICMODE_FASTCOMPRESS
|
压缩器进行快速的压缩(实时压缩)
|
|
ICMODE_FASTDECOMPRESS
|
解压缩器进行快速的解缩(实时解缩)
|
|
ICMODE_QUERY
|
查询压缩器或解压缩器信息
|
返回值:表示压缩器或解压缩器句柄。
(8)ICCompressGetFormat
该函数(实际是一个宏)从视频压缩器驱动程序中请求压缩数据的输出格式。
语法:
DWORD ICCompressGetFormat(hic,lpbiInput,lpbiOutput );
参数说明:
hic:表示压缩器句柄。
lpbiInput:为BITMAPINFO结构指针,表示输入格式。
lpbiOutput:为BITMAPINFO结构指针,用于获取输出格式。
(9)ICCompressQuery
该函数查询视频压缩驱动程序,判断其是否支持所标识的输入格式或者是否能够压缩所标识的输入格式到输出格式。
语法:
DWORD ICCompressQuery( hic, lpbiInput, lpbiOutput);
参数说明:
hic:表示压缩器句柄。
lpbiInput:为BITMAPINFO结构指针,表示输入格式。
lpbiOutput:为BITMAPINFO结构指针,表示输出格式。
(10)ICCompressBegin
该函数通知视频压缩程序开始压缩数据。
语法:
DWORD ICCompressBegin(hic,lpbiInput,lpbiOutput);
参数说明:
hic:表示压缩器句柄。
lpbiInput:为BITMAPINFO结构指针,表示输入格式。
lpbiOutput:为BITMAPINFO结构指针,表示输出格式。
(11)AVIStreamGetFrame
该函数获取流中指定帧的数据。
语法:
STDAPI_(LPVOID) AVIStreamGetFrame( PGETFRAME pgf, LONG lPos);
参数说明:
pgf:表示帧指针,通常为AVIStreamGetFrameOpen函数的返回值。
lPos:表示帧的位置。
(12)ICCompress
该方法用于压缩单帧图像。
语法:
DWORD ICCompress(HIC hic,DWORD dwFlags,LPBITMAPINFOHEADER lpbiOutput,LPVOID lpData, LPBITMAPINFOHEADER lpbiInput,LPVOID lpBits,LPDWORD lpckid,LPDWORD lpdwFlags,LONG lFrameNum, DWORD dwFrameSize, DWORD dwQuality, LPBITMAPINFOHEADER lpbiPrev, LPVOID lpPrev);
参数说明:
hic:表示压缩器句柄。
dwFlags:表示压缩标记,为ICCOMPRESS_KEYFRAME,表示压缩关键帧。
lpbiOutput:为一个BITMAPINFO格式指针,表示输出格式。
lpData:表示一个缓冲区,用于存储压缩后的数据。
lpbiInput:为一个BITMAPINFO格式指针,表示输入格式。
lpBits:表示输入缓冲区,即待压缩的帧数据。
lpckid:保留,暂时没有被使用。
lpdwFlags:表示一个标记指针,如果为AVIIF_KEYFRAME,表示当前帧是关键帧。
lFrameNum:表示帧的号码,也就是帧的索引。
dwFrameSize:表示压缩后帧的大小,可以为0。
dwQuality:表示压缩质量。
lpbiPrev:一个BITMAPINFO结构指针,表示之前帧的格式。
lpPrev:表示之前帧未压缩图像的大小。
(13)ICCompressEnd
该函数用于关闭打开的压缩或解压缩管理器。
语法:
DWORD ICCompressEnd(hic);
参数说明:
hic:表示打开的压缩器或解压缩器句柄。
(14)AVIStreamGetFrameClose
该方法用于释放解压视频帧的资源。
语法:
STDAPI AVIStreamGetFrameClose( PGETFRAME pget );
参数说明:
pget:表示AVIStreamGetFrameOpen函数返回的视频帧句柄。
下面通过一个示例介绍如何压缩视频文件。
例如,压缩视频文件
(1)创建一个基于对话框的工程,工程名称为“CompressAvi”,设计对话框资源如图1所示。
.jpg)
图1 对话框资源设计
(2)引用vfw.h头文件并链接Vfw32.lib库文件。
#include "Vfw.h" //引用头文件
#pragma comment (lib,"Vfw32.lib") //链接库文件
提示:为了应用上面介绍的视频压缩函数,需要引用vfw.h头文件,同时链接vfw32.lib库文件。
(3)处理“…”按钮的单击事件,选择待压缩的AVI文件和生成的压缩后的AVI文件。
void CCompressAviDlg::OnChooseSource()
{
CFileDialog flOpenDlg(TRUE,NULL,NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
"AVI文件|*.avi|所有文件|*.*||",this); //定义文件打开对话框
if (flOpenDlg.DoModal()==IDOK) //判断是否按OK按钮
{
m_SrcFile.SetWindowText(flOpenDlg.GetPathName()); //设置编辑框文本
}
}
void CCompressAviDlg::OnChooseDes()
{
CFileDialog flSaveDlg(TRUE,"ave","demo",OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
"AVI文件|*.avi|所有文件|*.*||",this); //定义保存对话框
if (flSaveDlg.DoModal()==IDOK) //判断是否按OK按钮
{
m_DesFile.SetWindowText(flSaveDlg.GetPathName()); //设置编辑框文本
}
}
(4)向对话框中添加CompressAVI方法,对AVI文件进行压缩。
void CCompressAviDlg::CompressAVI(LPCSTR szAviFileName, LPCSTR szNewFileName)
{
HIC hIC; //定义压缩器句柄
ICINFO icInfo; //定义压缩信息对象
icInfo.fccType = 1667524982; //编码解码器类型值
icInfo.fccHandler = 859066445; //编码解码器句柄值
BITMAPINFOHEADER InHeader ; //定义输入格式
BITMAPINFOHEADER OutHeader; //定义输出格式
AVIFileInit(); //初始化avi文件
PAVIFILE pOldFile,pNewFile; //定义AVI文件指针
HRESULT hRet; //定义结果变量
hRet = AVIFileOpen(&pOldFile,szAviFileName,OF_READ, NULL); //打开源文件
if (hRet != 0) //判断文件是否打开成功
{
MessageBox("打开源文件错误","提示"); //弹出错误提示
return;
}
//打开新文件
hRet = AVIFileOpen(&pNewFile,szNewFileName,OF_WRITE |OF_CREATE ,NULL);
if (hRet != 0 ) //判断新的文件是否打开成功
{
MessageBox("打开目标文件错误","提示"); //弹出错误提示
return;
}
PAVISTREAM pOldStream,pNewStream; //定义文件流
AVIFileGetStream(pOldFile, &pOldStream, streamtypeVIDEO, 0 ); //获取视频流
long StartFrame=AVIStreamStart(pOldStream); //获取流的起始帧
long FrameNum= AVIStreamLength(pOldStream); //获取流的帧长度
m_Progress.SetRange(StartFrame,FrameNum); //设置进度条范围
m_Progress.ShowWindow(SW_SHOW); //设置进度条
AVISTREAMINFO OldStreamInfo,NewStreamInfo; //定义流信息
//获取流信息
AVIStreamInfo(pOldStream,&OldStreamInfo,sizeof(AVISTREAMINFO));
long size = sizeof(BITMAPINFOHEADER); //获取位图信息头大小
AVIStreamReadFormat(pOldStream,StartFrame,&InHeader,&size); //读取流格式
OutHeader=InHeader; //设置输出格式
PGETFRAME pFrame; //定义帧接口对象
pFrame=AVIStreamGetFrameOpen(pOldStream, NULL); //在流中打开帧
void * lpOutData; //定义输出缓冲区
lpOutData=VirtualAlloc(NULL,OutHeader.biSizeImage,MEM_COMMIT,
PAGE_READWRITE); //分配输出缓冲区空间
//打开压缩管理器
hIC=ICOpen(icInfo.fccType,icInfo.fccHandler,ICMODE_COMPRESS);
ICCompressGetFormat(hIC,&InHeader,&OutHeader); //获取压缩格式
memset(&NewStreamInfo,0,sizeof(NewStreamInfo)); //初始化流信息
NewStreamInfo.fccType=streamtypeVIDEO; //设置流类型
NewStreamInfo.fccHandler=mmioFOURCC('M', 'S', 'V', 'C'); //设置压缩器驱动程序代码
NewStreamInfo.dwScale=1; //设置刻度
NewStreamInfo.dwRate=25; //设置速率
NewStreamInfo.dwSuggestedBufferSize=OutHeader.biSizeImage; //设置流缓冲区大小
//设置视频图像大小
SetRect(&NewStreamInfo.rcFrame,0,0,OutHeader.biWidth,OutHeader.biHeight);
if(ICCompressQuery(hIC,&InHeader,&OutHeader)==ICERR_OK) //判断压缩器是否支持输出格式
{
//开始数据压缩
ICCompressBegin(hIC,(BITMAPINFO*)&InHeader,(BITMAPINFO*)&OutHeader);
}
AVIFileCreateStream(pNewFile,&pNewStream,&NewStreamInfo); //创建新的文件流
//设置流格式
AVIStreamSetFormat(pNewStream,0,&OutHeader,sizeof(OutHeader));
for (int index=StartFrame; index<FrameNum; index++) //遍历源视频文件的每一帧
{
m_Progress.SetPos(index); //设置进度条的位置
long num = sizeof(InHeader); //获取输入格式的大小
AVIStreamReadFormat(pOldStream,index,&OutHeader,&num);//读取流格式
BYTE* pDIB = (BYTE*) AVIStreamGetFrame(pFrame, index); //获取帧数据
BYTE* pData=pDIB+sizeof(BITMAPINFOHEADER); //获取位图实际数据
DWORD dwCkID; //定义整型变量
DWORD dwCompFlags; //定义整型变量
DWORD dwQuality=100; //定义整型变量
if(ICCompress(hIC,ICCOMPRESS_KEYFRAME,&OutHeader,lpOutData,
&InHeader,pData,&dwCkID,&dwCompFlags,index,0,dwQuality,
NULL,NULL)==ICERR_OK) //压缩视频帧
{
//设置流格式
AVIStreamSetFormat(pNewStream,index,&OutHeader,sizeof(OutHeader));
AVIStreamWrite(pNewStream,index,1,(LPBYTE)lpOutData,
OutHeader.biSizeImage,AVIIF_KEYFRAME,NULL,NULL);//向流中写入数据
}
}
m_Progress.ShowWindow(SW_HIDE); //隐藏进度条
if(hIC!=NULL)
{
ICCompressEnd(hIC); //结束视频压缩
ICClose (hIC); //关闭压缩器
}
AVIStreamGetFrameClose(pFrame); //释放帧对象
AVIStreamRelease(pNewStream); //释放文件流
AVIStreamRelease(pOldStream); //释放文件流
AVIFileRelease(pOldFile); //释放AVI文件
AVIFileRelease(pNewFile); //释放AVI文件
VirtualFree(lpOutData,OutHeader.biSizeImage,MEM_DECOMMIT); //释放输出缓冲区
AVIFileExit(); //释放AVI函数库
MessageBox("压缩完成!","提示"); //弹出对话框
}
(5)处理“压缩”按钮的单击事件,对指定的AVI文件进行压缩。
void CCompressAviDlg::OnOK()
{
CString srcFile,desFile; //定义字符串变量
m_SrcFile.GetWindowText(srcFile); //获取编辑框文本
m_DesFile.GetWindowText(desFile); //获取编辑框文本
if (!srcFile.IsEmpty() && !desFile.IsEmpty()) //判断文本是否为空
{
CompressAVI(srcFile,desFile); //调用CompressAVI函数压缩文件
}
}
(6)运行程序,效果如图2所示。
.jpg)
图2 压缩AVI文件