How to control IP Video Source JPEG video filter from C# programmatically

User’s question about Alax.Info IP Video Source DirectShow filter for JPEG/M-JPEG video:

As it stands now, in order to configure the video capture source filter, I have to instantiate it […], set the properties (camera URL, and video dimensions), then save the filter state to a data array which is then compiled into the application. That doesn’t provide a feasible system for user configuration at run time: Telling our customers we need to have the IP address(es) of their camera(s) so we can build a custom version of our software for them isn’t going to be very marketable.

[…]

I looked on the Alax.Info Web site, and didn’t see anything that looks like an API for configuration. I also didn’t see anything inside […] that looks like configuration parameters. I find it hard to believe this isn’t a common requirement, so I suspect I’m overlooking something.

Is there an “easy” way to configure the parameters of the Alax.info video capture filter at run time? Where is it documented?

Is there a structure that describes the state info that is captured [into raw configuration data array]?

Are there any other considerations I’ve overlooked?

C++ and C# sample code (Trac, SVN) appears to be not easy no discover. The code snippets include PlayJpegLocation (C++) and PlayJpegLocationSharp (C#) projects/snippets demonstrating import of IP Video Source COM interfaces via COM and use them from code.

IP Video Source comes with a COM type library which is well suited for import by development environments, specifically by Visual Studio as a reference for .NET projects. To avoid bitness issues, it is recommended to have both 32 and 64 bit versions of IP Video Source installed.

C# integration is straightforward:

First, one needs to add a reference to AlaxInfoIpVideoSource type library, the same way DirectShow.NET is added to the project. The type library is imported and is available via .NET COM Interop at this point (specifically, visible in Object Browser). In code, the definitions are in AlaxInfoIpVideoSource namespace, JpegVideoSourceFilter represents a filter class, with IJpegVideoSourceFilter interface offering filter configuration.

The filter is also compatible with generic COM persistence: it implements standard IPersistStream, IPersistStreamInit, IPersistPropertyBag and other interfaces, its state can be saved to stream or property bad and loaded back.

C++ #import and x64 builds

I already wrote earlier on 32/64-bit issues with Visual Studio. The problems are not frequent but when they happen they are pretty confusing. Here is another one today.

C++ code is simple:

    #import "libid:59941706-0000-1111-2222-7EE5C88402D2" raw_interfaces_only no_namespace

    CComPtr<IObject> pObject;
    ATLENSURE_SUCCEEDED(pObject.CoCreateInstance(__uuidof(Object)));
    BYTE* pnData;
    ATLENSURE_SUCCEEDED(pObject->Method((ULONG_PTR) (BYTE*) pnData));

A COM method returns a pointer to data – pretty straightforward, what could have gone wrong?

COM server vendor designed the library for easy .NET integration and defined the pointer argument as an integer value. They suppose the values to be used further with System.Runtime.InteropServices.Marshal class.

32-bit builds worked well and 64-bit builds experienced memory access violations. An attempt to consume the COM server from C# project showed the same problem: unexpected exception in the call.

The problem is that cross-compiler importing COM type library using LIBID takes 32-bit library even when it builds 64-bit code. This is the problem for both C++ #import "libid:..." and .NET COM reference using the identifier.

The type library imports as the following IDL in 32-bits:

[id(1)]
HRESULT Method(
                [in] unsigned long bufPtr);

It is supposed that 64-bit builds get the following import:

[id(1)]
HRESULT Method(
                [in] uint64 bufPtr);

Effectively though, 64-bit builds get the 32-bit import and the argument which is supposed to carry casted pointer value is truncated to 32-bits, ULONG type. Cast to ULONG_PTR in 64-bit C++ code is, of course, not helpful since it’s trimmed anyway further fitting the IDL argument type.

The same happens with C# build.

It was developer’s choice to publish ordinal type argument, they wanted this to be “better” and ended up in bitness mess. If the argument remained a pointer type in the IDL then even incorrect bitness would not necessarily result in value truncation.

All together it is unsafe to import [an untrusted] type library using LIBID when it comes to 64-bit builds. It’s 32-bit library to be taken and it can result in incorrect import. Instead, such build should explicitly point to 64-bit type library, for example:

#if defined(_WIN64)
    #import "Win64\ThirdParty.DLL" raw_interfaces_only no_namespace
#else
    //#import "libid:59941706-0000-1111-2222-7EE5C88402D2" raw_interfaces_only no_namespace
    #import "Win32\ThirdParty.DLL" raw_interfaces_only no_namespace
#endif

Too bad! libid looked so nice and promising.

DirectShowSpy: Restore default system behavior

There was a problem reported for registered and relocated DirectShowSpy, which might be causing issues: Deleting faulty DirectShowSpy registry key.

Some users that use a 3rd party tool called DirectShowSpy may encounter errors when logging in to XSplit.

This can be caused by a fault registry key that is introduced when DirectShowSpy is registered to intercept Filter Graph initialization — Filter Graph is used by XSplit. The faulty DirectShowSpy registry key is usually caused by DirectShowSpy program begin relocated after registration.

To workaround this situation, XSplit1 detects the presence of HKEYCLASSESROOT\CLSID{E436EBB3-524F-11CE-9F53-0020AF0BA770}\TreatAs registry key2 when it fails to initialize Filter Graph and exits when it is found. In this case, user must manually correct the DirectShowSpy registration or delete3 the registry key. Only after either is done can XSplit be restarted.

The description of the problem is good, solution is good but incomplete.

DirectShowSpy intercepts a few COM classes, not just one, and removing single registry value is only a partial fix.

DirectShowSpy.dll exports UnregisterTreatAsClasses function to accurately restore operation of system classes. It does registry permission magic and updates all COM classes involved. Default unregistration (DllUnregisterServer, regsvr32 /u) behavior is to restore original classes only in case they are currently overridden by DirectShowSpy. That is, if the DLL is moved (deleted) the broken registrations are retained in the registry during unregistration process.

UnregisterTreatAsClasses resolved this problem by forcing recovery of original classes no matter who is overriding them at the moment.

C:\>rundll32 DirectShowSpy-Win32.dll,UnregisterTreatAsClasses
C:\>rundll32 DirectShowSpy-x64.dll,UnregisterTreatAsClasses

ManifestComDependency: Adding/Removing Registration-Free COM dependencies

In one of the earlier posts I mentioned a problem with registration-free COM dependency setup up by Visual Studio as a part of build process. This post is about the tool that offers an alternate solution for the problems, and also gives more:

  • standalone, independent from build, addition/removal registration-free COM links
  • automation of registration-free COM links for native code builds

Unless one deals with ACTCTX activation contexts programmatically, taking advantage of registration-free COM is essentially a setup of respective side-by-side manifests, either standalone or embedded into executable. The manifests override standard global COM object lookup procedure and point to COM servers directly, in some way similar to the way static links to exported DLL functions work.

To enable, registration-free COM reference COM client needs to prepare respective manifest file (declare an assembly) and list its COM dependencies. The COM dependency list is not just COM server files but rather their COM classes and type libraries. That is, when it comes to automation, the tool needs to go over the COM servers and extract respective identifiers, so that it could properly shape this information and include into manifest XML.

To deal with the challenge, I am using ManifestComDependency tool (no better came to mind when it was created). The command line tool syntax is the following:

 ManifestComDependency <path> [options]
 
 Options:
  * /i[:<assembly-name>] - updates <assemblyIdentity> element replacing it with automatically generated
  * /a[:tc] <dependency-path>[|LIBID[;X.Y]] - adds file <dependency-path> dependency, optionally without type library and COM classes reference
  * /r <dependency-path> - removes dependency from file <dependency-path>
  * /e <export-path> - exports manifest to file <export-path>

The tool opens a file <path> for manifest modification. The file can be a binary file, such as .exe or .dll, in which case the tool will be working with embedded manifest resource. Or, if the path extension is .manifest or .xml, then the file is treated as standalone XML manifest file. Both options are good for manifests associated with .NET builds as well, even though this surely does not need to be managed code development – native COM consumers are okay.

/i option updates <assemblyIdentity> element in the manifest data because it is mandatory and in the same time it might so happen that it is missing or incomplete, which ends up in painful troubleshooting. The tool will name file name, architecture and version information from the binary resource information (product version; the VERSIONINFO resource has to be present).

ManifestComDependency Output

/a option adds a dependency, defined by either path or registered type library identifier the tool would use to resolve the path. t and c modifiers exclude type library and COM server information respectively. The tool created a file element for the dependency and populates it with COM servers discovered there. Type library is obtained using LoadTypeLib API and COM servers are picked up by a trick with RegOverridePredefKey and friends (e.g. see example in regsvr42 source or GraphStudioNext source – both are excellent sources of code snippets for the curious).

The task of COM server discovery assumes load of the library into process (LoadLibrary API call), which assumes bitness match between the tool and the loaded module. This is where you need both Win32 and x64 builds of the tool: if wrong bitness is detected, the tool restarts its sister build passing respective command line arguments, just like regsvr32 does with the only difference that regsvr32 uses binaries in system32 and syswow64 directories, and my tool looks for -Win32 and -x64 suffixes and the files in the same directory.

/r option removes dependencies by name. It is safe to remove certain dependency before adding it back because addition does not check if dependency already exists.

/e option writes the current version of the manifest in external file, esp. for review and troubleshooting if the actual manifest is embedded.

The order of options is important because they are executed one by one in the order of appearance. Then the result is saved/re-embedded once all tasks are done.

Command line example of re-adding registration-free COM dependencies:

ManifestComDependency-x64.exe BatchExport-x64.exe /i /r Player.dll /r mp4demux.dll /a Player.dll /a mp4demux.dll

This can be set as post-build event in the project settings, or put into batch file or run on-demand.

Download links

Registration-Free COM dependencies and COM reference isolation

Visual Studio offers COM reference isolation to applications so that COM dependency is used in a usual way, and in the same time there is no need in its registration or another copy of the COM server might be registered system wide, or using per-user registration, and the application would still prefer a local copy of COM server.

Isolated Property

The advantage is obvious: no more COM registration hell, and the application can be distributed with lowered risk of conflicts with other installed software, without a risk to affect other applications by registering an unwanted piece of software. Also, with an option to use COM dependency without need of elevated privileges to perform COM registration.

The feature is using reg-free COM and is not new. Articles on internet on using the feature date back to 2007 and earlier (e.g. Isolated COM), reg-free COM existed earlier. The feature is cool and offers a one click access to an incredibly powerful option with complicated technology underneath.

Problem 1: It is 2015 fall today and Visual Studio 2013 still does not have this – as complicated as Enabled/Disabled option – working right.

Once enabled, the option has the following effect on the project:

  1. the manifest file is detached from the binary and is written to external file (Client.exe + Client.exe.manifest as opposed to Client.exe with manifest embedded as resource)
  2. the manifest receives assembly/file elements that establish a registration-free link to COM dependency; the content of the element is repeating registration of the COM dependency normally written into registry system wide
  3. the compiler uses RegOverridePredefKey and friends API to check COM dependency registration keys and update the manifest file (see previous item) respectively

Apparently the COM server has to be registered at compile time, so that compiler could convert the registration into manifest file. For whatever reason, Visual Studio 2013 looks for 32-bit COM server when it is doing 64-bit build. That is, building x64 configuration with x64 COM server registered and supposed to be used further fails if you don’t have a similar Win32 COM server registered. Bummer.

This simple solution ComIsolation01 (Trac, SVN) has two projects: C++ COM server with 32 and 64 bit configurations, and C# client consumer. A build of Debug/Release x64 configuration successfully builds Server.dll, registers it, then attempts to build Client.exe and fails:

2>C:\Program Files (x86)\MSBuild\12.0\bin\Microsoft.Common.CurrentVersion.targets(2234,5): warning MSB3284: Cannot get the file path for type library “ae2714e3-e8be-44c7-b737-5510e5f8abed” version 1.0. Library not registered. (Exception from HRESULT: 0x8002801D (TYPE_E_LIBNOTREGISTERED))
2>D:\Projects\Alax.Info\Repository-Public\Utilities\Miscellaneous\ComIsolation01\Client\Program.cs(14,13,14,22): error CS0246: The type or namespace name ‘ServerLib’ could not be found (are you missing a using directive or an assembly reference?)
2>D:\Projects\Alax.Info\Repository-Public\Utilities\Miscellaneous\ComIsolation01\Client\Program.cs(14,51,14,60): error CS0246: The type or namespace name ‘ServerLib’ could not be found (are you missing a using directive or an assembly reference?)

TYPE_E_LIBNOTREGISTERED, really? Because it looks for 32-bit type library and there is only 64-bit one registered. Build Win32 configuration once, and x64 builds are fixed. In other aspects, x64 build of Server.dll is correctly picked up.

Problem 2: Inflexible. The only COM reference isolation offered is a link with size and hash specification of the dependency.

Strict dependency check in manifest file

Why on earth? Okay it might be good for some people, perhaps. The only scenario I want to ever use is a link without checks for whether dependency is exactly as at build time. It is already isolated and the isolated file will be picked up. I would like to retain an option to patch it quickly by simply substituting a new file there, without an annoying need to patch manifest respectively. I don’t have an option like this.

Another post soon will show a solution for the problem, as well as easy way to apply isolation to C++ clients as well.

DirectShow Spy: Integration with GraphStudioNext

DirectShow Spy is introducing integration with GraphStudioNext (and GraphEdit too) to let a developer quickly open a filter graph through Running Object table with external inspection tool.

Note that you need a revision 301 GraphStudioNext or later, prebuilt versions available for download here: graphstudionext.exe (Win32)graphstudionext64.exe (x64).

DirectShow Filter Graph list window offers context menu items and hotkeys to launch GraphStudioNext with command line parameters to open specific filter graph.

GraphStudioNext Integration in Filter Graph List

DirectShow Filter Graph Property Frame dialog’s actions view has a button and a hotkey to open current filter graph in GraphStudioNext:

GraphStudioNext Integration in Filter Graph Actions

When invoked from DirectShowSpy UI, GraphStudioNext opens immediately at view of interest:

A Filter Graph in Graph Studio Next

Spy is looking for GraphStudioNext in last used location (stored in registry), in current spy’s directory, in GraphStudioNext registry subkey and if none of the mentioned works then prompts user to locate the binary.

Similar functionality is also available programmatically using one of the following ways:

  • ISpy::OpenGraphStudioNext([in] LONG nParentWindowHandle, [out, retval] VARIANT_BOOL* pbResult);
  • IFilterGraphHelper::OpenGraphStudioNext([in] LONG nParentWindowHandle, [in] BSTR sMonikerDisplayName, [out, retval] VARIANT_BOOL* pbResult);

Download links

DirectShow Spy: Filter Graph Data and Properties UI

Summary

DirectShow Spy receives a set of new powerful updates helping to develop and troubleshoot DirectShow applications:

  • UI combining Markdown formatted filter graph details as well as property pages for all participating filters
    • available interactively via filter graph list
    • available via helper COM class letting DirectShow application integrate incredible troubleshooting capabilities
  • Option to email filter graph details

On of the next posts should be summarizing all DirectShowSpy capabilities currently split across posts.

Filter Graph Details UI

Filter graph list window received a new button Properties with the associated action duplicated on double click handler for the list item. Once clicked, a new UI pops up to bring information about the entire graph.

Properties Button in the Filter Graph List

The window contains a tree control on the left, items of which show the following elements:

  1. Top “Filters” item shows Markdown formatted text, mentioned in an earlier post
  2. Second level items are one per each filter participating in the filter graph
    • a double click of such item brings up standard OleCreatePropertyFrame property sheet with filter properties
    • an edit control to the right lists filter specific information, with connections and media types
  3. Third level items are property pages of the filter, the property pages are integrated into the window; all together, the window provides access to all property pages of all filters in the graph
  4. Bottom first level “Email” items provides a convenient way to send the information via email

The picture below shows a property page integrated into the UI:

Integrated Filter Property Page

Email interface adds basic information about the system, and also provides space to enter a note about the graph. Email information (From, To etc) is put on registry for further reuse.

Email Filter Graph

Programmatic Interface

Additionally to interactive access from filter graph list, the new UI is also accessible viaFilterGraphHelper COM class. This lets applications integrate new capability right into the application as a troubleshooting option.

While accessing the graph through ROT using GraphStudioNext/GraphEdit application remains the most convenient interactive way to work with the graph, the helper’s advantage is access to the graph without crossing process boundaries: this, in particular, provides smooth access to property pages not implemented well for marshaling, such as, for example, Audio Renderer related property pages.

FilterGraphHelper‘s FilterGraph property accepts filter graph interface pointer (from now on, it is also possible to supply any filter or pin interface, and helper will walk up to the graph automatically).

IFilterGraphHelper::DoPropertyFrameModal method shows the UI modal window introduced above.

Two samples are demonstrating programmatic access to the new helper method.

  1. Sample\FilterGraphHelperDialog (SVN) C# application (code snippet) shows it straightforward way: to build a graph, to initialize helper, to show modal UI:
    IFilterGraph2 graph = new FilterGraph() as IFilterGraph2;
    graph.RenderFile(@"E:\Media\GoPro 2010 Highlights - You in HD - 1920x1080.mp4", "");
    FilterGraphHelper helper = new FilterGraphHelper();
    helper.FilterGraph = graph;
    helper.DoPropertyFrameModal(0);
    
  2. Sample\RegistrationFreeFilterGraphHelper (SVN) C++ application features an advanced technique to leverage new capability in application, without need to COM-register the Spy.

Registration of the DirectShowSpy requires, generally speaking, administrative privileges. Then in addition spy hooks the system which might be not desirable.

The application however load the Spy directly using LoadLibrary API and instantiates the helper directly bypassing COM registration. This is sufficient to start the new UI feature and access the graph interactively.

    CComPtr<IFilterGraphHelper> pFilterGraphHelper;
    #if TRUE
        const HMODULE hModule = LoadLibrary(_T("DirectShowSpy.dll"));
        [...]
        ATLENSURE_SUCCEEDED(pClassFactory->CreateInstance(NULL, __uuidof(IFilterGraphHelper), (VOID**) &pFilterGraphHelper));
        // TODO: FreeLibrary against hModule
    #else
        ATLENSURE_SUCCEEDED(pFilterGraphHelper.CoCreateInstance(__uuidof(FilterGraphHelper)));
    #endif
    ATLENSURE_SUCCEEDED(pFilterGraphHelper->put_FilterGraph(pFilterGraph));
    ATLENSURE_SUCCEEDED(pFilterGraphHelper->DoPropertyFrameModal(0));

The alternate #if code path shows the COM registration equivalent for the code.

Download links