{"id":433,"date":"2008-07-18T15:31:35","date_gmt":"2008-07-18T13:31:35","guid":{"rendered":"https:\/\/alax.info\/blog\/?p=433"},"modified":"2009-03-24T11:56:50","modified_gmt":"2009-03-24T09:56:50","slug":"how-to-implement-directshow-filter-using-directx-media-object-dmo-part-3-persistence-automation-and-property-pages","status":"publish","type":"post","link":"https:\/\/alax.info\/blog\/433","title":{"rendered":"How To: Implement DirectShow Filter using DirectX Media Object DMO (Part 3: Persistence, Automation and Property Pages)"},"content":{"rendered":"<p>Previously on the topic:<\/p>\n<ul>\n<li><a href=\"https:\/\/alax.info\/blog\/402\">Part 1: Starting the Project<\/a><\/li>\n<li><a href=\"https:\/\/alax.info\/blog\/412\">Part 2: Video Processing<\/a><\/li>\n<\/ul>\n<p>The principal task of video processing is done but there are still things mandatory for the filter to be usable. First of all, a custom interface is required to be able to control the filter from higher level application and to adjust brightness and constract correction values on the run time. Additionally, persistence would not hurt at all to be able to store correction settings along with other graph settings in <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms787460(VS.85).aspx\">GraphEdit<\/a> <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms787598(VS.85).aspx\">graph file<\/a> or anywhere else. Additionally, it would also be convenient to have a property page for the filter to be able to adjust the correction settings through GUI, both on graph composition and while the graph is running.<\/p>\n<p><a href=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image009.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-445\" title=\"Filter\/DMO Property Page\" src=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image009-261x300.png\" alt=\"\" width=\"261\" height=\"300\" srcset=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image009-261x300.png 261w, https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image009.png 386w\" sizes=\"auto, (max-width: 261px) 100vw, 261px\" \/><\/a><\/p>\n<p>All the mentioned tasks are interconnected and <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/t9adwcde.aspx\">ATL<\/a> has an answer in implementation of:<\/p>\n<ul>\n<li><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms221608(VS.85).aspx\">IDispatch<\/a>-derived automation interface through <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/494h01te.aspx\">IDispatchImpl<\/a> class to implement custom interface to be used for external control over the filter\/DMO and also to be used to access persistent properties<\/li>\n<li><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms690091(VS.85).aspx\">IPersistStream<\/a>\/<a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms682273(VS.85).aspx\">IPersistStreamInit<\/a> interfaces through <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/x2fwwk86.aspx\">IPersistStreamInitImpl<\/a> class and <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/y5s333c2.aspx\">PROP_MAP<\/a> macro map to implement persistence capabilities<\/li>\n<li><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms695217(VS.85).aspx\">ISpecifyPropertyPages<\/a> interface through <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/96ww582d.aspx\">ISpecifyPropertyPagesImpl<\/a> class to provide custom property page<\/li>\n<\/ul>\n<p><!--more--><\/p>\n<p>Also note that <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms783383(VS.85).aspx\">DMO Minimum Requirements<\/a> mention <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms690091(VS.85).aspx\">IPersistStream<\/a> and <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms695217(VS.85).aspx\">ISpecifyPropertyPages<\/a> as not required but recommended as useful, and these interfaces will be used by DirectShow.<\/p>\n<p>We start with custom <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms221608(VS.85).aspx\">IDispatch<\/a>-derived interface to be used to control filter and for persistence. ATL Project Wizard already prepared empty IBrightnessContrastObject interface as a part of project creation, where we are adding <strong>Brightness<\/strong> and <strong>Contrast<\/strong> properties (in the IDL definition) and adding implementation of corresponding methods to the filter\/DMO class:<\/p>\n<pre>interface IBrightnessContrastObject : IDispatch\r\n{\r\n\t[propget, id(1)] HRESULT Brightness([out, retval] LONG* pnBrightness);\r\n\t[propput, id(1)] HRESULT Brightness([in] LONG nBrightness);\r\n\t[propget, id(2)] HRESULT Contrast([out, retval] LONG* pnContrast);\r\n\t[propput, id(2)] HRESULT Contrast([in] LONG nContrast);\r\n};<\/pre>\n<pre>\/\/ IBrightnessContrastObject\r\n\tSTDMETHOD(get_Brightness)(LONG* pnBrightness) throw()\r\n\tSTDMETHOD(put_Brightness)(LONG nBrightness) throw()\r\n\tSTDMETHOD(get_Contrast)(LONG* pnContrast) throw()\r\n\tSTDMETHOD(put_Contrast)(LONG nContrast) throw()<\/pre>\n<p>To complete implemetnation of persistence we are to inherit from <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/x2fwwk86.aspx\">IPersistStreamInitImpl<\/a> and also add a property map:<\/p>\n<pre>class ATL_NO_VTABLE CBrightnessContrastObject :\r\n\t...\r\n\tpublic IPersistStreamInitImpl&lt;CBrightnessContrastObject&gt;,\r\n...\r\nBEGIN_COM_MAP(CBrightnessContrastObject)\r\n\tCOM_INTERFACE_ENTRY(IPersistStreamInit)\r\n\tCOM_INTERFACE_ENTRY_IID(IID_IPersistStream, IPersistStreamInit)\r\n...\r\nBEGIN_PROP_MAP(CBrightnessContrastObject)\r\n\tPROP_ENTRY_TYPE_EX(\"Brightness\", 1, CLSID_NULL, __uuidof(IBrightnessContrastObject), VT_I4)\r\n\tPROP_ENTRY_TYPE_EX(\"Contrast\", 2, CLSID_NULL, __uuidof(IBrightnessContrastObject), VT_I4)\r\nEND_PROP_MAP()\r\n...\r\npublic:\r\n\tBOOL m_bRequiresSave;<\/pre>\n<p>In the property map we enumerate the persistent properties using <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ds84x0xt.aspx\">PROP_ENTRY_TYPE_EX<\/a> (a successor of <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ds84x0xt(VS.80).aspx\">PROP_ENTRY_EX<\/a>, which is deprecated starting Visual Studio .NET 2008).<\/p>\n<p>Note that ATL implements <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms682273(VS.85).aspx\">IPersistStreamInit<\/a>, however <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms690091(VS.85).aspx\">IPersistStream<\/a> is compatible in method declaration so we can quick-implement this interface through declaring <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dcxf7dhx.aspx\">COM_INTERFACE_ENTRY_IID<\/a> and thus casting\u00a0<a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms682273(VS.85).aspx\">IPersistStreamInit<\/a> to <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms690091(VS.85).aspx\">IPersistStream<\/a>.<\/p>\n<p>Note CLSID_NULL which is a class identifier of the corresponding property page, if any, which we will replace with a non-NULL identifier below as soon as property page is ready.<\/p>\n<p>We also need a <strong>m_bRequiresSave<\/strong> variable which is used by <strong>IPersist*Impl<\/strong> classes and for this reason it has to be either public or instead we should friend the classes to allow access to the variable.<\/p>\n<p>To ensure automation interface property put accessors are working as expected, let us re-work debug pre-initialization of the correction variables through <strong>FinalConstruct<\/strong> method:<\/p>\n<pre>#if defined(_DEBUG)\r\n\tHRESULT FinalConstruct() throw()\r\n\t{\r\n\t\t_ATLTRY\r\n\t\t{\r\n\t\t\t__C(put_Brightness(-0x0010));\r\n\t\t\t__C(put_Contrast(0x2000));\r\n\t\t}\r\n\t\t_ATLCATCH(Exception)\r\n\t\t{\r\n\t\t\treturn Exception;\r\n\t\t}\r\n\t\treturn S_OK;\r\n\t}\r\n#endif \/\/ defined(_DEBUG)<\/pre>\n<p>The only things remained is a property page, for which we create a new COM object class CGeneralPropertyPage. Luckily, ATL has a wizard-based creation helper for property pages:<\/p>\n<p><a href=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image007.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-442\" title=\"Add ATL Class\" src=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image007-300x194.png\" alt=\"\" width=\"300\" height=\"194\" srcset=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image007-300x194.png 300w, https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image007.png 683w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p><a href=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image008.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-443\" title=\"ATL Property Page Class Settings\" src=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image008-300x253.png\" alt=\"\" width=\"300\" height=\"253\" srcset=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image008-300x253.png 300w, https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image008.png 615w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>We will start needing <a href=\"http:\/\/wtl.sourceforge.net\/\">WTL<\/a> for convenient GUI implementation. Please refer to <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/5h66xtbw.aspx\">Example: Implementing a Property Page<\/a> on how property page is implemented. Once implementation is complete, we need to inherit from <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/x2fwwk86.aspx\">IPersistStreamInitImpl<\/a> and reference the property page from filter\/DMO property map:<\/p>\n<pre>class ATL_NO_VTABLE CBrightnessContrastObject :\r\n\t...\r\n\tpublic ISpecifyPropertyPagesImpl&lt;CBrightnessContrastObject&gt;,\r\n...\r\nBEGIN_COM_MAP(CBrightnessContrastObject)\r\n\t...\r\n\tCOM_INTERFACE_ENTRY(ISpecifyPropertyPages)\r\n...\r\nBEGIN_PROP_MAP(CBrightnessContrastObject)\r\n\tPROP_PAGE(CLSID_GeneralPropertyPage)<\/pre>\n<p>Filter&#8217;s run time:<\/p>\n<p><a href=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image010.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-447\" title=\"Runtime\" src=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image010-300x129.png\" alt=\"\" width=\"300\" height=\"129\" srcset=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image010-300x129.png 300w, https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/image010.png 1234w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>Source code: <a href=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2008\/07\/dmobrightnesscontrastsample03.zip\">DmoBrightnessContrastSample.03.zip<\/a> (note that Release build binary is included)<\/p>\n<p>Continued by:<\/p>\n<ul>\n<li><a href=\"..\/481\">Part 4: Merit<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Previously on the topic: Part 1: Starting the Project Part 2: Video Processing The principal task of video processing is done but there are still things mandatory for the filter to be usable. First of all, a custom interface is required to be able to control the filter from higher level application and to adjust&hellip; <\/p>\n<p><a class=\"moretag\" href=\"https:\/\/alax.info\/blog\/433\">Read the full article<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11,13,10,20],"tags":[487,78,83,47,489],"class_list":["post-433","post","type-post","status-publish","format-standard","hentry","category-atl","category-source","category-video","category-wtl","tag-atl","tag-directshow","tag-dmo","tag-howto","tag-wtl"],"_links":{"self":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts\/433","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/comments?post=433"}],"version-history":[{"count":0,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts\/433\/revisions"}],"wp:attachment":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/media?parent=433"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/categories?post=433"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/tags?post=433"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}