<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Fooling Around &#187; C++</title>
	<atom:link href="http://alax.info/blog/tag/c/feed" rel="self" type="application/rss+xml" />
	<link>http://alax.info/blog</link>
	<description>Software Production Line</description>
	<lastBuildDate>Tue, 03 Aug 2010 09:48:06 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Attributed ATL: Accessing BLOB with ISequentialStream</title>
		<link>http://alax.info/blog/1136</link>
		<comments>http://alax.info/blog/1136#comments</comments>
		<pubDate>Sat, 10 Jul 2010 10:23:22 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[ATL]]></category>
		<category><![CDATA[Seriously]]></category>
		<category><![CDATA[blob]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[template]]></category>
		<category><![CDATA[visual studio]]></category>

		<guid isPermaLink="false">http://alax.info/blog/?p=1136</guid>
		<description><![CDATA[Before attributed ATL was deprecated, it was a convenient way to access databases using attributed classes on top of OLEDB Consumer Templates. Does not it look nice?

[
    db_command("SELECT ServerData FROM Server WHERE Server = ?")
]
class CGetServerData
{
public:
    [ db_param(1) ] LONG m_nServer;
    [ db_column(1, length = "m_nDataLength") [...]]]></description>
			<content:encoded><![CDATA[<p>Before <a href="http://social.msdn.microsoft.com/Forums/en/vclanguage/thread/a74bcacf-e1e3-44c7-994d-3ebb8fe37973">attributed ATL was deprecated</a>, it was a convenient way to access databases using attributed classes on top of <a href="http://msdn.microsoft.com/en-us/library/fk4h509a.aspx">OLEDB Consumer Templates</a>. Does not it look nice?</p>
<pre>
<pre style="color: #000000; background: none repeat scroll 0% 0% #ffffff;"><span style="color: #808030;">[</span>
    db_command<span style="color: #808030;">(</span><span style="color: #800000;">"</span><span style="color: #0000e6;">SELECT ServerData FROM Server WHERE Server = ?</span><span style="color: #800000;">"</span><span style="color: #808030;">)</span>
<span style="color: #808030;">]</span>
<span style="color: #800000; font-weight: bold;">class</span> CGetServerData
<span style="color: #800080;">{</span>
<span style="color: #800000; font-weight: bold;">public</span><span style="color: #e34adc;">:</span>
    <span style="color: #808030;">[</span> db_param<span style="color: #808030;">(</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: #808030;">]</span> <span style="color: #603000;">LONG</span> m_nServer<span style="color: #800080;">;</span>
    <span style="color: #808030;">[</span> db_column<span style="color: #808030;">(</span><span style="color: #008c00;">1</span><span style="color: #808030;">,</span> length <span style="color: #808030;">=</span> <span style="color: #800000;">"</span><span style="color: #0000e6;">m_nDataLength</span><span style="color: #800000;">"</span><span style="color: #808030;">)</span> <span style="color: #808030;">]</span> ISequentialStream<span style="color: #808030;">*</span> m_pDataStream<span style="color: #800080;">;</span> DBLENGTH m_nDataLength<span style="color: #800080;">;</span>
<span style="color: #800080;">}</span><span style="color: #800080;">;</span></pre>
</pre>
<p>It worked great with Visual Studio .NET 2003 and it failed to work with later releases. There are <a href="http://www.google.com/search?q=db_column+ISequentialStream">questions on internet about the problem</a>, but there few answers if any. As I recently had to convert a project from 2003 version of the compiler to Visual Studio 2008, the problem was finally to be resolved.</p>
<p><span id="more-1136"></span>The first problem, and an easier one was that you have to put attribute values in quotes. &#8216;Db_column(1, &#8230;&#8217; has to be &#8220;db_column(&#8221;1&#8243;, &#8230;&#8217; and besides going through code and making changes, it appeared that if earlier implementation managed to distinguish between ordinal column numbers and field names by checking provided argument to be a number or a string, with Visual Studio 2008 one has to provide strings at any time. If the string is a valid integer, the attribute processor will treat it as an ordinal number.</p>
<p>So if you want your code to be both compiled with 2003 and 2008 versions of Visual Studio, you have a puzzle now: you have to use quotes, but version 2003 will no longer treat the argument as ordinal number. Note that version 2003 will keep compiling the code and the problem will only come up on runtime. As eventually in the project of interest support for version 2003 will be dropped, I was not interested in this problem anymore.</p>
<p>The main problem is related to use of ISequentialStream pointer variable as a data field. Again, it worked great with Visual Studio .NET 2003, it no longer did with Visual Studio .NET 2005. First of all, you cannot use this data type anymore, as templates don&#8217;t provide an automatic mapper to OLE DB Type.  Instead of ISequentialStream you have to use IUnknown and QueryInterface the provided pointer before use.</p>
<p>This solves compilation problem, but still the code does not work on runtime. An attempt to bind to the column using such accessor results in DB_E_ERRORSOCCURRED (0&#215;80040E21) &#8220;Multiple-step OLE DB operation generated errors. Check each OLE DB status value, if available. No work was done.&#8221; A more thorough look gave DBBINDSTATUS_BADBINDINFO (3) for binding to the column of interest.</p>
<p>The cause of the problem is missing DBOBJECT object pointer in the corresponding DBBINDING::pObject field. Why? A general rule for troubleshooting this kind of problem is to enable expanding attributed source code to check what exactly is generated.</p>
<p>The problem is that unlike previous versions of Visual Studio, ATL attributed code provider is no longer generates correct OLEDB template map code for BLOB fields. And this results in incorrectly generated DBBINDING structure, inability to bind to column and as a final result in DB_E_ERRORSOCCURRED error code which does not give a sufficient clue on the root of the problem.</p>
<p>While the map needs a BLOB_* macro for the BLOB column (e.g. BLOB_ENTRY or <a href="http://msdn.microsoft.com/en-us/library/w3k8764c.aspx">BLOB_ENTRY_LENGTH</a>), the generated code uses _COLUMN_ENTRY_CODE instead. This causes missing (null) DBBINDING::pObject field in the binding structure:</p>
<pre>
<pre style="color: #000000; background: none repeat scroll 0% 0% #ffffff;">BEGIN_ACCESSOR_MAP<span style="color: #808030;">(</span>_CGetServerDataAccessor<span style="color: #808030;">,</span> <span style="color: #008c00;">1</span><span style="color: #808030;">)</span>
    BEGIN_ACCESSOR<span style="color: #808030;">(</span><span style="color: #008c00;">0</span><span style="color: #808030;">,</span> <span style="color: #800000; font-weight: bold;">true</span><span style="color: #808030;">)</span>
        <span style="color: #696969;">//_COLUMN_ENTRY_CODE(1, DBTYPE_IUNKNOWN, _SIZE_TYPE(m_pDataUnknown), 0, 0, offsetbuf(m_pDataUnknown), offsetbuf(m_nDataLength), 0)</span>
        BLOB_ENTRY_LENGTH<span style="color: #808030;">(</span><span style="color: #008c00;">1</span><span style="color: #808030;">,</span> <span style="color: #800000; font-weight: bold;">__uuidof</span><span style="color: #808030;">(</span>ISequentialStream<span style="color: #808030;">)</span><span style="color: #808030;">,</span> STGM_READ<span style="color: #808030;">,</span> m_pDataUnknown<span style="color: #808030;">,</span> m_nDataLength<span style="color: #808030;">)</span>
    END_ACCESSOR<span style="color: #808030;">(</span><span style="color: #808030;">)</span>
END_ACCESSOR_MAP<span style="color: #808030;">(</span><span style="color: #808030;">)</span></pre>
</pre>
<p>So it appears that to fix the problem, an attributed class needs to be replaced with a non-attributed update. To ease the conversion one can expand attributed source and use it as a base for the non-attributed class.</p>
<p>And example of such correction/replacement is provided below for a reference.</p>
<pre>
<pre style="color: #000000; background: none repeat scroll 0% 0% #ffffff;"><span style="color: #004a43;">#</span><span style="color: #004a43;">if</span><span style="color: #004a43;"> TRUE</span>
<span style="color: #800000; font-weight: bold;">class</span> _CGetServerDataAccessor
<span style="color: #800080;">{</span>
<span style="color: #800000; font-weight: bold;">public</span><span style="color: #e34adc;">:</span>

DEFINE_COMMAND_EX<span style="color: #808030;">(</span>_CGetServerDataAccessor<span style="color: #808030;">,</span> <span style="color: #800000;">L"</span><span style="color: #0000e6;">SELECT Data FROM Server WHERE Server = ?</span><span style="color: #800000;">"</span><span style="color: #808030;">)</span>

BEGIN_PARAM_MAP<span style="color: #808030;">(</span>_CGetServerDataAccessor<span style="color: #808030;">)</span>
    SET_PARAM_TYPE<span style="color: #808030;">(</span>DBPARAMIO_INPUT<span style="color: #808030;">)</span>
    <span style="color: #696969;">//_COLUMN_ENTRY_CODE(1, _OLEDB_TYPE(m_nServer), _SIZE_TYPE(m_nServer), 0, 0, offsetbuf(m_nServer), 0, 0)</span>
    COLUMN_ENTRY<span style="color: #808030;">(</span><span style="color: #008c00;">1</span><span style="color: #808030;">,</span> m_nServer<span style="color: #808030;">)</span>
END_PARAM_MAP<span style="color: #808030;">(</span><span style="color: #808030;">)</span>

BEGIN_ACCESSOR_MAP<span style="color: #808030;">(</span>_CGetServerDataAccessor<span style="color: #808030;">,</span> <span style="color: #008c00;">1</span><span style="color: #808030;">)</span>
    BEGIN_ACCESSOR<span style="color: #808030;">(</span><span style="color: #008c00;">0</span><span style="color: #808030;">,</span> <span style="color: #800000; font-weight: bold;">true</span><span style="color: #808030;">)</span>
        <span style="color: #696969;">//_COLUMN_ENTRY_CODE(1, DBTYPE_IUNKNOWN, _SIZE_TYPE(m_pDataUnknown), 0, 0, offsetbuf(m_pDataUnknown), offsetbuf(m_nDataLength), 0)</span>
        BLOB_ENTRY_LENGTH<span style="color: #808030;">(</span><span style="color: #008c00;">1</span><span style="color: #808030;">,</span> <span style="color: #800000; font-weight: bold;">__uuidof</span><span style="color: #808030;">(</span>ISequentialStream<span style="color: #808030;">)</span><span style="color: #808030;">,</span> STGM_READ<span style="color: #808030;">,</span> m_pDataUnknown<span style="color: #808030;">,</span> m_nDataLength<span style="color: #808030;">)</span>
    END_ACCESSOR<span style="color: #808030;">(</span><span style="color: #808030;">)</span>
END_ACCESSOR_MAP<span style="color: #808030;">(</span><span style="color: #808030;">)</span>

<span style="color: #800000; font-weight: bold;">public</span><span style="color: #e34adc;">:</span>
    <span style="color: #603000;">LONG</span> m_nServer<span style="color: #800080;">;</span>
    IUnknown<span style="color: #808030;">*</span> m_pDataUnknown<span style="color: #800080;">;</span>
    DBLENGTH m_nDataLength<span style="color: #800080;">;</span>
<span style="color: #800080;">}</span><span style="color: #800080;">;</span>

<span style="color: #800000; font-weight: bold;">class</span> CGetServerData <span style="color: #800080;">:</span>
    <span style="color: #800000; font-weight: bold;">public</span> CCommand<span style="color: #800080;">&lt;</span>CAccessor<span style="color: #800080;">&lt;</span>_CGetServerDataAccessor<span style="color: #800080;">&gt;</span> <span style="color: #800080;">&gt;</span>
<span style="color: #800080;">{</span>
<span style="color: #800000; font-weight: bold;">public</span><span style="color: #e34adc;">:</span>
<span style="color: #696969;">// CGetServerData</span>
    HRESULT OpenRowset<span style="color: #808030;">(</span><span style="color: #800000; font-weight: bold;">const</span> CSession<span style="color: #808030;">&amp;</span> Session<span style="color: #808030;">,</span> <span style="color: #603000;">LPCWSTR</span> pszCommand <span style="color: #808030;">=</span> <span style="color: #7d0045;">NULL</span><span style="color: #808030;">)</span> <span style="color: #800000; font-weight: bold;">throw</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span>
    <span style="color: #800080;">{</span>
        <span style="color: #800000; font-weight: bold;">if</span><span style="color: #808030;">(</span><span style="color: #808030;">!</span>pszCommand<span style="color: #808030;">)</span>
            _V<span style="color: #808030;">(</span>_CGetServerDataAccessor<span style="color: #800080;">::</span>GetDefaultCommand<span style="color: #808030;">(</span><span style="color: #808030;">&amp;</span>pszCommand<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        <span style="color: #800000; font-weight: bold;">return</span> Open<span style="color: #808030;">(</span>Session<span style="color: #808030;">,</span> pszCommand<span style="color: #808030;">,</span> <span style="color: #7d0045;">NULL</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    <span style="color: #800080;">}</span>
<span style="color: #800080;">}</span><span style="color: #800080;">;</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">else</span>
<span style="color: #808030;">[</span>
    db_command<span style="color: #808030;">(</span><span style="color: #800000;">"</span><span style="color: #0000e6;">SELECT Data FROM Server WHERE Server = ?</span><span style="color: #800000;">"</span><span style="color: #808030;">)</span>
<span style="color: #808030;">]</span>
<span style="color: #800000; font-weight: bold;">class</span> CGetServerData
<span style="color: #800080;">{</span>
<span style="color: #800000; font-weight: bold;">public</span><span style="color: #e34adc;">:</span>
    <span style="color: #808030;">[</span> db_param<span style="color: #808030;">(</span><span style="color: #800000;">"</span><span style="color: #0000e6;">1</span><span style="color: #800000;">"</span><span style="color: #808030;">)</span> <span style="color: #808030;">]</span> <span style="color: #603000;">LONG</span> m_nServer<span style="color: #800080;">;</span>
    <span style="color: #808030;">[</span> db_column<span style="color: #808030;">(</span><span style="color: #800000;">"</span><span style="color: #0000e6;">1</span><span style="color: #800000;">"</span><span style="color: #808030;">,</span> length <span style="color: #808030;">=</span> <span style="color: #800000;">"</span><span style="color: #0000e6;">m_nDataLength</span><span style="color: #800000;">"</span><span style="color: #808030;">)</span> <span style="color: #808030;">]</span> IUnknown<span style="color: #808030;">*</span> m_pDataUnknown<span style="color: #800080;">;</span> DBLENGTH m_nDataLength<span style="color: #800080;">;</span>
<span style="color: #800080;">}</span><span style="color: #800080;">;</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">endif</span></pre>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://alax.info/blog/1136/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MediaTools: Two samples to capture M-JPEG video into JPEG files and to play JPEG files back</title>
		<link>http://alax.info/blog/1127</link>
		<comments>http://alax.info/blog/1127#comments</comments>
		<pubDate>Thu, 13 May 2010 06:15:48 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[ATL]]></category>
		<category><![CDATA[Media Tools]]></category>
		<category><![CDATA[Source]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[DirectShow]]></category>
		<category><![CDATA[ip camera]]></category>
		<category><![CDATA[JPEG]]></category>
		<category><![CDATA[M-JPEG]]></category>
		<category><![CDATA[sample]]></category>
		<category><![CDATA[Video]]></category>

		<guid isPermaLink="false">http://alax.info/blog/?p=1127</guid>
		<description><![CDATA[I added two new simple samples for the MediaTools DirectShow filters to demonstrate how to capture M-JPEG video feed, esp. from an IP camera, and write the video frames into sequence of JPEG files. The other sample takes a directory on the input and plays the images back as video. If you are working on [...]]]></description>
			<content:encoded><![CDATA[<p>I added two new simple samples for the MediaTools DirectShow filters to demonstrate how to capture M-JPEG video feed, esp. from an IP camera, and write the video frames into sequence of JPEG files. The other sample takes a directory on the input and plays the images back as video. If you are working on certain transformation filter, it is an easy way to make a reference feed and use it for debugging purposes.</p>
<p>The filters behind that empower the sample are described in <a href="http://alax.info/blog/741">another post</a>.</p>
<p><strong>RenderHttpMjpegVideoIntoFiles01</strong> sample takes an URL on the input to generate <a href="http://www.w3schools.com/media/media_mimeref.asp">image/jpeg JPEG</a> or <a href="http://en.wikipedia.org/wiki/Motion_JPEG">multipart/x-mixed-replace M-JPEG</a> stream. For example, it might be <a href="http://demo1.stardotcams.com/nph-mjpeg.cgi">http://demo1.stardotcams.com/nph-mjpeg.cgi</a> feed from a demo <a href="http://www.stardot.com/">StarDot Technologies</a> IP camera.</p>
<p>The application will create a new directory to write files into, and will save each new video frame received into new JPEG file.</p>
<pre>Z:\MediaTools\Samples\RenderHttpMjpegVideoIntoFiles01\Release&gt;RenderHttpMjpegVideoIntoFiles01.exe http://demo1.stardotcams.com/nph-mjpeg.cgi
URL: http://demo1.stardotcams.com/nph-mjpeg.cgi
Writing to directory: Z:\MediaTools\Samples\RenderHttpMjpegVideoIntoFiles01\Release\2010-05-12 22-07-37
Event: nCode EC_CLOCK_CHANGED 0xD, nParameter1 0x00000000, nParameter2 0x00000000
Event: nCode EC_PAUSED 0xE, nResult 0x00000000, nParameter2 0x00000000
[...]
^C</pre>
<p>The application will generate the files and convert media sample time stamps into file time.</p>
<p><a href="http://alax.info/blog/wp-content/uploads/2010/05/13-Image001.png"><img class="alignnone size-medium wp-image-1128" title="RenderHttpMjpegVideoIntoFiles01 Sample Output" src="http://alax.info/blog/wp-content/uploads/2010/05/13-Image001-320x175.png" alt="RenderHttpMjpegVideoIntoFiles01 Sample Output" width="320" height="175" /></a></p>
<p>The DirectShow graph that implements the operation is the following:</p>
<p><img class="alignnone size-full wp-image-1129" title="RenderHttpMjpegVideoIntoFiles01 Filter Graph" src="http://alax.info/blog/wp-content/uploads/2010/05/13-Image002.png" alt="RenderHttpMjpegVideoIntoFiles01 Filter Graph" width="661" height="346" /></p>
<p>The other sample <strong>RenderJpegFiles01</strong> takes a directory path to look for JPEG files, e.g. generated by previous sample, and pushes them into DirectShow graph as a video feed. File times will be converted [back] to media sample times.</p>
<p><span id="more-1127"></span></p>
<pre>Z:\MediaTools\Samples\RenderJpegFiles01\Release&gt;RenderJpegFiles01 "Z:\MediaTools\Samples\RenderHttpMjpegVideoIntoFiles01\Release\2010-05-12 22-07-37"
Directory: Z:\MediaTools\Samples\RenderHttpMjpegVideoIntoFiles01\Release\2010-05-12 22-07-37
Event: nCode EC_CLOCK_CHANGED 0xD, nParameter1 0x00000000, nParameter2 0x00000000
Event: nCode EC_PAUSED 0xE, nResult 0x00000000, nParameter2 0x00000000
Event: nCode EC_VMR_RECONNECTION_FAILED 0x55, nResult 0x80004005, nParameter2 0x00000000
Event: nCode EC_VMR_RENDERDEVICE_SET 0x53, RendererDeviceType 0x2, nParameter2 0x00000000
Event: nCode EC_VIDEO_SIZE_CHANGED 0xA, Extent 384x240, nParameter2 0x00000000
Event: nCode EC_COMPLETE 0x1, nResult 0x00000000, nParameter2 0x00000000
Event: nCode EC_ERRORABORT 0x3, nResult 0x80004005, nParameter2 0x00000000
Detected resolution change, new resolution is 1024x768, recreating filter graph...
Event: nCode EC_VMR_RECONNECTION_FAILED 0x55, nResult 0x80004005, nParameter2 0x00000000
Event: nCode EC_VMR_RENDERDEVICE_SET 0x53, RendererDeviceType 0x2, nParameter2 0x00000000
Event: nCode EC_VIDEO_SIZE_CHANGED 0xA, Extent 384x240, nParameter2 0x00000000
Event: nCode EC_CLOCK_CHANGED 0xD, nParameter1 0x00000000, nParameter2 0x00000000
Event: nCode EC_PAUSED 0xE, nResult 0x00000000, nParameter2 0x00000000</pre>
<p><a href="http://alax.info/blog/wp-content/uploads/2010/05/13-Image003.png"><img class="alignnone size-medium wp-image-1130" title="RenderJpegFiles01 DirectShow Graph and Output" src="http://alax.info/blog/wp-content/uploads/2010/05/13-Image003-312x320.png" alt="RenderJpegFiles01 DirectShow Graph and Output" width="312" height="320" /></a></p>
<p>Download links:</p>
<ul>
<li>RenderHttpMjpegVideoIntoFiles01: <a href="http://www.assembla.com/code/roatl-utilities/subversion/nodes/trunk/MediaTools/Samples/RenderHttpMjpegVideoIntoFiles01">source code</a>, <a href="http://www.assembla.com/code/roatl-utilities/subversion/nodes/trunk/MediaTools/Samples/RenderHttpMjpegVideoIntoFiles01/Release/RenderHttpMjpegVideoIntoFiles01.exe?format=raw">binary</a></li>
<li>RenderJpegFiles01: <a href="http://www.assembla.com/code/roatl-utilities/subversion/nodes/trunk/MediaTools/Samples/RenderJpegFiles01">source code</a>, <a href="http://www.assembla.com/code/roatl-utilities/subversion/nodes/trunk/MediaTools/Samples/RenderJpegFiles01/Release/RenderJpegFiles01.exe?format=raw">binary</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://alax.info/blog/1127/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>DirectShow Filter Graph Spy on Vista</title>
		<link>http://alax.info/blog/944</link>
		<comments>http://alax.info/blog/944#comments</comments>
		<pubDate>Mon, 22 Jun 2009 08:36:15 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[Utilities]]></category>
		<category><![CDATA[ATL]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[COM]]></category>
		<category><![CDATA[DirectShow]]></category>
		<category><![CDATA[filter]]></category>
		<category><![CDATA[graph]]></category>
		<category><![CDATA[registry]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[Source]]></category>
		<category><![CDATA[spy]]></category>
		<category><![CDATA[Vista]]></category>

		<guid isPermaLink="false">http://alax.info/blog/?p=944</guid>
		<description><![CDATA[I have been receiving comments that Filter Graph Spy tool does not work with Microsoft Vista operating system. I never had a moment to check until recently, and this time I realized that it really does not work. I am definitely aware of dramatic changes introduced with this operating system, and in particular UAC feature, [...]]]></description>
			<content:encoded><![CDATA[<p>I have been receiving comments that <a href="http://alax.info/blog/777">Filter Graph Spy</a> tool does not work with Microsoft Vista operating system. I never had a moment to check until recently, and this time I realized that it really does not work. I am definitely aware of dramatic changes introduced with this operating system, and in particular UAC feature, virtualization and changes in security. No wonder this was the first guess that security was the cause, however the investigation showed there was a trail of issues underneath&#8230;</p>
<p>Investigation details deserve a separate post, while this one briefly outlines the issues and also accompany the repository update with a version compatible with Vista OS.</p>
<p>First of all, COM registration of the DLL (which definitely requires privilege elevation) succeeded on Vista. This means the registration procedure did not encounter any errors on the way, or poorly written code ignored the problem. It appeared that the source of the problem was <a href="http://msdn.microsoft.com/en-us/library/ms693452(VS.85).aspx">CoTreatAsClass</a> API, which failed to do the requested action, however returned status code indicating successful operation. This definitely looks like a bug and further comments on this particular behavior are expected to appear on <a href="http://social.msdn.microsoft.com/Forums/en-US/windowssecurity/threads/">Windows Applications Security MSDN forum</a>, where I opened <a href="http://social.msdn.microsoft.com/Forums/en-US/windowssecurity/thread/583793b8-a73c-4cb5-ae8c-a3620203aed9">a topic on the matter</a>.</p>
<p>With a COM TreatAs feature activated, the class&#8217;s behavior to instantiate instead of DirectShow&#8217;s CLSID_FilterGraph is restored, and in particular the DLL generates <em>FilterGraphSpy.log</em> log file on filter graph activity. Note that log file location is OS dependent (due to Vista&#8217;s permissions and file system virtualization):</p>
<ul>
<li>pre-Vista OS: root of syste drive, typically <em>C:\</em></li>
<li>starting Vista, administrator with elevated privileges: <em>CSIDL_COMMON_APPDATA</em>, typically <em>C:\ProgramData</em> (note this directory is hidden by deafult)</li>
<li>starting Vista, without elevated administrator privileges: <em>CSIDL_LOCAL_APPDATA</em>, typically <em>C:\Users\$(UserName)\AppData\Local</em> (note that <em>AppData</em> directory is hidden by deafult)</li>
</ul>
<p>Still even with the log file generated and indicating activation of the spy, it was unable to connect to remote graph through the running object table (ROT). It appeared that the ROT entires are there where expected, it was OK to get an object from ROT and the problem came from QueryInterface code:</p>
<pre>CComPtr<span style="color: #800080;">&lt;</span>IUnknown<span style="color: #800080;">&gt;</span> pFilterGraphUnknown<span style="color: #800080;">;</span>
ATLENSURE_SUCCEEDED<span style="color: #808030;">(</span>pRunningObjectTable<span style="color: #808030;">-</span><span style="color: #808030;">&gt;</span><span style="color: #400000;">GetObject</span><span style="color: #808030;">(</span>pMoniker<span style="color: #808030;">,</span> <span style="color: #808030;">&amp;</span>pFilterGraphUnknown<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
CComQIPtr<span style="color: #800080;">&lt;</span>IMyFilterGraph<span style="color: #800080;">&gt;</span> pFilterGraph <span style="color: #808030;">=</span> pFilterGraphUnknown<span style="color: #800080;">;</span> <span style="color: #696969;">// <span style="color: #ff0000;">E_NOINTERFACE</span></span></pre>
<p>The call reached the original object but COM subsystem was unable to marshal the interface through apartments to enable interprocess communication on it. The reason for this is absence of <em>PSFactoryBuffer</em> class (CLSID {92A3A302-DA7C-4A1F-BA7E-1802BB5D2D02}), which provides proxy/stub pairs for marshaling well known DirectShow interfaces in the Vista&#8217;s version of <em>quartz.dll</em>. <a href="http://social.msdn.microsoft.com/Forums/en-US/windowsdirectshowdevelopment/thread/5079f1c5-1275-4449-a11c-3cbb820e33eb">As mentioned by Microsoft&#8217;s Mike Wasson</a>, this class was moved from <em>quartz.dll</em> into Vista SDK&#8217;s <em>proppage.dll</em>, so in order to obtain connectivity to remote DirectShow graphs starting Vista, one needs to install this DLL with <a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=E6E1C3DF-A74F-4207-8586-711EBE331CDC&amp;displaylang=en">Windows SDK</a>, or otherwise have it registered with the operating system.</p>
<p>Also note that DirectShow-enabled applications that have their filter graphs published on ROT will need an utility application such as <a href="http://msdn.microsoft.com/en-us/library/ms787460(VS.85).aspx">GraphEdit</a> started with the same permissions (elevated or not) in order to be able to access ROT entires.</p>
<div style="background-color: #efa; border: 1px solid #ffc; padding: 2px; ">To sum everything up, to install Alax.Info DirectShow Filter Graph Spy on Vista:<br/></p>
<ul>
<li>get the latest <em>FilterGraphSpy.dll</em></li>
<li>regsvr32 <em>FilterGraphSpy.dll</em> on target system from administrative command prompt, with elevated privileges (note you should have a log file <em>FilterGraphSpy.log</em> generated in CSIDL_COMMON_APPDATA directory, with a few lines indicating registration success)</li>
<li>get <a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=E6E1C3DF-A74F-4207-8586-711EBE331CDC&amp;displaylang=en">Windows SDK</a> and make <em>$(WindowsSDK)\Bin\proppage.dll</em> file registered on target system (also administrative regsvr32), note that it is necessary to restart DirectShow-enabled applications and GraphEdit after DLL registration to get graphs visible through ROT</li>
<li>mind the log file directories with <em>FilterGraphSpy.log</em> file</li>
</ul>
</div>
<p>A partial Visual C++ .NET 2008 source code is <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/FilterGraphSpy">available from SVN</a>, release binary <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/FilterGraphSpy/Release%20Trace/FilterGraphSpy.dll?format=raw">included</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://alax.info/blog/944/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MediaTools: Tone Source Filter to generate reference audio feed, dual Audio Source Filter and Virtual Audio Capture Device</title>
		<link>http://alax.info/blog/859</link>
		<comments>http://alax.info/blog/859#comments</comments>
		<pubDate>Sat, 28 Feb 2009 09:40:19 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[ATL]]></category>
		<category><![CDATA[Audio]]></category>
		<category><![CDATA[Media Tools]]></category>
		<category><![CDATA[Source]]></category>
		<category><![CDATA[WTL]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[capture]]></category>
		<category><![CDATA[device]]></category>
		<category><![CDATA[DirectShow]]></category>
		<category><![CDATA[filter]]></category>
		<category><![CDATA[PCM]]></category>
		<category><![CDATA[tone]]></category>
		<category><![CDATA[virtual]]></category>

		<guid isPermaLink="false">http://alax.info/blog/?p=859</guid>
		<description><![CDATA[In order to obtain a reference audio source and especially useful for debugging purposes, including:

audio input device unrelated to physical device, to avoid conditions when device is already in use by someone else
non-zero audio signal which is independent of certain speaker or broadcasting service, including one that makes capture, transmission or rendering issues easily perceptible [...]]]></description>
			<content:encoded><![CDATA[<p>In order to obtain a reference audio source and especially useful for debugging purposes, including:</p>
<ul>
<li>audio input device unrelated to physical device, to avoid conditions when device is already in use by someone else</li>
<li>non-zero audio signal which is independent of certain speaker or broadcasting service, including one that makes capture, transmission or rendering issues easily perceptible by human</li>
<li>configurable to provide different audio media types, including through well known DirectShow interfaces, such as <a href="http://msdn.microsoft.com/en-us/library/dd319784(VS.85).aspx">IAMStreamConfig</a></li>
<li>configurable to present in system as both regular <a href="http://msdn.microsoft.com/en-us/library/dd375464(VS.85).aspx">DirectShow Filter</a>, and as an <a href="http://msdn.microsoft.com/en-us/library/dd375655(VS.85).aspx">Audio Capture Source</a> which can be enumerated by applications</li>
<li>to be able to check how exactly other applications are accessing and configuring audio capture source</li>
</ul>
<p>I added a new filter to the Media Tools library, a Tone Source Filter. The filter generates <a href="http://en.wikipedia.org/wiki/Pulse-code_modulation">PCM Audio</a> data, either infinite sine waveform, or interrupted signal  of requested parameters:</p>
<p><img class="alignnone size-full wp-image-863" title="Tone Source Filter General Property Page" src="http://alax.info/blog/wp-content/uploads/2009/02/27-image011.png" alt="Tone Source Filter General Property Page" width="370" height="428" /></p>
<p>The filter accepts connection on <a href="http://en.wikipedia.org/wiki/Pulse-code_modulation">PCM</a> media types with flexible sampling rates in range 8 KHz through 200 KHz, 8-bit audio and 1 channel. Media type is also configurable through or, if not configured this way, the first enumerated media type off the Output pin is also configurable through property page and/or persistent setting accessible via private IDispatch derived interface property.</p>
<p><img class="alignnone size-full wp-image-864" title="Tone Source Filter Prefered Format Property Page" src="http://alax.info/blog/wp-content/uploads/2009/02/27-image012.png" alt="Tone Source Filter Prefered Format Property Page" width="370" height="427" /></p>
<p>Additionally, the filter may configured as a system-wide available <a href="http://msdn.microsoft.com/en-us/library/ms783347(VS.85).aspx">audio input device</a>, such as for example available to <a href="http://videolan.org">VLC Media Player</a> or <a href="http://sourceforge.net/projects/guliverkli/">Media Player Classic</a>:</p>
<p><img class="alignnone size-full wp-image-865" title="VLC Media Player's Option to use Tone Source Filter based device" src="http://alax.info/blog/wp-content/uploads/2009/02/27-image013.png" alt="VLC Media Player's Option to use Tone Source Filter based device" width="528" height="352" /></p>
<p>In order to configure the filter as such device, the library exports function <em>DoToneSourceFilterDevicePropertySheetModal</em>, which can be called using <em>rundll32</em> utility (<em>&#8220;rundll32 Acquisition.dll,DoToneSourceFilterDevicePropertySheetModal&#8221;</em> from command line):</p>
<p><img class="alignnone size-full wp-image-866" title="Devices Property Page" src="http://alax.info/blog/wp-content/uploads/2009/02/27-image014.png" alt="Devices Property Page" width="367" height="426" /></p>
<p>A partial Visual C++ .NET 2008 source code is <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/Extended7zShell">available from SVN</a>, release binary <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/MediaTools/_Bin/Release%20Trace/Acquisition.dll?format=raw">included</a>.</p>
<h2>File and Class Summary</h2>
<h3>Acqusition.dll</h3>
<p>Acqusition.dll (<a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/MediaTools/_Bin/Release%20Trace/Acquisition.dll?format=raw">download</a>) hosts the following classes:</p>
<ul>
<li>DirectShow Filters
<ul>
<li><span style="text-decoration: underline;">Tone Source Filter</span>, to generate reference/debug audio data</li>
</ul>
</li>
</ul>
<h2>Class Overview</h2>
<h3>Tone Source Filter</h3>
<p>The filter generates uninterrupted or interrupted reference sine waveform in form of PCM audio data.</p>
<ul>
<li>Filter Executable: Acquisition.dll</li>
<li>Filter CLSID: __uuidof(ToneSourceFilter) {8002935A-B2EC-40ef-968C-E0358E5DED10}</li>
<li>Filter Property Page CLSID: __uuidof(ToneSourceFilterGeneralPropertyPage), {EBD8ABB2-6DD3-4c54-A7F1-9FE4DA283EDF}, ToneSourceFilterPreferedFormatPropertyPage, {EE224187-4FA3-4c3f-9D5D-492694CCFEB7}</li>
<li>Filter Merit: <a href="http://msdn.microsoft.com/en-us/library/ms787275%28VS.85%29.aspx">MERIT_DO_NOT_USE</a></li>
<li>Filter Category: <a href="http://msdn.microsoft.com/en-us/library/ms783347%28VS.85%29.aspx">CLSID_LegacyAmFilterCategory</a> (also configurable as <a href="http://msdn.microsoft.com/en-us/library/ms783347(VS.85).aspx">CLSID_AudioInputDeviceCategory</a>)</li>
<li>Filter Interfaces: <a href="http://msdn.microsoft.com/en-us/library/ms682273%28VS.85%29.aspx">IPersistStreamInit</a>, <a href="http://msdn.microsoft.com/en-us/library/ms690091%28VS.85%29.aspx">IPersistStream</a>, <a href="http://msdn.microsoft.com/en-us/library/ms695217%28VS.85%29.aspx">ISpecifyPropertyPages</a>, <a href="http://msdn.microsoft.com/en-us/library/aa768205(VS.85).aspx">IPersistPropertyBag</a>, <a href="http://msdn.microsoft.com/en-us/library/ms784601%28VS.85%29.aspx">IBaseFilter</a>, <a href="http://msdn.microsoft.com/en-us/library/ms785914%28VS.85%29.aspx">IMediaFilter</a>, <a href="http://msdn.microsoft.com/en-us/library/ms688695%28VS.85%29.aspx">IPersist</a>, <a href="http://msdn.microsoft.com/en-us/library/ms784083%28VS.85%29.aspx">IAMovieSetup</a>, <a href="http://msdn.microsoft.com/en-us/library/ms783950%28VS.85%29.aspx">IAMFilterMiscFlags</a>, IToneSourceFilter, <a href="http://msdn.microsoft.com/en-us/library/ms221608.aspx">IDispatch</a></li>
<li>Filter Pins: single ouput pin (Ouput)</li>
<li>Output Pin Media Types: MEDIATYPE_Audio/MEDIASUBTYPE_PCM</li>
<li>Output Pin Interfaces: <a href="http://msdn.microsoft.com/en-us/library/ms786565%28VS.85%29.aspx">IPin</a>, <a href="http://msdn.microsoft.com/en-us/library/ms786054%28VS.85%29.aspx">IMemInputPin</a>, <a href="http://msdn.microsoft.com/en-us/library/dd319764(VS.85).aspx">IAMPushSource</a>, <a href="http://msdn.microsoft.com/en-us/library/dd389383(VS.85).aspx">IAMLatency</a>, <a href="http://msdn.microsoft.com/en-us/library/dd389142(VS.85).aspx">IAMBufferNegotiation</a>, <a href="http://msdn.microsoft.com/en-us/library/bb174586(VS.85).aspx">IKsPropertySet</a>, <a href="http://msdn.microsoft.com/en-us/library/dd319784(VS.85).aspx">IAMStreamConfig</a></li>
</ul>
<h4>Remarks</h4>
<p>The hosting library (DLL) exposes <em>DoToneSourceFilterDevicePropertySheetModal</em> function which provides GUI to configure the filter as one or more audio capture source device, which can be enumerated by <a href="http://msdn.microsoft.com/en-us/library/dd407180(VS.85).aspx">System Device Enumerator</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://alax.info/blog/859/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MediaTools: HTTP Stream File Renderer Filter and more</title>
		<link>http://alax.info/blog/817</link>
		<comments>http://alax.info/blog/817#comments</comments>
		<pubDate>Sat, 14 Feb 2009 18:08:53 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[ATL]]></category>
		<category><![CDATA[Media Tools]]></category>
		<category><![CDATA[Source]]></category>
		<category><![CDATA[Video]]></category>
		<category><![CDATA[WTL]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[camera]]></category>
		<category><![CDATA[DirectShow]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[JPEG]]></category>
		<category><![CDATA[M-JPEG]]></category>

		<guid isPermaLink="false">http://alax.info/blog/?p=817</guid>
		<description><![CDATA[In order to better troubleshoot network cameras and other devices, I added a new DirectShow filter to write HTTP response headers and body into a single file, one file (actually a pairs of files, the second file contains UTF-8 formatted HTTP response headers) per HTTP response.
While troubleshootting JPEG artefact issue with I used the following [...]]]></description>
			<content:encoded><![CDATA[<p>In order to better troubleshoot network cameras and other devices, I added a new DirectShow filter to write HTTP response headers and body into a single file, one file (actually a pairs of files, the second file contains UTF-8 formatted HTTP response headers) per HTTP response.</p>
<p>While troubleshootting <a href="http://ijg.org">JPEG</a> artefact issue with I used the following graph:</p>
<p><a href="http://alax.info/blog/wp-content/uploads/2009/02/14-image001.png"><img class="alignnone size-medium wp-image-818" title="HTTP Stream File Renderer Filter in Filter Graph" src="http://alax.info/blog/wp-content/uploads/2009/02/14-image001-300x188.png" alt="HTTP Stream File Renderer Filter in Filter Graph" width="300" height="188" /></a></p>
<p>which started <a href="http://en.wikipedia.org/wiki/Motion_JPEG#M-JPEG_over_HTTP">M-JPEG</a> streaming from a camera and had entire stream written into <em>Response-01586228-0001-0001-Headers.txt</em> (Headers) and <em>Response-01586228-0001-0001.dat</em> (Data) files, while parsed JPEG frames were also written into individual files and were available for comparison.</p>
<p>Other changes include:</p>
<ul>
<li>new HTTP Stream File Renderer Filter (details are below)</li>
<li>attachable media types on internally used memory allocators, which enable filters to effectively reagree media types in running state</li>
<li>workaround for <a href="http://panasonic.co.jp/pcc/products/en/netwkcam/lineup/bl-c140/index.html">Panasonic BL-C140</a> camera (may be also helpful for other models), details <a href="http://alax.info/blog/741/comment-page-1#comment-5669">here</a></li>
</ul>
<p>There also seems to be a bug in <a href="http://www.intel.com/cd/software/products/asmo-na/eng/302910.htm">Intel IPP 6.0</a> JPEG decoder that leaves bottom right image block incorrectly decoded, only when a sequence of images is decoded and the frame is not the first in the sequence.</p>
<p>A partial Visual C++ .NET 2008 source code is <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/Extended7zShell">available from SVN</a>, release binary <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/MediaTools/_Bin/Release Trace/Acquisition.dll?format=raw">included</a>.</p>
<p><span id="more-817"></span></p>
<h2>File and Class Summary</h2>
<h3>Acqusition.dll</h3>
<p>Acqusition.dll (<a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/MediaTools/_Bin/Release Trace/Acquisition.dll?format=raw">download</a>) hosts the following classes:</p>
<ul>
<li>DirectShow Filters
<ul>
<li><span style="text-decoration: underline;">HTTP Stream File Renderer Filter</span>, to write raw HTTP response headers and data into files</li>
</ul>
</li>
</ul>
<h2>Class Overview</h2>
<h3>HTTP Stream File Renderer Filter</h3>
<p>The filter renders input stream of one or more HTTP responses and write HTTP response headers and data into files, per HTTP response, into provided directory.</p>
<ul>
<li>Filter Executable: Acquisition.dll</li>
<li>Filter CLSID: __uuidof(HttpStreamFileRendererFilter) {B02FDAEF-E851-4380-82D6-04772E33675D}</li>
<li>Filter Property Page CLSID: __uuidof(HttpStreamFileRendererFilterDirectoryPropertyPage), {FAF31D20-842A-46b4-B6F8-E410FB0188A5}</li>
<li>Filter Merit: <a href="http://msdn.microsoft.com/en-us/library/ms787275(VS.85).aspx">MERIT_DO_NOT_USE</a></li>
<li>Filter Category: <a href="http://msdn.microsoft.com/en-us/library/ms783347(VS.85).aspx">CLSID_LegacyAmFilterCategory</a></li>
<li>Filter Interfaces: <a href="http://msdn.microsoft.com/en-us/library/ms682273(VS.85).aspx">IPersistStreamInit</a>, <a href="http://msdn.microsoft.com/en-us/library/ms690091(VS.85).aspx">IPersistStream</a>, <a href="http://msdn.microsoft.com/en-us/library/ms695217(VS.85).aspx">ISpecifyPropertyPages</a>, <a href="http://msdn.microsoft.com/en-us/library/ms784601(VS.85).aspx">IBaseFilter</a>, <a href="http://msdn.microsoft.com/en-us/library/ms785914(VS.85).aspx">IMediaFilter</a>, <a href="http://msdn.microsoft.com/en-us/library/ms688695(VS.85).aspx">IPersist</a>, <a href="http://msdn.microsoft.com/en-us/library/ms683857.aspx"></a><a href="http://msdn.microsoft.com/en-us/library/ms784083(VS.85).aspx">IAMovieSetup</a>, <a href="http://msdn.microsoft.com/en-us/library/ms783950(VS.85).aspx">IAMFilterMiscFlags</a>, IHttpStreamFileRendererFilter, <a href="http://msdn.microsoft.com/en-us/library/ms221608.aspx">IDispatch</a>, <a href="http://msdn.microsoft.com/en-us/library/ms785712.aspx">IFileSinkFilter2</a> (not available in graphedt.exe process), <a href="http://msdn.microsoft.com/en-us/library/ms785715(VS.85).aspx">IFileSinkFilter</a> (not available in graphedt.exe process)</li>
<li>Filter Pins: single input pin (Input)</li>
<li>Input Pin Media Types: MEDIATYPE_Stream/MEDIASUBTYPE_NULL</li>
<li>Input Pin Interfaces: <a href="http://msdn.microsoft.com/en-us/library/ms786565(VS.85).aspx">IPin</a>, <a href="http://msdn.microsoft.com/en-us/library/ms786054(VS.85).aspx">IMemInputPin</a></li>
</ul>
<h4>Remarks</h4>
<p>None.</p>
]]></content:encoded>
			<wfw:commentRss>http://alax.info/blog/817/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>More on DirectShow Filter Graph Spy</title>
		<link>http://alax.info/blog/793</link>
		<comments>http://alax.info/blog/793#comments</comments>
		<pubDate>Sat, 07 Feb 2009 09:55:30 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[ATL]]></category>
		<category><![CDATA[Source]]></category>
		<category><![CDATA[Utilities]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[DirectShow]]></category>
		<category><![CDATA[filter]]></category>
		<category><![CDATA[graph]]></category>
		<category><![CDATA[spy]]></category>

		<guid isPermaLink="false">http://alax.info/blog/?p=793</guid>
		<description><![CDATA[Having DirectShow Filter Graph Spy installed in the system, I noticed a new and weird effect that when Media Player Classic plays a list of files and each MP3 file being currently decoded shows decoder&#8217;s system tray icon, a switch to a new file leaves an old icon in place and adds a new icon [...]]]></description>
			<content:encoded><![CDATA[<p>Having <a href="http://alax.info/blog/784">DirectShow Filter Graph Spy</a> installed in the system, I noticed a new and weird effect that when <a href="http://sourceforge.net/projects/guliverkli/">Media Player Classic</a> plays a list of files and each MP3 file being currently decoded shows decoder&#8217;s system tray icon, a switch to a new file leaves an old icon in place and adds a new icon for a new decoder.  I thought this might be also a decoder&#8217;s problem which I also recently updated to a new version, but then I noticed other artifacts, with other software and again with <a href="http://sourceforge.net/projects/guliverkli/">Media Player Classic</a>, that if I drop a new file onto the player and it starts playing it, old file is still being played as if player just releases all referenced to the graph and never calls <a href="http://msdn.microsoft.com/en-us/library/dd390178(VS.85).aspx">IMediaControl::Stop</a> to force stop of playback.</p>
<p>It was clear at that point that it is related to filter graph spy and the problem needs a fix. The problem definitely looked like the spy leave outstanding references to the filter graph and when the application releases graph and it is supposed to be automatically destroyed, it is still being alive and possibly, such as in case of <a href="http://sourceforge.net/projects/guliverkli/">Media Player Classic</a>, even running.</p>
<p>The first clue was that being published to running object table (ROT), the object gets more than one additional reference on it and as such it never gets removed from ROT until application terminates as ROT keeps a reference to the graph and there is noone to remove it. By original design spy published filter graph on ROT and removed one reference immediately after that to compensate the ROT&#8217;s reference (one only) making it a weak reference. So when all references are released from the filter graph but ROT reference, the graph gets destroyed and in <a href="http://msdn.microsoft.com/en-us/library/ea0ca499.aspx">FinalRelease</a> class on the COM object we re-add the reference back and automatically remove the object from ROT as well. The whole scheme works in assumption that ROT adds one reference only, if there are more then the object is referenced by the ROT forever.</p>
<p>Debugging however showed that ROT adds a single reference and there is no problem from this side. However I immediately noticed  another weird behavior in the same method <a href="http://msdn.microsoft.com/en-us/library/afkt56xx.aspx">FinalConstruct</a>. An underlying (real) filter graph object, i.e. CFilterGraph from quartz.dll, is adding a reference on the outer COM object when being queried for a private interface from <a href="http://msdn.microsoft.com/en-us/library/ms810016.aspx">implicit</a> (also known as main, raw) IUnknown when created as aggregated object. And it is not exactly the behavior one would expect from aggregated object because it basically break proper reference counting. I realize that aggregating is a rather rare technique and DirectShow COM base is one of the very first COM bases at all which then stopped being developed, so perhaps this problem was not identified on time and later left unfixed at all, so in present situation there should rather be a workaround.</p>
<p>It is also worth mentioning that there are two scenarios involved. My <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/FilterGraphSpy/Spy.h">CSpy</a> object creates DirectShow <a href="http://msdn.microsoft.com/en-us/library/dd375786(VS.85).aspx">CFilterGraph</a> as an aggregated object. And CSpy in its turn may be created as aggregated too. For example, <a href="http://www.microsoft.com/windows/windowsmedia/players.aspx">Windows Media Player</a> creates (as most applications in this universe do) filter graph object the regular way, as a standalone COM object. However it appears that <a href="http://sourceforge.net/projects/guliverkli/">Media Player Classic</a> creates filter graph as aggregated, for whatever it needs it for. Because of the discovered problem inner CFilterGraph adds an outstanding reference on the top level &#8220;controlling IUnknown&#8221;, which is either CSpy&#8217;s private IUnkown in case of <a href="http://www.microsoft.com/windows/windowsmedia/players.aspx">Windows Media Player</a>, or higher level IUnknown in case of <a href="http://sourceforge.net/projects/guliverkli/">Media Player Classic</a>.</p>
<p>So the proper workaround is to release unwanted references from controlling unknown rather than self:</p>
<pre>CComPtr<span style="color: #800080;">&lt;</span>IUnknown<span style="color: #800080;">&gt;</span> pUnknown<span style="color: #800080;">;</span>
ATLENSURE_SUCCEEDED<span style="color: #808030;">(</span>pClassFactory<span style="color: #808030;">-</span><span style="color: #808030;">&gt;</span>CreateInstance<span style="color: #808030;">(</span>pControllingUnknown<span style="color: #808030;">,</span> <span style="color: #800000; font-weight: bold;">__uuidof</span><span style="color: #808030;">(</span>IUnknown<span style="color: #808030;">)</span><span style="color: #808030;">,</span> <span style="color: #808030;">(</span><span style="color: #603000;">VOID</span><span style="color: #808030;">*</span><span style="color: #808030;">*</span><span style="color: #808030;">)</span> <span style="color: #808030;">&amp;</span>pUnknown<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
<span style="color: #696969;">// NOTE: DirectShow FilterGraph is incorrectly implementing COM aggregation adding outer reference to interfaces queried from private IUnknown</span>
CComQIPtr<span style="color: #800080;">&lt;</span>IFilterGraph2<span style="color: #800080;">&gt;</span> pFilterGraph2 <span style="color: #808030;">=</span> pUnknown<span style="color: #800080;">;</span>
ATLENSURE_THROW<span style="color: #808030;">(</span>pFilterGraph2<span style="color: #808030;">,</span> E_NOINTERFACE<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
pFilterGraph2<span style="color: #808030;">.</span>p<span style="color: #808030;">-</span><span style="color: #808030;">&gt;</span>Release<span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span> <span style="color: #696969;">// &lt;&lt;--------------------------</span></pre>
<p>It looked like the problem is finally worked around but in aggregated creation scenario it brought another problem up. Since instantiation of inner CFilterGraph takes place in FinalConstruct and CSpy is also being instantiated as aggregated, it appears that outer COM object is sensible to manipulation with its reference counter. CFilterGraph added a reference it should not, CSpy released it in compensation but on this early stage the compensating Release might zero reference counter and might initiate unexpected object destruction. It is not necessarily this way as it depends on outer COM object base, but it seems that Media Player Classic does not do <a href="http://msdn.microsoft.com/en-us/library/97c868sh.aspx">DECLARE_PROTECT_FINAL_CONSTRUCT</a> or its equavalent and things go the worst scenario.</p>
<p>CSpy is an inner object and we don&#8217;t have any external instance to hold a temporary reference for us while outer COM object is being created, so a workround for this is to temporarily hold a circular reference to an outer object to let it complete its instanatiation and pass another reference higher up to the controlling application. At which point we are going to be safe to release the temporary reference and normalize reference counters.</p>
<pre><span style="color: #800000; font-weight: bold;">if</span><span style="color: #808030;">(</span>m_bIsAggregated<span style="color: #808030;">)</span>
<span style="color: #800080;">{</span>
    pControllingUnknown<span style="color: #808030;">.</span>p<span style="color: #808030;">-</span><span style="color: #808030;">&gt;</span>AddRef<span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    <span style="color: #800000; font-weight: bold;">const</span> <span style="color: #603000;">ULONG</span> nReferenceCount <span style="color: #808030;">=</span> pControllingUnknown<span style="color: #808030;">.</span>p<span style="color: #808030;">-</span><span style="color: #808030;">&gt;</span>Release<span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    <span style="color: #800000; font-weight: bold;">if</span><span style="color: #808030;">(</span>nReferenceCount <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">1</span><span style="color: #808030;">)</span>
        m_pTemporaryUnknown <span style="color: #808030;">=</span> pControllingUnknown<span style="color: #800080;">;</span>
<span style="color: #800080;">}</span></pre>
<p>The only thing is left is to release the temporary reference early enough, but we have a filter graph, don&#8217;t we? What is typically done with a filter graph, adding a filter right? And it is a controlling application which has its own reference, who adds filters so we are safe to release temporary reference in <a href="http://msdn.microsoft.com/en-us/library/dd390016(VS.85).aspx">IFilterGraph::AddFilter</a> or <a href="http://msdn.microsoft.com/en-us/library/dd390087(VS.85).aspx">IGraphBuilder::AddSourceFilter</a> (actually it would not hurt to add this in other methods as well):</p>
<pre>STDMETHOD<span style="color: #808030;">(</span>AddFilter<span style="color: #808030;">)</span><span style="color: #808030;">(</span>IBaseFilter<span style="color: #808030;">*</span> pFilter<span style="color: #808030;">,</span> <span style="color: #603000;">LPCWSTR</span> pszName<span style="color: #808030;">)</span> <span style="color: #800000; font-weight: bold;">throw</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span>
<span style="color: #800080;">{</span>
    _Z4<span style="color: #808030;">(</span>atlTraceCOM<span style="color: #808030;">,</span> <span style="color: #008c00;">4</span><span style="color: #808030;">,</span> _T<span style="color: #808030;">(</span><span style="color: #800000;">"</span><span style="color: #0000e6;">pszName </span><span style="color: #0f69ff;">\"</span><span style="color: #0f69ff;">%s</span><span style="color: #0f69ff;">\"</span><span style="color: #0f69ff;">\n</span><span style="color: #800000;">"</span><span style="color: #808030;">)</span><span style="color: #808030;">,</span> <span style="color: #603000;">CString</span><span style="color: #808030;">(</span>pszName<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    ReleaseTemporaryUnknown<span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span> <span style="color: #696969;">// &lt;&lt;--------------------------</span>
    <span style="color: #800000; font-weight: bold;">return</span> m_pInnerFilterGraph2<span style="color: #808030;">-</span><span style="color: #808030;">&gt;</span>AddFilter<span style="color: #808030;">(</span>pFilter<span style="color: #808030;">,</span> pszName<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
<span style="color: #800080;">}</span></pre>
<p>Additionally, the spy also implements now <a href="http://msdn.microsoft.com/en-us/library/dd390170(VS.85).aspx">IMediaControl</a> interface and traces calls to log file, it is convenient.</p>
<p>A partial Visual C++ .NET 2008 source code is <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/FilterGraphSpy">available from SVN</a>, release binary <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/FilterGraphSpy/Release%20Trace/FilterGraphSpy.dll?format=raw">included</a>.</p>
<p>reg-FilterGraphSpy.dll.bat and unreg-FilterGraphSpy.dll.bat files <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/FilterGraphSpy/Release%20Trace">in SVN</a> are convenient batch files to register and unregister the spy with the operating system.</p>
]]></content:encoded>
			<wfw:commentRss>http://alax.info/blog/793/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>DirectShow Filter Graph Spy &#8211; CLSID_FilterGraphNoThread</title>
		<link>http://alax.info/blog/784</link>
		<comments>http://alax.info/blog/784#comments</comments>
		<pubDate>Thu, 29 Jan 2009 21:11:45 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[ATL]]></category>
		<category><![CDATA[Source]]></category>
		<category><![CDATA[Utilities]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[DirectShow]]></category>
		<category><![CDATA[filter]]></category>
		<category><![CDATA[graph]]></category>
		<category><![CDATA[spy]]></category>

		<guid isPermaLink="false">http://alax.info/blog/?p=784</guid>
		<description><![CDATA[At the time of original implementation I intentionally left off a variation of Filter Graph Manager that runs on application thread, CLSID_FilterGraphNoThread. MSDN says:
CLSID_FilterGraphNoThread creates the Filter Graph Manager on the application&#8217;s thread. If you use this CLSID, the thread that calls CoCreateInstance must have a message loop that dispatches messages; otherwise, deadlocks can occur. [...]]]></description>
			<content:encoded><![CDATA[<p>At the time of <a href="http://alax.info/blog/777">original implementation</a> I intentionally left off a variation of <a href="http://msdn.microsoft.com/en-us/library/dd375786(VS.85).aspx">Filter Graph Manager</a> that runs on application thread, CLSID_FilterGraphNoThread. MSDN says:</p>
<p style="padding-left: 30px;">CLSID_FilterGraphNoThread creates the Filter Graph Manager on the application&#8217;s thread. If you use this CLSID, the thread that calls CoCreateInstance must have a message loop that dispatches messages; otherwise, deadlocks can occur. Also, before the application thread exits, it must release the Filter Graph Manager and all graph objects (such as filters, pins, reference clocks, and so forth).</p>
<p>I have never needed to use this CLSID (by the way Windows SDK has one more CLSID &#8211; CLSID_FilterGraphPrivateThread &#8211; but it does not seem to be documented) and for this reason I left it for the time a need in it comes up. However it appeared that this CLSID was not created just for fun, there was a need in it. Filter graphs of Windows Media Player playing well known Windows&#8217; clock.avi appeared to be not published on <a href="http://msdn.microsoft.com/en-us/library/ms684004%28VS.85%29.aspx">Running Object Table</a>. Why?</p>
<p><a href="http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx">Process Monitor</a> showed clearly that Windows Media Player created filter graph through CLSID_FilterGraphNoThread and quite obviously it was not intercepted by <a href="http://alax.info/blog/777">Filter Graph Spy</a> (still I wonder what made it different previous time when I could see WMP&#8217;s graph).</p>
<p><a href="http://alax.info/blog/wp-content/uploads/2009/01/29-image001.png"><img class="alignnone size-medium wp-image-785" title="Another Windows Media Player Filter Graph" src="http://alax.info/blog/wp-content/uploads/2009/01/29-image001-300x119.png" alt="" width="300" height="119" /></a></p>
<p><span id="more-784"></span></p>
<p>In order to extend spy&#8217;s support to CLSID_FilterGraphNoThread I moved most of the code from CSpy COM class into CSpyT template class and derived two COM classes from it, CSpy and CNoThreadSpy, in order to cover and &#8220;COM Treat As&#8221; both DirectShow/Quartz COM classes.</p>
<pre><span style="color: #3f5fbf;">////////////////////////////////////////////////////////////</span>
<span style="color: #696969;">// CSpy</span>

<span style="color: #800000; font-weight: bold;">class</span> ATL_NO_VTABLE CSpy <span style="color: #800080;">:</span>
    <span style="color: #800000; font-weight: bold;">public</span> CSpyT<span style="color: #800080;">&lt;</span>CSpy<span style="color: #808030;">,</span> <span style="color: #808030;">&amp;</span>CLSID_FilterGraph<span style="color: #800080;">&gt;</span><span style="color: #808030;">,</span>
    <span style="color: #800000; font-weight: bold;">public</span> CComCoClass<span style="color: #800080;">&lt;</span>CSpy<span style="color: #808030;">,</span> <span style="color: #808030;">&amp;</span>CLSID_Spy<span style="color: #800080;">&gt;</span>
<span style="color: #800080;">{</span>
<span style="color: #800000; font-weight: bold;">public</span><span style="color: #e34adc;">:</span>
    <span style="color: #800000; font-weight: bold;">enum</span> <span style="color: #800080;">{</span> IDR <span style="color: #808030;">=</span> IDR_SPY <span style="color: #800080;">}</span><span style="color: #800080;">;</span>

<span style="color: #800000; font-weight: bold;">public</span><span style="color: #e34adc;">:</span>
<span style="color: #696969;">// CSpy</span>
<span style="color: #800080;">}</span><span style="color: #800080;">;</span>

OBJECT_ENTRY_AUTO<span style="color: #808030;">(</span><span style="color: #800000; font-weight: bold;">__uuidof</span><span style="color: #808030;">(</span>Spy<span style="color: #808030;">)</span><span style="color: #808030;">,</span> CSpy<span style="color: #808030;">)</span>

<span style="color: #3f5fbf;">////////////////////////////////////////////////////////////</span>
<span style="color: #696969;">// CNoThreadSpy</span>

<span style="color: #800000; font-weight: bold;">class</span> ATL_NO_VTABLE CNoThreadSpy <span style="color: #800080;">:</span>
    <span style="color: #800000; font-weight: bold;">public</span> CSpyT<span style="color: #800080;">&lt;</span>CNoThreadSpy<span style="color: #808030;">,</span> <span style="color: #808030;">&amp;</span>CLSID_FilterGraphNoThread<span style="color: #800080;">&gt;</span><span style="color: #808030;">,</span>
    <span style="color: #800000; font-weight: bold;">public</span> CComCoClass<span style="color: #800080;">&lt;</span>CNoThreadSpy<span style="color: #808030;">,</span> <span style="color: #808030;">&amp;</span>CLSID_NoThreadSpy<span style="color: #800080;">&gt;</span>
<span style="color: #800080;">{</span>
<span style="color: #800000; font-weight: bold;">public</span><span style="color: #e34adc;">:</span>
    <span style="color: #800000; font-weight: bold;">enum</span> <span style="color: #800080;">{</span> IDR <span style="color: #808030;">=</span> IDR_NOTHREADSPY <span style="color: #800080;">}</span><span style="color: #800080;">;</span>

<span style="color: #800000; font-weight: bold;">public</span><span style="color: #e34adc;">:</span>
<span style="color: #696969;">// CNoThreadSpy</span>
<span style="color: #800080;">}</span><span style="color: #800080;">;</span>

OBJECT_ENTRY_AUTO<span style="color: #808030;">(</span><span style="color: #800000; font-weight: bold;">__uuidof</span><span style="color: #808030;">(</span>NoThreadSpy<span style="color: #808030;">)</span><span style="color: #808030;">,</span> CNoThreadSpy<span style="color: #808030;">)</span></pre>
<p>A partial Visual C++ .NET 2008 source code is <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/FilterGraphSpy">available from SVN</a>, release binary <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/FilterGraphSpy/Release%20Trace/FilterGraphSpy.dll?format=raw">included</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://alax.info/blog/784/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>DirectShow Filter Graph Spy</title>
		<link>http://alax.info/blog/777</link>
		<comments>http://alax.info/blog/777#comments</comments>
		<pubDate>Wed, 28 Jan 2009 20:46:01 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[ATL]]></category>
		<category><![CDATA[Source]]></category>
		<category><![CDATA[Utilities]]></category>
		<category><![CDATA[aggregation]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[DirectShow]]></category>
		<category><![CDATA[filter]]></category>
		<category><![CDATA[graph]]></category>
		<category><![CDATA[spy]]></category>

		<guid isPermaLink="false">http://alax.info/blog/?p=777</guid>
		<description><![CDATA[Inspired by a post on DirectShow Development Forum, it is sometimes useful to find out how certain application is using DirectShow to capture live video and/or audio, or play certain file etc. Filter graph topology, details about media types on the pins, used interfaces and call order. There is quite an easy step for the [...]]]></description>
			<content:encoded><![CDATA[<p>Inspired by <a href="http://social.msdn.microsoft.com/Forums/en-US/windowsdirectshowdevelopment/thread/4a448884-9062-4e45-b73e-2c9ab681ade0">a post on DirectShow Development Forum</a>, it is sometimes useful to find out how certain application is using DirectShow to capture live video and/or audio, or play certain file etc. Filter graph topology, details about media types on the pins, used interfaces and call order. There is quite an easy step for the developer to spy over running filter graphs system wide by connecting <a href="http://msdn.microsoft.com/en-us/library/ms680573.aspx">Component Object Model</a> (COM) &#8220;Treat As&#8221; feature with filter graph remoting through <a href="http://msdn.microsoft.com/en-us/library/ms684004(VS.85).aspx">Running Object Table</a> and connecting to remote process graphs <a href="http://msdn.microsoft.com/en-us/library/dd390650(VS.85).aspx">using GraphEdit</a> utility.</p>
<p>Who could imagine Windows Media Player to build such a filter graph for a video and audio Windows Media file? SRS Wow DMO, equalizer, analyzer and stuff?</p>
<p><a href="http://alax.info/blog/wp-content/uploads/2009/01/28-image001.png"><img class="alignnone size-medium wp-image-778" title="Windows Media Player Filter Graph" src="http://alax.info/blog/wp-content/uploads/2009/01/28-image001-300x146.png" alt="" width="300" height="146" /></a></p>
<p><span id="more-777"></span></p>
<p>There are many tricks to catch the created filter graph but the most legit is the mentioned above &#8220;Treat As&#8221; feature to emulate certain COM class identifier (CLSID) through another coclass. COM API offers <a href="http://msdn.microsoft.com/en-us/library/ms693452(VS.85).aspx">CoTreatAsClass</a> and <a href="http://msdn.microsoft.com/en-us/library/ms694494(VS.85).aspx">CoGetTreatAsClass</a> functions to manage the emulation. To emulate DirectShow&#8217;s <a href="http://msdn.microsoft.com/en-us/library/ms783670(VS.85).aspx">CLSID_FilterGraph</a> through my CSpy class, I am adding the following code to the default COM registration process for the utility DLL:</p>
<pre><span style="font-weight: bold; color: #800000;">static</span> HRESULT <span style="color: #603000;">WINAPI</span> UpdateRegistry<span style="color: #808030;">(</span><span style="color: #603000;">BOOL</span> bRegister<span style="color: #808030;">)</span> <span style="font-weight: bold; color: #800000;">throw</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span>
<span style="color: #800080;">{</span>
    _Z2<span style="color: #808030;">(</span>atlTraceRegistrar<span style="color: #808030;">,</span> <span style="color: #008c00;">2</span><span style="color: #808030;">,</span> _T<span style="color: #808030;">(</span><span style="color: #800000;">"</span><span style="color: #0000e6;">bRegister </span><span style="color: #0f69ff;">%d</span><span style="color: #0f69ff;">\n</span><span style="color: #800000;">"</span><span style="color: #808030;">)</span><span style="color: #808030;">,</span> bRegister<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    _ATLTRY
    <span style="color: #800080;">{</span>
        CLSID TreatAsClassIdentifier<span style="color: #800080;">;</span>
        HRESULT nCoGetTreatAsClassResult <span style="color: #808030;">=</span> CoGetTreatAsClass<span style="color: #808030;">(</span>CLSID_FilterGraph<span style="color: #808030;">,</span> <span style="color: #808030;">&amp;</span>TreatAsClassIdentifier<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        __C<span style="color: #808030;">(</span>nCoGetTreatAsClassResult<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        __D<span style="color: #808030;">(</span><span style="color: #808030;">!</span>bRegister <span style="color: #808030;">|</span><span style="color: #808030;">|</span> nCoGetTreatAsClassResult <span style="color: #808030;">!</span><span style="color: #808030;">=</span> S_OK <span style="color: #808030;">|</span><span style="color: #808030;">|</span> TreatAsClassIdentifier <span style="color: #808030;">=</span><span style="color: #808030;">=</span> GetObjectCLSID<span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: #808030;">,</span> E_UNNAMED<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        <span style="font-weight: bold; color: #800000;">if</span><span style="color: #808030;">(</span><span style="color: #808030;">!</span>bRegister <span style="color: #808030;">&amp;</span><span style="color: #808030;">&amp;</span> nCoGetTreatAsClassResult <span style="color: #808030;">=</span><span style="color: #808030;">=</span> S_OK<span style="color: #808030;">)</span>
            __C<span style="color: #808030;">(</span>CoTreatAsClass<span style="color: #808030;">(</span>CLSID_FilterGraph<span style="color: #808030;">,</span> CLSID_NULL<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        _A<span style="color: #808030;">(</span>_pAtlModule<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        UpdateRegistryFromResource<span style="color: #800080;">&lt;</span>CSpy<span style="color: #800080;">&gt;</span><span style="color: #808030;">(</span>bRegister<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        <span style="font-weight: bold; color: #800000;">if</span><span style="color: #808030;">(</span>bRegister<span style="color: #808030;">)</span>
            __C<span style="color: #808030;">(</span>CoTreatAsClass<span style="color: #808030;">(</span>CLSID_FilterGraph<span style="color: #808030;">,</span> GetObjectCLSID<span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    <span style="color: #800080;">}</span>
    _ATLCATCH<span style="color: #808030;">(</span>Exception<span style="color: #808030;">)</span>
    <span style="color: #800080;">{</span>
        _C<span style="color: #808030;">(</span>Exception<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    <span style="color: #800080;">}</span>
    <span style="font-weight: bold; color: #800000;">return</span> S_OK<span style="color: #800080;">;</span>
<span style="color: #800080;">}</span></pre>
<p>The idea is that the emulation is enabled in case it was not enabled before or it was already enabled through our coclass. Every time an application cocreates an instance of Filter Graph Manager (FGM), CSpy class will be created instead.</p>
<p>Still as we have no plans to reinvent one of the most important DirectShow objects, we have to embed the original FGM insde to forward the calls to. The proper place in the ATL C++ code is FinalConstruct and what we actually do there is instantiating an aggregated FGM from the original in-process server. The safest would be to query registry for the original host, but we know it&#8217;s hosted by quartz.dll and it seems to be safe to hardcode:</p>
<pre><span style="color: #603000;">HINSTANCE</span> hModule <span style="color: #808030;">=</span> CoLoadLibrary<span style="color: #808030;">(</span>_T<span style="color: #808030;">(</span><span style="color: #800000;">"</span><span style="color: #0000e6;">quartz.dll</span><span style="color: #800000;">"</span><span style="color: #808030;">)</span><span style="color: #808030;">,</span> TRUE<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
_ATLTRY
<span style="color: #800080;">{</span>
    <span style="color: #800000; font-weight: bold;">typedef</span> HRESULT <span style="color: #808030;">(</span><span style="color: #603000;">WINAPI</span> <span style="color: #808030;">*</span>DLLGETCLASSOBJECT<span style="color: #808030;">)</span><span style="color: #808030;">(</span>REFCLSID<span style="color: #808030;">,</span> REFIID<span style="color: #808030;">,</span> <span style="color: #603000;">VOID</span><span style="color: #808030;">*</span><span style="color: #808030;">*</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    DLLGETCLASSOBJECT DllGetClassObject <span style="color: #808030;">=</span> <span style="color: #808030;">(</span>DLLGETCLASSOBJECT<span style="color: #808030;">)</span> <span style="color: #400000;">GetProcAddress</span><span style="color: #808030;">(</span>hModule<span style="color: #808030;">,</span> <span style="color: #800000;">"</span><span style="color: #0000e6;">DllGetClassObject</span><span style="color: #800000;">"</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    __E<span style="color: #808030;">(</span>DllGetClassObject<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    CComPtr<span style="color: #800080;">&lt;</span>IClassFactory<span style="color: #800080;">&gt;</span> pClassFactory<span style="color: #800080;">;</span>
    __C<span style="color: #808030;">(</span>DllGetClassObject<span style="color: #808030;">(</span>CLSID_FilterGraph<span style="color: #808030;">,</span> <span style="color: #800000; font-weight: bold;">__uuidof</span><span style="color: #808030;">(</span>IClassFactory<span style="color: #808030;">)</span><span style="color: #808030;">,</span> <span style="color: #808030;">(</span><span style="color: #603000;">VOID</span><span style="color: #808030;">*</span><span style="color: #808030;">*</span><span style="color: #808030;">)</span> <span style="color: #808030;">&amp;</span>pClassFactory<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    _A<span style="color: #808030;">(</span>pClassFactory<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    CComPtr<span style="color: #800080;">&lt;</span>IUnknown<span style="color: #800080;">&gt;</span> pUnknown<span style="color: #800080;">;</span>
    __C<span style="color: #808030;">(</span>pClassFactory<span style="color: #808030;">-</span><span style="color: #808030;">&gt;</span>CreateInstance<span style="color: #808030;">(</span>GetControllingUnknown<span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: #808030;">,</span> <span style="color: #800000; font-weight: bold;">__uuidof</span><span style="color: #808030;">(</span>IUnknown<span style="color: #808030;">)</span><span style="color: #808030;">,</span> <span style="color: #808030;">(</span><span style="color: #603000;">VOID</span><span style="color: #808030;">*</span><span style="color: #808030;">*</span><span style="color: #808030;">)</span> <span style="color: #808030;">&amp;</span>pUnknown<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    CComQIPtr<span style="color: #800080;">&lt;</span>IFilterGraph2<span style="color: #800080;">&gt;</span> pFilterGraph2 <span style="color: #808030;">=</span> pUnknown<span style="color: #800080;">;</span>
    __D<span style="color: #808030;">(</span>pFilterGraph2<span style="color: #808030;">,</span> E_NOINTERFACE<span style="color: #808030;">)</span><span style="color: #800080;">;</span></pre>
<p>What is remained at the point is to implement IFilterGraph2 (along with inherited IGraphBuilder and IFilterGraph) and forward all incoming calls to the inner aggregated object. It would also be helpful to trace calls to a log file C:\FilterGraphSpy.log:</p>
<pre>2009-01-28 22:07:51	4456	5656	dllmain.h(29): CFilterGraphSpyModule::CFilterGraphSpyModule: this 0x01cd327c
2009-01-28 22:07:51	4456	5656	Spy.h(75): CSpy::CSpy: this 0x01ce297c
2009-01-28 22:07:51	4456	5656	Spy.h(132): CSpy::AddFilter: pszName "C:\Documents and Settings\John Doe\Desktop\Melissa Cherry Interview.wmv"
2009-01-28 22:07:51	4456	5656	Spy.h(142): CSpy::EnumFilters: ...
2009-01-28 22:07:51	4456	5656	Spy.h(132): CSpy::AddFilter: pszName "ffdshow Audio Decoder"
2009-01-28 22:07:51	4456	5656	Spy.h(152): CSpy::ConnectDirect: ...
2009-01-28 22:07:51	4456	5656	Spy.h(137): CSpy::RemoveFilter: ...
2009-01-28 22:07:51	4456	5656	Spy.h(132): CSpy::AddFilter: pszName "WMAudio Decoder DMO"
2009-01-28 22:07:51	4456	5656	Spy.h(152): CSpy::ConnectDirect: ...
2009-01-28 22:07:51	4456	5656	Spy.h(142): CSpy::EnumFilters: ...</pre>
<p>And additionally, the filter graph is immediately published on the ROT and is accessible with <a href="http://msdn.microsoft.com/en-us/library/dd390650(VS.85).aspx">GraphEdit</a>. Feel free to spy over graphs system wide.</p>
<p>A partial Visual C++ .NET 2008 source code is <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/FilterGraphSpy">available from SVN</a>, release binary <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/FilterGraphSpy/Release%20Trace/FilterGraphSpy.dll?format=raw">included</a>.</p>
<div style="background-color: #efa; border: 1px solid #ffc; padding: 2px; ">To install Filter Graph Spy:</p>
<ul>
<li>download the binary DLL from version control repository (<em>DirectShowSpy.dll</em> &#8211; <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/FilterGraphSpy/Release%20Trace/Win32/DirectShowSpy.dll?format=raw">Win32</a>, <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/FilterGraphSpy/Release%20Trace/x64/DirectShowSpy.dll?format=raw">x64</a>)</li>
<li>COM register the DLL on target system, for example from command line using <em>regsvr32</em> utility &#8220;<em>regsvr32 </em><em>DirectShowSpy</em><em>.dll</em>&#8221; (administrative privilege elevation required on Vista)</li>
<li>on some operating systems (MS Vista in particular) it is also required to have SDK&#8217;s <em>proppage.dll</em> available in the system, see <a href="http://alax.info/blog/944">Vista related post</a> for more details</li>
</ul>
<p>To uninstall:</p>
<ul>
<li>use &#8220;<em>regsvr32 /u&#8221;</em> utility to unregister the DLL, close all DirectShow based applications or reboot OS, and delete the file; see also a <a href="http://social.msdn.microsoft.com/Forums/en-US/windowsdirectshowdevelopment/thread/20c7f4a1-b920-4420-9342-9534e5144769">related thread on MSDN Forums</a></li>
</ul>
</div>
]]></content:encoded>
			<wfw:commentRss>http://alax.info/blog/777/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>The easiest yet user-friendly WTL way to Minimize Application to System Tray Icon</title>
		<link>http://alax.info/blog/771</link>
		<comments>http://alax.info/blog/771#comments</comments>
		<pubDate>Wed, 21 Jan 2009 07:56:26 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[WTL]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Source]]></category>

		<guid isPermaLink="false">http://alax.info/blog/?p=771</guid>
		<description><![CDATA[A good old task to easily minimize to system tray icon to not clog the application bar and without too much thinking about it. How To with WTL?
The key points are:

manage NOTIFYICONDATA structure (obviously!), where non-zero .cbSize will be an indication of created icon
create an icon in WM_SYSCOMMAND/SC_MINIMIZE handler and hide instead default minimization
handle icon&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p>A good old task to easily minimize to system tray icon to not clog the application bar and without too much thinking about it. How To with WTL?</p>
<p>The key points are:</p>
<ul>
<li>manage <a href="http://msdn.microsoft.com/en-us/library/bb773352(VS.85).aspx">NOTIFYICONDATA</a> structure (obviously!), where non-zero .cbSize will be an indication of created icon</li>
<li>create an icon in <a href="http://msdn.microsoft.com/en-us/library/ms646360.aspx">WM_SYSCOMMAND</a>/SC_MINIMIZE handler and hide instead default minimization</li>
<li>handle icon&#8217;s <a href="http://msdn.microsoft.com/en-us/library/ms645606(VS.85).aspx">WM_LBUTTONDBLCLK</a> to restore and <a href="http://msdn.microsoft.com/en-us/library/ms646243(VS.85).aspx">WM_RBUTTONUP</a> to pop up a menu</li>
<li>use default dialog menu to avoid having private one, handle SC_RESTORE and SC_CLOSE system commands to restore and close from system tray icon popup menu</li>
</ul>
<p><a href="http://alax.info/blog/wp-content/uploads/2009/01/21-image001.png"><img class="alignnone size-medium wp-image-772" title="System Tray Menu Icon Menu" src="http://alax.info/blog/wp-content/uploads/2009/01/21-image001-300x170.png" alt="" width="300" height="170" /></a></p>
<p>Relevant source code from application main window (dialog) class:</p>
<p><span id="more-771"></span></p>
<pre style="background: #ffffff none repeat scroll 0% 0%; color: #000000;"><span style="font-weight: bold; color: #800000;">class</span> CMainDialog <span style="color: #800080;">:</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
<span style="color: #800080;">{</span>
<span style="font-weight: bold; color: #800000;">public</span><span style="color: #e34adc;">:</span>

BEGIN_MSG_MAP_EX<span style="color: #808030;">(</span>CMainDialog<span style="color: #808030;">)</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
    MSG_WM_INITDIALOG<span style="color: #808030;">(</span>OnInitDialog<span style="color: #808030;">)</span>
    MSG_WM_DESTROY<span style="color: #808030;">(</span>OnDestroy<span style="color: #808030;">)</span>
    MSG_WM_SYSCOMMAND<span style="color: #808030;">(</span>OnSysCommand<span style="color: #808030;">)</span>
    MESSAGE_HANDLER_EX<span style="color: #808030;">(</span>WM_SYSTEMTRAYICON<span style="color: #808030;">,</span> OnSystemTrayIcon<span style="color: #808030;">)</span>
    COMMAND_ID_HANDLER_EX<span style="color: #808030;">(</span>SC_RESTORE<span style="color: #808030;">,</span> OnScRestore<span style="color: #808030;">)</span>
    COMMAND_ID_HANDLER_EX<span style="color: #808030;">(</span>SC_CLOSE<span style="color: #808030;">,</span> OnScClose<span style="color: #808030;">)</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
END_MSG_MAP<span style="color: #808030;">(</span><span style="color: #808030;">)</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
    <span style="font-weight: bold; color: #800000;">enum</span>
    <span style="color: #800080;">{</span>
        WM_FIRST <span style="color: #808030;">=</span> WM_APP<span style="color: #808030;">,</span>
        WM_SYSTEMTRAYICON<span style="color: #808030;">,</span>
    <span style="color: #800080;">}</span><span style="color: #800080;">;</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
<span style="font-weight: bold; color: #800000;">private</span><span style="color: #e34adc;">:</span>
    <span style="color: #603000;">NOTIFYICONDATA</span> m_NotifyIconData<span style="color: #800080;">;</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
<span style="font-weight: bold; color: #800000;">public</span><span style="color: #e34adc;">:</span>
<span style="color: #696969;">// CMainDialog</span>
    CMainDialog<span style="color: #808030;">(</span><span style="color: #808030;">)</span> <span style="font-weight: bold; color: #800000;">throw</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span>
    <span style="color: #800080;">{</span>
        <span style="color: #400000;">ZeroMemory</span><span style="color: #808030;">(</span><span style="color: #808030;">&amp;</span>m_NotifyIconData<span style="color: #808030;">,</span> <span style="font-weight: bold; color: #800000;">sizeof</span> m_NotifyIconData<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        <span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
    <span style="color: #800080;">}</span>

<span style="color: #696969;">// Window message handlers</span>
    <span style="color: #603000;">LRESULT</span> OnInitDialog<span style="color: #808030;">(</span><span style="color: #603000;">HWND</span><span style="color: #808030;">,</span> <span style="color: #603000;">LPARAM</span><span style="color: #808030;">)</span>
    <span style="color: #800080;">{</span>
        <span style="color: #400000;">ZeroMemory</span><span style="color: #808030;">(</span><span style="color: #808030;">&amp;</span>m_NotifyIconData<span style="color: #808030;">,</span> <span style="font-weight: bold; color: #800000;">sizeof</span> m_NotifyIconData<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        <span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
    <span style="color: #800080;">}</span>
    <span style="color: #603000;">LRESULT</span> OnDestroy<span style="color: #808030;">(</span><span style="color: #808030;">)</span>
    <span style="color: #800080;">{</span>
        <span style="font-weight: bold; color: #800000;">if</span><span style="color: #808030;">(</span>m_NotifyIconData<span style="color: #808030;">.</span>cbSize<span style="color: #808030;">)</span>
        <span style="color: #800080;">{</span>
            <span style="color: #400000;">Shell_NotifyIcon</span><span style="color: #808030;">(</span><span style="color: #7d0045;">NIM_DELETE</span><span style="color: #808030;">,</span> <span style="color: #808030;">&amp;</span>m_NotifyIconData<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
            <span style="color: #400000;">ZeroMemory</span><span style="color: #808030;">(</span><span style="color: #808030;">&amp;</span>m_NotifyIconData<span style="color: #808030;">,</span> <span style="font-weight: bold; color: #800000;">sizeof</span> m_NotifyIconData<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        <span style="color: #800080;">}</span>
        <span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
    <span style="color: #800080;">}</span>
    <span style="color: #603000;">LRESULT</span> OnSysCommand<span style="color: #808030;">(</span><span style="color: #603000;">UINT</span> nCommand<span style="color: #808030;">,</span> <span style="color: #603000;">CPoint</span><span style="color: #808030;">)</span>
    <span style="color: #800080;">{</span>
        <span style="font-weight: bold; color: #800000;">switch</span><span style="color: #808030;">(</span>nCommand<span style="color: #808030;">)</span>
        <span style="color: #800080;">{</span>
        <span style="font-weight: bold; color: #800000;">case </span><span style="color: #7d0045;">SC_MINIMIZE</span><span style="color: #e34adc;">:</span>
            <span style="font-weight: bold; color: #800000;">if</span><span style="color: #808030;">(</span><span style="color: #808030;">!</span>m_NotifyIconData<span style="color: #808030;">.</span>cbSize<span style="color: #808030;">)</span>
            <span style="color: #800080;">{</span>
                m_NotifyIconData<span style="color: #808030;">.</span>cbSize <span style="color: #808030;">=</span> NOTIFYICONDATAA_V1_SIZE<span style="color: #800080;">;</span>
                m_NotifyIconData<span style="color: #808030;">.</span>hWnd <span style="color: #808030;">=</span> m_hWnd<span style="color: #800080;">;</span>
                m_NotifyIconData<span style="color: #808030;">.</span>uID <span style="color: #808030;">=</span> <span style="color: #008c00;">1</span><span style="color: #800080;">;</span>
                m_NotifyIconData<span style="color: #808030;">.</span>uFlags <span style="color: #808030;">=</span> NIF_ICON <span style="color: #808030;">|</span> NIF_MESSAGE <span style="color: #808030;">|</span> NIF_TIP<span style="color: #800080;">;</span>
                m_NotifyIconData<span style="color: #808030;">.</span>uCallbackMessage <span style="color: #808030;">=</span> WM_SYSTEMTRAYICON<span style="color: #800080;">;</span>
                m_NotifyIconData<span style="color: #808030;">.</span>hIcon <span style="color: #808030;">=</span> AtlLoadIconImage<span style="color: #808030;">(</span>IDI_MODULE<span style="color: #808030;">,</span> LR_DEFAULTCOLOR<span style="color: #808030;">,</span> <span style="color: #400000;">GetSystemMetrics</span><span style="color: #808030;">(</span>SM_CXSMICON<span style="color: #808030;">)</span><span style="color: #808030;">,</span> <span style="color: #400000;">GetSystemMetrics</span><span style="color: #808030;">(</span>SM_CYSMICON<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
                <span style="color: #603000;">CString</span> sWindowText<span style="color: #800080;">;</span>
                <span style="color: #400000;">GetWindowText</span><span style="color: #808030;">(</span>sWindowText<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
                _tcscpy_s<span style="color: #808030;">(</span>m_NotifyIconData<span style="color: #808030;">.</span>szTip<span style="color: #808030;">,</span> sWindowText<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
                <span style="font-weight: bold; color: #800000;">if</span><span style="color: #808030;">(</span><span style="color: #808030;">!</span><span style="color: #400000;">Shell_NotifyIcon</span><span style="color: #808030;">(</span><span style="color: #7d0045;">NIM_ADD</span><span style="color: #808030;">,</span> <span style="color: #808030;">&amp;</span>m_NotifyIconData<span style="color: #808030;">)</span><span style="color: #808030;">)</span>
                <span style="color: #800080;">{</span>
                    SetMsgHandled<span style="color: #808030;">(</span>FALSE<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
                    <span style="font-weight: bold; color: #800000;">return</span> <span style="color: #008c00;">0</span><span style="color: #800080;">;</span>
                <span style="color: #800080;">}</span>
            <span style="color: #800080;">}</span>
            <span style="color: #400000;">ShowWindow</span><span style="color: #808030;">(</span>SW_HIDE<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
            <span style="font-weight: bold; color: #800000;">break</span><span style="color: #800080;">;</span>
        <span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
<span style="color: #e34adc;">        </span><span style="font-weight: bold; color: #800000;">default</span><span style="color: #e34adc;">:</span>
            SetMsgHandled<span style="color: #808030;">(</span>FALSE<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        <span style="color: #800080;">}</span>
        <span style="font-weight: bold; color: #800000;">return</span> <span style="color: #008c00;">0</span><span style="color: #800080;">;</span>
    <span style="color: #800080;">}</span>
    <span style="color: #603000;">LRESULT</span> OnSystemTrayIcon<span style="color: #808030;">(</span><span style="color: #603000;">UINT</span><span style="color: #808030;">,</span> <span style="color: #603000;">WPARAM</span> wParam<span style="color: #808030;">,</span> <span style="color: #603000;">LPARAM</span> lParam<span style="color: #808030;">)</span>
    <span style="color: #800080;">{</span>
        ATLASSERT<span style="color: #808030;">(</span>wParam <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">1</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        <span style="font-weight: bold; color: #800000;">switch</span><span style="color: #808030;">(</span>lParam<span style="color: #808030;">)</span>
        <span style="color: #800080;">{</span>
        <span style="font-weight: bold; color: #800000;">case </span><span style="color: #7d0045;">WM_LBUTTONDBLCLK</span><span style="color: #e34adc;">:</span>
            <span style="color: #400000;">SendMessage</span><span style="color: #808030;">(</span><span style="color: #7d0045;">WM_COMMAND</span><span style="color: #808030;">,</span> SC_RESTORE<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
            <span style="font-weight: bold; color: #800000;">break</span><span style="color: #800080;">;</span>
        <span style="font-weight: bold; color: #800000;">case </span><span style="color: #7d0045;">WM_RBUTTONUP</span><span style="color: #e34adc;">:</span>
            <span style="color: #800080;">{</span>
                <span style="color: #400000;">SetForegroundWindow</span><span style="color: #808030;">(</span>m_hWnd<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
                CMenuHandle Menu <span style="color: #808030;">=</span> <span style="color: #400000;">GetSystemMenu</span><span style="color: #808030;">(</span>FALSE<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
                Menu<span style="color: #808030;">.</span><span style="color: #400000;">EnableMenuItem</span><span style="color: #808030;">(</span>SC_RESTORE<span style="color: #808030;">,</span> MF_BYCOMMAND <span style="color: #808030;">|</span> MF_ENABLED<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
                Menu<span style="color: #808030;">.</span><span style="color: #400000;">EnableMenuItem</span><span style="color: #808030;">(</span>SC_MOVE<span style="color: #808030;">,</span> MF_BYCOMMAND <span style="color: #808030;">|</span> MF_GRAYED<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
                Menu<span style="color: #808030;">.</span><span style="color: #400000;">EnableMenuItem</span><span style="color: #808030;">(</span>SC_SIZE<span style="color: #808030;">,</span> MF_BYCOMMAND <span style="color: #808030;">|</span> MF_GRAYED<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
                Menu<span style="color: #808030;">.</span><span style="color: #400000;">EnableMenuItem</span><span style="color: #808030;">(</span>SC_MINIMIZE<span style="color: #808030;">,</span> MF_BYCOMMAND <span style="color: #808030;">|</span> MF_GRAYED<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
                Menu<span style="color: #808030;">.</span><span style="color: #400000;">EnableMenuItem</span><span style="color: #808030;">(</span>SC_MAXIMIZE<span style="color: #808030;">,</span> MF_BYCOMMAND <span style="color: #808030;">|</span> MF_GRAYED<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
                Menu<span style="color: #808030;">.</span><span style="color: #400000;">EnableMenuItem</span><span style="color: #808030;">(</span>SC_CLOSE<span style="color: #808030;">,</span> MF_BYCOMMAND <span style="color: #808030;">|</span> MF_ENABLED<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
                <span style="color: #603000;">CPoint</span> Position<span style="color: #800080;">;</span>
                ATLVERIFY<span style="color: #808030;">(</span><span style="color: #400000;">GetCursorPos</span><span style="color: #808030;">(</span><span style="color: #808030;">&amp;</span>Position<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
                Menu<span style="color: #808030;">.</span><span style="color: #400000;">TrackPopupMenu</span><span style="color: #808030;">(</span>TPM_LEFTALIGN <span style="color: #808030;">|</span> TPM_BOTTOMALIGN<span style="color: #808030;">,</span> Position<span style="color: #808030;">.</span>x<span style="color: #808030;">,</span> Position<span style="color: #808030;">.</span>y<span style="color: #808030;">,</span> m_hWnd<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
            <span style="color: #800080;">}</span>
            <span style="font-weight: bold; color: #800000;">break</span><span style="color: #800080;">;</span>
        <span style="color: #800080;">}</span>
        <span style="font-weight: bold; color: #800000;">return</span> <span style="color: #008c00;">0</span><span style="color: #800080;">;</span>
    <span style="color: #800080;">}</span>
    <span style="color: #603000;">LRESULT</span> OnScRestore<span style="color: #808030;">(</span><span style="color: #603000;">UINT</span><span style="color: #808030;">,</span> <span style="color: #603000;">INT</span><span style="color: #808030;">,</span> <span style="color: #603000;">HWND</span><span style="color: #808030;">)</span>
    <span style="color: #800080;">{</span>
        <span style="font-weight: bold; color: #800000;">if</span><span style="color: #808030;">(</span>m_NotifyIconData<span style="color: #808030;">.</span>cbSize<span style="color: #808030;">)</span>
        <span style="color: #800080;">{</span>
            <span style="color: #400000;">Shell_NotifyIcon</span><span style="color: #808030;">(</span><span style="color: #7d0045;">NIM_DELETE</span><span style="color: #808030;">,</span> <span style="color: #808030;">&amp;</span>m_NotifyIconData<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
            <span style="color: #400000;">ZeroMemory</span><span style="color: #808030;">(</span><span style="color: #808030;">&amp;</span>m_NotifyIconData<span style="color: #808030;">,</span> <span style="font-weight: bold; color: #800000;">sizeof</span> m_NotifyIconData<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        <span style="color: #800080;">}</span>
        <span style="color: #400000;">ShowWindow</span><span style="color: #808030;">(</span>SW_SHOW<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        <span style="color: #400000;">BringWindowToTop</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        <span style="font-weight: bold; color: #800000;">return</span> <span style="color: #008c00;">0</span><span style="color: #800080;">;</span>
    <span style="color: #800080;">}</span>
    <span style="color: #603000;">LRESULT</span> OnScClose<span style="color: #808030;">(</span><span style="color: #603000;">UINT</span><span style="color: #808030;">,</span> <span style="color: #603000;">INT</span><span style="color: #808030;">,</span> <span style="color: #603000;">HWND</span><span style="color: #808030;">)</span>
    <span style="color: #800080;">{</span>
        <span style="color: #400000;">PostMessage</span><span style="color: #808030;">(</span><span style="color: #7d0045;">WM_COMMAND</span><span style="color: #808030;">,</span> IDCANCEL<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        <span style="font-weight: bold; color: #800000;">return</span> <span style="color: #008c00;">0</span><span style="color: #800080;">;</span>
    <span style="color: #800080;">}</span></pre>
]]></content:encoded>
			<wfw:commentRss>http://alax.info/blog/771/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JPEG Multi File Video Capture Source Filter, a virtual DirectShow camera</title>
		<link>http://alax.info/blog/762</link>
		<comments>http://alax.info/blog/762#comments</comments>
		<pubDate>Sat, 17 Jan 2009 11:20:33 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[ATL]]></category>
		<category><![CDATA[Source]]></category>
		<category><![CDATA[Video]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[camera]]></category>
		<category><![CDATA[capture]]></category>
		<category><![CDATA[DirectShow]]></category>
		<category><![CDATA[JPEG]]></category>

		<guid isPermaLink="false">http://alax.info/blog/?p=762</guid>
		<description><![CDATA[Provided that there already is a JPEG Multi File Source Filter that can act as a video source streaming video from local JPEG files, it looked to be useful to build a virtual camera on top of this filter. This is the main difference: an existing filter is generic and customizable: it requires to be [...]]]></description>
			<content:encoded><![CDATA[<p>Provided that there already is a <a href="http://alax.info/blog/741">JPEG Multi File Source Filter</a> that can act as a video source streaming video from local JPEG files, it looked to be useful to build a virtual camera on top of this filter. This is the main difference: an existing filter is generic and customizable: it requires to be provided a directory with the files, other settings may also apply. A virtual camera is the filter that has to work out of the box: a video enabled application, such as <a href="http://msdn.microsoft.com/en-us/library/ms778964.aspx">AMCap Sample</a>, <a href="http://sourceforge.net/projects/guliverkli/">Media Player Classic</a>, <a href="http://videolan.org">VideoLAN</a>, <a href="http://skype.com">Skype</a>, <a href="http://www.microsoft.com/windows/windowsmedia/9series/encoder/default.aspx">Windows Media Encoder</a> enumerates video capture sources, instantiate the one of the interest and it should already be ready to stream.</p>
<h3>Implementation Details</h3>
<p>The very first question is embedding of an existing filter into new filter. The two most common methods are:</p>
<ul>
<li><a href="https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=262853&amp;wa=wsignin1.0">COM aggregation</a></li>
<li>embedding a fully featured graph with a sink/renderer that intercepts media samples downstream and forwards to a higher level filter so that it streams them as a source filter</li>
</ul>
<p>The COM aggregation methods is much easier in implementation but it is subject to a few constraints, the two most important of which are:</p>
<ul>
<li>embedded filter should support instantiation as an aggregated object</li>
<li>it is the only underlying filter, not a chain of filters, which can produce required data</li>
</ul>
<p>COM aggregation is quite fitting for the purpose, so the second embedding method is being left for another topic (with a certain luck to be appear very soon, a DirectShow video capture source filter for a real network/IP camera).</p>
<p>The next step is a check of sufficient implementation in an underlying filter. Obviously, a video source that pretends to be a live video capture source needs an endless stream of media samples, while original implementation streams JPEG files as media samples once only, we need an option to loop the streaming and automatically repeat the sequence.</p>
<p>Playback looping is added to the original <a href="http://alax.info/blog/741">JPEG Multi File Source Filter</a> and its controlling private interface IJpegMultiFileSourceFilter received additional properties:</p>
<pre style="background: #ffffff none repeat scroll 0% 0%; color: #000000;"><span style="color: #800000; font-weight: bold;">interface</span> IJpegMultiFileSourceFilter <span style="color: #800080;">:</span> IDispatch
<span style="color: #800080;">{</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
    <span style="color: #808030;">[</span><span style="color: #800000; font-weight: bold;">propget</span><span style="color: #808030;">,</span> <span style="color: #800000; font-weight: bold;">id</span><span style="color: #808030;">(</span><span style="color: #008c00;">2</span><span style="color: #808030;">)</span><span style="color: #808030;">]</span> HRESULT AutoRepeat<span style="color: #808030;">(</span><span style="color: #808030;">[</span>out<span style="color: #808030;">,</span> retval<span style="color: #808030;">]</span> VARIANT_BOOL<span style="color: #808030;">*</span> pbAutoRepeat<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    <span style="color: #808030;">[</span><span style="color: #800000; font-weight: bold;">propput</span><span style="color: #808030;">,</span> <span style="color: #800000; font-weight: bold;">id</span><span style="color: #808030;">(</span><span style="color: #008c00;">2</span><span style="color: #808030;">)</span><span style="color: #808030;">]</span> HRESULT AutoRepeat<span style="color: #808030;">(</span><span style="color: #808030;">[</span>in<span style="color: #808030;">]</span> VARIANT_BOOL bAutoRepeat<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    <span style="color: #808030;">[</span><span style="color: #800000; font-weight: bold;">propget</span><span style="color: #808030;">,</span> <span style="color: #800000; font-weight: bold;">id</span><span style="color: #808030;">(</span><span style="color: #008c00;">3</span><span style="color: #808030;">)</span><span style="color: #808030;">]</span> HRESULT RepeatDelay<span style="color: #808030;">(</span><span style="color: #808030;">[</span>out<span style="color: #808030;">,</span> retval<span style="color: #808030;">]</span> <span style="color: #603000;">LONG</span><span style="color: #808030;">*</span> pnRepeatDelay<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
    <span style="color: #808030;">[</span><span style="color: #800000; font-weight: bold;">propput</span><span style="color: #808030;">,</span> <span style="color: #800000; font-weight: bold;">id</span><span style="color: #808030;">(</span><span style="color: #008c00;">3</span><span style="color: #808030;">)</span><span style="color: #808030;">]</span> HRESULT RepeatDelay<span style="color: #808030;">(</span><span style="color: #808030;">[</span>in<span style="color: #808030;">]</span> <span style="color: #603000;">LONG</span> nRepeatDelay<span style="color: #808030;">)</span></pre>
<p>as well the new property page:</p>
<p><a href="http://alax.info/blog/wp-content/uploads/2009/01/17-image001.png"><img class="alignnone size-medium wp-image-766" title="JPEG Multi File Source Filter Playback Property Page" src="http://alax.info/blog/wp-content/uploads/2009/01/17-image001-259x300.png" alt="" width="259" height="300" /></a></p>
<p><span id="more-762"></span></p>
<p>As soon as the underlying implementation is ready, it is the right time to get down to the immediate filter COM class. It will be require to implement certain interfaces, to expose certain interfaces of the aggregated object and possibly to hide certain interfaces of the aggregated object. However, the first thing to do is to instantiate aggregated object, for which the right place is ATLs&#8217; FinalConstruct:</p>
<pre><span style="color: #800000; font-weight: bold;">class</span> ATL_NO_VTABLE CFilter <span style="color: #800080;">:</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
<span style="color: #800080;">{</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
DECLARE_PROTECT_FINAL_CONSTRUCT<span style="color: #808030;">(</span><span style="color: #808030;">)</span>

DECLARE_GET_CONTROLLING_UNKNOWN<span style="color: #808030;">(</span><span style="color: #808030;">)</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
    CComPtr<span style="color: #800080;">&lt;</span>IUnknown<span style="color: #800080;">&gt;</span> m_pInnerUnknown<span style="color: #800080;">;</span>
    CComPtr<span style="color: #800080;">&lt;</span>IBaseFilter<span style="color: #800080;">&gt;</span> m_pInnerBaseFilter<span style="color: #800080;">;</span>
    CComPtr<span style="color: #800080;">&lt;</span>IPersistStream<span style="color: #800080;">&gt;</span> m_pInnerPersistStream<span style="color: #800080;">;</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
    HRESULT FinalContruct<span style="color: #808030;">(</span><span style="color: #808030;">)</span>
    <span style="color: #800080;">{</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
        __C<span style="color: #808030;">(</span>m_pInnerUnknown<span style="color: #808030;">.</span>CoCreateInstance<span style="color: #808030;">(</span><span style="color: #800000; font-weight: bold;">__uuidof</span><span style="color: #808030;">(</span>JpegMultiFileSourceFilter<span style="color: #808030;">)</span><span style="color: #808030;">,</span> GetControllingUnknown<span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        CComQIPtr<span style="color: #800080;">&lt;</span>IJpegMultiFileSourceFilter<span style="color: #800080;">&gt;</span> pJpegMultiFileSourceFilter <span style="color: #808030;">=</span> m_pInnerUnknown<span style="color: #800080;">;</span>
        __D<span style="color: #808030;">(</span>pJpegMultiFileSourceFilter<span style="color: #808030;">,</span> E_NOINTERFACE<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
        __C<span style="color: #808030;">(</span>pJpegMultiFileSourceFilter<span style="color: #808030;">-</span><span style="color: #808030;">&gt;</span>put_Directory<span style="color: #808030;">(</span>CComBSTR<span style="color: #808030;">(</span>sDirectory<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        __C<span style="color: #808030;">(</span>pJpegMultiFileSourceFilter<span style="color: #808030;">-</span><span style="color: #808030;">&gt;</span>put_AutoRepeat<span style="color: #808030;">(</span>ATL_VARIANT_TRUE<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        __C<span style="color: #808030;">(</span>pJpegMultiFileSourceFilter<span style="color: #808030;">-</span><span style="color: #808030;">&gt;</span>put_RepeatDelay<span style="color: #808030;">(</span>nRepeatDelay<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #800080;">;</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
        m_pInnerBaseFilter <span style="color: #808030;">=</span> CComQIPtr<span style="color: #800080;">&lt;</span>IBaseFilter<span style="color: #800080;">&gt;</span><span style="color: #808030;">(</span>m_pInnerUnknown<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        m_pInnerPersistStream <span style="color: #808030;">=</span> CComQIPtr<span style="color: #800080;">&lt;</span>IPersistStream<span style="color: #800080;">&gt;</span><span style="color: #808030;">(</span>m_pInnerUnknown<span style="color: #808030;">)</span><span style="color: #800080;">;</span>
        __D<span style="color: #808030;">(</span>m_pInnerBaseFilter<span style="color: #808030;">,</span> E_NOINTERFACE<span style="color: #808030;">)</span><span style="color: #800080;">;</span></pre>
<p>The aggregated (inner) object is created and its &#8220;main&#8221; IUnknown pointer is saved as lifetime reference (because for the COM aggregated object this is the only interface pointer which AddRef/Release has effect on internal reference counter &#8211; see <a href="http://msdn.microsoft.com/en-us/library/ms809989.aspx">COM Aggregation</a> details). The object is pre-initialzed immediately after creation and the effective values will be taken from registry key &#8220;SOFTWARE\Alax.Info\Media Tools\JPEG MultiFile Video Capture Filter&#8221; (see below). It also makes sense to pre-query most used object&#8217;s interfaces to be able to use them directly without querying for them each time they are required.</p>
<p>Once the inner object is here, we need to sort out the list of interfaces to support. Video capture source filters have specific requirements (see <a href="http://msdn.microsoft.com/en-us/library/dd391011(VS.85).aspx">Writing Capture Filters</a>), including indication of pin category and implementation of <a href="http://msdn.microsoft.com/en-us/library/ms785843(VS.85).aspx">IKsPropertySet</a> interface; optional availability of preview pin, which we can omit from implementation and let <a href="http://msdn.microsoft.com/en-us/library/ms783670(VS.85).aspx">Filter Graph Manager</a> use <a href="http://msdn.microsoft.com/en-us/library/ms787667.aspx">Smart Tee Pin Filter</a> to split stream into preview and capture.</p>
<p>Additionally, various software is expecting <a href="http://msdn.microsoft.com/en-us/library/ms784115(VS.85).aspx">IAMStreamConfig</a> interface on the output pin and would just fail to use the video capture device in case of its absence. We don&#8217;t have this interface implemented on the underlying filter, so we have to implement this on the wrapper. Since we need additional interfaces on the pin and the pin is an owned object of an inner aggregated object, it adds complexity to wrap original pin with a private pin class. An easy implementation of the latter point is not quite safe with COM aggregation, however a well written software that treats <a href="http://msdn.microsoft.com/en-us/library/ms786565(VS.85).aspx">IPin</a> interface as the &#8220;primary&#8221; pin&#8217;s interface and queries additional from it and holds the reference while using other interfaces will do just fine with a simple implementation on our side.</p>
<p>Another problem with the underlying filter which actually came up later but worth mentioning from the start. A <a href="http://alax.info/blog/741">JPEG Multi File Source Filter</a> streams video media sample with an empty format structure attached to <a href="http://msdn.microsoft.com/en-us/library/ms779120(VS.85).aspx">AM_MEDIA_TYPE</a> media type. This assumes that an instance of <a href="http://alax.info/blog/741">JPEG Frame Decoder Filter</a> will be added downstream and supply missing format by decoding JPEG data. While the filter is flexible enough to dynamically change media type along with JPEG data changes, this is not compatible with (not implemented by) most of the software. <a href="http://alax.info/blog/741">JPEG Frame Decoder Filter</a> makes a first guess by settings resolution to 320&#215;240. We need to set the right resolution immediately to avoid media type re-agreement.</p>
<p>To be able to do so we need to pass resolution information from original (inner) filter to the <a href="http://alax.info/blog/741">JPEG Frame Decoder Filter</a> instance. It is also important that the filters might have other filters connected in between and in most cases there will be at least <a href="http://msdn.microsoft.com/en-us/library/ms787667.aspx">Smart Tee Pin Filter</a>. To let the filters effectively communicate between each other and including in a reliable and documented way, available also to other/future filters, <a href="http://alax.info/blog/741">JPEG Frame Decoder Filter</a> inroduces a new interface IJpegFrameDecoderFilterSource which, on its input pin connection, it looks for among the upstream filters. Iteratively walking through upstream connections up to developed video capture source filter it reaches our implementation and is capable of changing the default resolution to the effective resolution and exposed correct media type on its output pin right from the start.</p>
<p>So the interfaces on the filter:</p>
<ul>
<li>implemented on the filter:
<ul>
<li><a href="http://msdn.microsoft.com/en-us/library/ms784601(VS.85).aspx">IBaseFilter</a> with IMediaFilter and IPersist to provide our own pin enumeration and supply a wrapper class for a pin object</li>
<li><a href="http://msdn.microsoft.com/en-us/library/ms682273(VS.85).aspx">IPersistStreamInit</a> and IPersistStream to be friendly with persistence-enabled applications</li>
<li><a href="http://msdn.microsoft.com/en-us/library/ms784083(VS.85).aspx">IAMovieSetup</a> to be able to be registered with DirectShow the way it is normally done</li>
<li>IFilter without <a href="http://msdn.microsoft.com/en-us/library/ms221608.aspx">IDispatch</a> is the filters private interface with IDispatch not exposed directly to let the underlying filter expose his</li>
<li>IJpegFrameDecoderFilterSource to communicate to downstream <a href="http://alax.info/blog/741">JPEG Frame Decoder Filter</a> and provide resolution information</li>
</ul>
</li>
<li>hidden implementation on the inner filter (other interfaces implemented by inner filter and not mentioned will be also available &#8220;outside&#8221;):
<ul>
<li><a href="http://msdn.microsoft.com/en-us/library/ms695217(VS.85).aspx">ISpecifyPropertyPages</a> to hide inner property pages since the inner object is initialized internally</li>
</ul>
</li>
<li>implemented on the pin:
<ul>
<li><a href="http://msdn.microsoft.com/en-us/library/ms786565(VS.85).aspx">IPin</a> to intercept method calls to possibly override media type enumeration and pin connection condition; while implementation of its methods (appeared to be not necessary, we still have to expose our private implementation of this interface since software will in most cases query additional interfaces from this interface pointer</li>
<li><a href="http://msdn.microsoft.com/en-us/library/ms785843(VS.85).aspx">IKsPropertySet</a> per MSDN requirement</li>
<li><a href="http://msdn.microsoft.com/en-us/library/ms784115(VS.85).aspx">IAMStreamConfig</a> to be compatible with wider range of software, specifically AMCap</li>
</ul>
</li>
</ul>
<p>Implementation details is available in reference implementation <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/MediaTools/Samples/JpegMultiFileVideoCaptureSource/Filter.h">Filter.h in SVN</a>.</p>
<p>An updated version of the dependent binaries Acquisition.dll and CodingI.dll is <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/MediaTools/_Bin/Release%20Trace">available from SVN</a>.</p>
<p>A few points to mention explicitly:</p>
<ul>
<li><a href="http://msdn.microsoft.com/en-us/library/ms785842(VS.85).aspx">IKsPropertySet::Get</a> marks the pin as a capture pin (PIN_CATEGORY_CAPTURE)</li>
<li><a href="http://msdn.microsoft.com/en-us/library/ms784115(VS.85).aspx">IAMStreamConfig</a> minimal implementation ignores media type in <a href="http://msdn.microsoft.com/en-us/library/ms784116(VS.85).aspx">SetFormat</a>; returns the only available media type in <a href="http://msdn.microsoft.com/en-us/library/ms784112(VS.85).aspx">GetFormat</a> using 300 fps frame rate value in order for it to be not zero and not very low to avoid collision of frames; returns one video capability in <a href="http://msdn.microsoft.com/en-us/library/ms784113(VS.85).aspx">GetNumberOfCapabilities</a> and <a href="http://msdn.microsoft.com/en-us/library/ms784114(VS.85).aspx">GetStreamCaps</a> &#8211; this should be sufficient provided that we actually don&#8217;t accept any cahnges/configuration on this interface</li>
<li><a href="http://msdn.microsoft.com/en-us/library/ms784599(VS.85).aspx">IBaseFilter::EnumPins</a> exposes a private wrapper pin class over original pin, so that we are capable of providing additional interfaces off the exposed output pin; we should have also provided an equal replacement through <a href="http://msdn.microsoft.com/en-us/library/ms784600(VS.85).aspx">IBaseFilter::FindPin</a> but this method is actually very rarely used and appears to be safe to omit, probably unless a compatibility issue arises later</li>
<li>IJpegFrameDecoderFilterSource::GetDefaultExtent is the way <a href="http://alax.info/blog/741">JPEG Frame Decoder Filter</a> obtains original video resolution.</li>
</ul>
<p>On initialization the filter will query registry key &#8220;SOFTWARE\Alax.Info\Media Tools\JPEG MultiFile Video Capture Filter&#8221; for initialization values. Both HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER might be used, HKEY_CURRENT_USER has the priority if present, unless HKEY_LOCAL_MACHINE has an additional non-zero REG_DWORD value named &#8220;Force&#8221;, in which case it takes priority over HKEY_CURRENT_USER. Initialization values are:</p>
<ul>
<li>&#8220;Directory&#8221; REG_SZ is the directory containing JPEG files to stream from</li>
<li>&#8220;Repeat Delay&#8221; REG_DWORD is the pause in milliseconds between last and first frame when looping when playback</li>
<li>&#8220;Video Width&#8221; and &#8220;Video Height&#8221; REG_DWORD is original video resolution and should match those of JPEG files</li>
</ul>
<p>Another necessary implementation stroke is related to time stamps. The original inner <a href="http://alax.info/blog/741">JPEG Multi File Source Filter</a> did not do any frame rate control at which the media samples are sent downstream. It was assumed that [video] renderer filter will enforce media sample presentation time and add necessary delay while playing video frames back. However, this approach does not work in a capture filter: when previewing video, a <a href="http://msdn.microsoft.com/en-us/library/ms787667.aspx">Smart Tee Pin Filter</a>, which is inserted to make preview samples out of capture samples, is stripping time stamps and video renderer renders media samples at full possible rate. To address this issue, the <a href="http://alax.info/blog/741">JPEG Multi File Source Filter</a> is received a new property Rate, which defaults to zero and implements uncontrolled sending of data downstream. With a Rate property value of 1.0, the filter does rate control and adds necessary delay as if the media samples are captured in real time.</p>
<h3>Compatibility</h3>
<p><a href="http://msdn.microsoft.com/en-us/library/ms787460(VS.85).aspx">Graph Edit</a> and <a href="http://www.monogram.sk/">Graph Studio</a> list, insert and are otherwise compatible with the new filter. They don&#8217;t have any assumptions on the filter implementation except the very basic, so they are quite capable of manipulating the new filter.</p>
<p><a href="http://msdn.microsoft.com/en-us/library/ms778964.aspx">AMCap Sample</a> recognizes the capture source and is rendering the filter through Capture Graph Builder, which automatically inserts <a href="http://msdn.microsoft.com/en-us/library/ms787667.aspx">Smart Tee Pin Filter</a>. The application is properly showing the video from the new filter.</p>
<p><a href="http://sourceforge.net/projects/guliverkli/">Media Player Classic</a>&#8217;s &#8220;Open Device&#8230;&#8221; menu command is capable of connecting to the new filter and the application is showing video.</p>
<p><a href="http://videolan.org">VideoLAN</a> [unexpectedly] failed to receive video from the new filter. It appears that the application is enumerating supported media types and is only capable of accepting those handled directly by the application. JPEG video with FOURCC code &#8216;AIJ0&#8242; is not on that list and the application does not make any attempt, that a proper application would do, to render the filter through intermediate codecs using <a href="http://msdn.microsoft.com/en-us/library/ms786503(VS.85).aspx">Intelligent Connect</a>. The application won&#8217;t be able to receive video from the filter before the latter implements decoding into a well known pixel format, such as for example <a href="http://fourcc.org/rgb.php#BI_RGB">RGB</a> or <a href="http://fourcc.org/yuv.php#YUY2">YUY2</a>.</p>
<p><a href="http://skype.com">Skype</a> beta 4.0.0.176 failed to receive video from the filter, though the filter is seen by the software as a video source. The logs don&#8217;t indicate the reason and it seems that the application does not like video capabilities and stops trying to get video from the device.</p>
<h3>Summary</h3>
<ul>
<li>Virtual DirectShow camera implemented</li>
<li>Supported by Graph Edit, Monogram Graph Studio (one has to re-enter file directory from GUI when adding a filter), AMCap, Media Player Classic</li>
<li>Does not integrate into VideoLAN because of its [unexpectedly] limited capabilities</li>
<li>Does not integrate into Skype for whatever reason, probably just another of a number of bugs in a beta version</li>
</ul>
<p>A partial reference Visual C++ .NET 2008 source code is <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/MediaTools/Samples/JpegMultiFileVideoCaptureSource">available from SVN</a>, release binary <a href="http://code.assembla.com/roatl-utilities/subversion/nodes/trunk/MediaTools/Samples/JpegMultiFileVideoCaptureSource/Release/JpegMultiFileVideoCaptureSource.dll?format=raw">included</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://alax.info/blog/762/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>
