DirectShow Spy: Easier Registration

Because DirectShow Spy is often a troubleshooting tool, one of its use scenarios is its being a drop-in module to quickly install on a system of interest in order to connect to graphs for troubleshooting purposes, such as to check topology and media types.

Its installation requires COM registration, and over time it changed gradually from simple to more and more complex step. Why? In Windows XP one had to open command prompt and regsvr32. With Vista’s UAC one needs a prompt, with privilege elevation, which opens typically in wrong directory, then UAC prompt. A relatively easy step became annoying multi-step operation. Then proxy/stub classes were moved into Windows SDK DLL…

Things are getting back to be easier with DirectShow Spy. It is given a special property sheet right there withing the DLL, to take care of all the important things:

  • checks registration status
  • buttons to Unregister/Register
  • per-user registration (not recommended though due to system wide class hooking)
  • automatically takes care of UAC prompt
  • place Windows SDK proppage.dll into the same directory near spy, and additional property page will help you to register this additional dependency

To invoke registration UI, start:

rundll32 DirectShowSpy.dll,DoRegistrationPropertySheetModal

from command line, or just have a .BAT file ready to do it for you.

Download links

DirectShow Spy: Who Sent EC_ERRORABORT?

persiflage@stackoverflow asks if there is a chance to use DirectShow Spy see who sent an EC_ERRORABORT notification, which filter exactly. Let us see first why there is no way to find this out, and then we will see what we can do.

DirectShow Filter Graph Manager accepts events from filters via its IMediaEventSink interface. The conversation taking place around event notifications is like this:

  • Filter: Hey, Graph Manager! Can I call you IMediaEventSink?
  • Manager: Yes, you can.
  • F: I notify you on EC_ERRORABORT event, here is HRESULT that I have: VFW_E_SOMETHING.
  • M: OK.

Filter graph manager (FGM) does not ask “Who’s taking?”. It does not need to know, it accepts information anonymously. Can a non-filter post an event? Absolutely, FGM does not have to care. This is simple, but when a question raised who posted the event, there is no answer for it – there was no such information in first place.

The good news through is that a developer does not need one hundred percent precision. The source of the event is important to understand which of the filters aborted streaming, and any information is helpful. Spy impersonates the whole FGM and as such it is capable of covering IMediaEventSink interface as well, in order to trace calls to the log file, and, even more helpful, trace call stack of the call which brought specific event in.

With the call stack information at the time of event notification, the filter of interest can be identified pretty precisely. Especially, having debug symbols available, so that Spy could provide symbols for the code locations on stack.

For instance, let us looks at Windows SDK AMCap Sample which previews video and uses video renderer, and hence has EC_VIDEO_SIZE_CHANGED event involved (just an example, spy from now on traces EC_ERRORABORT call stack only). Once this event reaches FGM, the call stack logged is:

FilterGraphSpy.h(850): CSpyT<class CSpy,&struct _GUID const CLSID_FilterGraph>::Notify: nEventCode EC_VIDEO_SIZE_CHANGED (0x0A), nParameter1 0x00F00140, nParameter2 0x00000000
  DirectShowSpy!6a3ba3b4 CSpyT<CSpy,&CLSID_FilterGraph>::Notify (+ 337) [d:\projects\\repository-public\directshowspy\filtergraphspy.h, 859] (+ 13) @6a3a0000
  quartz!6a243188 CBaseFilter::NotifyEvent (+ 46) @6a220000
  quartz!6a3394f6 CBaseControlVideo::OnVideoSizeChange (+ 56) @6a220000
  quartz!6a2a2f9a CRenderer::CompleteConnect (+ 175) @6a220000
  quartz!6a337668 CRendererInputPin::CompleteConnect (+ 25) @6a220000
  quartz!6a23a470 CBasePin::ReceiveConnection (+ 213) @6a220000
  quartz!6a2a3741 CVideoInputPin::ReceiveConnection (+ 92) @6a220000
  ksproxy!65e94dc0 CBasePin::AttemptConnection (+ 84) @65e70000
  ksproxy!65e94e81 CBasePin::TryMediaTypes (+ 104) @65e70000
  ksproxy!65e94f68 CBasePin::AgreeMediaType (+ 115) @65e70000
  ksproxy!65e966a0 CBasePin::Connect (+ 100) @65e70000
  ksproxy!65e7d711 CKsOutputPin::Connect (+ 381) @65e70000
  quartz!6a23252e CFilterGraph::ConnectDirectInternal (+ 233) @6a220000
  quartz!6a23847c CFilterGraph::ConnectRecursively (+ 44) @6a220000
  quartz!6a238d09 CFilterGraph::ConnectInternal (+ 331) @6a220000
  quartz!6a238c22 CFilterGraph::Connect (+ 23) @6a220000
  DirectShowSpy!6a3b8686 CSpyT<CSpy,&CLSID_FilterGraph>::Connect (+ 881) [d:\projects\\repository-public\directshowspy\filtergraphspy.h, 672] (+ 0) @6a3a0000
  quartz!6a2322f0 CEnumMediaTypes::Release (+ 39) @6a220000
  qcap!6a6c7a31 CBuilder2_2::DoesCategoryAndTypeMatch (+ 408) @6a6b0000
  qcap!6a6b3424 _GUID_00000000_0000_0000_0000_000000000000 (+ 4) @6a6b0000
  qcap!6a6cb9cb CBuilder2_2::RenderStream (+ 5294) @6a6b0000
  AMCap!01009723 @01000000
  AMCap!010041be @01000000
  AMCap!01005e27 @01000000
  AMCap!0100611c @01000000
  AMCap!01007600 @01000000
  AMCap!010076ba @01000000
  AMCap!0100a90d @01000000

It does not take a rocket scientist to see that event is posted by video renderer hosted by quartz.dll, which was a part of pin connection handling, where a pin of ksproxy’s filter – which has to be WDM Video Capture Filter – was connected to video renderer input pin.

DirectShow Spy started logging new items:

  • COM interface calls on filter graph IMediaEvent, IMediaEventEx, IMediaEventSink interfaces
  • Call staclk on IMediaEventSink::Notify call, with EC_ERRORABORT code (other codes are logged without call stack to reduce hook overhead and avoid logging stuff for no reason)

Download links:

DirectShow Spy: Intelligent Connect Trace, Selective Process Black Listing

DirectShow Spy is updated with a few new features:

  • retroactive Intelligent Connect trace
  • log output for IAMGraphBuilderCallback-related activity
  • process name based black list to selectively exclude processes from spying

Intelligent Connect Trace

The utility received a capability to read back from its own log file (DirectShowSpy.log, located typically in C:\ProgramData directory) and reconstruct graph building sequence, including steps taken by DirectShow Intelligent Connect.

In order to activate the Intelligent Connect Trace property sheet, one needs to call exported function “DoGraphBuilderCallbackPropertySheetModal“, such as using runndll32 tool:

C:\DirectShowSpy>rundll32 DirectShowSpy.dll,DoGraphBuilderCallbackPropertySheetModal

The upper part of the property page displays recently created DirectShow fitler graphs, newest to older. For a selected graph, the lower part displays events in chronological order. The events include:

The latter two methods also show “Application Result” column and values, which are HRESULT values returned by IAMGraphBuilderCallback callback provided by the application. Typically, a failure HRESULT code indicates that the application rejected the filter.

The trace log is good to expose all DirectShow junk installed in the system. For example,

Continue reading →

DirectShow Spy: Memory Allocator Properties

A small update to the DirectShow Spy today: DirectShow Filter Graph Spy prints memory allocator properties as a part of graph topology trace on transition to running state. Why is that and what it is for? Filters normally agree on allocator properties (ALLOCATOR_PROPERTIES, obtained from IMemAllocator, obtained from IMemInputPin) themselves without interference from controlling application. Sometimes an undesired choice of buffers can cause sick runtime behavior, including but not limited to the following:

  1. live audio capture buffers are too long, and introduce significant latency, including from live video capture stream taking place in parallel; controlling application might need to take advantage of IAMBufferNegotiation::SuggestAllocatorProperties and request shorter buffers.
  2. a filter, such as for example DMO Wrapper Filter, may default to 1 buffer on allocator, which means that if a buffer reference is for some reason held for future reference (e.g. a video filter might be holding a reference to be able to re-push a video sample if an application is requesting video update), the entire streaming might lock.
  3. some filters are requesting unreasonably many/large buffers and consume vast spaces of RAM, such as MainConcept MXF Demultiplexer requesting 200 buffers x 640 kilobytes each (128 MB in total out of sudden)
  4. some filters are requesting unreasonably few/small buffers resulting in inability to pre-buffer data

In a chase for answers to questions “Where is my memory?”, “Why is it so choppy?”, “I would really appreciate a nice lipsync” and to troubleshoot the mentioned scenarios it is helpful to understand buffering configuration. DirectShow Filter Spy is here to deliver this information. Once the graph is put into running state, spy prints out topology data into log file (which is in most cases C:\ProgramData\DirectShowSpy.log):

Pin 2: Name "Input 01", Direction "Input", Peer "Tee 0x087A5AF0.Output2"
 Connection media type:
 majortype {73646976-0000-0010-8000-00AA00389B71}, subtype {31435641-0000-0010-8000-00AA00389B71}, pUnk 0x00000000
 bFixedSizeSamples 0, bTemporalCompression 0, lSampleSize 1
 formattype {E06D80E3-DB46-11CF-B4D1-00805F6CBBEA}, cbFormat 170, pbFormat 0x07c46fc0
 rcSource { 0, 0, 0, 0 ), rcTarget { 0, 0, 0, 0 }
 dwBitRate 0, dwBitErrorRate 0, AvgTimePerFrame 0
 dwInterlaceFlags 0x0, dwCopyProtectFlags 0x0, dwPictAspectRatioX 16, dwPictAspectRatioY 9, dwControlFlags 0x0
 bmiHeader.biSize 40, bmiHeader.biWidth 1280, bmiHeader.biHeight 720, bmiHeader.biPlanes 1, bmiHeader.biBitCount 24, bmiHeader.biCompression avc1
 bmiHeader.biSizeImage 0, bmiHeader.biXPelsPerMeter 1, bmiHeader.biYPelsPerMeter 1, bmiHeader.biClrUsed 0, bmiHeader.biClrImportant 0
 dwStartTimeCode 0x00000000, cbSequenceHeader 38, dwProfile 100, dwLevel 31, dwFlags 0x4
 [0x0000] 00 1D 67 64 00 1F AC 24 88 05 00 5B BF F0 00 10
 [0x0010] 00 11 00 00 03 03 E8 00 00 E9 BA 0F 18 32 A0 00
 [0x0020] 05 68 EE 32 C8 B0
 Memory Allocator: 1 buffers, 1,024 bytes each (1,024 bytes total), align 1, prefix 0

Partial Visual C++ .NET 2008 source code is available from SVN, release binary included (Win32, x64); installation instructions are in another post.

DirectShow Spy: IAMFilterData interface

This quick update for DirectShow Filter Graph Spy adds tracing for (deprecated) IAMFilterData interface, and also fixes problem with Media Player Classic – Home Cinema, when the player crashes in External Filters windows.

Partial Visual C++ .NET 2008 source code is available from SVN, release binary included (Win32, x64); installation instructions are in another post.

DirectShow Spy: Filter Mapper Spy

DirectShow Filter Graph Spy was updated to add new functionality: spying over another DirectShow object – Filter Mapper. Filter Mapper object is used for filter registration purposes and also internally by DirectShow’s Intelligent Connect. IFilterMapper2 interface traces provide detailed description on steps DirectShow takes in order to connect and/or render filter pins. For example when trying to render MEDIATYPE_Audio pin, Filter Mapper suggested “RDP DShow Redirection Filter” and, then, “Default DirectSound Device”:

FilterMapperSpy.h(186): CFilterMapperSpy::EnumMatchingFilters: nFlags 0x0, bExactMatch 0, nMinimalMerit 0x00200001, bInputNeeded 1, nInputTypeCount 1, pInputPinCategory NULL, bRender 1, bOutputNeeded 0, nOutputTypeCount 0, pOutputPinCategory NULL
FilterMapperSpy.h(191): CFilterMapperSpy::EnumMatchingFilters: nInputTypeIndex 0, MajorType {73647561-0000-0010-8000-00AA00389B71}, Subtype {00000000-0000-0000-0000-000000000000}
FilterMapperSpy.h(212): CFilterMapperSpy::EnumMatchingFilters: pMoniker @device:sw:{083863F1-70DE-11D0-BD40-00A0C911CE86}\{AB9D6472-752F-43F6-B29E-61207BDA8E06}
FilterMapperSpy.h(220): CFilterMapperSpy::EnumMatchingFilters: sFriendlyName "RDP DShow Redirection Filter", sDescription "", sDevicePath ""
FilterMapperSpy.h(212): CFilterMapperSpy::EnumMatchingFilters: pMoniker @device:cm:{E0F158E1-CB04-11D0-BD4E-00A0C911CE86}\Default DirectSound Device
FilterMapperSpy.h(220): CFilterMapperSpy::EnumMatchingFilters: sFriendlyName "Default DirectSound Device", sDescription "", sDevicePath ""

Important is that DirectShow’s internals are using the mapper through COM instantiation and this provides a safe method to override default behavior process wide in order to, for example, extend Intelligent Connect functionality onto custom, unregistered globally, private filters.

Also, the binary is renamed from FilterGraphSpy.dll to DirectShowSpy.dll.

Partial Visual C++ .NET 2008 source code is available from SVN, release binary included (Win32, x64); installation instructions are in another post.

DirectShow Filter Graph Spy: 64-bit version and hook API

Today’s update for DirectShow Filter Graph Spy introduces 64-bit version (mind the beta state) and a mini-API for an external module to be involved into graph building process.

Filter Graph Spy is offering three new interfaces that provide extensibility of the spy:

  • IFilterGraphAddRemoveHook
  • IFilterGraphConnectHook
  • IFilterGraphStateControlHook

The interfaces are contained in the type library and can be imported using #import directive. An implementation of one or more of these interfaces will receive hook style calls corresponding to respective Filter Graph Manager calls, system wide including in context of other applications.

A COM object may be registered as a hook object with Spy and NoThreadSpy COM classes under predefined registry keys:

Registering a DirectShow Filter Graph Spy Hook

Spy will instantiate the registered hook objects and forward them the calls it receive, before passing them to system Filter Graph Manager object. A hook object has an option to override default processing, including, for example, inserting its own filter in between. For example, IFilterGraph::Reconnect call is implemented the following way:

STDMETHOD(Reconnect)(IPin* pPin) throw()
    _Z4(atlTraceCOM, 4, _T("...\n"));
        OnReconnect(pT, pPin, &bDefault);
    return m_pInnerFilterGraph2->Reconnect(pPin);

Before passing the call to original Reconnect method, spy is iterating through associated hooks, passing them IFilterGraphConnectHook::OnReconnect call. Setting bDefault parameter to FALSE will prevent spy from passing the call to original method.

Included BdaHooks project shows a sample implementation of the hooking COM classes (note .rgs registration).

Filter Graph Spy is compatible with all current Windows operating systems, 32-bit and 64-bit (x64), in particular including:

  • Windows 7
  • Windows Server 2008
  • Windows Vista
  • Windows Server 2003
  • Windows XP
  • Windows 2000

NOTE: DirectShow Filter Graph Spy is NOT suitable for production environment, it is NOT licensed to be redistributed to be a part of production state software item.

Partial Visual C++ .NET 2008 source code is available from SVN, release binary included (Win32, x64); installation instructions are in another post.