zoukankan      html  css  js  c++  java
  • 如何创建Filter的属性页 分类: DirectX 2014-10-17 17:32 550人阅读 评论(0) 收藏

    本篇文档我们将要讲述如何给一个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  运行后的结果

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    vue——项目技术储备
    Framework7—— 混合开发
    CSS——常见的问题
    Vue——常见问题
    Vue——使用 watch 注意项
    Node——微服务架构(二)
    C——基本词汇
    Go——空接口与断言
    Node——PM2
    Vue——组件异步加载与路由懒加载
  • 原文地址:https://www.cnblogs.com/mao0504/p/4706497.html
Copyright © 2011-2022 走看看