博文
WDM 视频捕获介绍(2006-05-10 01:31:00)
摘要:作者:孙涛
摘要:该文主要提供一些在Win98,win2K及以上操作系统中,进行Windows Driver Model(WDM)视频捕获的相关知识介绍。
Windos下视频捕获简史
VFW(Video For Windows)1.0版本在1992年11月发布,它是为在Windows3.1下将电影最优化地捕获到磁盘上而发布的。从那以后,视频捕获技术就越来越受到关注了。
VFW技术同样受到了很多批评,它捕获的数据保存到磁盘上会占用大量磁盘空间,每秒数据量超过20M,同时需要大量的客户端支撑软件,VFW体系架构上的不足在视频会议应用上和PC/TV应用上被暴露无遗,这样就要求一种新的视频捕获技术来弥补这些不足。
VFW的体系结构缺乏为视频会议,电视浏览,视频区域捕获和VBI(Vertical Blanking Interval)数据流提供强而有效的支持。一些视频卡等设备开发商在设计自己的产品时,针对这些缺陷,对VFW进行了功能扩展。由于没有统一的标准,我们的应用程序在使用这些扩充的功能时,就必须要写一些基于特定硬件的代码。这就意味着当要改变捕获驱动程序时,就必须要对显卡的驱动程序进行修改。
WDM和视频捕获
WDM 视频捕获设计就是为了来解决VFW体系结构中存在的这些问题。WDM视频捕获主要的好处体现在:
l 可以为设备(如基于USB,IEEE 1394通讯方式的摄像头 )提供32位的驱动程序。
l 允许DirectShow 和 WDM 流协同工作。
l 可以在视频捕获设备和DVD/MPEG设备间,为硬件(如video ports 和 chip sets)共享一个分类的驱动程序结构(Stream.sys)。
l 支持多个数据流。
l &n......
DirectShow应用程序快速入门(2006-04-20 15:42:00)
摘要:
摘要:该文介绍如何编写DirectShow应用程序,属于入门级文章。载自博客blog.csdn.net/suntaoznz。
一 环境设置... 1
二 DirectShow编程简介... 1
三 播放文件例程... 3
一环境设置
该节介绍如何建立DirectShow应用程序。你可以建立一个控制台程序,或者Visual Studio环境下的其它Visual C++的项目。
头文件
所有DirectShow 程序都使用下表中的头文件。
头文件
需 要
dshow.h
所有DirectShow 程序。
有些DirectShow接口会要求其他头文件,你可以去查看这些接口的参考手册。
库文件
DirectShow程序要使用的库文件如下:
库文件
说 明
Strmiids.lib
提供类标识(CLSIDs)和接口表示(IIDs)。所有的DirectShow程序都要求使用该库文件。
Quartz.lib
提供AMGetErrorText函数,如果你不调用这个函数,就可以不加载该库文件。
可以把DirectX SDK 的Include 和Lib目录放在Visual Studio的搜索路径的第一位。以确保你可以使用最新的版本!
二 DirectShow编程简介
该节对DirectShow编程的基本术语和概念进行介绍,通过对该节的阅读,你可以写你的一个DirectShow应用程序。
过滤器(Filters)和过滤器图表(Filter Graphs)
过滤器(Filters)就是一个软件组件,它执行一些针对多媒体流的操作。比如:
· 读入文件
· &......
Direcshow中视频捕捉和参数设置报告-5(2006-04-19 11:00:00)
摘要:7. 将设备从系统中移走时的事件通知(Device remove Notify)
如果用户将一个graph正在使用的即插即用型的设备从系统中去掉,filter图表管理器就会发送一个EC_DEVICE_LOST事件通知,如果该设备又可以使用了,filter图表管理器就发送另外的一个EC_DEVICE_LOST通知,但是先前组建的捕捉filter graph图就没法用了,用户必须重新组建graph图。
当系统中有新的设备添加时,dshow是不会发送任何通知的,所以,应用程序如果想要知道系统中何时添加新的设备,应用程序可以监控WM_DEVICECHANGE消息。
8. 从静止图像pin中捕捉图片
有些照相机,摄像头除了可以捕获视频流以外还可以捕获单张的,静止的图片。通常,静止的图片的质量要比流的质量要高。摄像头一般都一个按钮来触发,或者是支持软件触发。支持输出静态图片的摄像头一般都要提供一个静态图片pin,这个pin的种类是PIN_CATEGORY_STILL。
从设备中获取静态图片,我们一般推荐使用windows Image Acquisition (WIA) APIs。当然,你也可以用dshow来获取图片。
在graph运行的时候利用IAMVideoControl::SetMode来触发静态的pin。代码如下
pControl->Run(); // Run the graph.
IAMVideoControl *pAMVidControl = NULL;
hr = pCap->QueryInterface(IID_IAMVideoControl, (void**)&pAMVidControl);
if (SUCCEEDED(hr))
{
// Find the still pin.
IPin *pPin = 0;
hr = pBuild->FindPin(pCap, PINDIR_OUTPUT, &PIN_CATEGORY_STILL, 0, FALSE, 0, &am......
Direcshow中视频捕捉和参数设置报告-4(2006-04-19 10:59:00)
摘要:6. 如何配置一个视频捕捉设备
1显示VFW驱动的视频设备对话框
如果视频捕捉设备采用的仍然是VFW方式的驱动程序,则必须支持下面三个对话框,用来设置视频设备。
1 Video Source
用来选择视频输入设备并且调整设备的设置,比如亮度和对比度。
2Video Format
用来设置桢的大小和位
3Video Display
用来设置视频的显示参数
为了显示上面的三个对话框,你可以do the following
1停止graph。
2向捕捉filter请求IAMVfwCaptureDialogs接口,如果成功,表明设备支持VFW驱动。
3调用IAMVfwCaptureDialogs::HasDialog来检查驱动程序是否支持你请求的对话框,如果支持,返回S_OK,否则返回S_FALSE。注意不要用SUCCEDED宏。
4如果驱动支持该对话框,调用IAMVfwCaptureDialogs::ShowDialog显示该对话框。
5 重新运行graph
代码如下
pControl->Stop(); // Stop the graph.
// Query the capture filter for the IAMVfwCaptureDialogs interface. IAMVfwCaptureDialogs *pVfw = 0;
hr = pCap->QueryInterface(IID_IAMVfwCaptureDialogs, (void**)&pVfw); if (SUCCEEDED(hr))
{
// Check if the device supports this dialog box.
if (S_OK == pVfw->HasDialog(VfwCaptureDialog_Source))
{
// Show the dialog box.
hr = pVfw->ShowDialog(VfwCaptureDialog_Source, hwndParent);
&nbs......
Direcshow中视频捕捉和参数设置报告-3(2006-04-19 10:57:00)
摘要:5. 如何控制Capture Graph(Controlling Capture Graph)
Filter图表管理器可以通过IMediaControl接口控制整个graph的运行,停止和暂停。但是当一个graph有捕捉和预览两个数据流的时候,如果我们想单独的控制其中的一个数据流话,我们可以通过ICaptureGraphBuilder2::ControlStream 。
下面讲一下如何来单独控制捕捉和预览数据流。
1 控制捕捉视频流
下面的代码,让捕捉数据流在graph开始运行1秒后开始,允运行4秒后结束。
// Control the video capture stream.
REFERENCE_TIME rtStart = 1000 0000, rtStop = 5000 0000;
const WORD wStartCookie = 1, wStopCookie = 2; // Arbitrary values. hr = pBuild->ControlStream(
&PIN_CATEGORY_CAPTURE, // Pin category.
&MEDIATYPE_Video, // Media type.
pCap, // Capture filter.
&rtStart, &rtStop, // Start and stop times.
wStartCookie, wStopCookie // Values for the start and stop events.
);
pControl->Run();
第一个参数表明需要控制的数据流,一般采用的是pin种类GUID,
第二个参数表明了媒体类型。
第三个参数指明了捕捉的filter。如果想要控制graph图中的所有捕捉filter,第二个和第三个参数都要设置成NULL。
第四和第五个参数表明了流开始和结束的时间,这是一个相对于graph开始的时间。
只有你调用IMediaControl::Run以后,这个函数才有作用。如果graph正在运行,这个设置立即生效。
最后的两个参数用来设置当数据流停止,开始能够得到的事件通知。对......
Direcshow中视频捕捉和参数设置报告-2(2006-04-19 10:52:00)
摘要:4. 如何捕捉视频流并保存到文件(Capture video to File)
1) 将视频流保存到AVI文件
下面的图表显示了graph图
图2
AVI Mux filter接收从capture pin过来的视频流,然后将其打包成AVI流。音频流也可以连接到AVI Mux Filter上,这样mux filter就将视频流和视频流合成AVI流。File writer将AVI流写入到文件中。
可以像下面这样构建graph图
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); // (Optional) Receives a pointer to the file sink.
第一个参数表明文件的类型,这里表明是AVI,第二个参数是制定文件的名称。对于AVI文件,SetOutputFileName函数会创建一个AVI mux Filter 和一个 File writer Filter ,并且将两个filter添加到graph图中,在这个函数中,通过File Writer Filter 请求IFileSinkFilter接口,然后调用IFileSinkFilter::SetFileName方法,设置文件的名称。然后将两个filter连接起来。第三个参数返回一个指向 AVI Mux的指针,同时,它也通过第四个参数返回一个IFileSinkFilter参数,如果你不需要这个参数,你可以将这个参数设置成NULL。
然后,你应该调用下面的函数将capture filter 和AVI Mux连接起来。
hr = pBuild->RenderStream(
&PIN_C......
Direcshow中视频捕捉和参数设置报告-1(2006-04-19 10:50:00)
摘要:1. 关于视频捕捉(About Video Capture in Dshow)
1视频捕捉Graph的构建
一个能够捕捉音频或者视频的graph图都称之为捕捉graph图。捕捉graph图比一般的文件回放graph图要复杂许多,dshow提供了一个Capture Graph Builder COM组件使得捕捉graph图的生成更加简单。Capture Graph Builder提供了一个ICaptureGraphBuilder2接口,这个接口提供了一些方法用来构建和控制捕捉graph。
首先创建一个Capture Graph Builder对象和一个graph manger对象,然后用filter graph manager 作参数,调用ICaptureGraphBuilder2::SetFiltergraph来初始化Capture Graph Builder。看下面的代码把
HRESULT InitCaptureGraphBuilder(
IGraphBuilder **ppGraph, // Receives the pointer.
ICaptureGraphBuilder2 **ppBuild // Receives the pointer.
)
{
if (!ppGraph || !ppBuild)
{
return E_POINTER;
}
IGraphBuilder *pGraph = NULL;
ICaptureGraphBuilder2 *pBuild = NULL;
// Create the Capture Graph Builder.
HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL,
CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void**)&pGraph);
if (SUCCEEDED(hr))
{
// Create the Filter Graph Manager.
hr = CoCreateInst......
多线程同步技术-4(2006-03-11 00:19:00)
摘要: 互斥内核对象
互斥(Mutex)是一种用途非常广泛的内核对象。能够保证多个线程对同一共享资源的互斥访问。同临界区有些类似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。与其他几种内核对象不同,互斥对象在操作系统中拥有特殊代码,并由操作系统来管理,操作系统甚至还允许其进行一些其他内核对象所不能进行的非常规操作。为便于理解,可参照图6给出的互斥内核对象的工作模型:
图6 使用互斥内核对象对共享资源的保护
图(a)中的箭头为要访问资源(矩形框)的线程,但只有第二个线程拥有互斥对象(黑点)并得以进入到共享资源,而其他线程则会被排斥在外(如图(b)所示)。当此线程处理完共享资源并准备离开此区域时将把其所拥有的互斥对象交出(如图(c)所示),其他任何一个试图访问此资源的线程都有机会得到此互斥对象。
以互斥内核对象来保持线程同步可能用到的函数主要有CreateMutex()、OpenMutex()、ReleaseMutex()、WaitForSingleObject()和WaitForMultipleObjects()等。在使用互斥对象前,首先要通过CreateMutex()或OpenMutex()创建或打开一个互斥对象。CreateMutex()函数原型为:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全属性指针
BOOL bInitialOwner, // 初始拥有者
LPCTSTR lpName // 互斥对象名
);
参数bInitialOwner主要用来控制互斥对象的初始状态。一般多将其设置为FALSE,以表明互斥对象在创建时并没有为任何线程所占有。如果在创建互斥对象时指定了对象名,那么可以在本进程其他地方或是在其他进程通过OpenMutex()函数得到此互斥对象的句柄。OpenMutex()函数原型为:
HANDLE OpenMutex(
DWORD dwDesiredA......
多线程同步技术-3(2006-03-11 00:18:00)
摘要: 信号量内核对象
信号量(Semaphore)内核对象对线程的同步方式与前面几种方法不同,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。在用CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源计数加1。在任何时候当前可用资源计数决不可能大于最大资源计数。
图3 使用信号量对象控制资源
下面结合图例3来演示信号量对象对资源的控制。在图3中,以箭头和白色箭头表示共享资源所允许的最大资源计数和当前可用资源计数。初始如图(a)所示,最大资源计数和当前可用资源计数均为4,此后每增加一个对资源进行访问的线程(用黑色箭头表示)当前资源计数就会相应减1,图(b)即表示的在3个线程对共享资源进行访问时的状态。当进入线程数达到4个时,将如图(c)所示,此时已达到最大资源计数,而当前可用资源计数也已减到0,其他线程无法对共享资源进行访问。在当前占有资源的线程处理完毕而退出后,将会释放出空间,图(d)已有两个线程退出对资源的占有,当前可用计数为2,可以再允许2个线程进入到对资源的处理。可以看出,信号量是通过计数来对线程访问资源进行控制的,而实际上信号量确实也被称作Dijkstra计数器。
使用信号量内核对象进行线程同步主要会用到CreateSemaphore()、OpenSemaphore()、ReleaseSemaphore()、WaitForSingleObject()和WaitForMultipleObjects()等函数。其中,CreateSemaphore()用来创建一个信号量内核对象,其函数原型为:
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphor......
多线程同步技术-2(2006-03-11 00:17:00)
摘要:
在前面讲述线程通信时曾使用过事件内核对象来进行线程间的通信,除此之外,事件内核对象也可以通过通知操作的方式来保持线程的同步。对于前面那段使用临界区保持线程同步的代码可用事件对象的线程同步方法改写如下:
// 事件句柄
HANDLE hEvent = NULL;
// 共享资源
char g_cArray[10];
……
UINT ThreadProc12(LPVOID pParam)
{
// 等待事件置位
WaitForSingleObject(hEvent, INFINITE);
// 对共享资源进行写入操作
for (int i = 0; i < 10; i++)
{
g_cArray[i] = 'a';
Sleep(1);
}
// 处理完成后即将事件对象置位
SetEvent(hEvent);
return 0;
}
UINT ThreadProc13(LPVOID pParam)
{
// 等待事件置位
WaitForSingleObject(hEvent, INFINITE);
// 对共享资源进行写入操作
for (int i = 0; i < 10; i++)
{
g_cArray[10 - i - 1] = 'b';
Sleep(1);
}
// 处理完成后即将事件对象置位
SetEvent(hEvent);
return 0;
}
……
void CSample08View::OnEvent()
{
// 创建事件
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
// 事件置位
SetEvent(hEvent);
// 启动线程
AfxBeginThread(ThreadProc12, NULL);
AfxBeginThread(ThreadProc13, NULL);
// 等待计算完毕
Sleep(300);
// 报告计算结果
CString sResult = CString(g_cArray);
AfxMessageBox(sResult);
}
......