底本想用TvideoCap的办法操纵电脑摄像头,如以下是打开摄像头代码,能在XP和2003体系里能正常打开摄像头,但在win7里老是出各类题目,
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;
const WM_CAP_SEQUENCE_NOFILE =WM_CAP_START+ 63 ;
const WM_CAP_SET_OVERLAY =WM_CAP_START+ 51 ;
const WM_CAP_SET_PREVIEW =WM_CAP_START+ 50 ;
const WM_CAP_SET_CALLBACK_VIDEOSTREAM = WM_CAP_START +6;
const WM_CAP_SET_CALLBACK_ERROR=WM_CAP_START +2;
const WM_CAP_SET_CALLBACK_STATUSA= WM_CAP_START +3;
const WM_CAP_SET_CALLBACK_FRAME= WM_CAP_START +5;
const WM_CAP_SET_SCALE=WM_CAP_START+ 53 ;
const WM_CAP_SET_PREVIEWRATE=WM_CAP_START+ 52 ;
var
s:tpoint;
begin
s:=Pnl_View.ClientToScreen(point(0 ,0));
s:=ClientToScreen(point(Pnl_View.left,Pnl_View.Top));
hWndC := capCreateCaptureWindowA(""My Own Capture Window"",
WS_CHILD or WS_VISIBLE ,
Pnl_View.Left+3,Pnl_View.top+66,320,240,self.Handle,0);
if hWndC <> 0 then
SendMessage(hWndC, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0, 0);
SendMessage(hWndC, WM_CAP_SET_CALLBACK_ERROR, 0, 0);
SendMessage(hWndC, WM_CAP_SET_CALLBACK_STATUSA, 0, 0);
SendMessage(hWndC, WM_CAP_DRIVER_CONNECT, 0, 0);
SendMessage(hWndC, WM_CAP_SET_SCALE, 1, 0);
SendMessage(hWndC, WM_CAP_SET_PREVIEWRATE, 66, 0);
SendMessage(hWndC, WM_CAP_SET_OVERLAY, 1, 0);
SendMessage(hWndC, WM_CAP_SET_PREVIEW, 1, 0);
OpenVideo.Enabled :=false;
CloseVideo.Enabled:=true;
看到帖子上说"这种体式格式打开摄像头对摄像头的兼容性有请求,在XP试过很多,有的好用有的不好用,换过摄像头可能就好了。用DSPack控件,它是用Direct体式格式来做的,对摄像头的兼容性斗劲好。根蒂根基上手机、MP4、摄像头都可以用。"于是决意采取DSPack控件的体式格式。
DSPack,DSPack是一套应用微软Direct Show和DirectX技巧的类和组件,设计工作于DirectX 9,支撑体系Win9X, ME, 2000和Windows xp。可能看了下Direct Show和DirectX的百科,应当斗劲常用,而本身之前居然没有相干此方面的编程经验,其实忸捏,有机会有多补习才行。
DSPack 2.3.4 安装
源代码工程中有DSPack目次,本来进步前辈们用过它把码流转成Mpeg2的,可怜我今天才重视到这个控件啊:DSPack。
二. 安装
1 增长搜刮路径 (DSPackDir)\src\DirectX9 和 (DSPackDir)\src\DSPack
在delphi6中选择菜单【Tools】-【Enviroment Options】,在打开的窗口中选择Library页签,在Library Path一项中添加这两个目次\DSPACK234\src\Directx9 ;\DSPACK234\src\DSPack
(将其直接粘贴在原有内容的后面,或者点击Library Path后面的...按钮添加)
2 编译 (DSPackDir)\packages\DirectX9_D6.dpk
双击(DSPackDir)\packages\DirectX9_D6.dpk,在delphi6中会显示一个关于从头创建资料文件的对话框,点击OK就可以了。
在delphi6中的打开窗口中点击compile按钮,完成编译。
在delphi6中选择菜单【File】-【Close All】,在提问是否保存时,选择保存。
3 编译 (DSPackDir)\packages\DSPack_D6.dpk
双击(DSPackDir)\packages\DSPack_D6.dpk,在delphi6中会显示一个关于从头创建资料文件的对话框,点击OK就可以了。
在delphi6中的打开窗口中,点击compile按钮,完成编译。
在delphi6中选择菜单【File】-【Close All】,在提问是否保存时,选择保存。
4 编译并安装 (DSPackDir)\packages\DSPackDesign_D6.dpk
双击(DSPackDir)\packages\DSPackDesign_D6.dpk,在delphi6中会显示一个关于从头创建资料文件的对话框,点击OK就可以了。
在delphi6中的打开窗口中,点击compile按钮,完成编译。 点击Install按钮,完成安装。
在delphi6中选择菜单【File】-【Close All】,在提问是否保存时,选择保存。
至此,安装已完成,在delphi的控件面板上可以找到DSPack的页签了。
(安装过程中碰到了一个编译错误,说找不到Jedi.inc。这个文件在src\Directx9目次下,为什么不克不及搜刮。本来文档里写的路径是 (DSPackDir)\src\Directx9,而实际解紧缩出来的目次名是DirectX9,所以添加搜刮路径的时辰必然要重视这个题目。把x改成X后,编译就经由过程了)
DSPack各类应用办法
一:用DSPack播放视频
起首,要浏览一下(DSPackDir)\help目次下的help.chm文件,粗略地看了一下,内容太多看不出头绪。
还是先进修一下(DSPackDir)\Demos\D6-D7目次下的那些例子,边下手做边进修吧。
研究的第一个例子是PlayWin。研究了一下,首要应用TFilterGraph和TVideoWindow来完成。
TFilterGraph是DSPack中的核心类,其他类都要环绕着它,然则怎么懂得它还不清楚。TVideoWindow是个显示播放视频的控件。这两个类的关系如同是数据库控件中Dataset控件和DBGrid控件的关系一样。
看得差不久不多后,本身照葫芦画瓢仿造一个。
1. 新建一个应用,在界面上先放4、5个按钮。
2. 在控件面板上选择DSPack那页,把前两个控件(TFilterGraph和TVideoWindow)在窗口上各放一个。
3. 接洽关系
选中VideoWindow1控件,在属性窗口中将FilterGraph属性设置为FilterGraph1。
选中FilterGraph1控件,确认属性窗口中的Mode属性为gmNormal。
4.为Form1增长一个onCreate事务处理惩罚法度。
内容为:
if not FilterGraph1.Active then FilterGraph1.Active := true;
FilterGraph1.ClearGraph;
FilterGraph1.RenderFile(""E:\v\951.wmv""); // 简化一点,这里用你本地硬盘上的一个视频文件
5. 为Form1增长一个onCloseQuery事务处理惩罚法度。
内容为:
FilterGraph1.Active := false ;
6.把button1的Caption改为Start,并增长一个OnClick事务
内容为:
FilterGraph1.Play;
运行一下,就可以播放了。下面再增长几个功能按钮,如pause、stop。
7. 把button2的Caption改为Pause,并增长一个OnClick事务
内容为:
FilterGraph1.Pause;
?
8. 把button3的Caption改为Stop,并增长一个OnClick事务
内容为:
FilterGraph1.stop;
可以看出4-8步都是调用了TFilterGraph类的办法。
下面,再增长个全屏功能吧。
9. 把button4的Caption改为FullScreen,并增长一个OnClick事务
内容为:
VideoWindow1.FullScreen :=true ;
?
10. 为VideoWindow1增长一个OnClick事务
内容为:
if videowindow1.FullScreen then
videowindow1.FullScreen := false ; //退出全屏体式格式
一般的视频播放创窗口都有一个进度条,如今我们也来加一个。
A.1. 在DSPack控件面板上选择倒数第2个控件(TDSTrackBar),放到在窗口上。
A.2. 接洽关系
选中DSTrackBar1控件,在属性窗口中将FilterGraph属性设置为FilterGraph1。(这一步如同很熟悉哦)
从头运行法度,你就会看到一个进度条,并且可以或许应用这一进度条来调剂播放的进度。
?
二:应用DSPack打开摄像头
如今我们来看看(DSPackDir)\Demos\D6-D7目次下的PlayVideoCap,这是一个打开本机的视频输入设备的例子。
在这个例子中,又用到了一个新类:TFilter。
在我们依葫芦画瓢之前,你要装个摄像头或虚拟摄像头。虚拟摄像头可以用VCDCut、Softcam或Vcam等软件,也可以应用9158(http://www.9158.com/)或MVBox(http://www.mvbox.cn/)的虚拟视频。
先跟前次一样:
1. 新建一个应用,在界面上先放4、5个按钮,此次多放一个Listbox,这个列表框中将列出体系中安装的视频输入设备。
2. 在控件面板上选择DSPack那页,把前两个控件(TFilterGraph和TVideoWindow)在窗口上各放一个。
下面该有所不合了
3.在DSPack中选择TFilter控件,放到窗口上。
4.选中FilterGraph1控件,在属性窗口中将Mode属性设为gmCapture。
5. 接洽关系
选中VideoWindow1控件,将FilterGraph属性设置为FilterGraph1。
选中Filter1控件,将FilterGraph属性设置为FilterGraph1。
6. 在代码模式中,在Interface后的Uses中增长
DSUtil, DirectShow9,
在implementation前面的Var中增长
SysDev: TSysDevEnum;
?
7. 为Form1增长一个onCreate事务处理惩罚法度,读取体系中的视频输入设备。
内容为:
var
i: integer;
begin
// 读取体系中的视频输入设备
SysDev:= TSysDevEnum.Create(CLSID_VideoInputDeviceCategory);
if SysDev.CountFilters > 0 then
for i := 0 to SysDev.CountFilters - 1 do
begin
Listbox1.Items.Add(SysDev.Filters[i].FriendlyName)
end;
end;
7. 同前。为Form1增长一个onCloseQuery事务处理惩罚法度。
内容为:
SysDev.Free;
FilterGraph1.ClearGraph;
FilterGraph1.Active := false ;
8. 为Listbox1增长一个onClick事务处理惩罚法度
内容为:
FilterGraph1.ClearGraph;
FilterGraph1.Active := false;
//设filter为所选视频输入设备
Filter1.BaseFilter.Moniker := SysDev.GetMoniker(Listbox1.ItemIndex);
FilterGraph1.Active := true;
// 打开所选的视频输入设备
with FilterGraph1 as ICaptureGraphBuilder2 do
RenderStream(@PIN_CATEGORY_PREVIEW, nil, Filter1 as IBaseFilter, nil, VideoWindow1 as IbaseFilter);
// 显示出来
FilterGraph1.Play;
?
运行一下尝尝,看是否能看到本身的摄像头的内容。
?
几个按钮没用,比及下一个例子用吧。
三:DSPack抓帧
在例子PlayVideoCap中,还有抓帧和回放的功能。这用到第4个控件TSampleGrabber。
我们以前两个例子为根蒂根基,增长抓帧的功能。
打开前面的例子,然后:
1. 在DSPack中选择TSampleGrabber控件,放到窗口上。然后将其FilterGraph属性设置为FilterGraph1。
2. 在选择一个标准控件TImage(在Additional页签中),放在窗口上。
3.讲一个没用的按钮的Caption改为"Snapshot",在它的OnClick事务中写:
SampleGrabber1.GetBitmap(Image1.Picture.Bitmap);
前面两个例子都可以如许增长抓帧功能。不过,对于第二个例子(即操纵摄像头的例子),须要做额外的批改,就是:
将
RenderStream(@PIN_CATEGORY_PREVIEW, nil, Filter as IBaseFilter, nil, VideoWindow as IbaseFilter);
改为
RenderStream(@PIN_CATEGORY_PREVIEW, nil, Filter as IBaseFilter, SampleGrabber as IBaseFilter, VideoWindow as IbaseFilter);
如许就可以了
一开端老是不克不及保存录像文件,受到http://topic.csdn.net/t/20020519/11/734458.html 中这段话的启发,"在vfw中,在你显示设置属性(source,format,display)的时辰,是不克不及够捕获连接的.所以有你这个错误.并且在你在捕获了今后,你也没有办法调用这几个设置(会返回错误). 一般是如许的:你先设置完成,再捕获... 捕获时代不克不及够运行设置,必须先停止.因为dshow设置这些器材也是调用vfw的,所以有同样的题目."熟悉打听本来是我在捕获连接时代进行了设置,所以会返回错误,老是不克不及保存文件,批改为先停止设置完参数后再开端即可。
Delphi按钮控件最上层显示的办法:Button1.BringToFront;
摘录一些文章
DirectShow入门之Directshow的根蒂根基技能
作者:任雪梅揭晓于 2011-12-3 11:58:22 评论(0) 浏览(266)
? |
择要:本文首要讲述了Directshow开辟的一些根蒂根基概念和技能,首要内容如下: 1、视频播放(Video Rendering) 2、如何处理惩罚事务通知(Event Notification) 3、如何列举体系的设备和过虑器 4、如何列举Graph图中的对象(filter,pin) 5、Seeking Filter graph 6、如何设置Graph时钟(Setting Graph Clock) 视频播放(Video Rendering) dshow的视频提交过滤器可以在窗口模式和无窗口模式下工作。在窗口模式下,过滤器创建一个本身的窗口,在里面播放视频。在无窗口模式下,过滤器直接将视频在应用法度供给的窗口上显示,过滤器本身不创建窗口。 窗口模式 在窗口模式下,视频提交过滤器创建一个窗口,然后将视频祯帖到窗口上,你可以将这个窗口帖到你的应用法度的窗口。 Video Renderer只支撑窗口模式,VMR-7 and VMR-9缺省的是窗口模式,也支撑无窗口模式。 为了在你的应用法度中显示视频,你可以将视频窗口设置成应用法度的子窗口。你可以经由过程 IVideoWindow *pVidWin = NULL; pGraph->QueryInterface(IID_IVideoWindow, (void **)&g_pVidWin); pVidWin->put_Owner((OAHWND)hwnd); pVidWin->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS); RECT grc; GetClientRect(hwnd, &grc); pVidWin->SetWindowPosition(0, 0, grc.right, grc.bottom); 停止时必然要清理现场: pControl->Stop(); pVidWin->put_Visible(OAFALSE); pVidWin->put_Owner(NULL); 无窗口模式 当采取无窗口的模式时,就没有须要露出IVideoWindow接口了。 为了可以或许应用VMR的缺省行动,在构建Graph图之前必必要调剂VMR。 1、创建一个过虑器图表经管器 2、创建一个VMR,参加到graph中 3、调用VMR的IVMRFilterConfig::SetRenderingMode办法设置VMRMode_Windowless标记。 4、调用IVMRWindowlessControl::SetVideoClippingWindow 给视频指定一个显示窗口。 然后调用IGraphBuilder::RenderFile或者其他的办法来创建其他的Graph。 下面的代码显示了如何创建一个VMR,将其添加到Graph,如何设置无窗口模式 HRESULT InitWindowlessVMR( HWND hwndApp, // Window to hold the video. IGraphBuilder* pGraph, // Pointer to the Filter Graph Manager. IVMRWindowlessControl** ppWc, // Receives a pointer to the VMR. ) { if (!pGraph || !ppWc) return E_POINTER; IBaseFilter* pVmr = NULL; IVMRWindowlessControl* pWc = NULL; // Create the VMR. HRESULT hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVmr); if (FAILED(hr)) { return hr; } // Add the VMR to the filter graph. hr = pGraph->AddFilter(pVmr, L"Video Mixing Renderer"); if (FAILED(hr)) { pVmr->Release(); return hr; } // Set the rendering mode. IVMRFilterConfig* pConfig; hr = pVmr->QueryInterface(IID_IVMRFilterConfig, (void**)&pConfig); if (SUCCEEDED(hr)) { hr = pConfig->SetRenderingMode(VMRMode_Windowless); pConfig->Release(); } if (SUCCEEDED(hr)) { // Set the window. hr = pVmr->QueryInterface(IID_IVMRWindowlessControl, (void**)&pWc); if( SUCCEEDED(hr)) { hr = pWc->SetVideoClippingWindow(hwndApp); if (SUCCEEDED(hr)) { *ppWc = pWc; // Return this as an AddRef""d pointer. } else { // An error occurred, so release the interface. pWc->Release(); } } } pVmr->Release(); return hr; } 你也可以调用下面的函数 IVMRWindowlessControl *pWc = NULL; hr = InitWindowlessVMR(hwnd, pGraph, &g_pWc); if (SUCCEEDED(hr)) { // Build the graph. For example: pGraph->RenderFile(wszMyFileName, 0); // Release the VMR interface when you are done. pWc->Release(); } 下面看看如何设置视频的地位 有两个矩形须要推敲,一个是源矩形,一个是目标矩形。源矩形决意开端播放视频的地位,目标矩形决意在窗口显示视频的区域。VMR将源矩形遵守目标矩形的大小进行扩大。 IVMRWindowlessControl::SetVideoPosition可以设置两个矩形的大小,源矩形必须小于便是本地视频大小。你可以经由过程IVMRWindowlessControl::GetNativeVideoSize获取本地的视频区域大小。 // Find the native video size. long lWidth, lHeight; HRESULT hr = g_pWc->GetNativeVideoSize(&lWidth, &lHeight, NULL, NULL); if (SUCCEEDED(hr)) { RECT rcSrc, rcDest; // Set the source rectangle. SetRect(&rcSrc, 0, 0, lWidth/2, lHeight/2); // Get the window client area. GetClientRect(hwnd, &rcDest); // Set the destination rectangle. SetRect(&rcDest, 0, 0, rcDest.right/2, rcDest.bottom/2); // Set the video position. hr = g_pWc->SetVideoPosition(&rcSrc, &rcDest); } 处理惩罚窗口消息 因为VMR没有本身的窗口,所以当视频须要重画或者改变的时辰你要通知它。 1、当你接到一个WM_PAINT消息,你就要调用IVMRWindowlessControl::RepaintVideo来重画视频 2、当你接到一个WM_DISPLAYCHANGE消息,你就要调用IVMRWindowlessControl::DisplayModeChanged. 3、当你接到一个WM_SIZE消息时,从头策画视频的地位,然后调用SetVideoPostion。 下面的代码演示了WM_PAINT消息的处理惩罚: void OnPaint(HWND hwnd) { PAINTSTRUCT ps; HDC hdc; RECT rcClient; GetClientRect(hwnd, &rcClient); hdc = BeginPaint(hwnd, &ps); if (g_pWc != NULL) { // Find the region where the application can paint by subtracting // the video destination rectangle the client area. // (Assume that g_rcDest was calculated previously.) HRGN rgnClient = CreateRectRgnIndirect(&rcClient); HRGN rgnVideo = CreateRectRgnIndirect(&g_rcDest); CombineRgn(rgnClient, rgnClient, rgnVideo, RGN_DIFF); // Paint on window. HBRUSH hbr = GetSysColorBrush(COLOR_BTNFACE); FillRgn(hdc, rgnClient, hbr); // Clean up. DeleteObject(hbr); DeleteObject(rgnClient); DeleteObject(rgnVideo); // Request the VMR to paint the video. HRESULT hr = g_pWc->RepaintVideo(hwnd, hdc); } else // There is no video, so paint the whole client area. { FillRect(hdc, &rc2, (HBRUSH)(COLOR_BTNFACE + 1)); } EndPaint(hwnd, &ps); } 尽管我们要本身处理惩罚onpaint消息,然则已经很是简单了。 如何处理惩罚事务通知(Event Notification) 当一个Directshow的应用法度运行的时辰,在 filter Graph内部就会产生各类百般的事务,例如,一个filter也许产生数据流错误。Filter经由过程给graph mangaer发送事务通知来和graph通信,这个事务通知包含一个事务码和两个事务参数。事务码默示产闹事务的类型,两个参数用来传递信息。 Filter发送的这些事务,此中的一项目组可以被Manager直接处理惩罚,不通知应用法度,但有一项目组事务,Manager将事务放入到一个队列中,守候应用法度处理惩罚。这里我们首要评论辩论在应用法度中经常碰到的三种事务 EC_COMPLETE注解回放已经停止 EC_USERABORT注解用户中断了回放。用户封闭视频播放窗口时,视频Render会产生这个事务 EC_ERRORABORT注解呈现了一个错误。 应用法度可以通知filter graph manager,在某个指定的事务产生时,向指定的窗口产生一个指定的消息。如许应用法度就可以在消息轮回中对产生的事务产生反响。 起首定义消息: #define WM_GRAPHNOTIFY WM_APP + 1 然后向filter graph manager恳求IMediaEventEx接口,然后调用IMediaEventEx::SetNotifyWindow办法来设置消息通知窗口: IMediaEventEx *g_pEvent = NULL; g_pGraph->QueryInterface(IID_IMediaEventEx, (void **)&g_pEvent); g_pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0); 然后在WindowProc函数增长一个处理惩罚WM_GRAPHNOTIFY消息的函数: case WM_GRAPHNOTIFY: HandleGraphEvent(); break; HandleGraphEvent()函数具体定义如下 void HandleGraphEvent() { // Disregard if we don""t have an IMediaEventEx pointer. if (g_pEvent == NULL) { return; } // Get all the events long evCode; LONG_PTR param1, param2; HRESULT hr; while (SUCCEEDED(g_pEvent->GetEvent(&evCode, ?m1, ?m2,0))) { g_pEvent->FreeEventParams(evCode, param1, param2); switch (evCode) { case EC_COMPLETE: // Fall through. case EC_USERABORT: // Fall through. case EC_ERRORABORT: CleanUp(); PostQuitMessage(0); return; } } } 在开释IMediaEventEx指针前,要作废事务通知消息,代码如下: // Disable event notification before releasing the graph. g_pEvent->SetNotifyWindow(NULL, 0, 0); g_pEvent->Release(); g_pEvent = NULL; 如何列举体系的设备和过虑器 有时,应用法度须要查看体系中所有的filter。例如,视频应用法度须要列出体系中可用的捕获设备。因为dshow基于com布局的,你在设计法度的时辰是没法知道体系中正在应用的过滤器。Directshow供给了两种办法来列举体系中注册的过虑器。 1、体系设备列举器 体系设备列举器供给了一个很好的办法按照种类来列举体系中注册的过虑器。也许枚一种不合的硬件都邑有本身的过虑器,或许所有的硬件设备共用同一个filter。这个对于采取WDM驱动法度的硬件很有效。 体系设备列举器按照不合的种类创建了一个列举器,例如,音频紧缩,视频捕获。不合种类的列举器对于每一种设备返回一个自力的名称(moniker)。种类列举器主动将相干的即插即用,演播设备包含进来。 遵守下面的步调应用设备列举器: 1) 创建列举器组件,CLSID为CLSID_SystemDeviceEnum 2) 指定某一种类型设备,参数CLSID,经由过程ICreateDevEnum::CreateClassEnumerator获取某一种类的列举器,这个函数返回一个IEnumMoniker接口指针,若是该种类的空或者不存在,这个办法就返回S_FALSE。是以,当你调用这个函数时必然要搜检返回值是否为S_OK,而不要用SUCCEEDED宏。 3) 然后IEnumMoniker::Next列举每一个moniker。这个办法返回一个IMoniker接口指针。 4) 要想知道设备的名称,可以经由过程下面的函数IMoniker::BindToStorage 5) 然后哄骗IMoniker::BindToObject生成绑定道设备上的filter。调用IFilterGraph::AddFilter将filter添加到Graph图中。 // Create the System Device Enumerator. HRESULT hr; ICreateDevEnum *pSysDevEnum = NULL; hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum); if (FAILED(hr)) { return hr; } // Obtain a class enumerator for the video compressor category. IEnumMoniker *pEnumCat = NULL; hr=pSysDevEnum->CreateClassEnumerator(CLSID_VideoCompressorCategory, &pEnumCat,0); if (hr == S_OK) { // Enumerate the monikers. IMoniker *pMoniker = NULL; ULONG cFetched; while(pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) { IPropertyBag *pPropBag; hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);//知道设备的名称 if (SUCCEEDED(hr)) { // To retrieve the filter""s friendly name, do the following: VARIANT varName; VariantInit(&varName); hr = pPropBag->Read(L"FriendlyName", &varName, 0); if (SUCCEEDED(hr)) { // Display the name in your UI somehow. } VariantClear(&varName); // To create an instance of the filter, do the following: IBaseFilter *pFilter; hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,(void**)&pFilter); //生成一个filter绑定到设备上。 // Now add the filter to the graph. //Remember to release pFilter later. pPropBag->Release(); } pMoniker->Release(); } pEnumCat->Release(); } pSysDevEnum->Release(); 在上方我们用IMoniker::BindToObject生成绑定道设备上的filter,当然我们还可以用别的的一种办法来生成绑定到设备上的filter 哄骗IMoniker::GetDisplayName获得moniker的名字。然后你把moniker的名字做参数传递给IFilterGraph2::AddSourceFilterForMoniker,就可以创建一个绑定到设备的filter了。在上方我们是调用IMoniker::BindToObject生成filter的,还是上方的简单些。看看代码吧。 LPOLESTR strName = NULL; IBaseFilter pSrc = NULL; hr = pMoniker->GetDisplayName(NULL, NULL, &strName); if (SUCCEEDED(hr)) { // Query the Filter Graph Manager for IFilterGraph2. IFilterGraph2 *pFG2 = NULL; hr = pGraph->QueryInterface(IID_IFilterGraph2, (void**)&pFG2); if (SUCCEEDED(hr)) { hr = pFG2->AddSourceFilterForMoniker(pMoniker, 0, L"Source", &pSrc); pFG2->Release(); } CoTaskMemFree(strName); } // If successful, remember to release pSrc. 2、Filter Mapper 搜刮体系中的filter的另一个办法就是采取Filer Mapper。Filter mapper是一个com对象,它遵守必然的前提来搜刮体系的filer,它比体系设备列举器(System Device Enumerator)的效力要低一些。所以当你要列举某特定种类的filter时,你应当应用体系设备列举器,然则当你搜刮支撑某种媒体类型的filter时,同时也找不到清楚的filter,你应当应用filter mapper。 Filter Mapper 露出一个IFilerMapper2接口,要想搜刮一个接口,你可以调用该接口的IFilterMapper2::EnumMatchingFilters办法,这个办法须要传递一些参数来定义搜刮前提,同时该办法返回一个合适前提的filter的列举器,这个列举器供给一个IEnumMoniker接口,并且对于每个合适的filter都供给一个零丁的moniker。 下面的例子演示了,列举所有的支撑DV,并且至少有一个输出pin的filter,这个filter支撑任何媒体类型。 IFilterMapper2 *pMapper = NULL; IEnumMoniker *pEnum = NULL; hr =CoCreateInstance( CLSID_FilterMapper2,NULL, CLSCTX_INPROC, IID_IFilterMapper2, (void **) &pMapper); if (FAILED(hr)) { // Error handling omitted for clarity. } GUID arrayInTypes[2]; arrayInTypes[0] = MEDIATYPE_Video; arrayInTypes[1] = MEDIASUBTYPE_dvsd; hr = pMapper->EnumMatchingFilters( &pEnum, 0, // Reserved. TRUE, // Use exact match? MERIT_DO_NOT_USE+1, // Minimum merit. TRUE, // At least one input pin? 1, // Number of major type/subtype pairs for input. arrayInTypes, // Array of major type/subtype pairs for input. NULL, // Input medium. NULL, // Input pin category. FALSE, // Must be a renderer? TRUE, // At least one output pin? 0, // Number of major type/subtype pairs for output. NULL, // Array of major type/subtype pairs for output. NULL, // Output medium. NULL); // Output pin category. // Enumerate the monikers. IMoniker *pMoniker; ULONG cFetched; //下面就是列举filter了,就是体系列举设备filter while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) { IPropertyBag *pPropBag = NULL; hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag); if (SUCCEEDED(hr)) { // To retrieve the friendly name of the filter, do the following: VARIANT varName; VariantInit(&varName); hr = pPropBag->Read(L"FriendlyName", &varName, 0); if (SUCCEEDED(hr)) { // Display the name in your UI somehow. } VariantClear(&varName); // To create an instance of the filter, do the following: IBaseFilter *pFilter; hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pFilter); // Now add the filter to the graph. Remember to release pFilter later. // Clean up. pPropBag->Release(); } pMoniker->Release(); } // Clean up. pMapper->Release(); pEnum->Release(); // Create the System Device Enumerator. HRESULT hr; ICreateDevEnum *pSysDevEnum = NULL; hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum); if (FAILED(hr)) { return hr; } // Obtain a class enumerator for the video compressor category. IEnumMoniker *pEnumCat = NULL; hr=pSysDevEnum->CreateClassEnumerator(CLSID_VideoCompressorCategory, &pEnumCat,0); if (hr == S_OK) { // Enumerate the monikers. IMoniker *pMoniker = NULL; ULONG cFetched; while(pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) { IPropertyBag *pPropBag; hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);//知道设备的名称 if (SUCCEEDED(hr)) { // To retrieve the filter""s friendly name, do the following: VARIANT varName; VariantInit(&varName); hr = pPropBag->Read(L"FriendlyName", &varName, 0); if (SUCCEEDED(hr)) { // Display the name in your UI somehow. } VariantClear(&varName); // To create an instance of the filter, do the following: IBaseFilter *pFilter; hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,(void**)&pFilter); //生成一个filter绑定到设备上。 // Now add the filter to the graph. //Remember to release pFilter later. pPropBag->Release(); } pMoniker->Release(); } pEnumCat->Release(); } pSysDevEnum->Release(); 在上方我们用IMoniker::BindToObject生成绑定道设备上的filter,当然我们还可以用别的的一种办法来生成绑定到设备上的filter 哄骗IMoniker::GetDisplayName获得moniker的名字。然后你把moniker的名字做参数传递给IFilterGraph2::AddSourceFilterForMoniker,就可以创建一个绑定到设备的filter了。在上方我们是调用IMoniker::BindToObject生成filter的,还是上方的简单些。看看代码吧。 LPOLESTR strName = NULL; IBaseFilter pSrc = NULL; hr = pMoniker->GetDisplayName(NULL, NULL, &strName); if (SUCCEEDED(hr)) { // Query the Filter Graph Manager for IFilterGraph2. IFilterGraph2 *pFG2 = NULL; hr = pGraph->QueryInterface(IID_IFilterGraph2, (void**)&pFG2); if (SUCCEEDED(hr)) { hr = pFG2->AddSourceFilterForMoniker(pMoniker, 0, L"Source", &pSrc); pFG2->Release(); } CoTaskMemFree(strName); } // If successful, remember to release pSrc. 2、Filter Mapper 搜刮体系中的filter的另一个办法就是采取Filer Mapper。Filter mapper是一个com对象,它遵守必然的前提来搜刮体系的filer,它比体系设备列举器(System Device Enumerator)的效力要低一些。所以当你要列举某特定种类的filter时,你应当应用体系设备列举器,然则当你搜刮支撑某种媒体类型的filter时,同时也找不到清楚的filter,你应当应用filter mapper。 Filter Mapper 露出一个IFilerMapper2接口,要想搜刮一个接口,你可以调用该接口的IFilterMapper2::EnumMatchingFilters办法,这个办法须要传递一些参数来定义搜刮前提,同时该办法返回一个合适前提的filter的列举器,这个列举器供给一个IEnumMoniker接口,并且对于每个合适的filter都供给一个零丁的moniker。 下面的例子演示了,列举所有的支撑DV,并且至少有一个输出pin的filter,这个filter支撑任何媒体类型。 IFilterMapper2 *pMapper = NULL; IEnumMoniker *pEnum = NULL; hr =CoCreateInstance( CLSID_FilterMapper2,NULL, CLSCTX_INPROC, IID_IFilterMapper2, (void **) &pMapper); if (FAILED(hr)) { // Error handling omitted for clarity. } GUID arrayInTypes[2]; arrayInTypes[0] = MEDIATYPE_Video; arrayInTypes[1] = MEDIASUBTYPE_dvsd; hr = pMapper->EnumMatchingFilters( &pEnum, 0, // Reserved. TRUE, // Use exact match? MERIT_DO_NOT_USE+1, // Minimum merit. TRUE, // At least one input pin? 1, // Number of major type/subtype pairs for input. arrayInTypes, // Array of major type/subtype pairs for input. NULL, // Input medium. NULL, // Input pin category. FALSE, // Must be a renderer? TRUE, // At least one output pin? 0, // Number of major type/subtype pairs for output. NULL, // Array of major type/subtype pairs for output. NULL, // Output medium. NULL); // Output pin category. // Enumerate the monikers. IMoniker *pMoniker; ULONG cFetched; //下面就是列举filter了,就是体系列举设备filter while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) { IPropertyBag *pPropBag = NULL; hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag); if (SUCCEEDED(hr)) { // To retrieve the friendly name of the filter, do the following: VARIANT varName; VariantInit(&varName); hr = pPropBag->Read(L"FriendlyName", &varName, 0); if (SUCCEEDED(hr)) { // Display the name in your UI somehow. } VariantClear(&varName); // To create an instance of the filter, do the following: IBaseFilter *pFilter; hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pFilter); // Now add the filter to the graph. Remember to release pFilter later. // Clean up. pPropBag->Release(); } pMoniker->Release(); } // Clean up. pMapper->Release(); pEnum->Release(); 3、查找媒体类型 每个pin都支撑一个IPin::EnumMediaTypes办法,可以来列举pin支撑的媒体类型。它返回一个IEnumMediaTypes接口,这个接口的办法IEnumMediaTypes::Next返回一个指向AM_MEDIA_TYPE类型的指针。可以参考上方的代码来遍历pin所支撑的媒体类型。 Seeking Filter graph 首要讲述了如安在一个媒体数据流中定位,随便率性指定开端播放的地位。 1、搜检是否支撑seek Directshow经由过程IMediaSeeking接口支撑seeking。Filter graph经管器支撑这个接口,然则实际seeking的功能是有graph中的filter来实现的。 有一些数据是不克不及seek的,例如,你不成能seek从拍照机中采集的活动的视频流。若是一个数据流可以被seek,然则,seek的类型还分以下几种类型,可以给你的数据流选择一种 1) 定位到数据流中的一个绝对地位 2) 返回数据流的连气儿时候 3) 返回数据流中的当前播放地位 4) 回放。 IMediaSeeking接口定义了一套标记AM_SEEKING_SEEKING_CAPABILITIES,用来描述可能支撑的seek功能。 typedef enum AM_SEEKING_SeekingCapabilities { AM_SEEKING_CanSeekAbsolute = 0 x1, AM_SEEKING_CanSeekForwards = 0 x2, AM_SEEKING_CanSeekBackwards = 0 x4, AM_SEEKING_CanGetCurrentPos = 0 x8, AM_SEEKING_CanGetStopPos = 0 x10, AM_SEEKING_CanGetDuration = 0 x20, AM_SEEKING_CanPlayBackwards = 0 x40, AM_SEEKING_CanDoSegments = 0 x80, AM_SEEKING_Source = 0 x100 } AM_SEEKING_SEEKING_CAPABILITIES; 可以经由过程IMediaSeeking::GetCapabilities查看数据流支撑的seek才能都有哪些。应用法度可以采取 &测试每一项。例如,下面的代码搜检了graph是否可以seek 一个随便率性的地位 DWORD dwCap = 0; HRESULT hr = pSeek->GetCapabilities(&dwCap); if (AM_SEEKING_CanSeekAbsolute & dwCap) { // Graph can seek to absolute positions. } 2、Setting and Retrieving the Position Filter graph包含两个地位,当前地位和停止地位,定义如下: 1) 当前地位,当一个graph正处于运行的时辰,当前地位就是当前的回放地位,相对于开端的地位而言。若是graph处于停止或者暂停状况的时辰,当前地位就是数据流下次开端播放的地位点。 2) 停止地位,停止地位就是数据流将要停止的地位,当一个graph达到一个停止地位时,将没稀有据流,filter graph经管器将会发送一个EC_COMPLETE事务。 可以经由过程IMediaSeeking::GetPositions办法可以获取这些地位值。返回值都是相对于原始的开端地位。 经由过程IMediaSeeking::SetPositions办法可以seek一个新的地位,见下面: #define ONE_SECOND 10000000 REFERENCE_TIME rtNow = 2 * ONE_SECOND, rtStop = 5 * ONE_SECOND; hr = pSeek->SetPositions( &rtNow, AM_SEEKING_AbsolutePositioning, &rtStop, AM_SEEKING_AbsolutePositioning ); 注:1秒是10,000,000参考时候单位。为了便利,这个例子将这个值定义为ONE_SECOND,若是你应用的dshow的基类,常量CUITS的值和这个值相等。 RtNow参数指定新的当前地位,第二个参数用来标示如何来定位rtNow参数。在这个例子中,AM_SEEKING_AbsolutePositioning 标记默示rtNow指定的地位是一个绝对的地位。RtStop参数指定了停止时候,最后一个参数也指定了绝对地位。 若是想指定一个相对的地位,可以指定一个AM_SEEKING_RelativePositioning参数,为了设置这个地位不克不及改变,可以指定一个AM_SEEKING_NoPositioning参数。此时,参考时候应当设置为NULL。下面的例子将地位向前seek 10秒,然后停止地位不变。 REFERENCE_TIME rtNow = 10 * ONE_SECOND; hr = pSeek->SetPositions( &rtNow, AM_SEEKING_RelativePositioning, NULL, AM_SEEKING_NoPositioning ); 3、Setting the Playback Rate 调用IMediaSeeking::SetRate办法可以改变回放的速度。经由过程将新的速度设置成本来速度的倍数就可以设置新的速度,例如,pSeek->SetRate(2.0),将新的速度设置为本来速度的两倍。比率大于1申明回放的速度比本来的大,若是介于0和1之间,就比正常的速度慢。 若是我们不推敲回放速度,当前地位和停止地位相对于开端地位都是不变的。举个例子,若是我们有一个可以播放20秒的文件,将当前时候设置为10秒就会将播放地位设置到中心,若是播放的速度进步要本来的2倍,若是停止时候是20秒,你将播放地位设置到本来的10秒处,成果如今只能播放5秒了,因为速度进步了两倍。 4、Time Formats For Seek Commands IMediaSeeking接口中的很多函数的参数都请求指定一个地位值,比如当前地位,或者停止地位,缺省的景象下这些参数是以of 100 nanoseconds为时候单位的,称为参考时候,任何支撑seek的filter必须支撑按参考时候来进行定位。一些filter也支撑采取其他时候单位进行定位。例如,按照指定的桢的数量,或在数据流偏移的字节数进行定位。 这种用来定位的时候单位称为时候格局,采取一个GUID来标示。Directshow定义了一系列的时候格局,具体地可以参考SDK。第三方也可以定义本身的时候格局。 为了断定graph中的当前的filter是否支撑特定的时候格局,可以调用 IMediaSeeking::IsFormatSupported办法,若是filter支撑该时候格局,该函数返回ok不然返回false或者一个错误码。若是filter支撑某种指定的时候格局,可以调用IMediaSeeking::SetTimeFormat办法切换到其他的时候格局。若是SetTimeFormat办法成功,下面的seek号令就要应用新的时候格局。 下面的代码搜检graph是否支撑用桢的数量进行定位,若是支撑,定位到第20桢。 hr = pSeek->IsFormatSupported(&TIME_FORMAT_FRAME); if (hr == S_OK) { hr = pSeek->SetTimeFormat(&TIME_FORMAT_FRAME); if (SUCCEEDED(hr)) { // Seek to frame number 20. LONGLONG rtNow = 20; hr = pSeek->SetPositions(&rtNow, AM_SEEKING_AbsolutePositioning,0, AM_SEEKING_NoPositioning); } } 6、如何设置Graph时钟(Setting Graph Clock) 当你构建了一个graph后,graph经管器会主动地给你的graph选择一个参考时钟的。Graph中的所有filter都同步于时钟。特此外,Renderer filter还要按照参考时钟的时候来决意每一个sample的Presentation 时候。 凡是的景象下,应用法度是没有须要从头设置graph经管器选择好的参考时钟的。然则,若是你想批改参考时钟,你可以经由过程graph经管器供给的IMediaFilter::SetSyncSource办法来从头设置参考时钟。这个办法的参数是一个时钟的IReferenceClock接口指针。可以在graph停止的时辰调用这个函数,下面的例子演示了如何指定一个时钟 IGraphBuilder *pGraph = 0; IReferenceClock *pClock = 0; CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph); // Build the graph. pGraph->RenderFile(L"C:\\Example.avi", 0); // Create your clock. hr = CreateMyPrivateClock(&pClock); if (SUCCEEDED(hr)) { // Set the graph clock. IMediaFilter *pMediaFilter = 0; pGraph->QueryInterface(IID_IMediaFilter, (void**)&pMediaFilter); pMediaFilter->SetSyncSource(pClock); pClock->Release(); pMediaFilter->Release(); } 这段代码假定CreateMyPrivateClock 是应用法度定义的一个函数,用来创建一个时钟,然后返回一个IReferenceClock接口。 你也可以在graph没有设置时钟的景象下运行graph。当SetSyncSource 函数的参数为NULL的时辰就给graph设置了一个空的参考时钟。若是graph没有时钟,graph将运行的快很多。因为renderer 不消再遵守sample的presentation 时候了,只要sample达到了renderer filter,就可以立即被提交。所以,当你想处理惩罚数据尽可能快,而不是还要推敲预览的现及时候,你就可以给graph设置一个空的时候。 |
?
DSPack各类应用办法http://hi.baidu.com/zhangyihappy/blog/item/2038b28aa30ddbd8fd1f1085.html
?
?
一:用DSPack播放视频 起首,要浏览一下(DSPackDir)\help目次下的help.chm文件,粗略地看了一下,内容太多看不出头绪。 还是先进修一下(DSPackDir)\Demos\D6-D7目次下的那些例子,边下手做边进修吧。 研究的第一个例子是PlayWin。研究了一下,首要应用TFilterGraph和TVideoWindow来完成。 TFilterGraph是DSPack中的核心类,其他类都要环绕着它,然则怎么懂得它还不清楚。TVideoWindow是个显示播放视频的控件。这两个类的关系如同是数据库控件中Dataset控件和DBGrid控件的关系一样。 看得差不久不多后,本身照葫芦画瓢仿造一个。 1. 新建一个应用,在界面上先放4、5个按钮。 2. 在控件面板上选择DSPack那页,把前两个控件(TFilterGraph和TVideoWindow)在窗口上各放一个。 3. 接洽关系 选中VideoWindow1控件,在属性窗口中将FilterGraph属性设置为FilterGraph1。 选中FilterGraph1控件,确认属性窗口中的Mode属性为gmNormal。 4.为Form1增长一个onCreate事务处理惩罚法度。 内容为: if not FilterGraph1.Active then FilterGraph1.Active := true; FilterGraph1.ClearGraph; FilterGraph1.RenderFile(""E:\v\951.wmv""); // 简化一点,这里用你本地硬盘上的一个视频文件 5. 为Form1增长一个onCloseQuery事务处理惩罚法度。 内容为: FilterGraph1.Active := false ; 6.把button1的Caption改为Start,并增长一个OnClick事务 内容为: FilterGraph1.Play; 运行一下,就可以播放了。下面再增长几个功能按钮,如pause、stop。 7. 把button2的Caption改为Pause,并增长一个OnClick事务 内容为: FilterGraph1.Pause; 8. 把button3的Caption改为Stop,并增长一个OnClick事务 内容为: FilterGraph1.stop; 可以看出4-8步都是调用了TFilterGraph类的办法。 下面,再增长个全屏功能吧。 9. 把button4的Caption改为FullScreen,并增长一个OnClick事务 内容为: VideoWindow1.FullScreen :=true ; 10. 为VideoWindow1增长一个OnClick事务 内容为: if videowindow1.FullScreen then videowindow1.FullScreen := false ; //退出全屏体式格式 一般的视频播放创窗口都有一个进度条,如今我们也来加一个。 A.1. 在DSPack控件面板上选择倒数第2个控件(TDSTrackBar),放到在窗口上。 A.2. 接洽关系 选中DSTrackBar1控件,在属性窗口中将FilterGraph属性设置为FilterGraph1。(这一步如同很熟悉哦) 从头运行法度,你就会看到一个进度条,并且可以或许应用这一进度条来调剂播放的进度。 二:应用DSPack打开摄像头 如今我们来看看(DSPackDir)\Demos\D6-D7目次下的PlayVideoCap,这是一个打开本机的视频输入设备的例子。 在这个例子中,又用到了一个新类:TFilter。 在我们依葫芦画瓢之前,你要装个摄像头或虚拟摄像头。虚拟摄像头可以用VCDCut、Softcam或Vcam等软件,也可以应用9158(http://www.9158.com/)或MVBox(http://www.mvbox.cn/)的虚拟视频。 先跟前次一样: 1. 新建一个应用,在界面上先放4、5个按钮,此次多放一个Listbox,这个列表框中将列出体系中安装的视频输入设备。 2. 在控件面板上选择DSPack那页,把前两个控件(TFilterGraph和TVideoWindow)在窗口上各放一个。 下面该有所不合了 3.在DSPack中选择TFilter控件,放到窗口上。 4.选中FilterGraph1控件,在属性窗口中将Mode属性设为gmCapture。 5. 接洽关系 选中VideoWindow1控件,将FilterGraph属性设置为FilterGraph1。 选中Filter1控件,将FilterGraph属性设置为FilterGraph1。 6. 在代码模式中,在Interface后的Uses中增长 DSUtil, DirectShow9, 在implementation前面的Var中增长 SysDev: TSysDevEnum; 7. 为Form1增长一个onCreate事务处理惩罚法度,读取体系中的视频输入设备。 内容为: var i: integer; begin // 读取体系中的视频输入设备 SysDev:= TSysDevEnum.Create(CLSID_VideoInputDeviceCategory); if SysDev.CountFilters > 0 then for i := 0 to SysDev.CountFilters - 1 do begin Listbox1.Items.Add(SysDev.Filters[i].FriendlyName) end; end; 7. 同前。为Form1增长一个onCloseQuery事务处理惩罚法度。 内容为: SysDev.Free; FilterGraph1.ClearGraph; FilterGraph1.Active := false ; 8. 为Listbox1增长一个onClick事务处理惩罚法度 内容为: FilterGraph1.ClearGraph; FilterGraph1.Active := false; //设filter为所选视频输入设备 Filter1.BaseFilter.Moniker := SysDev.GetMoniker(Listbox1.ItemIndex); FilterGraph1.Active := true; // 打开所选的视频输入设备 with FilterGraph1 as ICaptureGraphBuilder2 do RenderStream(@PIN_CATEGORY_PREVIEW, nil, Filter1 as IBaseFilter, nil, VideoWindow1 as IbaseFilter); // 显示出来 FilterGraph1.Play; 运行一下尝尝,看是否能看到本身的摄像头的内容。 几个按钮没用,比及下一个例子用吧。 三:DSPack抓帧 在例子PlayVideoCap中,还有抓帧和回放的功能。这用到第4个控件TSampleGrabber。 我们以前两个例子为根蒂根基,增长抓帧的功能。 打开前面的例子,然后: 1. 在DSPack中选择TSampleGrabber控件,放到窗口上。然后将其FilterGraph属性设置为FilterGraph1。 2. 在选择一个标准控件TImage(在Additional页签中),放在窗口上。 3.讲一个没用的按钮的Caption改为"Snapshot",在它的OnClick事务中写: SampleGrabber1.GetBitmap(Image1.Picture.Bitmap); 前面两个例子都可以如许增长抓帧功能。不过,对于第二个例子(即操纵摄像头的例子),须要做额外的批改,就是: 将 RenderStream(@PIN_CATEGORY_PREVIEW, nil, Filter as IBaseFilter, nil, VideoWindow as IbaseFilter); 改为 RenderStream(@PIN_CATEGORY_PREVIEW, nil, Filter as IBaseFilter, SampleGrabber as IBaseFilter, VideoWindow as IbaseFilter); 如许就可以了 |
|
|