本篇文档我们将要讲述如何给一个filter创建一个属性页,通过CBasePropertyPage基类。这篇文档的实例代码演 示了创建属性页的步骤,这里我们假设我们要创建属性页的视频filter支持饱和度属性页,这个属性页有一个滑动条,用户可以通过这个滑动条来控制饱和 度。
第一步,设置属性的机理
Filter必须支持一种和属性页沟通的方式,通过属性页可以设置或者获取filter的属性,下面是可能的三种方式
1暴露一个接口
2通过IDispatch支持自动化属性
3暴露IPropertyBag 接口,并定义一系列的属性
下面的例子利用了一个普通的COM接口,叫做ISaturaton,这并不是一个真正的com接口,只是我们用来在这里举例的,你也可以自己定义任何的com对象。
首先我们在一个头文件中声明接口的ID和定义。

// Always create new GUIDs! Never copy a GUID from an example.
DEFINE_GUID(IID_ISaturation, 0x19412d6e, 0x6401, 
0x475c, 0xb0, 0x48, 0x7a, 0xd2, 0x96, 0xe1, 0x6a, 0x19); 
interface ISaturation : public IUnknown
{
STDMETHOD(GetSaturation)(long *plSat) = 0;
STDMETHOD(SetSaturation)(long lSat) = 0;
};

你也可以用IDL定义接口,并用MIDL编译器创建头文件,然后在Filter上实现这个接口,这个例子采用“Get”,“Set”方法来设置饱和度的值,注意,修改这个m_lSaturation的值的时候一定要进行保护

class CGrayFilter : public ISaturation, 
{
private:
CCritSec m_csShared; // Protects shared data.
long m_lSaturation; // Saturation level.
public:
STDMETHODIMP GetSaturation(long *plSat)
{
if (!plSat) return E_POINTER;
CAutoLock lock(&m_csShared);
*plSat = m_lSaturation;
return S_OK;
}
STDMETHODIMP SetSaturation(long lSat)
{
CAutoLock lock(&m_csShared);
if (lSat < SATURATION_MIN || lSat > SATURATION_MAX)
{
return E_INVALIDARG;
}
m_lSaturation = lSat;
return S_OK;
}
};

当然你实现接口的一些细节可能和上面的代码不一致。反正你自己实现就是了

第二步,实现ISpecifyPropertyPages接口

做完了上一步,下面就要在你个filter中实现ISpecifyPropertyPages接口,这个接口只有一个方法,GetPages,这个方法返回filter所支持的所有的属性页的CLSID。在这个例子里,Filter只支持一个属性页,
首先产生一个CLSID,并在头文件声明

// Always create new GUIDs! Never copy a GUID from an example.
DEFINE_GUID(CLSID_SaturationProp, 0xa9bd4eb, 0xded5, 
0x4df0, 0xba, 0xf6, 0x2c, 0xea, 0x23, 0xf5, 0x72, 0x61);

然后要实现ISpecifyPropertyPages接口的GetPages方法:

class CGrayFilter : public ISaturation,
public ISpecifyPropertyPages, 

{
public:
STDMETHODIMP GetPages(CAUUID *pPages)
{
if (pPages == NULL) return E_POINTER;
pPages->cElems = 1;
pPages->pElems = (GUID*)CoTaskMemAlloc(sizeof(GUID));
if (pPages->pElems == NULL) 
{
return E_OUTOFMEMORY;
}
pPages->pElems[0] = CLSID_SaturationProp;
return S_OK;
}
}; 

}

第三步,支持QueryInterface
为了暴露Filter的接口,照着下面的步骤作哦

1 在你的filter中包含DECLARE_IUNKNOWN宏的声明:
Public:
DECLARE_IUNKNOWN;
2 重载CUnknown::NonDelegatingQueryInterface 方法来检查两个接口的IIDs。
STDMETHODIMP CGrayFilter::NonDelegatingQueryInterface(REFIID riid, 
void **ppv)
{
if (riid == IID_ISpecifyPropertyPages)
{
return GetInterface(static_cast<ISpecifyPropertyPages*>(this),
ppv);
}
if (riid == IID_ISaturation)
{
return GetInterface(static_cast<IYuvGray*>(this), ppv);
}
return CBaseFilter::NonDelegatingQueryInterface(riid, ppv);
}

第四步,创建属性页
到这一步,filter已经支持一个属性页的所需要的东西了,下一步就是要实现属性页本身了。
首先创建一个对话框的资源,然后以这个对话的资源声明一个类,要从CBasePropertyPage. 派生,

图1
下面的代码显示了部分的声明,包含了我们在后面将要用到的部分变量。

class CGrayProp : public CBasePropertyPage
{
private:
ISaturation *m_pGray; // Pointer to the filter's custom interface.
long m_lVal // Store the old value, so we can revert.
long m_lNewVal; // New value.
public:

};

看看构造函数吧

CGrayProp::CGrayProp(IUnknown *pUnk) : 
CBasePropertyPage(NAME("GrayProp"), pUnk, IDD_PROPPAGE, IDS_PROPPAGE_TITLE),
m_pGray(0)
{ }

下面,你还要记得重载CBasePropertyPage 的几个方法哦
OnConnect,当属性页创建的时候,会调用这个方法,通过这个方法将IUnknown指针付给Filter。
OnActivate 当对话框创建的时候被调用
OnReceiveMessage 当对话框接收到窗口消息时被调用
OnApplyChanges当用户单击OK或者Apply 按钮来确认对属性进行更新时,调用
OnDisconnect 当用户取消Property sheet时调用

第五步,保存filter的一个指针
通过重载CBasePropertyPage::OnConnect方法将一个指针保存到filter,下面的例子演示了如何通过方法传递过来的参数查询filter支持的接口

HRESULT CGrayProp::OnConnect(IUnknown *pUnk)
{
if (pUnk == NULL)
{
return E_POINTER;
}
ASSERT(m_pGray == NULL);
return pUnk->QueryInterface(IID_ISaturation, 
reinterpret_cast<void**>(&m_pGray));
}

第六步,初始化对话框
通过重载CBasePropertyPage::OnActivate方法来初始化一个对话框,在这个例子里,属性页使用了滑动条,所以,在初始化的第一步就是要初始化控件动态库,然后再初始化slider。

HRESULT CGrayProp::OnActivate(void)
{
INITCOMMONCONTROLSEX icc;
icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
icc.dwICC = ICC_BAR_CLASSES;
if (InitCommonControlsEx(&icc) == FALSE)
{
return E_FAIL;
} 
ASSERT(m_pGray != NULL);
HRESULT hr = m_pGray->GetSaturation(&m_lVal);
if (SUCCEEDED(hr))
{
SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETRANGE, 0,
MAKELONG(SATURATION_MIN, SATURATION_MAX)); 
SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETTICFREQ, 
(SATURATION_MAX - SATURATION_MIN) / 10, 0); 
SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETPOS, 1, m_lVal);
}
return hr;
}

第七步,处理窗口消息
重载CBasePropertyPage::OnReceiveMessage方法来处理用户的输入等消息。如果你不想处理消息,你只需简单调用父类的OnReceiveMessage 即可。
无论何时用户改变了属性,都会做下面的事情
1 将属性页的m_bDirty设置为TRUE;
2调用属性框的IPropertyPageSite::OnStatusChange方法,并传递一个PROPPAGESTATUS_DIRTY,这个标志用来通知property frame应该将Apply按钮可用,
CBasePropertyPage::m_pPageSite变量保存着一个IPropertyPageSite接口
为了简化步骤,你可以在你的属性页中添加下面的代码

private:
void SetDirty()
{
m_bDirty = TRUE;
if (m_pPageSite)
{
m_pPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY);
}
}

当用户改变了属性的时候,在OnReceiveMessage方法中调用上面的函数。

BOOL CGrayProp::OnReceiveMessage(HWND hwnd,
UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_COMMAND:
if (LOWORD(wParam) == IDC_DEFAULT)
{
// User clicked the 'Revert to Default' button.
m_lNewVal = SATURATION_DEFAULT;
m_pGray->SetSaturation(m_lNewVal); 
// Update the slider control.
SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETPOS, 1,
m_lNewVal);
SetDirty();
return (LRESULT) 1;
}
break; 
case WM_HSCROLL:
{
// User moved the slider.
switch(LOWORD(wParam))
{
case TB_PAGEDOWN:
case SB_THUMBTRACK:
case TB_PAGEUP:
m_lNewVal = SendDlgItemMessage(m_Dlg, IDC_SLIDER1,
TBM_GETPOS, 0, 0);
m_pGray->SetSaturation(m_lNewVal);
SetDirty();
}
return (LRESULT) 1;
}
} // Switch.

// Let the parent class handle the message.
return CBasePropertyPage::OnReceiveMessage(hwnd,uMsg,wParam,lParam);
}

第八步,处理属性的改变
重载CBasePropertyPage::OnApplyChanges方法来提交属性页的改变,如果用户单击了确定,或者应用按钮,OnApplyChanges方法都会调用到

HRESULT CGrayProp::OnApplyChanges(void)
{
m_lVal = m_lNewVal;
return S_OK;
}

第九步,断开属性页连接
重载 CBasePropertyPage::OnDisconnect方法来释放你在OnConnect方法中请求的所有的接口,如果用户没有更新属性,而是 单击了取消按钮,你还要将属性的原始值保存下来。当用户单击取消按钮,但是没有相应的响应这个消息的方法,所以,你要检查用户是否调用了 OnApplyChanges方法,看看例子也好:

HRESULT CGrayProp::OnDisconnect(void)
{
if (m_pGray)
{
// If the user clicked OK, m_lVal holds the new value.
// Otherwise, if the user clicked Cancel, m_lVal is the old value.
m_pGray->SetSaturation(m_lVal); 
m_pGray->Release();
m_pGray = NULL;
}
return S_OK;
}

第十步,支持com的注册
最后一步就是要支持com的注册,因此 属性框才能够创建你属性页的实例,首先在全局数组g_Templates添加一个类厂模板的说明。这个全局的数组是你的DLL中创建的所有的com对象都要用到的。

const AMOVIESETUP_FILTER FilterSetupData = 
{ 

}; 
CFactoryTemplate g_Templates[] =
{ 
// This entry is for the filter.
{
wszName,
&CLSID_GrayFilter,
CGrayFilter::CreateInstance,
NULL,
&FilterSetupData 
},
// This entry is for the property page.
{ 
L"Saturation Props",
&CLSID_SaturationProp,
CGrayProp::CreateInstance, 
NULL, NULL
}
};

如果你用下面的方式声明全局数组,数组的大小就会自动地得到修改
int g_cTemplates = sizeof(g_Templates)/sizeof(g_Templates[0]);
同时,还要在属性页类中添加一个CreateInstance方法

static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr) 
{
CGrayProp *pNewObject = new CGrayProp(pUnk);
if (pNewObject == NULL) 
{
*pHr = E_OUTOFMEMORY;
}
return pNewObject;
} 

如果想测试属性页,可以注册DLL,然后将filter加载到GraphEdit,鼠标右击来查看filter的属性。 

采集参数的设置

采集前需要对要采集的视频格式、图像质量进行设置,如视频的分辨率、帧率和数据格式,图像的亮度、色度和饱和度参数设置等。下面分别对这两种设置进行讲述。

1.视频格式设置

对采集设备的参数进行配置,首先确定设备的类型:VFW、WDM驱动模型。目前流行的采集设备大部分采用WDM模式,采集设备在 DirectShow中被当做一个滤波器组件,所以配置采集设备就是对该滤波器的引脚进行设置。在确保滤波器链表完全建立前,可以使用如下的过程配置视频 格式。

void CCaptureClass::ConfigCameraPin(HWND hwndParent)
{
HRESULT hr;       //返回值
IAMStreamConfig *pSC;    //流配置接口
ISpecifyPropertyPages *pSpec;  //属性页接口
//只有停止后,才能进行引脚属性的设置
m_pMC->Stop();
//首先查询捕获CAPTURE、视频Video接口
hr = m_pCapture->FindInterface(&PIN_CATEGORY_CAPTURE,
&MEDIATYPE_Video, m_pBF,
IID_IAMStreamConfig, (void **)&pSC);
CAUUID cauuid;       //所有属性页结构体
hr = pSC->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
if(hr == S_OK)
{
hr = pSpec->GetPages(&cauuid);  //获取所有属性页
//显示属性页
hr = OleCreatePropertyFrame(hwndParent, 30, 30, NULL, 1, 
(IUnknown **)&pSC, cauuid.cElems,
(GUID *)cauuid.pElems, 0, 0, NULL);
//释放内存、资源
CoTaskMemFree(cauuid.pElems);
pSpec->Release();
pSC->Release();
}
//回复运行
m_pMC->Run();
}

上述代码首先以视频捕获方式获取流配置接口,在该接口下查询属性页接口,然后获取所有属性页,接着显示获取的属性页,最后释放资源。在显示了属性页后用户就可以对其属性页的对话框进行设置了,运行后的结果如图9-1所示。

 
(点击查看大图)图9-1  视频格式属性设置

2.图像参数设置

图像参数设置类似于视频格式配置,根据设备的驱动类型VFW、WDM使用不同的技术配置设备的参数,这里我们同样假定采集设备的类型为WDM,配置过程如下。

void CCaptureClass::ConfigCameraFilter(HWND hwndParent)
{
HRESULT hr=0;
ISpecifyPropertyPages *pProp;
hr = m_pBF->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pProp);
if (SUCCEEDED(hr)) 
{
//获取滤波器名称和IUnknown接口指针
FILTER_INFO FilterInfo;
hr = m_pBF->QueryFilterInfo(&FilterInfo); 
IUnknown *pFilterUnk;
m_pBF->QueryInterface(IID_IUnknown, (void **)&pFilterUnk);
//显示该页
CAUUID caGUID;
pProp->GetPages(&caGUID);
OleCreatePropertyFrame(
hwndParent,    //父窗口
0, 0,                 //Reserved
FilterInfo.achName, //对话框标题
1,                    //该滤波器的目标数目
&pFilterUnk,       //目标指针数组
caGUID.cElems,      //属性页数目
caGUID.pElems,      //属性页的CLSID数组
0,                     //本地标识
0, NULL             //Reserved
);
//释放内存、资源
CoTaskMemFree(caGUID.pElems);
pFilterUnk->Release();
FilterInfo.pGraph->Release(); 
pProp->Release();
}
m_pMC->Run();
}

捕获滤波器属性是对图像参数进行配置和修改,包括图像参数、白平衡和模式控制。这些参数一旦更改,预览的视频图像马上起作用。当调整到合适的情况下,单击"确定"按钮,关闭该对话框,运行后的结果如图9-2所示。

 
(点击查看大图)图9-2  运行后的结果

如何创建Filter的属性页的更多相关文章

  1. MFC属性页对话框

    属性页对话框 分类 分页和引导 类 CPropertyPage-父亲CDialog类别,所谓的属性页或网页对话框. CPropertySheet-父类是CWnd,称为属性表单. 一个完整的属性页对话框 ...

  2. directshow filter中添加属性页

    directShow 属性页的制作,为CBall filter加了一个属性页 具体为分以下步骤: 1.在要显示属性的类中继承现ISpecifyPropertyPages类,并实现此类的GetPages ...

  3. MFC编程入门之十五(对话框:一般属性页对话框的创建及显示)

    属性页对话框包括向导对话框和一般属性页对话框两类,上一节讲了如何创建并显示向导对话框,本节将继续介绍一般属性页对话框的创建和显示. 实际上,一般属性页对话框的创建和显示过程和向导对话框是很类似的.将上 ...

  4. VS2010/MFC对话框:一般属性页对话框的创建及显示

    一般属性页对话框的创建及显示 本节将介绍一般属性页对话框的创建和显示. 实际上,一般属性页对话框的创建和显示过程和向导对话框是很类似的.鸡啄米将上一节中的向导对话框进行少量修改,使其成为一般属性页对话 ...

  5. VS2010/MFC编程入门之十五(对话框:一般属性页对话框的创建及显示)

    属性页对话框包括向导对话框和一般属性页对话框两类,上一节鸡啄米讲了如何创建并显示向导对话框,本节将继续介绍一般属性页对话框的创建和显示. 实际上,一般属性页对话框的创建和显示过程和向导对话框是很类似的 ...

  6. VS2010-MFC(对话框:一般属性页对话框的创建及显示)

    转自:http://www.jizhuomi.com/software/169.html 属性页对话框包括向导对话框和一般属性页对话框两类,上一节演示了如何创建并显示向导对话框,本节将继续介绍一般属性 ...

  7. MFC编程入门之十三(对话框:属性页对话框及相关类的介绍)

    前面讲了模态对话框和非模态对话框,本节来将一种特殊的对话框--属性页对话框. 属性页对话框的分类 属性页对话框想必大家并不陌生,XP系统中桌面右键点属性,弹出的就是属性页对话框,它通过标签切换各个页面 ...

  8. 坑爹的vector iterators incompatible错误(VS中属性页-->C/C++-->代码生成-->>运行库)

    之前一直被这个错误折磨着,就是不知道问题在那,后来找了很多资料,大概都是说这是因为多个线程同时操作vector的问题(参考这里).可是我这里的代码并没有问题,因为同样的代码在别的解决方案中已经成功运行 ...

  9. VC++在对话框中加入属性页

    当一个基于对话框的程序中有相当多的控件时,你一定会想到使用属性页来将这些控件分类放置.本文针对这种方法来讨论几种可能实现的方案. 方案一本方案的例子请见源代码打包文件中的Property1部分 在对话 ...

随机推荐

  1. ABP官方文档翻译 9.2 Entity Framework Core

    Entity Framework Core 介绍 DbContext 配置 在Startup类中 在模块PreInitialize方法中 仓储 默认仓储 自定义仓储 应用程序特定基础仓储类 自定义仓储 ...

  2. 夏令营讲课内容整理 Day 3.

    本日主要内容是树与图.   1.树 树的性质 树的遍历 树的LCA 树上前缀和   树的基本性质: 对于一棵有n个节点的树,必定有n-1条边.任意两个点之间的路径是唯一确定的.   回到题目上,如果题 ...

  3. BZOJ 2809: [Apio2012]dispatching [斜堆]

    题意:主席树做法见上一题 我曾发过誓再也不写左偏树(期末考试前一天下午5个小时没写出棘手的操作) 于是我来写斜堆啦 从叶子往根合并,维护斜堆就行了 题目连拓扑序都给你了... 说一下斜堆的操作: 合并 ...

  4. POJ1269 Intersecting Lines[线段相交 交点]

    Intersecting Lines Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 15145   Accepted: 66 ...

  5. Android开发常用的插件及工具

    1.GitHub,这个不管是做安卓还是其他,只要是开发就必上的网站,也是天朝没有墙掉为数不多的网站 2.Stack OverFlow,这个和上面一样,国外非常著名的问答网站,在上面基本上很多问题都可以 ...

  6. Python比较运算符

    判断两个对象之间的关系,和条件选择和循环结合使用的 以下假设变量a为10,变量b为20: 示例1:输入三个互不相等的整数,按照从小到大输出 num01,num02,num03 = eval(input ...

  7. linux 下安装php curl扩展

    方法一 安装cURL wget https://curl.haxx.se/download/curl-7.53.1.tar.gz tar -zxf curl-7.17.1.tar.gz ./confi ...

  8. Visual Studio 2017 Enterprise 发布 15.3.3 版,附离线安装包百度网盘下载。

    Visual Studio 2017 Enterprise 发布 15.3.3 版,附离线安装包百度网盘下载. Visual Studio 2017 Enterprise 更新至 15.3.3 ,本安 ...

  9. ErrorKiller:Failed to decode response: zlib_decode(): data error

    先更新composer自己,composer self-update 然后再更新依赖关系 composer update

  10. ./init的含义

    .代表当前目录,./后往往会跟上要运行的脚本文件.相关的例子,..代表上一级目录.