视频采集学习笔记

  
视频采集学习笔记
 
 
 
 
 
说起视频捕捉问题,我们先要来看一下视频捕捉卡。根据使用的驱动程序的不同来分类,目前市场上大致有两种捕捉卡: VFW (Video for Windows) 卡和 WDM (Windows Driver Model) 卡。前者是一种趋于废弃的驱动模型,而后者是前者的替代模型; WDM 还支持更多新的特性,比如直接支持电视接收、视频会议、 1394 接口的设备、桌面摄像机、多条视频流( Line-21 Closed-Caption 等)同时输出等等。采用 VFW 的一般都是些以前生产的卡;市面上新出现的,一般都是采用了 WDM 驱动程序。另外,视频捕捉卡的接口,可以是以 PCI AGP 的方式插入 PC 机箱,也可以直接以 USB 接口的方式外挂;还有就是通过 1394 接口与 PC 机相连的数码摄像机等等。
二、vfw视频采集
FW 是微软公司 1992 年推出的关于数字视频的一个软件包,它能使应用程序通过数字化设备从传统的模拟视频源得到数字化的视频剪辑。 VFW 的一个关键思想是播放时不需要专用硬件,为了解决数字视频数据量大的问题,需要对数据进行压缩。它引进了一种叫 AVI 的文件标准,该标准未规定如何对视频进行捕获、压缩及播放,仅规定视频和音频该如何存储在硬盘上,以及在 AVI 文件中交替存储视频帧和与之相匹配的音频数据。 VFW 使程序员能通过发送消息或设置属性来捕获、播放和编辑视频剪辑。在 Windows   9x 系统中 , 当用户在安装 VFW 时,安装程序会自动地安装配置视频所需要的组件,如设备驱动程序、视频压缩程序等。       
 VFW 主要由以下 6 个模块组成:       
  AVICAP.DLL :包含执行视频捕获的函数,它给 AVI 文件的 I/O 处理和视频、音频设备驱动程序提供一个高级接口;        
  MSVIDEO.DLL :包含一套特殊的 DrawDib 函数,用来处理屏幕上的视频操作;       
  MCIAVI.DRV :包括对 VFW MCI 命令解释器的驱动程序;       
  AVIFILE.DLL :包含由标准多媒体 I/O mmio )函数提供的更高的命令,用来访问 .AVI 文件;       
  ●压缩管理器( ICM ):用于管理的视频压缩 / 解压缩的编译码器( Codec );       
  ●音频压缩管理器 ACM :提供与 ICM 相似的服务,适用于波形音频。       
  开发步骤     
 AVICap 窗口类支持实时的视频流捕获和单帧捕获,并提供对视频源的控制。虽然 MCI 也提供数字视频服务 ( 比如它为显示 .AVI 文件的视频提供了 AVI   VIDEO 命令集 ) ,为视频叠加提供了 Overlay 命令集,但这些命令主要是基于文件的操作,它们不能满足实时地从视频缓存中取数据的要求,    对于使用没有视频叠加能力的捕获卡的 PC 机来说,    MCI 提供的命令集是无法捕获视频流的。而 AVICap 窗口类在捕获视频方面具有一定的优势,它能直接访问视频缓冲区,不需要生成中间文件,实时性很强,效率很高。而且,它还可将数字视频捕获到一个文件中。       
 1. 创建“捕获窗”     
  在进行视频捕获之前必需要先创建一个“捕获窗”,并以它为基础进行所有的捕获及设置操作。“捕获窗”用 AVICap 窗口类的“ CapCreateCaptureWindow ”函数来创建,其窗口风格一般为 WS_CHILD WS_VISIBLE     
  捕获窗类似于标准控件(如按钮、列表框等),并具有下列功能:       
  ●将视频流和音频流捕获到一个 AVI 文件中;       
  ●动态地同视频和音频输入器件连接或断开;       
  ●以 Overlay Preview 模式对输入的视频流进行实时显示;       
  ●在捕获时,可指定所用的文件名并能将捕获文件的内容拷贝到另一个文件;       
  ●设置捕获速率;       
  ●显示控制视频源、视频格式、视频压缩的对话框;       
  ●创建、保存或载入调色板;       
  ●将图像和相关的调色板拷贝到剪贴板;       
  ●将捕获的单帧图像保存为 DIB 格式的文件。       
 2 .关联捕获窗和驱动程序     
  单独定义的一个捕获窗是不能工作的,它必需与一个设备相关联,这样才能取得视频信号。用函数 CapDriverConnect 可使一个捕获窗与一个设备驱动程序相关联。     
 3 .设置视频设备的属性     
  通过设置 TcaptureParms 结构变量的各个成员变量,可以控制设备的采样频率、中断采样按键、状态行为等等。设置好 TCaptureParms 结构变量后,可以用函数 CapCaptureSetSetup 使设置生效。之后还可以用 CapPreviewScale CapPreviewRate 来设置预览的比例与速度,也可以直接使用设备的默认值。     
 4 .打开预览     
  利用函数 CapOverlay 选择是否采用叠加模式预览,这样占用系统资源小,并且视频显示速度快。然后用 CapPreview 启动预览功能,这时就可以在屏幕上看到来自摄像机的图像了。     
  通过以上 4 步就可以建立一个基本的视频捕获程序。但如果想自已处理从设备捕获到的视频数据,则要使用捕获窗回调函数来处理,比如一帧一帧地获得视频数据或以流的方式获得视频数据等等。
三、directshow视频采集:
使用 DirectShow 来处理一般的视频捕捉问题,是相对比较简单的。这当然得益于 DirectShow 这一整套先进的应用架构。捕捉卡通常也是以一个 (Capture) Filter 的形式出现的。处理视频捕捉,我们同样是使用 Filter Graph ,同样是操作 Filter ;控制起来,就似于操作媒体文件的播放。
 当视频捕捉卡正确安装到系统中后,使用 GraphEdit 插入 Filter ,我们可以在 “Video Capture Sources” 目录下看到代表捕捉卡的那个 Filter 。一般一个 Capture Filter 至少有一个 Capture Output Pin ;典型的情况下,还有一个 Preview Pin 或者 Video Port Pin (一般 Preview Pin VP Pin 不会共存)。有些视频捕捉卡,能够同时捕捉 Video Audio ,那么它的 Filter 自然还应该有 Audio 部分的输出 Pin 。视频捕捉卡都注册在 CLSID_VideoInputDeviceCategory 目录之下;要知道系统中安装了哪些捕捉卡,需要利用系统枚举,如下:

ICreateDevEnum *pDevEnum = NULL;
IEnumMoniker *pEnum = NULL;
// Create the System Device Enumerator.
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
reinterpret_cast<void**>(&pDevEnum));
if (SUCCEEDED(hr))
{
// Create an enumerator for the video capture category.
hr = pDevEnum->CreateClassEnumerator(
CLSID_VideoInputDeviceCategory, &pEnum, 0);
}
   Capture Filter 的创建也不是象其他 Filter 一样使用 CoCreateInstance ,而是在枚举的过程中 BindToObject 。在这一点上,对 WDM 卡和 VFW 卡的处理是一致的。

  将 Capture Filter 加入 Filter Graph 之后,剩下的 Filter 怎么连接? DirectShow 给我们提供了一个简单的解决方法:使用 ICaptureGraphBuilder2 接口。如下创建:

IGraphBuilder *pGraph = 0;
ICaptureGraphBuilder2 *pBuild = 0;
// Create the Capture Graph Builder.
HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, 0,
CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2,
(void**)&pGraph);
if (SUCCEEDED(hr))
{
// Create the Filter Graph Manager.
hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void**)&pGraph);
if (SUCCEEDED(hr))
{
// Initialize the Capture Graph Builder.
pBuild->SetFiltergraph(pGraph);
}
}
  接下来,就是使用 ICaptureGraphBuilder2::RenderStream 来继续各个 Output Pin 的连接。值得注意的是,这里有一个 Pin Category 的概念,作为 RenderStream 的第一个参数,比如 Preview Pin 的目录为 PIN_CATEGORY_PREVIEW Capture Pin 的目录为 PIN_CATEGORY_CAPTURE 等等。下面是 Preview Pin 的连接示例:

ICaptureGraphBuilder2 *pBuild; // Capture Graph Builder
// Initialize pBuild (not shown).
IBaseFilter *pCap; // Video capture filter.
/* Initialize pCap and add it to the filter graph (not shown). */
hr = pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,
pCap, NULL, NULL);
  调用 RenderStream 实现 Preview 链路,不管 Capture Filter 是否有 Preview Pin 或者只有 VP Pin Capture Graph Builder 都能自动正确地处理。(如果只有 VP Pin ,则自动连接 VP Pin; 如果 Capture Filter 只有一个 Capture Output Pin ,则自动插入一个 Smart Tee Filter 然后再连接。)
三层交换技术 交换机与路由器密码恢复 交换机的选购 路由器设置专题 路由故障处理手册 数字化校园网解决方案
要实现视频捕捉到文件,最简单的方法也是使用 ICaptureGraphBuilder2::RenderStream 。如下(假设生成的是 AVI 文件):

IBaseFilter *pMux;
hr = pBuild->SetOutputFileName(
&MEDIASUBTYPE_Avi, // Specifies AVI for the target file.
L"C://Example.avi", // File name.
&pMux, // Receives a pointer to the mux.
NULL);
hr = pBuild->RenderStream(
&PIN_CATEGORY_CAPTURE, // Pin category.
&MEDIATYPE_Video, // Media type.
pCap, // Capture filter.
NULL, // Intermediate filter (optional).
pMux); // Mux or file sink filter
使用 Capture Graph Builder 构建 Filter 链路的好处,还在于它能自动加入 Crossbar Filter (用于选择捕捉卡的输入端子,一般有三种: AV S-Video TV ),如果是电视卡的话还有 TV Tuner Filter 等等;使用 ICaptureGraphBuilder2::FindInterface 就可以找到相应的控制接口等等。
第二节 vfw 视频采集中的一些问题:
一、 vfw 中的四个结构体:
1、 CAPSTATUS:定义了捕获窗口的当前状态,如图像的宽、高等;
  uiImageWidth
图像宽度
uiImageHeight
图像高度
fLiveWindow
       活动窗口标记,如果窗口正以预览的方式展示图像,那么该值为真。
fOverlayWindow
      叠加窗口标志位,如果正在使用硬件叠加,则该位是真。
fScale
      输入所放标志位,如果窗口是正在缩放视频到客户区,那么该位是真。当使用硬件叠加时,改位无效。
ptScroll
被展示在窗口客户区左上角的那个象素的x、y坐标偏移量。
fUsingDefaultPalette
默认调色板标志位,如果捕获窗口正在使用当前默认调色板,该值为真
 
fAudioHardware
       音频硬件标志位,如果系统已经安装了音频硬件,该值为真。
fCapFileExists
      捕获文件标志位,如果一个捕获文件已经被创建,该值为真。
dwCurrentVideoFrame
      当前或最近流捕获过程中,所处理的桢的数目。包括丢弃的桢。
dwCurrentVideoFramesDropped
      当前流捕获过程中丢弃的桢的数目。
Number of waveform-audio samples processed during the current (or most recent) streaming capture.
dwCurrentTimeElapsedMS
      从当前流捕获开始计算,程序所用的时间,以毫秒为单位。
hPalCurrent
当前剪切板的句柄。
fCapturingNow
捕获标志位,当捕获是正在进行时,改位是真
dwReturn
      错误返回值,当你的应用程序不支持错误回调函数时可以应用改位
wNumVideoAllocated
     被分配的视频缓存的数目。
wNumAudioAllocated
被分配的音频缓存的数目。
   
 2、CAPDRIVERCAPS:定义了捕获驱动器的能力,如有无视频叠加能力、有无控制视频源、视频格式的对话框等;
wDeviceIndex
      捕获驱动器的索引值,该值可以由0到9变化。
fHasOverlay
      视频叠加标志,如果设备支持视频叠加这该位是真。
fHasDlgVideoSource
      视频资源对话框标志位,如果设备支持视频选择、控制对话框,该值为真。
fHasDlgVideoFormat
      视频格式对话框标志位,如果设备支持对视频格式对话框的选择,该位真。
fHasDlgVideoDisplay
      视频展示对话框标志位,如果设备支持对视频捕获缓存区的重新播放,该位是真。
fCaptureInitialized
     捕获安装标志位,如果捕获驱动器已经成功连接,该值为真。
fDriverSuppliesPalettes
      驱动器调色板标志位,如果驱动器能创建调色板,则该位是真。

 3、CAPTUREPARMS:包含控制视频流捕获过程的参数,如捕获帧频、指定键盘或鼠标键以终止捕获、捕获时间限制等;
 
 
dwRequestMicroSecPerFrame
      期望的桢播放率,以毫秒为单位,默认为66667,相当于15桢每秒。
fMakeUserHitOKToCapture
      开始捕获标志位,如果值为真,则在开始捕获前要产生一个询问对话框,默认为假。
wPercentDropForError
      所允许的最大丢桢百分比,可以从0变化到100,默认值为10。
fYield
      另起线程标志位,如果为真,则程序重新启动一个线程用于视频流的捕获,默认值是假。但是如果你是为了真,你必须要在程序中处理一些潜在的操作,因为当视频捕获时,其他操作并没有被屏蔽。
dwIndexSize
      在AVI文件中所允许的最大数目的索引项
wChunkGranularity
      AVI 文件的逻辑尺寸,以字节为单位。如果值是0,则说明该尺寸渐增
在 Win32程序中无用。
wNumVideoRequested
      所被允许分配的最大视频缓存。
fCaptureAudio
      音频标志位,如果音频流正在捕获,则该值为真。
 
wNumAudioRequested
      最大数量的音频缓存,默认值为10。
vKeyAbort
      终止流捕获的虚拟键盘码,默认值为VK_ESCAPE.
fAbortLeftMouse
      终止鼠标左键标志位,如果该值为真,则在流捕获过程中如果点击鼠标左键则该捕获终止,默认值为真。
fAbortRightMouse
      终止鼠标右键标志位。
fLimitEnabled
      捕获操作时间限制,如果为真,则时间到了以后捕获操作终止,默认为假
wTimeLimit
具体终止时间,只有 fLimitEnabled是真时.该位才有效。
fMCIControl
MCI 设备标志。
fStepMCIDevice
MCI 设备标志。 .
wStepCaptureAverageFrames
当基于平均采样来创建桢时,桢的采样时间,典型值是5。
dwAudioBufferSize
音频缓存的尺寸,如果用默认值0,缓存尺寸是最大0.5秒,或10k字节。
 
AVStreamMaster
      音视频同步标志。

 4、VIDEOHDR:定义了视频数据块的头信息,在编写回调函数时常用到其数据成员lpData(指向数据缓存的指针)和dwBufferLength(数据缓存的大小)
capDriverGetCaps
得到驱动器的状态信息。
capCaptureGetSetup(m_hCapWnd,&CapParms,sizeof(CAPTUREPARMS));
得到CAPTUREPARMS的状态信息。
capCaptureSetSetup(m_hCapWnd,&CapParms,sizeof(CAPTUREPARMS));
把新设好的状态信息写入结构体中,只有此时新的状态才会生效。
capGetStatus
得到 CAPSTATUS 的信息。
二、有关视频窗口的创建
首先要创建一个父窗口句柄,注意该句柄一定要设置成类变量,因为下面的采集窗口要用到该变量,而他如果为局部变量则在此函数外要销毁,那么采集窗口就找不到父窗口了。
m_hCapWnd=capCreateCaptureWindow("视频捕捉预览程序",
                               WS_VISIBLE|WS_SYSMENU|WS_CAPTION,
                            150,150,325,270,hwnMain,0);
用于创建捕获窗口,注意句柄m_hCapWnd最好为全局变量,因为后面的回调函数要用到它,而回调函数只能是全局函数。注意状态WS_SYSMENU可以让捕获窗口带有关闭按钮,状态只能在捕获窗口中加入,而父窗口的类型将不能反映出来。
全局变量不可在头文件中定义,因为头文件被多个源程序包含时,会引起错误。全局变量可以在源程序的任意位置定义,只要在源程序的开始声明,就能在任意位置使用。
1、可以定义在一个单独的H文件中,但只能有一个类包含它,否则会报错。  
2、好的方法,是在用到此变量(或函数)的程序(CPP文件)中定义此变量。  
3、如果其它的程序(CPP文件)也要用到此变量,在用之前,用extern声明。例如:定义了一个int   nIndex,在另一个程序中这样定义:extern   int   nIndex;
  全局函数只要定义在类外的函数就是全局的。
全局函数的定义在一个CPP中书写函数体,在一个.h中写函数的原型声明!使用该函数的CPP都要include该头文件! 
如果其他文件要用到此全局函数,只须在使用前用extern声明一下即可,补充一下,关于定义全局函数根本不用在头文件中声明。如果其他文件要用到此全局函数,只须在使用前用extern声明一下即可

六、vc怎么与硬件的驱动相连?
通过函数capGetDriverDescription可以得到本地机器所连的所有视频读入设备的信息
利用如下程序段:
       char   achDeviceVersion[80]   ; //   设备版本信息   
       char   achDeviceAndVersion[160];//设备名及版本信息   
       int   uIndex;  
       int    DriverCount=0;
       for(uIndex=0;uIndex<9;uIndex++)   {            if(capGetDriverDescription(uIndex,(LPSTR)achDeviceAndVersion,sizeof(achDeviceAndVer
        sion),      (LPSTR)achDeviceVersion,sizeof(achDeviceVersion)))  
              {   strcat(achDeviceAndVersion,",");  
              strcat(achDeviceAndVersion,achDeviceVersion);  
              DriverCount++;
可以得到所连的视频设备的描述,并可有DriverCount的值确定所连了几台捕获设备,最大值为9。
确定了可用的视频捕获设备的详细信息之后就可以利用capDriverConnect(m_hCapWnd,0)来连接0号视频设备,当然也可以利用for语句连接DriverCount个视频设备,但最多只可为9个,这里的数字0就是具体的视频设备的驱动接口,仅仅是在该函数中可以利用0-9表示视频设备,在其他硬件的描述中要有另外的方法。
三、回调函数
回调函数是至今为止最有用的编程机制之一。在 Windows 中,回调函数更是窗口
程、 钩子 过程、异步过程调用所必需的,在整个回调过程中自始至终地使用回调方法。人们可以注册回调方法以获得加载 / 卸载通知,未处理异常通知,数据库 / 窗口状态修改通知,文件系统修改通知,菜单项选择,完成的异步操作通知,过滤一组条目等等。在 VFW 中有几条这样的宏函数 , 如用于设置在发生某事件后能作出反应的回调函数的宏函数 , 它和中断服务机制很相似 , 条件一满足 , 程序会自动进入相应的回调函数体中 , 该函数究竟要做些什么 , 全由开发者借助其参数自行编制程序来确定。利用 VFW 获取实时视频数据通常可以运用视频处理的回调机制 (call-backmechanism) 获得实时数据缓冲区的首址和长度并对图像数据进行处理,同时也可以进行视频数据的直接传输。
回调函数一定是公有函数,不可以为任何成员函数。特别是用 MFC 编程时,如果在某个按钮出发函数中定义回调函数,势必要编译报错。在 MFC 下要把回调函数定义成公有函数,就是在 .cpp 文件中直接定义函数体,且不可定义为某个类的成员函数。在打算调用回调函数的的程序中只需声明回调函数体,及注册该回调函数则可。
声明及注册回调函数:
LRESULT PASCAL FrameCallbackProc(HWND, LPVIDEOHDR); 
capSetCallbackOnFrame(m_hCapWnd, FrameCallbackProc);
定义回调函数:
LRESULT CALLBACK FrameCallbackProc(HWND hWnd,LPVIDEOHDR lpVHdr)
//lpVHdr 指向数据结构 VIDEOHEADER ,这里面就是每一帧画面的数据, RGB 格式的
// 注意无论是否设置了压缩格式,在此函数中得到的,始终是未经过压缩的原始 bmp 数据,所以加上 bmp 文件头写入文件就可以了
//lpVHdr->lpData 中存储的只是位图象素信息,必须加入头、与象素信息后才可组成一组图像   
{if( 、、、、 )
      {return FALSE;
else return (LRESULT)TRUE;}
注意该回调函数中一定要有一个返回值。
利用函数 capCaptureSequence(m_hCapWnd); 可以把采集到的视频流保存成图像。系统将在 c 盘上默认一个捕获文件 c:/NEWFILE.AVI ,但也可以用函数 capFileSetCaptureFile(m_hCapWnd,filename); 自己定义捕获文件。在捕获过程中,因为屏蔽了其他操作所以使得捕获过程看起来有点卡,此时有两种方法可以解决,另起一个用于捕获的线程,或者是把CAPTUREPARMS中的 fYield 设为true。但此时因为你另起了一个线程采集图像,所以必须要处理一些鼠标键盘的操作,例如,如果 fAbortLeftMouse为真,则当你点击鼠标左键时,你的采集就终止了,这是我们不希望看到的。
capGrabFrame(m_hCapWnd);//capGrabFrameNoStop 将没有停顿 可以捕获当前桢。
capFileSaveDIB(m_hCapWnd, achFileName); 可以把视频缓存中的内容保存成文件。
但是如果想要在回调函数FrameCallbackProc(HWND hWnd,LPVIDEOHDR lpVHdr)中的信息保存成图片并不那么容易。
因为lpVHdr->lpData中存放的仅仅是视频的像素信息,必须给该信息加入
BITMAPFILEHEADER 及BITMAPINFO   bihI; 两个头信息后才成为真正的图片信息。例如:
用于获得信息头文件的各个变量值:
    BITMAPINFO   bihI;  
    DWORD   dwSize;  
    dwSize=capGetVideoFormatSize(m_hCapWnd);
    capGetVideoFormat(m_hCapWnd,&bihI,dwSize);
用来给文件头的各个变量赋值:
   BITMAPFILEHEADER   bfh; // 定义头文件信息
    bfh.bfType   =   0x4d42;     //(WORD)('M'   <<   8   |   'B');  
    bfh.bfSize   =     sizeof(BITMAPFILEHEADER)+    
    bihI.bmiHeader.biSize   +   bihI.bmiHeader.biSizeImage;  
    bfh.bfReserved1   =   bfh.bfReserved2   =   0;  
bfh.bfOffBits=(DWORD)sizeof(BITMAPFILEHEADER)+ bihI.bmiHeader.biSize; 、、、、、、、
 
九、win32 application与win32 console application的异同
(1)win32   application编译器寻找的入口函数是WinMain()  
      win32   console   application编译器寻找的入口函数是main()  
(2)WIN32   APP   通常是多线程的,而WIN32   CONSOLE   APP   通常是单线程的 
十、有关最基本的c++程序的结构的一个完整例子
适用于 vc 的初级入门者的一个例子:
#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK WinMYProc(
 HWND hWnd,      // handle to window
 UINT uMsg,      // message identifier
 WPARAM wParam, // first message parameter
 LPARAM lParam   // second message parameter
);
 
int WINAPI WinMain(
 HINSTANCE hInstance,      // handle to current instance
 HINSTANCE hPrevInstance, // handle to previous instance
 LPSTR lpCmdLine,          // command line
 int nCmdShow              // show state
)
{
WNDCLASS wndcls;// 注册窗口类
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
wndcls.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);// 窗口画刷
wndcls.hCursor=LoadCursor(NULL,IDC_ARROW);// 鼠标
wndcls.hIcon=LoadIcon(NULL,IDI_WINLOGO);// 图标
wndcls.hInstance=hInstance;// 窗口实例
wndcls.lpfnWndProc=WinMYProc;// 窗口函数
wndcls.lpszClassName="BEYOND";// 窗口类名
wndcls.lpszMenuName=NULL;// 菜单名(这里为空)
wndcls.style=CS_HREDRAW | CS_VREDRAW;// 窗口风格(水平重绘和垂直重绘)
RegisterClass(&wndcls);// 注册窗口
// 注册完窗口,接着是创建和显示窗口了
HWND hWnd;
//=CreateWindow ("BEYOND","lovebeyond",WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);
hWnd=CreateWindow ("BEYOND","lovebeyond",WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);
ShowWindow(hWnd,SW_SHOWNORMAL);// 显示
UpdateWindow(hWnd);// 更新
// 接下来是消息循环了
MSG msg;
while(GetMessage(&msg,NULL,0,0))// 当不为 WM_QUIT 时,继续消息循环
{
TranslateMessage(&msg);// 用于翻译消息从字符码消息到 wmchar 消息,
DispatchMessage(&msg);// 将消息路由给了操作系统然后系统再调用相应窗口的回调函数中
}
return msg.wParam;// 返回一个参数
}
// 消息响应函数
LRESULT CALLBACK WinMYProc(
 HWND hWnd,      // handle to window
 UINT uMsg,      // message identifier
 WPARAM wParam, // first message parameter
 LPARAM lParam   // second message parameter
)
{
switch(uMsg)
{
case WM_CHAR:// 键盘消息
char szChar[20];
sprintf(szChar,"char is %d",wParam);
MessageBox(hWnd,szChar,"char",0);
break;
case WM_LBUTTONDOWN:// 鼠标左键按下
MessageBox(hWnd,"mouse clicked","message",0);
break;
case WM_PAINT:// 窗口重绘消息
HDC hDC;
PAINTSTRUCT ps;
hDC=BeginPaint(hWnd,&ps);
TextOut(hDC,0,0,"lovebeyond",strlen("lovebeyond"));
EndPaint(hWnd,&ps);
break;
case WM_CLOSE:// 关闭消息
if(IDYES==MessageBox(hWnd," 想要结束吗? ","message",MB_YESNO))
{
DestroyWindow(hWnd);
}
break;
case WM_DESTROY:// 销毁窗口消息
PostQuitMessage(0);
break;
default:// 默认窗口消息处理
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
return 0;
}
十一、基于Mfc对话框的源程序、
// vfw2Dlg.cpp : implementation file
#include "stdafx.h"
#include "vfw2.h"
#include "vfw2Dlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
HWND   m_hCapWnd;
/
// CAboutDlg dialog used for App About
 
class CAboutDlg : public CDialog
{
public:
    CAboutDlg();
   
    // Dialog Data
    //{{AFX_DATA(CAboutDlg)
    enum { IDD = IDD_ABOUTBOX };
    //}}AFX_DATA
   
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CAboutDlg)
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    //}}AFX_VIRTUAL
   
    // Implementation
protected:
    //{{AFX_MSG(CAboutDlg)
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};
 
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
    //{{AFX_DATA_INIT(CAboutDlg)
    //}}AFX_DATA_INIT
}
 
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CAboutDlg)
    //}}AFX_DATA_MAP
}
 
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
 
/
// CVfw2Dlg dialog
 
CVfw2Dlg::CVfw2Dlg(CWnd* pParent /*=NULL*/)
: CDialog(CVfw2Dlg::IDD, pParent)
{
    //{{AFX_DATA_INIT(CVfw2Dlg)
    // NOTE: the ClassWizard will add member initialization here
    //}}AFX_DATA_INIT
    // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
 
void CVfw2Dlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CVfw2Dlg)
    // NOTE: the ClassWizard will add DDX and DDV calls here
    //}}AFX_DATA_MAP
}
 
BEGIN_MESSAGE_MAP(CVfw2Dlg, CDialog)
//{{AFX_MSG_MAP(CVfw2Dlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_SAVE, OnSave)
ON_BN_CLICKED(IDC_STOP, OnStop)
ON_BN_CLICKED(IDC_camora, Oncamora)
ON_BN_CLICKED(IDC_FORMAT, OnFormat)
ON_BN_CLICKED(IDC_preview, Onpreview)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
 
/
// CVfw2Dlg message handlers
 
BOOL CVfw2Dlg::OnInitDialog()
{
    CDialog::OnInitDialog();
   
    // Add "About..." menu item to system menu.
   
    // IDM_ABOUTBOX must be in the system command range.
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);
   
    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
           CString strAboutMenu;
           strAboutMenu.LoadString(IDS_ABOUTBOX);
           if (!strAboutMenu.IsEmpty())
           {
                  pSysMenu->AppendMenu(MF_SEPARATOR);
                  pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
           }
    }
   
    // Set the icon for this dialog. The framework does this automatically
    // when the application's main window is not a dialog
    SetIcon(m_hIcon, TRUE);                 // Set big icon
    SetIcon(m_hIcon, FALSE);// Set small icon
   
   
   
    // TODO: Add extra initialization here
   
    return TRUE; // return TRUE unless you set the focus to a control
}
 
void CVfw2Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
           CAboutDlg dlgAbout;
           dlgAbout.DoModal();
    }
    else
    {
           CDialog::OnSysCommand(nID, lParam);
    }
}
 
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
 
void CVfw2Dlg::OnPaint()
{
    if (IsIconic())
    {
           CPaintDC dc(this); // device context for painting
          
           SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
          
           // Center icon in client rectangle
           int cxIcon = GetSystemMetrics(SM_CXICON);
           int cyIcon = GetSystemMetrics(SM_CYICON);
           CRect rect;
           GetClientRect(&rect);
           int x = (rect.Width() - cxIcon + 1) / 2;
           int y = (rect.Height() - cyIcon + 1) / 2;
          
           // Draw the icon
           dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
           CDialog::OnPaint();
    }
}
 
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CVfw2Dlg::OnQueryDragIcon()
{
    return (HCURSOR) m_hIcon;
   
}
 
 
 
 
void CVfw2Dlg::Onpreview()
{ CAPDRIVERCAPS CapDriverCaps;    // 设备驱动信息
    char   achDeviceVersion[80]   ; // 设备版本信息   
    char   achDeviceAndVersion[160];// 设备名及版本信息   
    int    uIndex;  
    int    DriverCount=0;// 支持的设备驱动程序个数  
    CAPTUREPARMS CapParms;// 捕获参数设置
   
    hwnMain=CreateWindow("GenericAppclass","super video",WS_THICKFRAME|WS_SYSMENU|WS_CAPTION ,
           100,100,100,100,NULL,NULL,NULL,NULL);// 创建主窗口
   
    for(uIndex=0;uIndex<5;uIndex++)  
    {  
           if(capGetDriverDescription(uIndex,(LPSTR)achDeviceAndVersion,sizeof(achDeviceAndVersion),
                  (LPSTR)achDeviceVersion,sizeof(achDeviceVersion)))  
           {  
                  strcat(achDeviceAndVersion,",");  
                  strcat(achDeviceAndVersion,achDeviceVersion);  
                  DriverCount++;// 可以得出 vfw 设备的信息并可知连了多少设备
           }  
           else  
                  break;  
    } 
    if(DriverCount==0)  
           MessageBox(" 找不到视频设备,请确认已正确连接 "," 错误信息 ",MB_ICONSTOP|MB_OK);  
    else
    {m_hCapWnd=capCreateCaptureWindow(" 视频捕捉预览程序 ",  
    WS_VISIBLE|WS_SYSMENU|WS_CAPTION,
    150,150,325,270,hwnMain,0);
    capCaptureGetSetup(m_hCapWnd,&CapParms,sizeof(CAPTUREPARMS));
    CapParms.fMakeUserHitOKToCapture=TRUE;
    CapParms.fMakeUserHitOKToCapture=true;
    CapParms.fYield=true;
    capCaptureSetSetup(m_hCapWnd,&CapParms,sizeof(CAPTUREPARMS));
   
   
    if (capDriverConnect(m_hCapWnd,0))// 判断采集窗口是否与 0 号采集卡驱动相连接
    {
           capDriverGetCaps(m_hCapWnd,&CapDriverCaps,sizeof(CAPDRIVERCAPS));  
           if(CapDriverCaps.fCaptureInitialized)// 初始化成功
           {capPreviewRate(m_hCapWnd,66);
           LRESULT PASCAL FrameCallbackProc(HWND, LPVIDEOHDR); 
          
           capSetCallbackOnFrame(m_hCapWnd, FrameCallbackProc);
          
           capPreview(m_hCapWnd, TRUE); // 启动 Preview 模式   
                  }
    }
    else
           MessageBox(" 视频采集窗口不能被连接 ");   
    }
   
}
 
 
 
void CVfw2Dlg::OnSave()
{ CAPDRIVERCAPS CapDriverCaps;
    char   achDeviceVersion[80]   ; //   设备版本信息   
    char   achDeviceAndVersion[160];// 设备名及版本信息   
    int    uIndex;  
    int    DriverCount=0;// 支持的设备驱动程序个数  
    CAPTUREPARMS CapParms;
    hwnMain=CreateWindow("GenericAppclass","super video",WS_THICKFRAME|WS_SYSMENU|WS_CAPTION ,
           100,100,100,100,NULL,NULL,NULL,NULL);// 创建主窗口
    for(uIndex=0;uIndex<5;uIndex++)     {  
           if(capGetDriverDescription(uIndex,(LPSTR)achDeviceAndVersion,sizeof(achDeviceAndVersion),
                  (LPSTR)achDeviceVersion,sizeof(achDeviceVersion)))  
           {   strcat(achDeviceAndVersion,",");  
           strcat(achDeviceAndVersion,achDeviceVersion);  
           DriverCount++;// 可以得出 vfw 设备的信息并可知连了多少设备
           }  
           else  
                  break;  
    } 
    if(DriverCount==0)  
           MessageBox(" 找不到视频设备,请确认已正确连接 "," 错误信息 ",MB_ICONSTOP|MB_OK);  
    else
    {m_hCapWnd=capCreateCaptureWindow(" 视频捕捉预览程序 ",  
    WS_VISIBLE|WS_SYSMENU|WS_CAPTION,
    150,150,325,270,hwnMain,0);       
    capCaptureGetSetup(m_hCapWnd,&CapParms,sizeof(CAPTUREPARMS));
    CapParms.fMakeUserHitOKToCapture=true;
    float FramesPerSec = 10.0;
    CapParms.dwRequestMicroSecPerFrame=(DWORD) (1.0e6 /
           FramesPerSec);  
    CapParms.fYield=true;
    CapParms.fAbortLeftMouse=false;// 注意此处阿,如果是真那么程序会相应鼠标事件,让捕获终止
    capCaptureSetSetup(m_hCapWnd,&CapParms,sizeof(CAPTUREPARMS));    
if (capDriverConnect(m_hCapWnd,0))// 判断采集窗口是否与 0 号采集卡驱动相连接
    {       
       {char sz[]="e:/NEWFILE.AVI";
        char sz0[]="e:/N0.AVI";
           capFileSetCaptureFile(m_hCapWnd,sz0);// 命名捕捉文件 , 默认在 c 盘上
        capFileAlloc(m_hCapWnd,(1024L*1024L*500));// 与分配 500 兆空间
         capPreviewRate(m_hCapWnd,66);    
            capPreview(m_hCapWnd, TRUE); // 启动 Preview 模式
            capCaptureSequence(m_hCapWnd);          
           }
    }
    else
           MessageBox(" 视频采集窗口不能被连接 ");   
    }    
}
 
 
void CVfw2Dlg::OnStop()
{// 因为保存中既有预览、又有存储,所以要断开两次才可以完成断开。
char sz[]="e:/NEWFILE.AVI";
capFileSaveAs(m_hCapWnd,sz);
capCaptureAbort(m_hCapWnd);
capCaptureStop(m_hCapWnd);
capDriverDisconnect(m_hCapWnd);
 
}
 
 
 
void CVfw2Dlg::Oncamora()
{
    char achFileName[]="e:/mp.bmp";
    capGrabFrame(m_hCapWnd);//capGrabFrameNoStop 将没有停顿
    capFileSaveDIB(m_hCapWnd, achFileName);
    capCaptureStop(m_hCapWnd);
   
}
void CVfw2Dlg::OnFormat()
{CAPDRIVERCAPS CapDriverCaps;
char   achDeviceVersion[80]   ; //   设备版本信息   
char   achDeviceAndVersion[160];// 设备名及版本信息   
int    uIndex;  
int    DriverCount=0;// 支持的设备驱动程序个数  
CAPTUREPARMS CapParms;
hwnMain=CreateWindow("GenericAppclass","super video",WS_THICKFRAME|WS_SYSMENU|WS_CAPTION ,
                                 100,100,100,100,NULL,NULL,NULL,NULL);// 创建主窗口
 
for(uIndex=0;uIndex<5;uIndex++)  
{  
    if(capGetDriverDescription(uIndex,(LPSTR)achDeviceAndVersion,sizeof(achDeviceAndVersion),
           (LPSTR)achDeviceVersion,sizeof(achDeviceVersion)))  
    {  
        strcat(achDeviceAndVersion,",");  
        strcat(achDeviceAndVersion,achDeviceVersion);  
        DriverCount++;// 可以得出 vfw 设备的信息并可知连了多少设备
    }  
    else  
        break;  
if(DriverCount==0)  
MessageBox(" 找不到视频设备,请确认已正确连接 "," 错误信息 ",MB_ICONSTOP|MB_OK);  
else
{       m_hCapWnd=capCreateCaptureWindow(" 视频捕捉预览程序 ",  
                                                                    WS_VISIBLE|WS_SYSMENU|WS_CAPTION,
                                                                    150,150,325,270,hwnMain,0);
if (capDriverConnect(m_hCapWnd,0))// 判断采集窗口是否与 0 号采集卡驱动相连接
{
    capDriverGetCaps(m_hCapWnd,&CapDriverCaps,sizeof(CAPDRIVERCAPS)); 
    if(CapDriverCaps.fCaptureInitialized)// 初始化成功
           if   (CapDriverCaps.fHasDlgVideoSource)    
                  capDlgVideoSource(m_hCapWnd);
           if   (CapDriverCaps.fHasDlgVideoFormat)
                  capDlgVideoFormat(m_hCapWnd);
           capPreviewRate(m_hCapWnd,66);    
           capPreview(m_hCapWnd, TRUE); // 启动 Preview 模式
          
          
}
else
MessageBox(" 视频采集窗口不能被连接 ");
}
 
}
 
// 回调函数必须为公有函数
LRESULT CALLBACK FrameCallbackProc(HWND hWnd,LPVIDEOHDR lpVHdr)
//lpVHdr 指向数据结构 VIDEOHEADER ,这里面就是每一帧画面的数据, RGB 格式的
// 注意无论是否设置了压缩格式,在此函数中得到的,始终是未经过压缩的原始 bmp 数据,所以加上 bmp 文件头写入文件就可以了
{//lpVHdr->lpData 中存储的只是位图象素信息,必须加入头、与象素信息后才可组成一组图像  
    FILE * fp;     
    if(!(fp=fopen("e:/caram2.AVI","a")))
           return FALSE;
    BITMAPINFO   bihI;  
    DWORD   dwSize;  
    dwSize=capGetVideoFormatSize(m_hCapWnd);
    capGetVideoFormat(m_hCapWnd,&bihI,dwSize); // 获得状态信息只有把该状态信息加到 lpVHdr->lpData 中才是一副完整图片  
    FILE   *fp2   =   fopen("e:/caram3.bmp","w"); 
    BITMAPFILEHEADER   bfh; // 定义头文件信息  
    bfh.bfType   =   0x4d42;  
    bfh.bfSize   =     sizeof(BITMAPFILEHEADER)+    
           bihI.bmiHeader.biSize   +   bihI.bmiHeader.biSizeImage;  
    bfh.bfReserved1   =   bfh.bfReserved2   =   0;  
    bfh.bfOffBits   =   (DWORD) sizeof(BITMAPFILEHEADER)+ bihI.bmiHeader.biSize;     
    fwrite((LPVOID)   &bfh,   sizeof(BITMAPFILEHEADER),   1,   fp2);   //   header  
    fwrite((LPVOID)   &bihI, bihI.bmiHeader.biSize,   1,   fp2);
    fwrite((LPVOID)   lpVHdr->lpData, bihI.bmiHeader.biSizeImage,   1,   fp2);
    fwrite((LPVOID)   &bfh,   sizeof(BITMAPFILEHEADER),   1,   fp);   //   header  
    fwrite((LPVOID)   &bihI, bihI.bmiHeader.biSize,   1,   fp);
    fwrite(lpVHdr->lpData,1,lpVHdr->dwBufferLength,fp);
          fclose(fp);
    fclose(fp2);       
    return (LRESULT)TRUE;
}
 
 
 
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值