Firstsnow的文章

  • 基于VFW实现开启摄像头录制并保存为AVI视频文件

    背景如果你关注过一些关于黑客的新闻,应该了解,有些黑客会悄悄打开你电脑的摄像头,然后监控你的一举一动,更有甚者,还会录制一些较为隐私的视频并勒索。那时,你会不会惊叹说,太厉害了。
    其实,编程实现开启摄像头,并录制成视频文件,这个技术并没有那么复杂了。只不过是我们很少用到这方面的技术,缺乏对这方面技术的了解而已。对于应用层来说,所有的功能实现,无非就是通过强大的WIN32 API函数来实现,这个程序也不例外。我们使用 Windows 提供的 VFW(Microsoft Video for Windows)实现,现在,我就把这个程序实现的过程和原理整理成文档,分享给大家。
    函数介绍capCreateCaptureWindow 函数
    创建一个捕获窗口。
    函数声明
    HWND VFWAPI capCreateCaptureWindow( LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWnd, int nID );
    参数

    lpszWindowName包含用于捕获窗口的名称的空端字符串。dwStyle用于捕获窗口的窗口样式。 使用CreateWindowEx函数描述窗口样式。x捕获窗口左上角的x坐标。y捕获窗口左上角的y坐标。nWidth捕获窗口的宽度。nHeight参数捕获窗口的高度。hWnd父窗口句柄。nID窗口标识符。
    返回值

    如果成功则返回捕获窗口的句柄,否则返回NULL。

    capDriverConnect 函数
    capDriverConnect宏将捕获窗口连接到捕获驱动程序。 您可以使用此宏或显式发送WM_CAP_DRIVER_CONNECT消息。
    函数声明
    BOOL capDriverConnect( hwnd, iIndex);
    参数

    hwnd捕获窗口的句柄。iIndex捕获驱动程序的索引。 索引范围为0到9。
    返回值

    如果成功则返回TRUE,如果指定的捕获驱动程序无法连接到捕获窗口,则返回FALSE。
    备注

    将捕获驱动程序连接到捕获窗口将自动断开任何以前连接的捕获驱动程序。

    capDriverGetCaps 函数
    capDriverGetCaps宏返回当前连接到捕获窗口的捕获驱动程序的硬件功能。 您可以使用此宏或显式发送WM_CAP_DRIVER_GET_CAPS消息。
    函数声明
    BOOL capDriverGetCaps( hwnd, psCaps, wSize);
    参数

    hwnd捕获窗口的句柄。psCaps指向CAPDRIVERCAPS结构以包含硬件功能。wSize由psCaps引用的结构的大小(以字节为单位)。
    返回值

    如果成功则返回TRUE,如果捕获窗口未连接到捕获驱动程序,则返回FALSE。
    备注

    CAPDRIVERCAPS中返回的功能对于给定的捕获驱动程序是不变的。 当捕获驱动程序首次连接到捕获窗口时,应用程序需要检索该信息。

    capPreviewRate 函数
    capPreviewRate宏在预览模式下设置帧显示速率。 您可以使用此宏或显式调用WM_CAP_SET_PREVIEWRATE消息。
    函数声明
    BOOL capPreviewRate( hwnd, wMS);
    参数

    hwnd捕获窗口的句柄。wMS以毫秒为单位,捕获和显示新帧的速率。
    返回值

    如果成功则返回TRUE,如果捕获窗口未连接到捕获驱动程序,则返回FALSE。
    备注

    预览模式使用大量的CPU资源。 当另一个应用程序关注时,应用程序可以禁用预览或降低预览速率。 在流视频捕获期间,预览任务比将帧写入磁盘的优先级低,只有在没有其他缓冲区可用于写入时才会显示预览帧。

    capPreview 函数
    capPreview宏启用或禁用预览模式。 在预览模式下,帧从捕获硬件传输到系统存储器,然后使用GDI功能显示在捕获窗口中。 您可以使用此宏或显式调用WM_CAP_SET_PREVIEW消息。
    函数声明
    BOOL capPreview( hwnd, f);
    参数

    hwnd捕获窗口的句柄。f预览标志。 为此参数指定TRUE以启用预览模式或FALSE禁用它。
    返回值

    如果成功返回TRUE,否则返回FALSE。
    备注

    预览模式使用大量的CPU资源。 当另一个应用程序关注时,应用程序可以禁用预览或降低预览速率。 CAPSTATUS结构的fLiveWindow成员指示是否启用预览模式。启用预览模式会自动禁用重叠模式。

    capSetCallbackOnVideoStream 函数
    capSetCallbackOnVideoStream宏在应用程序中设置回调函数。 当视频缓冲区被填满时,AVICap会在流式传输捕获过程中调用此过程。 您可以使用此宏或显式调用WM_CAP_SET_CALLBACK_VIDEOSTREAM消息。
    函数声明
    BOOL capSetCallbackOnVideoStream( hwnd, fpProc);
    参数

    hwnd捕获窗口的句柄。fpProc指向视频流回调功能的指针,类型为capVideoStreamCallback。 为此参数指定NULL以禁用先前安装的视频流回调函数。
    返回值

    如果成功则返回TRUE,如果流捕获或单帧捕获会话正在进行,则返回FALSE。
    备注

    捕获窗口在将捕获的帧写入磁盘之前调用回调函数。 如果需要,这允许应用程序修改框架。如果视频流回调功能用于流式捕获,则必须在启动捕获会话之前安装该过程,并且必须在会话期间保持启用。 流式传输捕获结束后可以禁用。

    capFileSetCaptureFile 函数
    capFileSetCaptureFile宏命名用于视频捕获的文件。 您可以使用此宏或显式调用WM_CAP_FILE_SET_CAPTURE_FILE消息。
    函数声明
    BOOL capFileSetCaptureFile( hwnd, szName);
    参数

    hwnd捕获窗口的句柄。szName的指向包含要使用的捕获文件的名称的以 NULL 结尾的字符串。
    返回值

    如果成功则返回TRUE,如果文件名无效或流媒体或单帧捕获正在进行,则返回FALSE。
    备注

    此消息将文件名存储在内部结构中。 它不创建,分配或打开指定的文件。 默认的捕获文件名是C:\CAPTURE.AVI。

    capCaptureGetSetup 函数
    capCaptureGetSetup宏检索流捕获参数的当前设置。 您可以使用此宏或显式发送WM_CAP_GET_SEQUENCE_SETUP消息。
    函数声明
    BOOL capCaptureGetSetup( hwnd, s, wSize);
    参数

    hwnd捕获窗口的句柄。s指向CAPTUREPARMS结构的指针。wSize由s引用的结构的大小(以字节为单位)。
    返回值

    如果成功返回TRUE,否则返回FALSE。
    备注

    有关用于控制流捕获的参数的信息,请参阅CAPTUREPARMS结构。

    capCaptureSetSetup 函数
    capCaptureSetSetup宏设置与流捕获一起使用的配置参数。 您可以使用此宏或明确发送WM_CAP_SET_SEQUENCE_SETUP消息。
    函数声明
    BOOL capCaptureSetSetup( hwnd, psCapParms, wSize);
    参数

    hwnd捕获窗口的句柄。psCapParms指向CAPTUREPARMS结构的指针。wSize由s引用的结构的大小(以字节为单位)。
    返回值

    如果成功返回TRUE,否则返回FALSE。
    备注

    有关用于控制流捕获的参数的信息,请参阅CAPTUREPARMS结构。

    capCaptureSequence 函数
    capCaptureSequence宏启动流视频和音频捕获到一个文件。 您可以使用此宏或明确发送WM_CAP_SEQUENCE消息。
    函数声明
    BOOL capCaptureSequence( hwnd);
    参数

    hwnd
    捕获窗口的句柄。

    返回值

    如果成功返回TRUE,否则返回FALSE。如果发生错误,并使用capSetCallbackOnError宏设置错误回调函数,则调用错误回调函数。
    备注

    如果要更改控制流捕获的参数,请在开始捕获之前使用capCaptureSetSetup宏。默认情况下,捕获窗口不允许其他应用程序在捕获期间继续运行。 要覆盖这一点,可以将CAPTUREPARMS结构的fYield成员设置为TRUE,或者安装yield回调函数。在流式传输捕获期间,捕获窗口可以选择向您的应用程序发出特定类型条件的通知。

    capCaptureStop 函数
    capCaptureStop宏停止捕获操作。 您可以使用此宏或显式发送WM_CAP_STOP消息。在步骤帧捕获中,在发送该消息之前收集的图像数据保留在捕获文件中。 如果启用了音频捕获,音频数据的等效持续时间也保留在捕获文件中。
    函数声明
    BOOL capCaptureStop( hwnd);
    参数

    hwnd
    捕获窗口的句柄。

    返回值

    如果成功返回TRUE,否则返回FALSE。
    备注
    捕获操作必须设置了 yield 才能使用此消息。 使用capCaptureAbort宏放弃当前的捕获操作。

    实现原理这个程序可以分成三个部分,分别是:摄像头的初始化操作、摄像头捕获录制、摄像头停止捕获录制。现在,我们就分别对每个部分进行详细介绍。
    摄像头初始化
    首先,我们调用 capCreateCaptureWindow 函数来创建摄像头的捕获句柄,指定摄像头画面显示的窗口、大小、风格样式、父窗口等。调用成功后,便获取摄像头句柄
    然后,调用 capDriverConnect 函数连接摄像头设备的0号驱动程序,这个程序有时会出错,但是,多尝试几次就可以成功连接。所以,这里我们设置一个循环,直到成功建立连接,循环才结束
    接着,调用 capDriverGetCaps 获取驱动程序的性能,根据结构体 CAPDRIVERCAPS 中的 fCaptureInitialized 成员,判断摄像头初始化操作是否成功,若没有成功,则返回操作。成功,则进行下一步操作
    在初始化成功之后,我们可以将摄像头画面进行预示。在预示之前,可以使用 capPreviewRate 设置预示的帧率,调用 capPreview 函数来决定是否在上述设置的窗口中显示摄像头的预示画面
    最后,返回摄像头捕获句柄

    摄像头捕获
    首先,我们可以使用 capSetCallbackOnVideoStream 设置捕获过程的回调函数,也可以不设置
    然后,我们使用 capFileSetCaptureFile 设置生成的视频文件的保存路径和文件名称
    接着,使用 capCaptureGetSetup 获取摄像头的捕获参数,并在原来的基础上,进行修改和设置。设置录制音频、异步录制、屏蔽鼠标的操作、设置停止捕获的按键、设置捕获帧率等。完成后调用 capCaptureSetSetup 函数设置新的捕获参数
    最后,调用 capCaptureSequence 开始捕获摄像头数据,并存储到AVI文件中

    摄像头停止捕获摄像头停止捕获的操作比较简单,就是直接调用 capCaptureStop 函数,停止捕获操作即可。
    编码实现导入VFW库#include <vfw.h>#pragma comment(lib, "vfw32.lib")
    初始化摄像头HWND InitCamera(HWND hDisplayWnd){ // 驱动器性能 CAPDRIVERCAPS driverCaps = {0}; // 捕捉窗口的状态 CAPSTATUS capStatus = {0}; BOOL bRet = FALSE; // 获取窗口的大小 RECT rcDisplay = { 0 }; ::GetWindowRect(hDisplayWnd, &rcDisplay); // 创建预示窗口 HWND hCameraWnd = capCreateCaptureWindow("My Camera Program", WS_CHILD | WS_VISIBLE | WS_EX_CLIENTEDGE | WS_EX_DLGMODALFRAME, 0, 0, (rcDisplay.right - rcDisplay.left), (rcDisplay.bottom - rcDisplay.top), hDisplayWnd, 0); if (NULL == hCameraWnd) { ShowError("capCreateCaptureWindow"); return NULL; } // 连接第0号驱动器 // capDriverConnect有时会调用失败, 所以需要使用一个循环来知道调用成功为止 do { bRet = capDriverConnect(hCameraWnd, 0); } while (FALSE == bRet); // 得到驱动器的性能 bRet = capDriverGetCaps(hCameraWnd, &driverCaps, sizeof(CAPDRIVERCAPS)); if (FALSE == bRet) { ShowError("capDriverGetCaps"); return NULL; } // 判断是否初始化成功 if (FALSE == driverCaps.fCaptureInitialized) { ShowError("Video Capture Init"); return NULL; } // 设置预示帧频 capPreviewRate(hCameraWnd, 30); // 设置预示方式 capPreview(hCameraWnd, TRUE); return hCameraWnd;}
    摄像头开始录制// 摄像头开始录制BOOL StartCaptureCamera(HWND hCameraWnd, char *pszSaveFileName){ // 捕捉参数 CAPTUREPARMS capParams = {0}; // 设置回调函数 capSetCallbackOnVideoStream(hCameraWnd, capVideoStreamCallback); // 设置文件名 capFileSetCaptureFile(hCameraWnd, pszSaveFileName); // 得到参数 capCaptureGetSetup(hCameraWnd, &capParams, sizeof(CAPTUREPARMS)); // 设置参数 capParams.fCaptureAudio = TRUE; // 录制音频 capParams.fYield = TRUE; // 如果为TRUE,将产生一个后台线程来进行视频捕捉 capParams.fAbortLeftMouse = FALSE; // 为TRUE,表示单击鼠标左键停止捕捉 capParams.fAbortRightMouse = FALSE; // 为TRUE,表示单击鼠标右键停止捕捉 capParams.vKeyAbort = VK_ESCAPE; // 表示设置终止捕捉的虚拟键,此处设置为ESC键 capParams.dwRequestMicroSecPerFrame = 200000; // 以微秒为单位设置捕捉帧率,默认值为66667,即每秒15帧 1000000/15 = 66667 capCaptureSetSetup(hCameraWnd, &capParams, sizeof(CAPTUREPARMS)); // 捕捉到文件 capCaptureSequence(hCameraWnd); return TRUE;}
    摄像头结束录制// 摄像头停止录制void StopCaptureCamera(HWND hCameraWnd){ // 停止捕捉 capCaptureStop(hCameraWnd);}
    程序测试我们直接运行程序,程序界面上成功显示摄像头捕获到的信息,说明摄像头初始化操作成功。然后,点击“Start”摄像头开始录制,保存为AVI视频文件。最后,点击“Stop”,摄像头结束录制,生成一个AVI视频文件。双击运行AVI视频文件,成功播放刚才的录制画面。

    总结这个程序不是很复杂,按部就班地对着上述给出的参考程序去实现就好。其中,需要注意三个地方:

    在摄像头初始化阶段,我们的程序调用 capDriverConnect 函数去连接0号驱动程序,这个函数有可能会出现失败,但是出现失败多尝试连接几次,还是能成功连接上。所以,我们通过写了一个循环让它一直连接,直到成功连接上
    在摄像头开始录制的时候,我们设置摄像头的捕获参数,其中要注意一个参数的设置,即 CAPTUREPARMS 结构体中的 fYield 成员,这里建议设置成 TRUE,表示异步捕获录制视频,否则阻塞录制,主线程会卡住,知道录制完毕。当我们将 fYield 设置为 TRUE 的时候,才能使用 capCaptureStop 函数来结束捕获
    我们可以使用 capSetCallbackOnVideoStream 函数设置捕获的回调函数,也可以不设置。设置的话,我们就可以获取捕获到的视频数据和大小,方便我们对数据的操作

    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2018-12-17 12:48:25

发送私信

背负的太多,没等到击垮敌人,就先累死了自己

9
文章数
8
评论数
eject