注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

逍遥子 曰:

得失失得 何必患得患失 舍得得舍 不妨不舍不得

 
 
 

日志

 
 

使用VFW在windows下编程控制摄像头(续)  

2010-01-23 10:48:07|  分类: 多媒体技术 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

测量视频质量

测量视频质量的一个方法是去限定在一个捕获操作期间丢掉的捕获图像帧的数字。当流捕获完成后,质量 = 丢掉的帧 / 所有的帧。如果这个数(百分数)大于wPercentDropForError的值,AVICap将发一个错误信息给错误回调函数。WPercentDropForError是CAPTUREPARMS数据结构体的一个数据成员。

通过WM_CAP_GET_SEQUENCE_SETUP消息(capCaptureGetSetup宏)可以得到当前设定的丢掉帧的限定值。同样对wPercentDropForError进行修改,再把修改后的数据结构体发送给捕获窗体就可以完成对限定值的修改。WM_CAP_GET_SEQUENCE_SETUP消息(capCaptureSetSetup), wPercentDropForErrorm默认值10 (10%)。


 

用户初始化捕获

    通过WM_CAP_GET_SEQUENCE_SETUP消息(capCaptureGetSetup宏)可以得到当前用户初始化捕获的状态值。该值存放在fMakeUserHitOKToCapture中。在开始一个捕获会话前,设置该值为TRUE,可以为用户提供精确的控制。系统为所有的音视频分配好数据缓存区后,AVICap会显示一个对话框。它让用户清除因为软件初始化导致的捕获延迟。如果你的应用程序为视频数据分配的数据缓存区很小,那么这个对话框可能就没的必要了。该值的默认值是FALSE。


 

和调色板一起工作

       最初,如果视频捕获格式需要一个调色板,那么捕获窗口将使用捕获驱动提供的调色板来代替。这个调色板可能由灰度值,或者可选的彩色值组成。使用WM_CAP_PAL_PASTE或者WM_CAP_PAL_OPEN消息(capPalettePaste或capPaletteOpen)可以获得一个现存的调色板去替换默认的调色板。你还可以创建一个自定义的调色板去替换默认的调色板,你要使用WM_CAP_PAL_AUTOCREATE 或者 WM_CAP_PAL_MANUALCREATE消息 ( capPaletteAuto 或 capPaletteManual )。当你替换了调色板后,捕获窗体和驱动程序将使用替换后的调色板,直到你创建或打开其他的调色板为止。


 

WM_CAP_PAL_AUTOCREATE 或者 WM_CAP_PAL_MANUALCREATE 消息将创建一个基于当前视频输入最优化的调色板。这个自定义的调色板将为视频提供一个最好的颜色逼真度,因为这个调色板的颜色是基于这个视频的。捕获窗口创建一个采样颜色的3维柱状图。它会减小真实颜色和渐近色间的值。

 

在发送WM_CAP_PAL_AUTOCREATE消息时, 你必须指定AVICap采样的帧数以及调色板的颜色尺寸。在指定帧数时,要保证帧数足够大以保证所有的视频颜色可以被采样收集到。

    使用WM_CAP_PAL_MANUALCREATE消息可以对当前帧进行采样。通过该消息,进行几次手动选择帧采样操作,你可以创建自己的调色板,它包含了你想要的颜色信息。


 

一个调色板可以包含256种颜色。如果你要合并调色板,或者在视频队列中同时在显示其他视频或图像。你可以去使用一个小的颜色集合,这样不同图像或视频的颜色就可以共存了。

使用WM_CAP_PAL_SAVE消息(capPaletteSave),可以保促一个新的调色板。通过WM_CAP_PAL_OPEN消息还可以得到当前的调色板。你可以在调色板处理前保存一个调色板,或者为其他应用程序使用去保存一个调色板。


 

使用WM_CAP_PAL_PASTE可以把剪贴板中的调色板粘贴到捕获窗口中。捕获窗口通过这个调色板到捕获驱动。其他程序可以拷贝调色板到剪贴板中。你也可以把调色板粘贴到剪贴板中。使用WM_CAP_ENDIT_COPY消息(capEditCopy)。它将拷贝视频缓存区(包括调色板)到剪贴区。


在AVI文件中的嵌入信息块

你可以在一个AVI文件中插入信息块,比如文本或者自定义的数据。通过使用下面的消息:WM_CAP_FILE_SET_INFOCHUNK(capFileSetInfoChunk)。可以使用这个消息还可以清除掉一个AVI文件中的信息块。


 

用户数据消息

       通过使用WM_CAP_GET_USER_DATA和WM_CAP_SET_USER_DATA消息可以关联数据到一个捕获窗体。(capGetUserData 和 capSetUserData宏)。使用…Get…消息可以得到一个LONG数据值,可以通过_Set_消息去设置该值。


 


 

3.1.8 AVICap回调函数
       你的应用程序可以为一个捕获窗口注册一些回调函数,它们可以告诉你的应用程序一些变化。比如捕获状态发生变化了,或者有错误发生了,音视频缓存区可使用了。下面的消息设置回调函数。

消          
 说          
 
WM_CAP_SET_CALLBACK_CAPCONTROL

CapSetCallbackOnCapControl 宏
 在应用程序中指定回调函数用于控制捕获的开始和结束。
 
WM_CAP_SET_CALLBACK_ERROR

CapSetCallbackOnError宏
 在应用程序中指定回调函数,当出错的时候就调用它。
 
WM_CAP_SET_CALLBACK_FRAME

CapSetCallbackOnFrame宏
 在应用程序中指定回调函数,当预览图像帧被捕获了的时候就调用它。
 
WM_CAP_SET_CALLBACK_STATUS

CapSetCallbackOnStatus宏
 在应用程序中指定回调函数,当状态(status)改变的时候就调用它。
 
WM_CAP_SET_CALLBACK_VIDEOSTREAM

CapSetCallbackOnVideoStream宏
 在应用程序中指定回调函数,在流捕获期间,当一个新的视频缓存区可用的时候就调用它。
 
WM_CAP_SET_CALLBACK_WAVESTREAM

CapSetCallbackOnWaveStream宏
 在应用程序中指定回调函数,在流捕获期间,当一个新的音频缓存区可用的时候就调用它。
 
WM_CAP_SET_CALLBACK_YIELD

CapSetCallbackOnYield宏
 在应用程序中指定回调函数,在流捕获期间Yielding(产生?)
 


 

精确捕获控制

捕获窗口可以提供捕获回调函数,这个回调函数可以对流捕获的开始和结束时刻进行精确的控制。在捕获驱动程序(capture driver)完成所有缓存区分配和其他捕获准备后,捕获驱动程序就发送第一个消息给回调处理程序,把nState参数设置为:

CONTROLCALLBACK_PREROLL

这个消息告诉应用程序将要开启视频源了。(这个回调函数指定nState为它的第二个参数)回调函数将在开始时刻产生返回值。返回值为TRUE那么将继续捕获。为FALSE就中断捕获。一旦捕获开始,这个回调函数将频繁的调用,把nState设置为:

CONTROLCALLBACK_CAPTURING

将允许应用程序通过返回false去结束捕获。


 

错 误

       捕获窗口使用错误通知消息去告诉你的应用程序,发生了AVICap错误,比如磁盘空间已经用完了,尝试对一个只读文件进行写操作,不能访问硬件,掉帧太多。错误通知内容报价一个消息ID和一个格式化的文本字符(用来显示)。你的应用程序可以通过使用这个消息ID去过滤错误通报,还可以让该错误信息不显示给用户。消息ID为0表示一个新操作正在开始并且这个回调函数会清除掉所有的显示的错误信息。


 

帧(Frame)

       A capture window uses frame callback notification messages to notify your application when a new video frame is available. The capture window enables these callback notifications only if the preview rate is nonzero and streaming capture is not in progress.


 

状态回调函数

当视频捕获向磁盘写数据,或者在其他较长的操作期间,捕获窗口可以发送消息给状态回调函数通知你正在处理该操作的应用程序。状态信息包括一个消息ID和和一个格式化的文本字符(用来显示)。你的应用程序可以通过使用消息ID去过滤通报,还可以限制该信息是否显示给用户。在捕获操作期间,发给回调函数的第一个消息总是ID_CAP_GEGIN,最后一个总是ID_CAP_END。消息ID为0表示,一个新操作正在进行并且回调函数将清除当前状态。


 

视频流

       在流捕获期间,应用程序可以使用视频流回调函数去处理一个捕获的视频帧。视频窗体只能在每次向磁盘写数据帧前,调用视频流回调函数。


 

音频流

在流捕获期间,应用程序可以使用音频流回调函数去处理音频缓存区。视频窗体只能在每次向磁盘写数据帧前,调用音频流回调函数。


 

Yield 回调函数

应用程序在流捕获期间可以使用Yield回调函数。(Yield回调函数一般是由一个消息循环组成,可以调用PeekMessage,TranslateMessage,DispatchMessage)。捕获窗口在每次捕获视频帧时至少调用一次Yield回调函数。但是具体要调用多少次由帧率来决定。


 

关闭回调函数

你可以暂时或永久关闭所有的回调函数的功能,在发送消息设置回调函数的时候,用NULL替换调回调函数就可以了。

3.2使用视频捕获
3.2.1创建捕获窗体

下面的例子通过使用capCreateCaptureWindow函数来创建一个捕获窗体

hWndC = capCreateCaptureWindow (

    (LPSTR) "My Capture Window",       // 如果是Pop-up窗口的窗口名称

    WS_CHILD | WS_VISIBLE,          // 窗口类型

    0, 0, 160, 120,                      // 窗口位置和尺寸

    (HWND) hwndParent,

    (int) nID );


 

 

 

3.2.2连接到一个捕获驱动器

下面举例,如何通过捕获窗口的句柄hWndC连接到MS VIDEO驱动程序上,同时还演示了如何断开连接。使用capDriverDisconnect:

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

// 或者使用宏连接:

 

 

// fOK = capDriverConnect(hWndC, 0);

 

 

 

 

 

// 关闭连接

capDriverDisconnect (hWndC);

 

 

 

3.2.3列举安装的捕获驱动程序

使用capGetDriverDescription 函数来获得系统已经安装的所有捕获驱动程序的名称和版本。

char szDeviceName[80];

char szDeviceVersion[80];

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

{

    if (capGetDriverDescription (wIndex, szDeviceName,

        sizeof (szDeviceName), szDeviceVersion,

        sizeof (szDeviceVersion))

    {

        // 加入名字到一个已经安装的设备列表中

 

 

        // 让用户选择一个使用。

    }

}

 

 

 

 

 

 

3.2.4获得捕获驱动器的性能参数

       WM_CAP_DRIVER_GET_CAPS消息可以返回捕获驱动程序以及其硬件的性能参数。这些信息存放在一个CAPDRIVERCAPS的数据结构中。当你的应用程序的捕获窗口连接到一个新的捕获驱动器后,都会刷新这个CAPDRIVERCAPS数据结构。下面将使用capDriverGetCaps宏来获得捕获设备的性能参数。

CAPDRIVERCAPS CapDrvCaps;

SendMessage (hWndC, WM_CAP_DRIVER_GET_CAPS,

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

// 或者,使用宏来获得驱动器的新能参数

 

 

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

 

 

 

 

 

3.2.5获得捕获窗口状态(Status)

下面例子使用SetWindowPos函数区设置捕获窗口的尺寸,这个尺寸的大小是基于输入的视频流大小的。输入视频流的尺寸大小由capGetStatus宏来获得,获得信息放在一个CAPSTATUS的数据结构体中。

 

 

 

CAPSTATUS CapStatus;

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

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

             CapStatus.uiImageHeight, SWP_NOZORDER | SWP_NOMOVE);


3.2.6显示对话框区设置视频属性

       每个捕获驱动器都可以提高3个以上的不同对话框来控制数字视频的特性和捕获处理。下面的例子示范如何显示这些对话框。在显示每个对话框前,该例会调用capDriverGetCaps宏并且检查返回的CAPDRIVERCAPS对象来查看是否可以能够显示特定的对话框。

 

 

 

CAPDRIVERCAPS CapDrvCaps;

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

// 视频源对话框

 

 

if (CapDriverCaps.fHasDlgVideoSource)

    capDlgVideoSource(hWndC); 

// 视频格式对话框

 

 

if (CapDriverCaps.fHasDlgVideoFormat)

{

    capDlgVideoFormat(hWndC);

    // 是否由新的图像尺寸?Are there new image dimensions?

 

 

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

    // 如果有,发送通知给父窗口,告诉它尺寸改变了

 

 

// 视频显示对话框

 

 

if (CapDriverCaps.fHasDlgVideoDisplay)

    capDlgVideoDisplay(hWndC);

 

 

 

3.2.7获得和设定视频格式

BITMAPINFO数据结构体可以实现长度可调节地去适应标准压缩的数据格式。因为它的长度可以变,所以在每次获得当前视频格式前,都必须去查询这个结构的长度以及分配的内存大小。该例子使用了capGetVideoFormatSize宏去获得缓存区大小,使用capGetVideoFormat宏区获得当前视频格式。

 

 

 

LPBITMAPINFO lpbi;

DWORD dwSize;

 

 

 

dwSize = capGetVideoFormatSize(hWndC);

lpbi = GlobalAllocPtr (GHND, dwSize);

capGetVideoFormat(hWndC, lpbi, dwSize);

 

 

 

// 访问视频格式,并且释放分配的内存。

 

 

 

 

 

应用程序使用capSetVideoFormat宏(WM_CAP_SET_VIDEOFORMAT),把一个BITMAPINFO结构发送给捕获窗口,显示修改。因为视频格式由设备指定的,你的应用程序可以去检查获得的返回值,来知道这个视频格式是不是公开的。

 

 

 

3.2.8预览视频
       下面使用capPreviewRate宏来设置预览模式的帧频率为66毫秒/帧,使用capPreview宏在捕获窗口预览图像。

 

 

 

capPreviewRate(hWndC, 66);      // 速度,微秒

 

 

capPreview(hWndC, TRUE);       // 开始预览

 

 

 

 

 

capPreview(hWnd, FALSE);        // 屏蔽预览

 

 

 

3.2.9允许视频覆盖(Overlay)

下面使用capDriverGetCaps宏去检测这个捕获驱动是否支持覆盖(Overlay)模式,如果支持,就允许视频覆盖模式;

 

 

 

CAPDRIVERCAPS CapDrvCaps;

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

if (CapDrvCaps.fHasOverlay)

    capOverlay(hWndC, TRUE);

 

 

 

 

3.2.10捕获文件命名

    下例使用capFileSetCaptureFile宏来指定一个要命名的文件名(mycap.avi),使用capFileAlloc宏去预分配5MB的文件。

 

 

 

char szCaptureFile[] = "MYCAP.AVI";

capFileSetCaptureFile( hWndC, szCaptureFile);

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

 

3.2.11格式化音频捕获

下例使用capSetAudioFormat来设置音频格式为11-KHz PCM 8-bit,立体声。

 

 

 

WAVEFORMATEX wfex;

wfex.wFormatTag = WAVE_FORMAT_PCM;

wfex.nChannels = 2;                     // 使用立体声

wfex.nSamplesPerSec = 11025;

wfex.nAvgBytesPerSec = 22050;

wfex.nBlockAlign = 2;

wfex.wBitsPerSample = 8;

wfex.cbSize = 0;

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

 

 

 

3.2.12改变视频捕获设置

       下例使用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));

 

 

 

3.2.13捕获数据

       下例使用capCaptureSequence宏开始视频捕获,使用capFileSaveAs宏从捕获文件拷贝数据到其他文件NEWFILE.AVI中。

 

 

 

char szNewName[] = "NEWFILE.AVI";

// Set up the capture operation.

capCaptureSequence(hWndC);

// Capture.

capFileSaveAs(hWndC, szNewName);

 

 

 

3.2.14加入信息块

如果你想添加其他信息(除了音视频),你可以建一个信息块并把它们插入到一个捕获文件中去。信息块可以包含这个方面的内容。比如版权信息,视频源的ID,外部显示的时间信息。下面的例子保存外部时间信息SMPTE()到一个信息块中,并加入使用capFileSetInfoChunk宏加入到捕获文件中。

 

 

 

//  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);

 

 

 

3.2.15在程序中加入回调函数
应用程序可以注册捕获窗口的回调函数,这样就可以把下面的情况通知给应用程序:

        状态变化了

        错误发生了

        视频和音频的缓冲区的数据可以使用了

        在捕获期间,应用程序将yield

下面的例子将创建一个捕获窗口并在应用的消息循环中,注状态、错误、视频流、帧的回调函数。

 

 

 

case WM_CREATE:

{

    char    achDeviceName[80] ;

    char    achDeviceVersion[100] ;

    char    achBuffer[100] ;

    WORD    wDriverCount = 0 ;

    WORD    wIndex ;

    WORD    wError ;

    HMENU   hMenu ;

 

    // 使用capCreateCaptureWindow宏创建一个捕获窗体.

 

 

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

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

 

    // 使用capSetCallbackOnError宏注册错误回调函数

 

 

     capSetCallbackOnError(ghWndCap, fpErrorCallback);

 

    // 使用capSetCallbackOnStatus宏注册状态回调函数

 

 

    capSetCallbackOnStatus(ghWndCap, fpStatusCallback);

 

    //使用capSetCallbackOnVideoStream宏注册视频流回调函数

    capSetCallbackOnVideoStream(ghWndCap, fpVideoCallback);

 

    //使用capSetCallbackOnFrame宏注册帧回调函数

    capSetCallbackOnFrame(ghWndCap, fpFrameCallback);

 

    // 连接到一个捕获驱动器上

 

 

 

 

 

    break;

}

case WM_CLOSE:

{

//使用capSetCallbackOnFrame宏关闭帧回调函数

// 类似可调用其他存在的回调函数。

 

 

 

 

 

    capSetCallbackOnFrame(hWndC, NULL);

  break;

}

 

 

 

 

 

 

 

3.2.16创建一个状态回调函数

下面的例子是一个简单的状态回调函数,使用capSetCallbackOnStatus宏来注册这个回调函数。

// StatusCallbackProc: 状态回调函数

 

 

// hWnd:            捕获窗体句柄

 

 

// nID:              当前状态的状态码

 

 

// lpStatusText:       当前状态的文本字符

//

LRESULT PASCAL StatusCallbackProc(HWND hWnd, int nID,

    LPSTR lpStatusText)

{

    if (!ghWndMain)

        return FALSE;

 

    if (nID == 0) {           // 清除旧的状态信息

        SetWindowText(ghWndMain, (LPSTR) gachAppName);

        return (LRESULT) TRUE;

    }

    // 显示状态ID和状态文本..

 

 

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

 

    SetWindowText(ghWndMain, (LPSTR)gachBuffer);

    return (LRESULT) TRUE;

}

 

3.2.17创建一个错误的回调函数

下面例子是一个简单的错误回调函数。通过capSetCallbackOnError宏来注册回调。

// ErrorCallbackProc:    错误回调函数

 

 

// hWnd:              捕获窗口句柄

 

 

// nErrID:              错误代码

 

 

// lpErrorText:          关于错误的文本信息

//

LRESULT PASCAL ErrorCallbackProc(HWND hWnd, int nErrID,

    LPSTR lpErrorText)

{

 

    if (!ghWndMain)

        return FALSE;

 

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

        return TRUE;          // 清除旧的错误

 

    // 显示错误ID和错误文本信息

 

 

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

 

    MessageBox(hWnd, lpErrorText, gachBuffer,

               MB_OK | MB_ICONEXCLAMATION);

 

    return (LRESULT) TRUE;

}

 

 

 

3.2.18创建一个帧回调函数

下面是一个简单的帧回调函数。通过capSetCallbackFrame宏来注册回调函数。

 

 

 

// FrameCallbackProc:   帧回调函数

 

 

// hWnd:              捕获窗体句柄

 

 

// lpVHdr:             指向一个包含帧信息的数据结构体

//

LRESULT PASCAL FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr)

{

    if (!ghWndMain)

        return FALSE;

 

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

    SetWindowText(ghWndMain, (LPSTR)gachBuffer);

return (LRESULT) TRUE ;

}

  评论这张
 
阅读(1127)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018