DirectShow的中文资料_中文
DirectShow的中文资料由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“中文”。
Dshow采集视频
DirectShow的中文资料之设备列举和捕捉接口
这篇解释和示例如何通过DirectShow的接口去初始化和访问系统的硬件设备。代表性的,DirectShow应用程序使用下面类型的硬件。
音/视频捕捉卡音频或视频回放卡音频或视频压缩或解压卡(象MPEG解码器)
下面将以AV设备作参考。
如何列举设备
包括在DirectShow SDK中的接口,类,和例子提供了音/视频捕捉和回放的功能。因为文件源过滤器和filter graph manager处理了内在的工作,所有,添加捕捉功能到一个应用程序中,只需添加很少的代码。你可以通过列举系统硬件设备和得到设备列表完成特别的任务(例如:所有的视频捕捉卡的列表)。DirectShow自动为win32和Video for Windows 设备实例化过滤器。要AV设备工作,首先,你必须检测当前系统存在的设备。ICreateDevEnum接口建立指定类型的列表。提供你需要的检测和设置硬件的功能。访问一个指定的设备有三步,详细的说明和代码如下:
建立系统硬件设备的列表
首先,申明一个列表指针,然后通过 CoCreateInstance 建立。CLSID_SystemDeviceEnum是我们想建立对象的类型,IID_ICreateDevEnum是接口的GUID。
ICreateDevEnum*pCreateDevEnum;
CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,(void**)&pCreateDevEnum);
其次,建立一个特别类型的硬件设备的列表(例如视频捕捉卡)
申明一个IEnumMoniker接口,并把他传给ICreateDevEnum::CreateClaEnumerator 方法。你就可以使用他访问新得到的列表了。IEnumMoniker *pEnumMon;
pCreateDevEnum->CreateClaEnumerator([specify device GUID here], &pEnumMon, 0);
最后,列举列表直到你得到你想要的设备为止。
如果先前的CreateClaEnumerator调用成功了,你可以用IEnumMoniker::Next得到设备。调用IMoniker::BindToObject建立一个和选择的device联合的filter,并且装载filter的属性(CLSID,FriendlyName, and DevicePath)。不需要为if语句的(1 == cFetched)困惑,在测试合法性之前,pEnumMon->Next(1, &pMon, &cFetched)方法会设置他为返回对象的数字(如果成功了为1)。ULONG cFetched = 0;
IMoniker *pMon;
if(S_OK ==(pEnumMon->Next(1, &pMon, &cFetched))&&(1 == cFetched))
{
pMon->BindToObject(0, 0, IID_IBaseFilter,(void **)&[desired interface here]);
好,现在你有了一个IMoniker指针,你可以添加设备的filter到filter graph。一旦你添加了filter,你就不需要IMoniker指针,设备列表,或系统设备列表。
pGraph->AddFilter([desired interface here], L“[filter name here]”);
pMon->Release();// Release moniker
}
pEnumMon->Release();// Release the cla enumerator
}
pCreateDevEnum->Release();
实例:AMCap中的设备列表代码
AMCap例子中,把所有的接口指针和一些成员变量保存在一个全局结构gcap中了。
定义如下:
struct _capstuff {
charszCaptureFile[_MAX_PATH];
WORDwCapFileSize;// size in Meg
ICaptureGraphBuilder *pBuilder;
IVideoWindow*pVW;
IMediaEventEx*pME;
IAMDroppedFrames*pDF;
IAMVideoCompreion*pVC;
IAMVfwCaptureDialogs *pDlg;
IAMStreamConfig*pASC;// for audio cap
IAMStreamConfig*pVSC;// for video cap
IBaseFilter*pRender;
IBaseFilter*pVCap, *pACap;
IGraphBuilder*pFg;
IFileSinkFilter*pSink;
IConfigAviMux*pConfigAviMux;
intiMasterStream;
BOOLfCaptureGraphBuilt;
BOOLfPreviewGraphBuilt;
BOOLfCapturing;
BOOLfPreviewing;
BOOLfCapAudio;
intiVideoDevice;
intiAudioDevice;
doubleFrameRate;
BOOLfWantPreview;
longlCapStartTime;
longlCapStopTime;
charachFriendlyName[120];
BOOLfUseTimeLimit;
DWORDdwTimeLimit;
} gcap;
例子用uIndex变量循环列举系统的硬件设备。
BOOL InitCapFilters()
{
HRESULT hr;
BOOL f;
UINT uIndex = 0;
f = MakeBuilder();//MakeBuilder函数建立了一个filter graph builder(参考建立一个捕捉程序)。
//建立设备列表对象,得到ICreateDevEnum接口
ICreateDevEnum *pCreateDevEnum;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,(void**)&pCreateDevEnum);
建立一个特别类型的硬件设备的列表,类的ID是CLSID_VideoInputDeviceCategory。现在有了一个IEnumMoniker指针,可以访问捕捉设备的列表了。
IEnumMoniker *pEm;
hr = pCreateDevEnum->CreateClaEnumerator(CLSID_VideoInputDeviceCategory, &pEm, 0);
pCreateDevEnum->Release();// We don't need the device enumerator anymore
pEm->Reset();// Go to the start of the enumerated list
现在需要实际的设备了,调用IEnumMoniker::Next,然后用得到的指针pM调用IMoniker::BindToObject,绑定filter到设备。如果你不想建立联合的filter,使用IMoniker::BindToStorage 代替IMoniker::BindToObject。
ULONG cFetched;
IMoniker *pM;// This will acce the actual devices
gcap.pVCap = NULL;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
{
if((int)uIndex == gcap.iVideoDevice)
{
// This is the one we want.Instantiate it.hr = pM->BindToObject(0, 0, IID_IBaseFilter,(void**)&gcap.pVCap);
pM->Release();// We don't need the moniker pointer anymore
break;
}
pM->Release();
uIndex++;
}
pEm->Release();// We've got the device;don't need the enumerator anymore
当有了设备后,通过接口指针去测量帧数,得到driver的名字,得到捕捉的尺寸(size)。在例子中,把每个指针都存储才gcap全局结构中了。, and get the capture size.AMCap stores each pointer in the gcap global structure.// We use this interface to get the number of captured and dropped frames
gcap.pBuilder->FindCaptureInterface(gcap.pVCap, IID_IAMDroppedFrames,(void **)&gcap.pDF);
// We use this interface to get the name of the driver
gcap.pBuilder->FindCaptureInterface(gcap.pVCap, IID_IAMVideoCompreion,(void **)&gcap.pVC);
// We use this interface to set the frame rate and get the capture size
gcap.pBuilder->FindCaptureInterface(gcap.pVCap, IID_IAMVideoStreamConfig,(void **)&gcap.pVSC);
然后得到媒体的类型和显示窗口的大小去匹配视频格式的尺寸。
AM_MEDIA_TYPE *pmt;
gcap.pVSC->GetFormat(&pmt);// Current capture format
ResizeWindow(HEADER(pmt->pbFormat)->biWidth, EADER(pmt->pbFormat)->biHeight);
DeleteMediaType(pmt);
现在,已经有了视频设备和他的相关信息,重复这个过程,得到音频设和他的信息并存储到全局机构中去。注意,这次是用参数CLSID_AudioInputDeviceCategory 调用ICreateDevEnum::CreateClaEnumerator。
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,(void**)&pCreateDevEnum);uIndex = 0;
hr = pCreateDevEnum->CreateClaEnumerator(CLSID_AudioInputDeviceCategory, &pEm, 0);
pCreateDevEnum->Release();
pEm->Reset();
gcap.pACap = NULL;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
{
if((int)uIndex == gcap.iAudioDevice)
{
// this is the one we want
hr = pM->BindToObject(0, 0, IID_IBaseFilter,(void**)&gcap.pACap);
pM->Release();
break;
}
pM->Release();
uIndex++;
}
pEm->Release();
// AMCap also repeats the proce of retrieving the format interface, this time for the audio device.hr = gcap.pBuilder->FindCaptureInterface(gcap.pACap, IID_IAMAudioStreamConfig,(void **)&gcap.pASC);
}
如何保持DirectShow Filter(Properties)
IPropertyBag 和 IPersistPropertyBag 接口存储和返回Properties的“bags”组。通过这些接口存储的Properties是可以持久保持的。同一个对象在不同的实例之间,他们保持一致。Filter可以存储他们的Properties(CLSID, FriendlyName, and DevicePath)。当一个filter存储完他的Properties之后,实例一个filter时,DirectShow会自动得到他们。添加功能到你的filter中,执行IPersistPropertyBag接口和他的方法。你可以用IPropertyBag::Read 方法装载filter Properties 到Win32 VARIANT 变量中,然后初始化输入输出pin。下面的代码演示DirectShow的VfWCapture filter如何执行IPersistPropertyBag::Load方法的。记住:在执行期间,你的filter必须提供一个有效的IPropertyBag指针。
STDMETHODIMP CVfwCapture::Load(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog)
{
HRESULT hr;
CAutoLock cObjectLock(m_pLock);// Locks the object;automatically unlocks it in the destructor.if(m_pStream)// If the filter already exists for this stream
return E_UNEXPECTED;
VARIANT var;// VARIANT from Platform SDK
var.vt = VT_I4;// four-byte integer(long)
/ VFWIndex is the private name used by the Vidcap Cla Manager to refer to the VFW Capture filter
hr = pPropBag->Read(L“VFWIndex”, &var, 0);
if(SUCCEEDED(hr))// If it read the properties succefully
{
hr = S_OK;// Defaults return value to S_OK
m_iVideoId = var.lVal;// Stores the specified hardware device number
CreatePins(&hr);// Inits the pins, replacing the return value if neceary
}
return hr;// Returns S_OK or an error value, if CreatePins failed
}、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、DirectShow的使用:
(1):头文件和库要包含dshow.h头文件,并且链接Strmiids.lib和Quartz.lib库文件。
(2):初始化DirectShow程序开始的时候必须要初始化COM,包括CoInitialize和CoUninitialize。
(3):创建filter graph 每一个DirectShow程序必须创建一个filter graph,它表现为一个媒体文件的解码和播放的机构。Filter graph通过graph builders建立(IGraphBuilder),这个接口通过CoCreateInstance来建立。
(4):建立媒体控制 创建好一个可播放媒体文件的filter graph之后,你必须创建媒体的控制用来完成相关的基本操作,比方说播放或者停止等。这个可以通过CoCreateInstance建立IMediaControl接口来实现。
(5):创建和配置事件机制 你要想知道当文件播放停止,错误发生,用户终止等操作的发生。这样就必须使用IMediaEventEx接口,同样它通过CoCreateInstance建立。
(6):装载一个文件你必须建立一个filter graph用来播放文件。可以通过IGraphBuilder::RenderFile来实现。
(7):播放文件media control 接口可以被用来播放文件。
(8):处理媒体事件播放文件的同时,可能会发生许多的事件----文件可能要播放完了,文件可能会发生错误,用户可能会停止播放。为了通知你,会发送一个通用的消息给WndProc,IMediaEventEx接口将会立即获得这个消息。
(9):清除当你结束使用DirectShow时,你必须释放你使用的接口,并且卸载COM。DirectShow的系统组成:
DirectShow技术是建立在DirectDraw和DirectSound组件基础之上的,它通过DirectDraw对显卡进行控制以显示视频,通
过DirectSound对声卡进行控制以播放声音。DirectShow可提供高质量的多媒体流的捕获和回放功能;支持多种媒体格式,包括ASF(Advanced Systems Format),MPEG(Motion Picture Experts Group),AVI(Audio-Video Interleaved),MP3(MPEG Audio Layer-3)和WAV声音文件;可以从硬件上捕获媒体数据流;可以自动检测并使用视频和音频加速硬件。因此,DirectShow可以充分发挥媒体的性能,提高运行速度,可以简化媒体播放、媒体间的格式转换和媒体捕获等工作。同时,它还具有极大的可扩展性和灵活性,可以由用户自己创建组件,并将这个组件加入DirectShow结构中以支持新的格式或特殊的效果。
应用程序与DirectShow组件以及DirectShow所支持的软硬件之间的关系如图1所示:
2.1 过滤器(filter)
由图1可以看到,过滤器是DirectShow最基本的组成元件。过滤器是一个COM组件,是完成DirectShow处理过程的基本单元。DirectShow提供了一组标准的过滤器供应用程序使用,程序开发者也可以创建自定义的过滤器来扩充DirectShow的功能,但必须是以COM形式建立的。DirectX为用户提供了DirectShow基类库(DirectShow Base Cla Library),用户自定义的过滤器都可以从基类库提供的基类和接口派生出来。
过滤器主要分为以下几种类型:
(1)源过滤器(source filter):源过滤器引入数据到过滤器图表中,数据来源可以是文件、网络、照相机等。不同的源过滤器处理不同类型的数据源。
(2)变换过滤器(transform filter):变换过滤器的工作是获取输入流,处理数据,并生成输出流。变换过滤器对数据的处理包括编解码、格式转换、压缩解压缩等。
(3)提交过滤器(renderer filter):提交过滤器在过滤器图表里处于最后一级,它们接收数据并把数据提交给外设。
(4)分割过滤器(splitter filter):分割过滤器把输入流分割成多个输出。例如,AVI分割过滤器把一个AVI格式的字节流分割成视频流和音频流。
(5)混合过滤器(mux filter):混合过滤器把多个输入组合成一个单独的数据流。例如,AVI混合过滤器把视频流和音频流合成一个AVI格式的字节流。
过滤器的这些分类并不是绝对的,例如一个ASF读过滤器(ASF Reader filter)既是一个源过滤器又是一个分割过滤器。
在DirectShow里,一组过滤器称为一个过滤器图表(filter graph)。过滤器图表用来连接过滤器以控制媒体流,它也可以将数据返回给应用程序,并搜索所支持的过滤器。过滤器有三种可能的状态:运行、停止和暂停。暂停是一种中间状态,停止状态到运行状态必定经过暂停状态。暂停可以理解为数据就绪状态,是为了快速切换到运行状态而设计的。在暂停状态下,数据线程是启动的,但被提交过滤器阻塞了。通常情况下,过滤器图表中所有过滤器的状态是一致的。
2.2 引脚(pin)
过滤器可以和一个或多个过滤器相连,连接的接口也是COM形式的,称为引脚。过滤器利用引脚在各个过滤器间传输数据。每个引脚都是从Ipin这个COM对象派生出来的。每个引脚都是过滤器的私有对象,过滤器可以动态的创建引脚,销毁引脚,自由控制引脚的生存时间。引脚可以分为输入引脚(Input pin)和输出引脚(Output pin)两种类型,两个相连的引脚必须是不同种类的,即输入引脚只能和输出引脚相连,且连接的方向总是从输出引脚指向输入引脚。
过滤器之间的连接(也就是引脚之间的连接),实际上是连接双方媒体类型(Media Type)协商的过程。连接的大致过程为:如果调用连接函数时已经指定了完整的媒体类型,则用这个媒体类型进行连接,成功与否都结束连接过程;如果没有指定或不完全指定了媒体类型,则进入下面的枚举过程--枚举欲连接的输入引脚上所有的媒体类型,逐一用这些媒体类型与输出引脚进行连接(如果连接函数提供了不完全媒体类型,则要先将每个枚举出来的媒体类型与它进行匹配检查),如果输出引脚也接受这种媒体类型,则引脚之间的连接宣告成功;如果所有输入引脚上枚举的媒体类型,输出引脚都不支持,则枚举输出引脚上的所有媒体类型,并逐一用这些媒体类型与输入引脚进行连接,如果输入引脚接受其中的一种媒体类型,则引脚之间的连接宣告成功;如果输出引脚上的所有媒体类型,输入引脚都不支持,则这两个引脚之间的连接过程宣告失败。过滤器与引脚连接如图2所示。
2.3 媒体类型(Media Type)
媒体类型是描述数字媒体格式的一种通用的可扩展方式。两个过滤器相连时,必须使用一致的媒体类型,否则这两个过滤器就不能相连。媒体类型能识别上一级过滤器传送给下一级过滤器的数据类型,并对数据进行分类。
实际在很多应用程序中,用户根本不需要担心媒体类型的问题,DirectShow会处理好所有的细节。但有些应用程序需要对媒体类型进行操作。媒体类型一般可以有两种表示:AM_MEDIA_TYPE和CMediaType。前者是一个结构,后者是从这个结构继承过来的类。
每个AM_MEDIA_TYPE由三部分组成:Major type、Subtype和Format type。这三个部分都使用GUID(全局唯一标识符)来唯一标示。Major type主要定性描述一种媒体类型,这种媒体类型可以是视频、音频、比特数据流或MIDI数据等等;Subtype进一步细化媒体类型,如果是视频的话可以进一步指定是RGB-24,还是RGB-32,或是UYVY等等;Format type则用一个结构更进一步细化媒体类型。如果媒体类型的三个部分都指定了某个具体的GUID值,则称这个媒体类型是完全指定的;如果媒体类型的三个部分中有任何一个值是GUID_NULL,则称这个媒体类型是不完全指定的。GUID_NULL具有通配符的作用。
2.4 过滤器图表管理器(Filter Graph Manager)
DirectShow通过过滤器图表管理器来控制过滤器图表中的过滤器。过滤器图表管理器是COM 形式的,它的功能有:协调过滤器间的状态转变;建立参考时钟;把事件(event)传送给应用程序;为应用程序提供建立过滤器图表的方法。
一些常用的过滤器图表管理器接口如下:
IGraphBuilder:为应用程序提供创建过滤器图表的方法。
IMediaControl:提供控制过滤器图表中多媒体数据流的方法,包括运行、暂停和停止。IMediaEventEx:继承自IMediaEvent接口,处理过滤器图表的事件。
IVideoWindow:用于设置多媒体播放器窗口的属性,应用程序可以用它来设置窗口的所有者、位置和尺寸等属性。
IBasicAudio:用于控制音频流的音量和平衡。
IBasicVideo:用于设置视频特性,如视频显示的目的区域和源区域。
IMediaSeeking:提供搜索数据流位置和设置播放速率的方法。
IMediaPosition:用于寻找数据流的位置。
IVideoFrameStep:用于步进播放视频流,可使DirectShow应用程序,包括DVD播放器一次只播放一帧视频。
2.5 过滤器图表中的数据流动
当用户要创建自定义的过滤器时,就需要了解媒体数据是如何在过滤器图表中传输的。为了在过滤器图表中传送媒体数据,DirectShow过滤器需要支持一些协议,称之为传输协议(transport)。相连的过滤器必须支持同样的传输协议,否则不能交换媒体数据。
大多数的DirectShow过滤器把媒体数据保存在主存储器中,并通过引脚把数据提交给其它的过滤器,这种传输称为局部存储器传输(local memory transport)。虽然局部存储器传输在DirectShow中最常用,但并不是所有的过滤器都使用它。例如,有些过滤器通过硬件传送媒体数据,引脚只是用来提交控制信息,如IOverlay接口。
DirectShow为局部存储器传输定义了两种机制:推模式(push model)和拉模式(pull model)。在推模式中,源过滤器生成数据并提交给下一级过滤器。下一级过滤器被动的接收数据,完成处理后再传送给再下一级过滤器。在拉模式中,源过滤器与一个分析过滤器相连。分析过滤器向源过滤器请求数据后,源过滤器才传送数据以响应请求。推模式使用的是IMemInputPin接口,拉模式使用IAsyncReader接口,推模式比拉模式要更常用。利用DirectShow开发简单媒体播放器
本节介绍基于DirectShow开发简单媒体播放器的关键步骤。
3.1 初始化DirectShow
由于DirectShow的组件都是以COM形式存在的,因此首先要调用CoInitializeEx函数来初始化COM库,嵌入所有的动态链接库和资源。否则,所有对QueryInterface的调用都会失败。
3.2 创建过滤器图表管理器接口
首先申明并初始化所需的接口:
// DirectShow interfaces
IGraphBuilder *pGB = NULL;
IMediaControl *pMC = NULL;
IMediaEventEx *pME = NULL;
IVideoWindow *pVW = NULL;
IBasicAudio *pBA = NULL;
IBasicVideo *pBV = NULL;
IMediaSeeking *pMS = NULL;
IMediaPosition *pMP = NULL;
IVideoFrameStep *pFS = NULL;
然后实例化一个过滤器图表管理器,并查询各接口:
// Get the interface for DirectShow's GraphBuilder
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,IID_IGraphBuilder,(void **)&pGB);
// QueryInterface for DirectShow interfaces
pGB->QueryInterface(IID_IMediaControl,(void **)&pMC);
pGB->QueryInterface(IID_IMediaEventEx,(void **)&pME);
pGB->QueryInterface(IID_IMediaSeeking,(void **)&pMS);
pGB->QueryInterface(IID_IMediaPosition,(void **)&pMP);
// Query for video interfaces, which may not be relevant for audio files
pGB->QueryInterface(IID_IVideoWindow,(void **)&pVW);
pGB->QueryInterface(IID_IBasicVideo,(void **)&pBV);
// Query for audio interfaces, which may not be relevant for video-only files
pGB->QueryInterface(IID_IBasicAudio,(void **)&pBA);
3.3 创建过滤器图表
应用DirectShow创建过滤器图表时,用户完全不需要操心系统使用了哪一类过滤器以及过滤器是怎样连接的。只要调用IGraphBuilder::RenderFile函数,就可以建成一个完整的过滤器图表。
// Have the graph builder construct its the appropriate graph automatically
pGB->RenderFile(wFile, NULL);
创建成功后,过滤器图表就可以用来播放多媒体文件了。DirectShow调用IMediaControl::Run函数来播放媒体文件。// Run the graph to play the media file
pMC->Run();
3.4 使用DirectShow的事件响应机制
DirectShow的事件响应机制是过滤器图表管理器与用户进行交互的接口,DirectShow处理的可以是一些事先可以预期的事件,比如数据流的结束;也可以是一些无法预期的错误。有的事件可以由过滤器图表管理器自己处理,但如果过滤器图表管理器自己无法处理这些事件,它就把事件的通知放在事件队列里。用户程序就可以通过IMediaEventEx接口得到事件,并对它做出相应的处理。
3.5 清除DirectShow
在程序结束时必须调用Release函数释放DirectShow的接口指针,并调用CoUninitialize函数来卸载COM库,释放所有的动态链接库和资源。结束语
应用DirectX的组件DirectShow进行多媒体应用程序的开发需了解多方面的知识,但在很多应用中利用DirectShow的特性可以减少工作量并能获得非常高的运行效率。在Visual C++ 6.0的开发环境中利用DirectShow开发的简单媒体播放器,具有随机播放、暂停和调整播放速率等功能,且可以播放多种媒体文件,播放效果非常流畅。因此,基于DirectShow开发多媒体应用程序的方法简单高效,是一种值得推荐的方法。