AVI函数截取BMP(中间部分代码提示了播放AVI的办法)

 下面的代码以只读方式打开AVI文件.szFile是打开文件的名字.title[100]用来修改window标题(显示AVI文件信息).  
首先调用AVIFileInit().他初始化AVI文件库(使东西能用?鹄?).

打开AVI文件有很多方法.我采用AVIStreamOpenFromFile(...).他能打开AVI文件中单独一个流(AVI文件可以包含多个流).它的参数如下:pavi是接收流句柄的缓冲的指针,szFile是打开文件的名字(包括路径).第三参数是打开的流的类型.在这个工程里,我们只对视频流感兴趣(streamtypeVIDEO).第四参数是0,这表示我们需要第一次读到的视频流(一个AVI文件里会有多个视频流,我们要第一个).OF_READ表示以只读方式打开文件.最后一个参数是一个类标识句柄的指针.说实话,我也不清楚他是干吗的.我让windows自己设定,于是把NULL传过去.
void   OpenAVI(LPCSTR   szFile)//   打开AVI文件szFile
{
TCHAR title[100];//   包含修改了的window标题
AVIFileInit();//   打开AVI文件库
//   打开AVI流
if   (AVIStreamOpenFromFile(&pavi,   szFile,   streamtypeVIDEO,   0,   OF_READ,   NULL)   !=0)
{
//   打开流时的出错处理
MessageBox   (HWND_DESKTOP,   "打开AVI流失败 ",   "错误 ",   MB_OK   |   MB_ICONEXCLAMATION);
}
  到目前为止,我们假定文件被正确打开,流被正确定位!然后用AVIStreamInfo(...)从AVI文件里抓取一些信息.  
先前我们创建了叫psi的结构体来保存AVI流的信息.下面第一行,我们把AVI信息填入该结构体.从流的宽度(以像素计)到动画的帧速等所有的信息都会存到psi中.那些想要精确控制播放速度的要记住我刚才说的.更多的信息参阅MSDN.

我们通过右边位置减左边位置算出帧宽.这个结果是以像素记的精确的帧宽.至于高度,可以用底边位置减顶边位置得到.这样得到高度的像素值.

然后用AVIStreamLength(...)得到AVI文件最后一帧的序号.AVIStreamLength(...)返回动画最后一帧的序号.结果存在lastframe里.

计算帧速很简单.每秒帧速(fps)=   psi.dwRate/psi,dwScale.返回的值应该匹配显示帧的速度(你在AVI动画中右击鼠标可以看到).你会问那么这和mpf有什么关系呢?第一次写这个代码时,我试着用fps来选择动画了正确的帧面.我遇到一个问题...视频放的太快!于是我看了一下视频属性.face2.avi文件有3.36秒长.帧速是29.974fps.视频动画共有91帧.而3.36*29.974   =   100.71.非常奇怪!!

所以我采用一些不同的方法.不是计算帧速,我计算每一帧播放所需时间.AVIStreamSampleToTime()把在动画中的位置转换位你到达该位置所需的时间(毫秒计).所以通过计算到达最后一帧的时间就得到整个动画的播放时间.再拿这个结果除以动画总帧数(lastframe).这样就给出了每帧的显示时间(毫秒计).结果存在mpf(milliseconds   per   frame)里.你也能通过获取动画中一帧的时间来算每帧的毫秒数,代码为:AVIStreamSampleToTime(pavi,1).两种方法都不错!非常感谢Albert   Chaulk提供思路!
我说每帧的毫秒数不精确是因为mpf是一个整型值,所以所有的浮点数都会被取整.
AVIStreamInfo(pavi,   &psi,   sizeof(psi));//   把流信息读进psi
width=psi.rcFrame.right-psi.rcFrame.left;//   宽度为右边减左边
height=psi.rcFrame.bottom-psi.rcFrame.top;//   高为底边减顶边
lastframe=AVIStreamLength(pavi);//   最后一帧的序号
mpf=AVIStreamSampleToTime(pavi,lastframe)/lastframe;//   mpf的不精确值
  我们可利用Windows   Dib函数去做.  
首先要做的是描述我们想要的图像的类型.于是我们要以所需参数填好bmih这个BitmapInfoHeader结构.
首先设定该结构体的大小.再把位平面数设为1.3字节的数据有24比特(RGB).要使图像位256像素宽,256像素高,最后要让数据返回为UNCOMPRESSED(非压缩)的RGB数据(BI_RGB).  

CreateDIBSection创建一个可直接写的设备无关位图(dib).如果一切顺利,hBitmap会指向该dib的比特值.hdc是设备上下文(DC)的句柄第二参数是BitmapInfo结构体的指针.该结构体包含了上述dib文件的信息.第三参数(DIB_RGB_COLORS)设定数据是RGB值.data是指向DIB比特值位置的指针的指针(呜,真绕口).第五参数设为NULL,我们的DIB已被分配好内存.末了,最后一个参数可忽略(设为NULL).

引自MSDN:SelecObject函数选一个对象进入设备上下文(DC).

现在我们建好一个能直接写的DIB,yeah:)
bmih.biSize=   sizeof   (BITMAPINFOHEADER);//   BitmapInfoHeader的大小
bmih.biPlanes=   1;//   位平面
bmih.biBitCount=   24;//比特格式(24   Bit,   3   Bytes)
bmih.biWidth=   256; //   宽度(256   Pixels)
bmih.biHeight=   256;//   高度   (256   Pixels)
bmih.biCompression =   BI_RGB; //   申请的模式   =   RGB
hBitmap   =   CreateDIBSection   (hdc,   (BITMAPINFO*)(&bmih),   DIB_RGB_COLORS,   (void**)(&data),   NULL,   NULL);
SelectObject   (hdc,   hBitmap);//   选hBitmap进入设备上下文(hdc)
     
  在从AVI中读取帧面前还有几件事要做.接下来使程序做好从AVI文件中解出帧面的准备.用AVIStreamGetFrameOpen(...)函数做这一点.  
你能给这个函数传一个结构体作为第二参数(它会返回一个特定的视频格式).糟糕的是,你能改变的唯一数据是返回的图像的宽度和高度.MSDN也提到能传AVIGETFRAMEF_BESTDISPLAYFMT为参数来选择一个最佳显示格式.奇怪的是,我的编译器没有定义这玩艺儿.

如果一切顺利,一个GETFRAME对象被返回(用来读帧数据).有问题的话,提示框会出现在屏幕上告诉你有错误!
pgf=AVIStreamGetFrameOpen(pavi,   NULL); //   用要求的模式建PGETFRAME
if   (pgf==NULL)
{
//   解帧出错
MessageBox   (HWND_DESKTOP,   "不能打开AVI帧 ",   "错误 ",   MB_OK   |   MB_ICONEXCLAMATION);
}
  下面的代码把视频宽,高和帧数传给window标题.用函数SetWindowText(...)在window顶部显示标题.以窗口模式运行程序看看以下代码的作用.  
//   bt标题栏信息(宽   /   高/   帧数)
wsprintf   (title,   "NeHe 's   AVI   Player:   Width:   %d,   Height:   %d,   Frames:   %d ",   width,   height,   lastframe);
SetWindowText(g_window-> hWnd,   title);//   修改标题栏
}
下面是有趣的东西...从AVI中抓取一帧,把它转为大小和色深可用的图象.lpbi包含一帧的BitmapInfoHeader信息.我们在下面第二行完成了几件事.先是抓了动画的一帧...我们需要的帧面由这些帧确定.这会让动画走掉这一帧,lpbi会指向这一帧的头信息.  
下面是有趣的东西...我们要指向图像数据了.要跳过头信息(lpbi-> biSize).一件事直到写本文时我才意识到:也要跳过任何的色彩信息.所以要跳过biClrUsed*sizeof(RGBQUAD)(译者:我想他是说要跳过调色板信息).做完这一切,我们就得到图像数据的指针了(pdata).

也要把动画的每一帧的大小转为纹理能用的大小,还要把数据转为RGB数据.这用到DrawDibDraw(...).一个大概的解释.我们能直接写设定的DIB图像.那就是DrawDibDraw(...)所做的.第一参数是DrawDib   DC的句柄.第二参数是DC的句柄.接下来用左上角(0,0)和右下角(256,256)构成目标矩形.lpbi指向刚读的帧的bitmapinfoheader信息.pdata是刚读的帧的图像数据指针.再把源图象(刚读的帧)的左上角设为(0,0),右下角设为(帧宽,帧高).最后的参数应设为0.这个方法可把任何大小、色深的图像转为256*256*24bit的图像.
void   GrabAVIFrame(int   frame)//   从流中抓取一帧
{
LPBITMAPINFOHEADER   lpbi;//   存位图的头信息
lpbi   =   (LPBITMAPINFOHEADER)AVIStreamGetFrame(pgf,   frame);//   从AVI流中得到数据
pdata=(char   *)lpbi+lpbi-> biSize+lpbi-> biClrUsed   *   sizeof(RGBQUAD);//   数据指针,由AVIStreamGetFrame返回(跳过头  
//信息和色彩信息)
//   把数据转为所需格式
DrawDibDraw   (hdd,   hdc,   0,   0,   256,   256,   lpbi,   pdata,   0,   0,   width,   height,   0);
  我们得到动画的每帧数据(红蓝数据颠倒的).为解决这个问题,我们的高速代码flipIt(...).记住,data是指向DIB比特值位置的指针的指针变量.这意味着调用DrawDibDraw后,data指向一个调整过大小(256*256),修改过色深(24bits)的位图数据.  
flipIt(data); //   交换红蓝数据
//   更新纹理
。。。。。这里保存或位图
}
接下来的部分当程序退出时调用,我们关掉DrawDib   DC,释放占用的资源.然后释放AVI   GetFrame资源.最后释放AVI流和文件.    
void   CloseAVI(void)//   关掉AVI资源
{
DeleteObject(hBitmap);//释放设备无关位图信息
DrawDibClose(hdd); //   关掉DrawDib   DC
AVIStreamGetFrameClose(pgf);//   释放AVI   GetFrame资源
AVIStreamRelease(pavi);//   释放AVI流
AVIFileExit();//   释放AVI文件
}
void   flipIt(void*   buffer)//   交换红蓝数据(256x256)
{
void*   b   =   buffer;//   缓冲指针
__asm//   汇编代码
{
mov   ecx,   256*256 //   设置计数器
mov   ebx,   b//   ebx存数据指针
label: //   循环标记
mov   al,[ebx+0]//   把ebx位置的值赋予al
mov   ah,[ebx+2]//   把ebx+2位置的值赋予ah
mov   [ebx+2],al//   把al的值存到ebx+2的位置
mov   [ebx+0],ah//   把ah的值存到ebx+0的位置

add   ebx,3 //   向前走3个字节
dec   ecx //   循环计数器减1
jnz   label //   ecx非0则跳至label
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值