{"id":762,"date":"2009-01-17T13:20:33","date_gmt":"2009-01-17T11:20:33","guid":{"rendered":"https:\/\/alax.info\/blog\/?p=762"},"modified":"2009-03-03T11:11:31","modified_gmt":"2009-03-03T09:11:31","slug":"jpeg-multi-file-video-capture-source-filter-a-virtual-directshow-camera","status":"publish","type":"post","link":"https:\/\/alax.info\/blog\/762","title":{"rendered":"JPEG Multi File Video Capture Source Filter, a virtual DirectShow camera"},"content":{"rendered":"<p>Provided that there already is a <a href=\"https:\/\/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>\n<h3>Implementation Details<\/h3>\n<p>The very first question is embedding of an existing filter into new filter. The two most common methods are:<\/p>\n<ul>\n<li><a href=\"https:\/\/connect.microsoft.com\/VisualStudio\/feedback\/ViewFeedback.aspx?FeedbackID=262853&amp;wa=wsignin1.0\">COM aggregation<\/a><\/li>\n<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>\n<\/ul>\n<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>\n<ul>\n<li>embedded filter should support instantiation as an aggregated object<\/li>\n<li>it is the only underlying filter, not a chain of filters, which can produce required data<\/li>\n<\/ul>\n<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>\n<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>\n<p>Playback looping is added to the original <a href=\"https:\/\/alax.info\/blog\/741\">JPEG Multi File Source Filter<\/a> and its controlling private interface IJpegMultiFileSourceFilter received additional properties:<\/p>\n<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\r\n<span style=\"color: #800080;\">{<\/span>\r\n<span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span>\r\n    <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>\r\n    <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>\r\n    <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>\r\n    <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>\n<p>as well the new property page:<\/p>\n<p><a href=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2009\/01\/17-image001.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-766\" title=\"JPEG Multi File Source Filter Playback Property Page\" src=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2009\/01\/17-image001-259x300.png\" alt=\"\" width=\"259\" height=\"300\" srcset=\"https:\/\/alax.info\/blog\/wp-content\/uploads\/2009\/01\/17-image001-259x300.png 259w, https:\/\/alax.info\/blog\/wp-content\/uploads\/2009\/01\/17-image001.png 370w\" sizes=\"auto, (max-width: 259px) 100vw, 259px\" \/><\/a><\/p>\n<p><!--more--><\/p>\n<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>\n<pre><span style=\"color: #800000; font-weight: bold;\">class<\/span> ATL_NO_VTABLE CFilter <span style=\"color: #800080;\">:<\/span>\r\n<span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span>\r\n<span style=\"color: #800080;\">{<\/span>\r\n<span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span>\r\nDECLARE_PROTECT_FINAL_CONSTRUCT<span style=\"color: #808030;\">(<\/span><span style=\"color: #808030;\">)<\/span>\r\n\r\nDECLARE_GET_CONTROLLING_UNKNOWN<span style=\"color: #808030;\">(<\/span><span style=\"color: #808030;\">)<\/span>\r\n<span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span>\r\n    CComPtr<span style=\"color: #800080;\">&lt;<\/span>IUnknown<span style=\"color: #800080;\">&gt;<\/span> m_pInnerUnknown<span style=\"color: #800080;\">;<\/span>\r\n    CComPtr<span style=\"color: #800080;\">&lt;<\/span>IBaseFilter<span style=\"color: #800080;\">&gt;<\/span> m_pInnerBaseFilter<span style=\"color: #800080;\">;<\/span>\r\n    CComPtr<span style=\"color: #800080;\">&lt;<\/span>IPersistStream<span style=\"color: #800080;\">&gt;<\/span> m_pInnerPersistStream<span style=\"color: #800080;\">;<\/span>\r\n<span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span>\r\n    HRESULT FinalContruct<span style=\"color: #808030;\">(<\/span><span style=\"color: #808030;\">)<\/span>\r\n    <span style=\"color: #800080;\">{<\/span>\r\n<span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span>\r\n        __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>\r\n        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>\r\n        __D<span style=\"color: #808030;\">(<\/span>pJpegMultiFileSourceFilter<span style=\"color: #808030;\">,<\/span> E_NOINTERFACE<span style=\"color: #808030;\">)<\/span><span style=\"color: #800080;\">;<\/span>\r\n<span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span>\r\n        __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>\r\n        __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>\r\n        __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>\r\n<span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span><span style=\"color: #808030;\">.<\/span>\r\n        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>\r\n        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>\r\n        __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>\n<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>\n<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>\n<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>\n<p>Another problem with the underlying filter which actually came up later but worth mentioning from the start. A <a href=\"https:\/\/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=\"https:\/\/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=\"https:\/\/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>\n<p>To be able to do so we need to pass resolution information from original (inner) filter to the <a href=\"https:\/\/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=\"https:\/\/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>\n<p>So the interfaces on the filter:<\/p>\n<ul>\n<li>implemented on the filter:\n<ul>\n<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>\n<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>\n<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>\n<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>\n<li>IJpegFrameDecoderFilterSource to communicate to downstream <a href=\"https:\/\/alax.info\/blog\/741\">JPEG Frame Decoder Filter<\/a> and provide resolution information<\/li>\n<\/ul>\n<\/li>\n<li>hidden implementation on the inner filter (other interfaces implemented by inner filter and not mentioned will be also available &#8220;outside&#8221;):\n<ul>\n<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>\n<\/ul>\n<\/li>\n<li>implemented on the pin:\n<ul>\n<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>\n<li><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms785843(VS.85).aspx\">IKsPropertySet<\/a> per MSDN requirement<\/li>\n<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>\n<\/ul>\n<\/li>\n<\/ul>\n<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>\n<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>\n<p>A few points to mention explicitly:<\/p>\n<ul>\n<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>\n<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>\n<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>\n<li>IJpegFrameDecoderFilterSource::GetDefaultExtent is the way <a href=\"https:\/\/alax.info\/blog\/741\">JPEG Frame Decoder Filter<\/a> obtains original video resolution.<\/li>\n<\/ul>\n<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>\n<ul>\n<li>&#8220;Directory&#8221; REG_SZ is the directory containing JPEG files to stream from<\/li>\n<li>&#8220;Repeat Delay&#8221; REG_DWORD is the pause in milliseconds between last and first frame when looping when playback<\/li>\n<li>&#8220;Video Width&#8221; and &#8220;Video Height&#8221; REG_DWORD is original video resolution and should match those of JPEG files<\/li>\n<\/ul>\n<p>Another necessary implementation stroke is related to time stamps. The original inner <a href=\"https:\/\/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  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=\"https:\/\/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>\n<h3>Compatibility<\/h3>\n<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>\n<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>\n<p><a href=\"http:\/\/sourceforge.net\/projects\/guliverkli\/\">Media Player Classic<\/a>&#8216;s &#8220;Open Device&#8230;&#8221; menu command is capable of connecting to the new filter and the application is showing video.<\/p>\n<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&#8217; 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>\n<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>\n<h3>Summary<\/h3>\n<ul>\n<li>Virtual DirectShow camera implemented<\/li>\n<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>\n<li>Does not integrate into VideoLAN because of its [unexpectedly] limited capabilities<\/li>\n<li>Does not integrate into Skype for whatever reason, probably just another of a number of bugs in a beta version<\/li>\n<\/ul>\n<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>\n","protected":false},"excerpt":{"rendered":"<p>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&hellip; <\/p>\n<p><a class=\"moretag\" href=\"https:\/\/alax.info\/blog\/762\">Read the full article<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11,13,10],"tags":[487,38,82,162,78,75],"class_list":["post-762","post","type-post","status-publish","format-standard","hentry","category-atl","category-source","category-video","tag-atl","tag-c","tag-camera","tag-capture","tag-directshow","tag-jpeg"],"_links":{"self":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts\/762","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/comments?post=762"}],"version-history":[{"count":0,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/posts\/762\/revisions"}],"wp:attachment":[{"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/media?parent=762"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/categories?post=762"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/alax.info\/blog\/wp-json\/wp\/v2\/tags?post=762"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}