• 视频捕获软件开发完全教学


    前 言

    视频捕获是指由专用的视频采集卡捕获声频和视频信息,然后将其进行数据化处理,再经过软件的压缩进行处理,这时就可对这些数据进行保存、回放、传输等各种操作。

    Windows专门提供了Video for Windows来对视频处理进行支持,提供的接口可以被大多数的视频采集卡支持,并有多种视频压缩驱动供选择(当然视频压缩可以自己开发),采集卡支持摄像头,TV等多种输入。


    一. 视频捕获快速入门

    视频捕捉将一个视频流和音频流数字化, 然后存储在硬盘或其他存储介质上.

    一个AVICap视窗口句柄描述了声频与视频流的细节, 这样就使你的应用程序从AVI文件格式, 声频视频缓冲管理, 低层声频视频驱动访问等等解脱出来, AVICap为应用程序提供了一个灵活的介面, 你可以仅仅使用如下几行代码就可以将视频捕捉加入你的程序:

    hWndC = capCreateCaptureWindow ( "My Own Capture Window",

    WS_CHILD | WS_VISIBLE , 0, 0, 160, 120, hwndParent, nID);

    SendMessage (hWndC, WM_CAP_DRIVER_CONNECT, 0 /* wIndex */, 0L);

    SendMessage (hWndC, WM_CAP_SEQUENCE, 0, 0L);

    一个宏其实也是使用SendMessage, 只不过提供给程序一个更易读的代码而已, 下面的这些示例就是使用宏的方法将视频捕捉加入程序:

    hWndC = capCreateCaptureWindow ( "My Own Capture Window",

    WS_CHILD | WS_VISIBLE , 0, 0, 160, 120, hwndParent, nID);

    capDriverConnect (hWndC, 0);

    capCaptureSequence (hWndC);


    当你创建了一个AVICap类的捕捉窗口并将它连接到一个视频驱动时, 此捕捉窗口即可以开始捕捉数据, 你的程序可以简单的发送WM_CAP_SEQUENCE消息(或者使用capCaptureSequence宏)来开始捕捉.

    如果是缺省的设置, WM_CAP_SEQUENCE会开始捕捉视频音频流到CAPTURE.AVI文件中, 直到下面的某一事件发生为止:

    用户按下了ESC键或者一个鼠标键

    你的应用程序终止或异常中断捕捉操作

    磁盘已满


    在一个应用程序里, 你可以发送WM_CAP_STOP消息来终止捕捉数据(或者使用capCaptureStop宏), 你也可以发送WM_CAP_ABORT消息(或者使用capCaptureAbort宏)来终止.


    二.基本的捕获设置

    基 本的捕获设置包括:设置捕获速度(每秒捕获多少帧),是否同时捕获声频,捕获缓冲,允许最大丢失多少帧,是否使用DOS内存,以及用键盘的哪个键或鼠标的 哪个键来终止捕获等等。这些基本的设置都可以使用CAPTUREPARAMS结构来描述,你可以使用capCaptureGetSetup宏来得到当前的 设置,然后改变此结构的成员变量,再使用capCaptureSetSetup宏设置新的设置。

    例如:

    1.设置捕获速度:

    捕 捉速度是指捕捉任务每秒钟捕获的帧数, 你可以发送WM_CAP_GET_SEQUENCE_SETUP消息(或者使用capCaptureGetSetup宏)来得到当前的捕捉速度, 当前的捕捉速度保存在CAPTUREPARAMS结构的dwRequestMicroSecPerFrame成员变量中, 你可以通过设置此变量来改变当前设置, 单位是每毫秒连续的帧数, 你可以发送WM_CAP_SET_SEQUENCE_SETUP消息(或者使用capCaptureSetSetup宏), dwRequestMicroSecPerFrame的值是66667, 相当于每秒15帧.


    2.设置终止捕获

    你可以允许用户按下某键或某组合键或者鼠标的左右键来终止一个捕获任务, 如果是实时的捕获, 则捕获的文件将会被丢弃; 如果是单步捕获, 在终止之前所捕获的内容将会被保存.

    你 可以通过发送WM_CAP_GETQUENCE_SETUP消息(或者使用capCaptureGetSetup宏)来得到当前的设置, 当前的按键设置保存在CAPTUREPARAMS的vKeyAbort成员中, 当前的鼠标设置保存在fAbortLeftMouse和fAbortRightMouse成员中. 你可以设置新的按键或按键组合, 或者鼠标左右键, 当你修改的CAPTUREPARAMS后,应该发送WM_CAP_SET_SEQUENCE_SETUP消息来进行更新(或者使用 capCaptureSetSetup宏). 缺省的按键是VK_ESCAPE. 你必须在指定按键之前使用RegisterHotKey函数, 鼠标缺省的值是fAbortLeftMouse和fAbortRightMouse都为TRUE.


    3.捕获的时间限制

    CAPTUREPARAMS结构中的fLimitEnabled指示是否有时间限度, wTimeLimit指示最大的持续时间, 单位为秒.

    得 到fLimitEnabled和wTimeLimit的值可以发送WM_CAP_GET_SEQUENCE_SETUP消息(或使用 capCatureGetSetup宏), 当设置了这些成员变量后, 应该发送消息WM_CAP_SET_SEQUENCE_SETUP消息(或capCaptureSetSetup宏)来更新CAPTUREPARAMS结 构.


    三.关于捕获窗口

    在捕获之前必须创建一个捕获窗口(capture window),在发送消息或使用宏的过程中都需要使用此窗口。

    1.创建一个AVICap捕获窗口

    你可以使用capCreateCaptureWindow函数来创建一个AVICap捕获窗口, 此函数将会返回一个句柄, 此句柄以后在发送消息时要用.

    你可以在一个程序里创建一个或多个捕获窗口, 然后给每一个窗口连接不同的捕获设置.


    2.将一个捕获窗口连接至捕获设备

    你可以动态的在一个捕获窗口与一个捕获设备之前连接或断接, 你可以发送WM_CAP_DRIVER_CONNECT消息来使一个捕获窗口与一个捕获设备连接或关联. 当连接上以后, 你就可以通过捕获窗口向捕获设备发送各种消息.

    如果你的系统里装有多个捕获设备, 你可以在发送WM_CAP_DRIVER_CONNECT消息时用wParam参数指定使用哪一个, 此参数是登记在SYSTEM.INI文件的[drivers]一节里的列表中的某一项, 0为第一个.

    你可以使用capGetDriverDescription函数来得到已安装的捕获设备的名称及版本, 这样你的程序就可以列举所有已安装的捕获设备和驱动, 这样用户就可以选择其中的一个来与你的捕获窗口连接.

    你可以发送WM_CAP_DRIVER_GET_NAME消息(或capDriverGetName宏)来得到连接到捕获窗口的捕获设备的名称, 得到版本发送WM_CAP_DRIVER_GET_VERSION消息(或capDriverGetVersion宏)

    你可以发送WM_CAP_DRIVER_DISCONNECT消息(或capDriverDisconnect宏)来断接.


    3. 父窗口与子窗口的交互

    一些象WM_PALETTECHANGED和WM_QUERYNEWPALETTE的系统级消息只能发送到顶级窗口或OVERLAPPED窗口, 如果一个捕获窗口是子窗口,就必须通过父窗口转送.

    同样的, 如果父窗口的尺寸改变了, 它就需要通知捕获窗口, 相反地, 如果捕获窗口的尺寸改变了, 捕获窗口就需要发送消息给父窗口, 一个简单的方法就是始终保持捕获窗口的尺寸与视频流的尺寸一致, 并随时将尺寸的改变通知父窗口.


    4.捕获窗口的状态

    你可以发送WM_CAP_GET_STATUS消息(或capGetStatus宏)来得到当前捕获窗口的状态, 得到的是一个CAPSTATUS结构的拷贝, 它包含图片的尺寸, 卷轴的当前位置, overlay和preview是否已设置.

    因为CAPSTATUS信息是动态的, 你的程序应该只要捕获的视频流的尺寸或格式可能发生了改变就应该进行刷新(例如: 显示了捕获设备的视频格式以后).

    改变捕获窗口的尺寸并不影响实际的捕获的视频流的尺寸, 视频捕获设备的格式对话框捕获频流的尺寸.


    四.视频捕获驱动和音频驱动

    1.视频捕获驱动的性能:

    你可以通过发送WM_CAP_DRIVER_GET_CAPS消息(或者capDriverGetCaps宏)来得到当前连接的视频驱动的硬件性能. 得到的信息保存在CAPDRIVERCAPS结构中.


    2.视频对话框:

    每一个视频驱动能够提供四个对话框来控制视频捕获和数字化处理, 定义压缩品质等, 这些对话框都定义在视频捕获驱动中.

    Video Source对话框用于控制选择视频来源, 此对话框列举了此视频捕获卡连接的所有视频源(典型的例如:SVHS和合成输入), 并提供了改变色调, 对比度, 饱和度. 如果视频驱动支持此对话框, 你就可以显示并更新它, 使用WM_CAP_DLG_VIDEOSOURCE消息(或capDlgVideoSource宏).

    Video Format对话框定义视频帧的尺寸以及精度, 视频捕获卡的压缩设置. 如果卡支持的话, 可以发送消息WM_CAP_DLG_VIDEOFORMAT消息或(capDlgVideoFormat宏).

    Video Display对话框控制在视频捕获期间在显示器上的显示, 此控制不会影响视频数字数据, 但是他们可能会影响数字信号的表现形式, 例如: 如果捕获设备支持overlay, 可能允许改变色调和饱和度, 关键色彩 或者overlay队列. 如果卡支持, 你可以发送WM_CAP_DLG_VIDEODISPLAY消息(或者使用capDlgVideoDisplay宏).

    Video Compression对话框控制压缩品质, 如果卡支持, 发送消息WM_CAP_DLG_VIDEOCOMPRESSION(或capDlgVideoCompression宏).


    3.Preview 和 Overlay模式:

    一个视频捕获驱动对进入的视频流有两种工作模式: Preview模式和overlay模式, 如果一个捕获驱动能够执行两种方法, 用户可以在其中选择一种.

    Preview模式把从捕获硬件传来的数据送入系统内存并使用图形设备介面(GDI)将数字化帧显示在捕获窗口内. 应用程序可以在父窗口失去焦点时减缓显示速度, 当重新又得到焦点后加快显示速度, 此种模式要占用大量CPU时间.

    有三种消息控制Preview操作:

    WM_CAP_SET_PREIVEW消息(capPreview宏)允许或禁止preview模式

    WM_CAP_SET_PREVIEWRATE(capPreviewRate宏)当帧在preview模式显示时设置速度.

    WM_CAP_SET_SCALE(capPreviewScale宏)允许或禁止preview视频的缩放比例.

    当preview和scaling同时使用, 捕获的视频帧将会根据捕获窗口的尺寸自动缩放, 允许preview模式会自动关闭overlay模式.

    overlay模式是一个硬件函数它将数据送入捕获缓冲区中因而不占用CPU资源. 你可以发送消息WM_CAP_SET_OVERLAY(或capOverlay宏)给捕获窗口来启用或终止overlay模式, 允许overlay模式会自动禁止preview模式.

    你同时也可以在preview模式或overlay模式里发送WM_CAP_SET_SCROLL消息(或capSetScrollPos宏)来设置视频帧的客户区卷轴位置.


    4.视频格式

    你 可以通过发送WM_CAP_GET_VIDEOFORMAT消息(或capGetVideoFormat和capGetVideoFormatSize 宏)来得到视频格式的结构或结构的尺寸. 你可以通过发送CAP_SET_VIDEOFORMAT消息(或capSetVideoFormat宏)来设置视频格式.


    5.视频捕获设置

    CAPTUREPARMS结构包含了对视频捕获流的控制参数, 你可以完成以下这些任务:

    指定帧数

    指定分配多少视频缓冲

    允许或禁止声频捕获

    指定捕获的时间间隔

    指定在捕获的过程中是否使用MCI设置(VCR或者videodisc)

    指定终止流的键盘或鼠标

    specify the type of video averaging applied during capture.


    得到:WM_CAP_GET_SEQUENCE_SETUP消息(或capCaptureGetSetup宏)

    设置:WM_CAP_SET_SEQUENCE_SETUP消息(或capCaptureSetSetup宏)


    6.声频格式

    你 可以通过发送WM_CAP_GET_AUDIOFORMAT消息(或capGetAudioFormat宏和capGetAudioFormatSize 宏)来得到当前捕获音频数据的格式或尺寸格式。缺省的声频格式是:单声道、8位、11kHz PCM。 当你使用WM_CAP_GET_AUDIOFORMAT时,总是使用WAVEFORMATEX结构。

    设置发送消息WM_CAP_SET_AUDIOFORMAT消息(或capSetAudioFormat宏),可以传送WAVEFORMAT,WAVEFORMATEX,PCMWAVEFORMAT结构指针。

    五.使用视频捕获

    1.创建捕获窗口(Creating a Capture Window)

    hWndC = capCreateCaptureWindow (

    (LPSTR) "My Capture Window", // window name if pop-up

    WS_CHILD | WS_VISIBLE, // window style

    0, 0, 160, 120, // window position and dimensions

    (HWND) hwndParent,

    (int) nID /* child ID */);


    2.连接到捕获驱动(Connecting to a Capture Driver)

    下面的例子是将MSVIDEO驱动连接到句柄为hWndC的捕获窗口, 然后调用capDriverDisconnect宏来断接.

    fOK = SendMessage (hWndC, WM_CAP_DRIVER_CONNECT, 0, 0L);

    //

    // Or, use the macro to connect to the MSVIDEO driver:

    // fOK = capDriverConnect(hWndC, 0);

    //

    // Place code to set up and capture video here.

    //

    capDriverDisconnect (hWndC);


    3.列举所有已安装的捕获驱动(Enumerating Installed Capture Drivers)

    下面的例子使用capGetDriverDescription函数得到已安装的捕获驱动的名称及版本:

    char szDeviceName[80];

    char szDeviceVersion[80];


    for (wIndex = 0; wIndex < 10; wIndex++)

    {

    if (capGetDriverDescription (wIndex, szDeviceName,

    sizeof (szDeviceName), szDeviceVersion,

    sizeof (szDeviceVersion))

    {

    // Append name to list of installed capture drivers

    // and then let the user select a driver to use.

    }

    }


    4.得到捕获驱动的性能(Obtaining the Capabilities of a Capture Driver)

    发 送WM_CAP_DRIVER_GET_CAPS消息可以得到捕获驱动的性能,并保存入一个CAPDRIVERCAPS结构.每当程序连接一个新的捕获驱 动到一个捕获窗口时, 就应该更新CAPDRIVERCAPS结构. 下面的程序举例说明了如何使用capDriverGetCaps宏来得到捕获驱动的性能:


    CAPDRIVERCAPS CapDrvCaps;

    SendMessage (hWndC, WM_CAP_DRIVER_GET_CAPS,

    sizeof (CAPDRIVERCAPS), (LONG) (LPVOID) &CapDrvCaps);

    // Or, use the macro to retrieve the driver capabilities.

    // capDriverGetCaps(hWndC, &CapDrvCaps, sizeof (CAPDRIVERCAPS));


    5.得到捕获窗口的状态(Obtaining the Status of a Capture Window)

    下面的例子使用SetWindowPos函数使捕获窗口与进来的视频流尺寸保持一致, 视频流的基本信息是使用capGetStatus宏得到的, 保存在CAPSTATUS结构中.


    CAPSTATUS CapStatus;

    capGetStatus(hWndC, &CapStatus, sizeof (CAPSTATUS));

    SetWindowPos(hWndC, NULL, 0, 0, CapStatus.uiImageWidth,

    CapStatus.uiImageHeight, SWP_NOZORDER | SWP_NOMOVE);


    6.显示对话框设置视频特征(Displaying Dialog Boxes to Set Video Characteristics)

    每个视频捕获卡一般能提供三个不同的对话框用于控制视频捕获及数字化处理. 下面的例子说明如何显示这些对话框, 在显示这些对话框之前,使用了capDriverGetCaps宏来检查CAPDRIVERCAPS结构, 以检测该卡是否有显示这些对话框:


    CAPDRIVERCAPS CapDrvCaps;

    capDriverGetCaps(hWndC, &CapDrvCaps, sizeof (CAPDRIVERCAPS));


    // Video source dialog box.

    if (CapDriverCaps.fHasDlgVideoSource)

    capDlgVideoSource(hWndC);


    // Video format dialog box.

    if (CapDriverCaps.fHasDlgVideoFormat)

    {

    capDlgVideoFormat(hWndC);

    // Are there new image dimensions?

    capGetStatus(hWndC, &CapStatus, sizeof (CAPSTATUS));

    // If so, notify the parent of a size change.

    }


    // Video display dialog box.

    if (CapDriverCaps.fHasDlgVideoDisplay)

    capDlgVideoDisplay(hWndC);


    7.得到和设置视频格式(Obtaining and Setting the Video Format)

    BITMAPINFO 结构的长度既适应于标准的也适应于压缩的数据格式, 所有程序必须总是询问此结构的尺寸以便在得到当前的视频格式之前分配内存. 下面的例子就是使用capGetVideoFormatSize宏来得到缓冲区尺寸并调用capGetVideoFormat宏来得到当前的视频格式.


    LPBITMAPINFO lpbi;

    DWORD dwSize;

    dwSize = capGetVideoFormatSize(hWndC);

    lpbi = GlobalAllocPtr (GHND, dwSize);

    capGetVideoFormat(hWndC, lpbi, dwSize);


    // Access the video format and then free the allocated memory.


    程序可以使用capSetVideoFormat宏(或WM_CAP_SET_VIDEOFORMAT消息)发送一个BITMAPINFO头结构给捕获窗口, 因为视频格式是设备细节, 你的程序应该检查返回值以便确定此格式是否已被接受.


    8. 预览视频(Previewing Video)

    下面的例子使用capPreviewRate宏来设置每66毫秒显示一帧, 并使用capPreview宏将它放置在捕获窗口里.


    capPreviewRate(hWndC, 66); // rate, in milliseconds

    capPreview(hWndC, TRUE); // starts preview

    // Preview

    capPreview(hWnd, FALSE); // disables preview


    9.将视频设置为overlay模式(Enabling Video Overlay)

    下面的例子: capDriverGetCaps宏确定此捕获卡是否有overlay功能, 如果有就使用宏来设置它


    CAPDRIVERCAPS CapDrvCaps;

    capDriverGetCaps(hWndC, &CapDrvCaps, sizeof (CAPDRIVERCAPS));


    if (CapDrvCaps.fHasOverlay)

    capOverlay(hWndC, TRUE);


    10.命名捕获文件(Naming the Capture File)

    下面的例子: 使用capFileSetCaptureFile宏来指定预备文件名为:MYCAP.AVI, capFileAlloc宏预先指定它的大小为5M.


    char szCaptureFile[] = "MYCAP.AVI";

    capFileSetCaptureFile( hWndC, szCaptureFile);

    capFileAlloc( hWndC, (1024L * 1024L * 5));


    11.格式化声频捕获(Formatting Audio Capture)

    下面的例子使用capSetAudioFormat来设置声频格式为:11kHz, PCM 8位, 立体声


    WAVEFORMATEX wfex;

    wfex.wFormatTag = WAVE_FORMAT_PCM;

    wfex.nChannels = 2; // Use stereo

    wfex.nSamplesPerSec = 11025;

    wfex.nAvgBytesPerSec = 22050;

    wfex.nBlockAlign = 2;

    wfex.wBitsPerSample = 8;

    wfex.cbSize = 0;


    capSetAudioFormat(hWndC, &wfex, sizeof(WAVEFORMATEX));


    12.改变视频捕获设置(Changing a Video Capture Setting)

    下面的例子使用capCaptureGetSetup和capCaptureSetSetup宏得将捕获帧数从缺省的15帧改成每秒10帧.


    CAPTUREPARMS CaptureParms;

    float FramesPerSec = 10.0;


    capCaptureGetSetup(hWndC, &CaptureParms, sizeof(CAPTUREPARMS));


    CaptureParms.dwRequestMicroSecPerFrame = (DWORD) (1.0e6 /FramesPerSec);

    capCaptureSetSetup(hWndC, &CaptureParms, sizeof (CAPTUREPARMS));


    13.捕获数据(Capturing Data)

    下面的例子使用capCaptureSequence宏来开始捕获视频并使用capFileSaveAs宏来将捕获的数据拷贝至NEWFILE.AVI文件中.


    char szNewName[] = "NEWFILE.AVI";

    // Set up the capture operation.

    capCaptureSequence(hWndC);

    // Capture.

    capFileSaveAs(hWndC, szNewName);


    14.增加一个信息块(Adding an Information Chunk)

    如 果你需要在你的程序捕获的声频和视频数据中加入你的其他信息, 你可以创建一个信息块并将它们插入捕获文件中, 信息块可以包含一些典型的信息, 例如:版权信息,视频来源, 外部定位信息等. 下面的例子使用capFileSetInfoChunk宏来插入一个信息块, 里面包含了一个SMPTE的时间代码.


    // This example assumes the application controls

    // the video source for preroll and postroll.

    CAPINFOCHUNK cic;

    // .

    // .

    // .

    cic.fccInfoID = infotypeSMPTE_TIME;

    cic.lpData = "00:20:30:12";

    cic.cbData = strlen (cic.lpData) + 1;

    capFileSetInfoChunk (hwndC, &cic);


    15.在程序中加入一个回调函数(Adding Callback Functions to an Application)

    一个程序可以为捕获窗口登记一个回调函数以便在以下的这些情况下通知程序.


    状态改变

    错误发生

    视频框架和声频缓冲区变得可用

    程序应用在捕获视频流的过程中接收


    下面的例子创建一个捕获窗口并登记状态,错误,视频流和框架回调函数在消息处理对列中, 也包括了一个终止回调函数的说明.


    case WM_CREATE:

    {

    char achDeviceName[80]

    char achDeviceVersion[100]

    char achBuffer[100]

    WORD wDriverCount = 0

    WORD wIndex

    WORD wError

    HMENU hMenu


    // Create a capture window using the capCreateCaptureWindow macro.

    ghWndCap = capCreateCaptureWindow((LPSTR)"Capture Window",

    WS_CHILD | WS_VISIBLE, 0, 0, 160, 120, (HWND) hWnd, (int) 0);


    // Register the error callback function using the

    // capSetCallbackOnError macro.

    capSetCallbackOnError(ghWndCap, fpErrorCallback);


    // Register the status callback function using the

    // capSetCallbackOnStatus macro.

    capSetCallbackOnStatus(ghWndCap, fpStatusCallback);


    // Register the video-stream callback function using the

    // capSetCallbackOnVideoStream macro.

    capSetCallbackOnVideoStream(ghWndCap, fpVideoCallback);


    // Register the frame callback function using the

    // capSetCallbackOnFrame macro.

    capSetCallbackOnFrame(ghWndCap, fpFrameCallback);


    // Connect to a capture driver


    break;

    }

    case WM_CLOSE:

    {

    // Use the capSetCallbackOnFrame macro to

    // disable the frame callback. Similar calls exist for the other

    // callback functions.


    capSetCallbackOnFrame(hWndC, NULL);


    break;

    }


    16.创建一个状态回调函数(Creating a Status Callback Function)

    下面的例子是创建一个简单的状态回调函数,登记此回调函数使用capSetCallbackOnStatus宏.


    // StatusCallbackProc: status callback function

    // hWnd: capture window handle

    // nID: status code for the current status

    // lpStatusText: status text string for the current status

    //

    LRESULT PASCAL StatusCallbackProc(HWND hWnd, int nID,

    LPSTR lpStatusText)

    {

    if (!ghWndMain)

    return FALSE;


    if (nID == 0) { // Clear old status messages.

    SetWindowText(ghWndMain, (LPSTR) gachAppName);

    return (LRESULT) TRUE;

    }

    // Show the status ID and status text...

    wsprintf(gachBuffer, "Status# %d: %s", nID, lpStatusText);


    SetWindowText(ghWndMain, (LPSTR)gachBuffer);

    return (LRESULT) TRUE;

    }


    17.创建一个错误回调函数( Creating an Error Callback Function)

    下面的例子是创建一个简单的错误回调函数,登记此回调函数使用capsetCallbackOnError宏:


    // ErrorCallbackProc: error callback function

    // hWnd: capture window handle

    // nErrID: error code for the encountered error

    // lpErrorText: error text string for the encountered error

    //

    LRESULT PASCAL ErrorCallbackProc(HWND hWnd, int nErrID,

    LPSTR lpErrorText)

    {

    if (!ghWndMain)

    return FALSE;


    if (nErrID == 0) // Starting a new major function.

    return TRUE; // Clear out old errors.


    // Show the error identifier and text.

    wsprintf(gachBuffer, "Error# %d", nErrID);


    MessageBox(hWnd, lpErrorText, gachBuffer,

    MB_OK | MB_ICONEXCLAMATION);


    return (LRESULT) TRUE;

    }


    18.创建一个框架回调函数(Creating a Frame Callback Function)

    登记此回调函数使用capSetCallbackOnFrame宏:


    // FrameCallbackProc: frame callback function

    // hWnd: capture window handle

    // lpVHdr: pointer to struct containing captured

    // frame information

    //

    LRESULT PASCAL FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr)

    {

    if (!ghWndMain)

    return FALSE;


    wsprintf(gachBuffer, "Preview frame# %ld ", gdwFrameNum++);

    SetWindowText(ghWndMain, (LPSTR)gachBuffer);

    return (LRESULT) TRUE

    }

     

    六.将四个标准对话框改成函数调用形式

    系统提供了四个标准的对话框:AudioFormat, VideoFormat, VideoSource, Video Compression,但有时程序希望通过函数控制它们,而不是使用系统提供的那个单一的对话框,此时就应该使用函数调用的方法:


    AudioFormat对话框

    可以通过使用capSetAudioFormat来实现,此时要使用WAVEFORMATEX结构。

    例如:改成PCM格式,立体声,16声道,12.05kHz,则:

    WAVEFORMATEX audioFormat;

    // 确定宽度

    acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT,&dwSize);

    dwSize = max (dwSize, capGetAudioFormatSize (m_hwCapCapturing));

    // 设置参数

    audioFormat.wFormatTag = WAVE_FORMAT_PCM;

    audioFormat.nChannels = 2;

    audioFormat.nSamplesPerSec = 120500;

    audioFormat.wBitsPerSample =16;

    audioFormat.nBlockAlign = nBitsPerSample * nChannels / 8;

    audioFormat.nAvgBytesPerSec =

    audioFormat.nBlockAlign * nSamplesPerSec;

    // 更新

    capSetAudioFormat(ghCapWnd,&audioFormat,dwSize);

    VideoFormat对话框

    可以通过使用capSetVideoFormat来实现,此时要使用BITMAPINFOHEADER结构。

    例如:设置图片大小为RGB24位岁,大小为230X160

    BITMAPINFOHEADER bi;

    DWORD dwSize,dw;

    bi.biSize = sizeof(BITMAPINFOHEADER);

    bi.biWidth = 320; // 起作用

    bi.biHeight = 160; // 起作用

    bi.biPlanes = 1;

    bi.biBitCount = 24;

    bi.biCompression = BI_RGB;

    bi.biSizeImage = 0;

    bi.biXPelsPerMeter = 176;

    bi.biYPelsPerMeter = 144;

    bi.biClrUsed = 0;

    bi.biClrImportant = 0;

    dwSize = bi.biSize + ((bi.biBitCount > 8 || bi.biClrUsed) ? (bi.biClrUsed * sizeof(PALETTEENTRY)) : (2 ^ bi.biBitCount * sizeof(PALETTEENTRY)));

    dw = capSetVideoFormat(m_hwCapCapturing, &bi, dwSize);

    VideoSource对话框

    没有找到现成的方法,但视频捕获卡提供的CD里面有一个动态链接库可以实现。

    Video Compression对话框

    可以通过使用ICOpen,ICInfo等函数联合起来,得到当前系统里面的视频压缩驱动的列表,并可选择其一,MSDN里面有一个程序示范了此用户,程序名叫:ICWalk。

    --------------------------------------------------------------------------------


    用Delphi开发视频捕获程序

    华东地质学院2000级研究生班 342信箱(344000) 杨 锐

    --------------------------------------------------------------------------------

    在 许多关于视频的软件(如视频会议、可视电话等)开发中,都应用了视频捕获技术。微软为软件开发人员提供了一个专门用于视频捕获的VFW (Video for Windows) SDK。VFW SDK为在Windows系统中实现视频捕获提供了标准的接口,从而大大降低了程序的开发难度。由于VFW SDK只有VC和VB版,没有Delphi版,因此需要在Delphi中一一声明DLL中的各个函数和变量(可以参考MSDN中的VC的函数声明以及变量 定义)。本文分3部分介绍如何利用VFW在Delphi中开发视频捕获程序。

    VFW简介

    VFW 是微软公司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步就可以建立一个基本的视频捕获程序。但如果想自已处理从设备捕获到的视频数据,则要使用捕获窗回调函数来处理,比如一帧一帧地获得视频数据或以流的方式获得视频数据等等。

    实例编程

    下面以一个一帧一帧地从视频设备上捕获视频数据的Delphi程序为例,来说明每个函数的作用以及开发过程。

    该程序的功能是可以在屏幕上显视捕获到的视频,并可以获得每一帧的图像数据。

    新建一个工程,并将AVICAP32.PAS包含到USES中。

    在Form1上放置一个TPanel控件,设Name为“gCapVideoArea”,该控件用于显示视频。再放置两个TButton控件,一个Name为“Openvideo”,另一个Name为“Closevideo”。

    定义全局变量:

    var

    //定义捕获窗句柄

    ghCapWnd: THandle;

    //可以得到视频数据指针的结构变量,用于回调函数中

    VideoStr: LPVIDEOHDR;

    //用于设置设备属性的结构变量

    CapParms: TCaptureParms;

    在Name为“Openvideo”的TButton 的Click事件中写入以下代码:

    procedure TForm1.OpenvideoClick(Sender: TObject);

    begin

    //使用Tpanel控件来创建捕获窗口

    ghCapWnd := CapCreateCaptureWindow

    ( PChar(‘KruwoSoft'), //捕获窗口的名字

    WS_CHILD or WS_VISIBLE,//窗口样式

    0, //X坐标

    0, //Y坐标

    gCapVideoArea.Width, //窗口宽

    gCapVideoArea.Height, //窗口高

    gCapVideoArea.Handle, //窗口句柄

    0); //一般为0

    {为了能够捕获视频帧,要启动一个捕获帧回调函数VideoStreamCallBack。捕获一个视频流或当前设备状态时分别使用以下函数:

    //捕获一个视频流

    CapSetCallbackOnVideoStream;

    //得到一个设备错误

    CapSetCallbackOnError;

    //得到一个设备状态

    CapSetCallbackOnStatus

    }

    //定义一个帧捕获回调函数

    CapSetCallbackOnFrame (ghCapWnd,LongInt(@VideoStreamCallBack));

    //将一个捕获窗口与一个设备驱程相关联,第二个参数是个序号,当系统中装有多个显视驱动程序时,其值分别依次为0到总个数

    CapDriverConnect(ghCapWnd, 0);

    //设置设备属性的结构变量

    CapParms.dwRequestMicroSecPerFrame:=40000;

    CapParms.fLimitEnabled := FALSE;

    CapParms.fCaptureAudio := FALSE; // NO Audio

    CapParms.fMCIControl := FALSE;

    CapParms.fYield := TRUE;

    CapParms.vKeyAbort := VK_ESCAPE;

    CapParms.fAbortLeftMouse := FALSE;

    CapParms.fAbortRightMouse := FALSE;

    //使设置生效

    CapCaptureSetSetup(ghCapWnd,LongInt(@CapParms),sizeof(TCAPTUREPARMS));

    //设置预览时的比例

    CapPreviewScale(ghCapWnd, 1);

    //设置预览时的帧频率

    CapPreviewRate(ghCapWnd,66);

    //如果要捕获视频流,则要使用函数指定不生成文件。否则将会自动生成AVI文件

    CapCaptureSequenceNoFile(ghCapWnd);

    //指定是否使用叠加模式,使用为1,否则为0

    CapOverlay(ghCapWnd, 1);

    //打开预览

    CapPreview(ghCapWnd, 1);

    end;

    在Name为“Closevideo”的TButton 的Click事件中写入以下代码:

    procedure TForm1.ClosevideoClick(Sender: TObject);

    begin

    //停止捕获

    capCaptureAbort(ghCapWnd);

    //将捕获窗同驱动器断开

    capDriverDisconnect(ghCapWnd);

    end;

    定义捕获帧回调函数:

    function FrameCallBack(hWnd:HWND; lpVHdr:LongInt) :LongInt; stdcall;

    var

    DataPoint:^byte;

    DibLen,RectWidth,RectHeight:integer;

    begin

    //转换从回调函数中得到的指针

    VideoStr:=LPVIDEOHDR(lpVHdr);

    //得到返回的数据大小

    DibLen:=VideoStr^.dwBufferLength;

    GetMem(DataPoint,64000);

    //将帧数据COPY到一个内存中,注意DATAPOINT要先分配空间

    CopyMemory(DataPoint,VideoStr^.lpData,Diblen);

    //一些其他处理

    ……

    end;

    灵活地使用AVICap窗口类的回调函数可以满足各种不同的需求,但要注意从视频卡中捕获的视频数据的格式和图像的长宽要参考视频卡的参数。而且有些视频卡通过设置可支持多种的格式和图像长宽,所以在还原图像时要注意参考所用的视频卡的参数。

    --------------------------------------------------------------------------------


    视频采集,存成avi
    unit Unit1;

    interface

    uses
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
    Dialogs, ExtCtrls, StdCtrls;

    type
    TForm1 = class(TForm)
    Panel1: TPanel;
    OpenVideo: TButton;
    CloseVideo: TButton;
    GrabFrame: TButton;
    SaveBMP: TButton;
    StartAVI: TButton;
    StopAVI: TButton;
    SaveDialog1: TSaveDialog;
    procedure FormCreate(Sender: TObject);
    procedure OpenVideoClick(Sender: TObject);
    procedure CloseVideoClick(Sender: TObject);
    procedure GrabFrameClick(Sender: TObject);
    procedure SaveBMPClick(Sender: TObject);
    procedure StartAVIClick(Sender: TObject);
    procedure StopAVIClick(Sender: TObject);
    private
    { Private declarations }
    hWndC : THandle;
    CapturingAVI : bool;
    public
    { Public declarations }
    end;

    var
    Form1: TForm1;

    implementation

    {$R *.DFM}

    const WM_CAP_START = WM_USER;
    const WM_CAP_STOP = WM_CAP_START + 68;
    const WM_CAP_DRIVER_CONNECT = WM_CAP_START + 10;
    const WM_CAP_DRIVER_DISCONNECT = WM_CAP_START + 11;
    const WM_CAP_SAVEDIB = WM_CAP_START + 25;
    const WM_CAP_GRAB_FRAME = WM_CAP_START + 60;
    const WM_CAP_SEQUENCE = WM_CAP_START + 62;
    const WM_CAP_FILE_SET_CAPTURE_FILEA = WM_CAP_START + 20;

    function capCreateCaptureWindowA(lpszWindowName : PCHAR;
    dwStyle : longint;
    x : integer;
    y : integer;
    nWidth : integer;
    nHeight : integer;
    ParentWin : HWND;
    nId : integer): HWND;
    STDCALL EXTERNAL 'AVICAP32.DLL';

    procedure TForm1.FormCreate(Sender: TObject);
    begin
    CapturingAVI := false;
    hWndC := 0;
    SaveDialog1.Options :=
    [ofHideReadOnly, ofNoChangeDir, ofPathMustExist]
    end;

    procedure TForm1.OpenVideoClick(Sender: TObject);
    begin
    hWndC := capCreateCaptureWindowA('My Own Capture Window',
    WS_CHILD or WS_VISIBLE ,
    Panel1.Left,
    Panel1.Top,
    Panel1.Width,
    Panel1.Height,
    Form1.Handle,
    0);
    if hWndC <> 0 then
    SendMessage(hWndC, WM_CAP_DRIVER_CONNECT, 0, 0);
    end;

    procedure TForm1.CloseVideoClick(Sender: TObject);
    begin
    if hWndC <> 0 then begin
    SendMessage(hWndC, WM_CAP_DRIVER_DISCONNECT, 0, 0);
    hWndC := 0;
    end;
    end;

    procedure TForm1.GrabFrameClick(Sender: TObject);
    begin
    if hWndC <> 0 then
    SendMessage(hWndC, WM_CAP_GRAB_FRAME, 0, 0);
    end;

    procedure TForm1.SaveBMPClick(Sender: TObject);
    begin
    if hWndC <> 0 then begin
    SaveDialog1.DefaultExt := 'bmp';
    SaveDialog1.Filter := 'Bitmap files (*.bmp)|*.bmp';
    if SaveDialog1.Execute then
    SendMessage(hWndC,
    WM_CAP_SAVEDIB,
    0,
    longint(pchar(SaveDialog1.FileName)));
    end;
    end;

    procedure TForm1.StartAVIClick(Sender: TObject);
    begin
    if hWndC <> 0 then begin
    SaveDialog1.DefaultExt := 'avi';
    SaveDialog1.Filter := 'AVI files (*.avi)|*.avi';
    if SaveDialog1.Execute then begin
    CapturingAVI := true;
    SendMessage(hWndC,
    WM_CAP_FILE_SET_CAPTURE_FILEA,
    0,
    Longint(pchar(SaveDialog1.FileName)));
    SendMessage(hWndC, WM_CAP_SEQUENCE, 0, 0);
    end;
    end;
    end;

    procedure TForm1.StopAVIClick(Sender: TObject);
    begin
    if hWndC <> 0 then begin
    SendMessage(hWndC, WM_CAP_STOP, 0, 0);
    CapturingAVI := false;
    end;
    end;

    end.
  • 相关阅读:
    php 解析富文本编辑器中的hmtl内容,富文本样式正确输出
    thinkphp 中区块block和模板继承extend用法举例
    RabbitMQ安装
    the temporary upload location [/tmp/tomcat.4519862041770988807.8082/work/Tomcat/localhost/ROOT] is not valid
    java.lang.UnsupportedOperationException
    containsAll
    mysql 函数 group_concat 长度限制
    Linux安装jdk8
    nginx实现负载均衡
    linux系统服务器复制文件到另一台服务器上
  • 原文地址:https://www.cnblogs.com/MaxWoods/p/1304801.html
Copyright © 2020-2023  润新知