Archive for January, 2009

Published by Roman on 29 Jan 2009

DirectShow Filter Graph Spy – CLSID_FilterGraphNoThread

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’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).

I have never needed to use this CLSID (by the way Windows SDK has one more CLSID – CLSID_FilterGraphPrivateThread – 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’ clock.avi appeared to be not published on Running Object Table. Why?

Process Monitor showed clearly that Windows Media Player created filter graph through CLSID_FilterGraphNoThread and quite obviously it was not intercepted by Filter Graph Spy (still I wonder what made it different previous time when I could see WMP’s graph).

Continue Reading »

Published by Roman on 28 Jan 2009

DirectShow Filter Graph Spy

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 developer to spy over running filter graphs system wide by connecting Component Object Model (COM) “Treat As” feature with filter graph remoting through Running Object Table and connecting to remote process graphs using GraphEdit utility.

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?

Continue Reading »

Published by Roman on 21 Jan 2009

The easiest yet user-friendly WTL way to Minimize Application to System Tray Icon

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’s WM_LBUTTONDBLCLK to restore and WM_RBUTTONUP to pop up a menu
  • 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

Relevant source code from application main window (dialog) class:

Continue Reading »

Published by Roman on 17 Jan 2009

JPEG Multi File Video Capture Source Filter, a virtual DirectShow camera

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 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 AMCap Sample, Media Player Classic, VideoLAN, Skype, Windows Media Encoder enumerates video capture sources, instantiate the one of the interest and it should already be ready to stream.

Implementation Details

The very first question is embedding of an existing filter into new filter. The two most common methods are:

  • COM aggregation
  • 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

The COM aggregation methods is much easier in implementation but it is subject to a few constraints, the two most important of which are:

  • embedded filter should support instantiation as an aggregated object
  • it is the only underlying filter, not a chain of filters, which can produce required data

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).

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.

Playback looping is added to the original JPEG Multi File Source Filter and its controlling private interface IJpegMultiFileSourceFilter received additional properties:

interface IJpegMultiFileSourceFilter : IDispatch
{
...
    [propget, id(2)] HRESULT AutoRepeat([out, retval] VARIANT_BOOL* pbAutoRepeat);
    [propput, id(2)] HRESULT AutoRepeat([in] VARIANT_BOOL bAutoRepeat);
    [propget, id(3)] HRESULT RepeatDelay([out, retval] LONG* pnRepeatDelay);
    [propput, id(3)] HRESULT RepeatDelay([in] LONG nRepeatDelay)

as well the new property page:

Continue Reading »

Published by Roman on 17 Jan 2009

AMCap issue

While playing around with a camera DirectShow video source filter, AMCap, which is widely used sample and which I believed to be very stable, started crashing in an inner call CDeviceMoniker::IsEqual:

 	devenum.dll!CDeviceMoniker::IsEqual()  + 0x13 bytes
>	AmCap.exe!ChooseDevices(IMoniker * pmVideo=0x003e9a38, IMoniker * pmAudio=0x00000000)  Line 2672 + 0x2a bytes	C++
 	AmCap.exe!ChooseDevices(wchar_t * szVideo=0x0013f5b8, wchar_t * szAudio=0x0013edb0)  Line 2753 + 0x13 bytes	C++
 	AmCap.exe!AppInit(HINSTANCE__ * hInst=0x00400000, HINSTANCE__ * hPrev=0x00000000, int sw=1)  Line 379 + 0x13 bytes	C++
 	AmCap.exe!WinMain(HINSTANCE__ * hInst=0x00400000, HINSTANCE__ * hPrev=0x00000000, char * szCmdLine=0x00161f32, int sw=1)  Line 453 + 0x11 bytes	C++
 	AmCap.exe!__tmainCRTStartup()  Line 578 + 0x35 bytes	C
 	AmCap.exe!WinMainCRTStartup()  Line 403	C
 	kernel32.dll!_BaseProcessStart@4()  + 0x23 bytes

It appeared that while setting a checkmark on proper menu item the code does not check for the moniker tobe not NULL and a NULL IMoniker pointer passed as an argument into IMoniker::IsEqual is not checked inside devenum.dll (which is obviously a bug for an API entry).

To hotfix the problem, it is necessary to add an extra check near line 2650 of amcap.cpp:

    int i;
    for(i = 0; i < NUMELMS(gcap.rgpmVideoMenu); i++)
    {
        if(gcap.rgpmVideoMenu[i] == NULL)
            break;
        // HOTFIX: Avoid calling IMoniker::IsEqual(NULL) due to possible memory access violation
        if(!gcap.pmVideo)
            continue;
        CheckMenuItem(GetMenu(ghwndApp),
            MENU_VDEVICE0 + i,
            (S_OK == gcap.rgpmVideoMenu[i]->IsEqual(gcap.pmVideo)) ? MF_CHECKED : MF_UNCHECKED);
    }

    for(i = 0; i < NUMELMS(gcap.rgpmAudioMenu); i++)
    {
        if(gcap.rgpmAudioMenu[i] == NULL)
            break;
        // HOTFIX: Avoid calling IMoniker::IsEqual(NULL) due to possible memory access violation
        if(!gcap.pmAudio)
            continue;
        CheckMenuItem(GetMenu(ghwndApp), MENU_ADEVICE0 + i,
            (S_OK == gcap.rgpmAudioMenu[i]->IsEqual(gcap.pmAudio)) ? MF_CHECKED : MF_UNCHECKED);
    }

Published by Roman on 10 Jan 2009

MediaTools to deliver video from network/IP cameras and video servers into DirectShow environment

I decided to gather DirectShow code and filters related to processing video from network/IP cameras and video servers into a library (in fact, a few libraries) so that it could be easily used for testing, research and other purposes. As time is going to permit, documentation and sample code will be provided, further development will be carried out.

This initial post publishes two libraries (DLLs) which host DirectShow filters to receive JPEG or M-JPEG video from network using HTTP based protocol, decode video with the help of Intel IPP 6.0 library (UMC version of the JPEG implementation), as well as perform additional helpful features, including writing series of frames into .JPG files and emulating video feed from saved .JPG files, conversion filters to obtain video in 24-bit and 32-bit RGB formats, YUY2 and YV12 formats.

File and Class Summary

Acqusition.dll

Acqusition.dll (download) hosts the following classes:

  • DirectShow Filters
    • HTTP Stream Source Filter, to receive HTTP content and deliver into DirectShow environment through output pin
    • JPEG HTTP Stream Parser Filter, to parse HTTP content (typically received from HTTP Stream Source Filter) of image/jpeg and multipart/x-mixed-replace types and output JPEG frames
    • JPEG Multi File Renderer Filter, to write parsed JPEG frames (typically received from JPEG HTTP Stream Parser Filter) into files
    • JPEG Multi File Source Filter, to read JPEG files (typically written by JPEG Multi File Renderer Filter) and stream video into DirectShow environmentto emulate video feed

Acqusition.dll is dependent only on well known DLLs and does not require presence/redistribution of specific dependency files.

CodingI.dll

CodingI.dll (download) hosts the following classes:

  • DirectShow Filters
    • JPEG Frame Decoder Filter, to parse JPEG data and pass frames downstream with a corresponding mediatype provided with VIDEOINFOHEADER format and correct resolution; in case of resolution changes the filter is capable of dynamically re-agreeing media type with downstream peer
    • JPEG Decoder Filter, to decode JPEG data into 24-bit/32-bit RGB (unfortunately Intel IPP codec has limited capabilities of decoding video into YUV pixel formats)
    • YUY2 Encoder Filter, to convert RGB data into YUY2 pixel format
    • YV12 Encoder Filter, to convert RGB data into YV12 pixel format
  • Shell Extensions
    • JPEG File Resolution Shell Property Page, to provide additional information (such as sampling, color format, resolution) about .JPG and .JPEG files through a shell property page

CodingI.dll is dependent on Intel IPP 6.0 library DLLs and relies on availability of these dependencies in the system. For the CodingI.dll to be operationable, it is required to have a free, trial or registered version of Intel IPP library installed. In particular, the files that are sufficient to be present/redistributed are:

  • libguide40.dll, libiomp5md.dll
  • ippcore-6.0.dll
  • ippi-6.0.dll, ippj-6.0.dll, ipps-6.0.dll, ippcc-6.0.dll, ippvc-6.0.dll
  • ippipx-6.0.dll, ippjpx-6.0.dll, ippspx-6.0.dll, ippccpx-6.0.dll, ippvcpx-6.0.dll

Quick Usage Example

Given a M-JPEG compatible network camera on IP address 98.76.54.32, the graph constructed as shown below is capable to deliver video data and render it through standard Video Mixing Renderer Filter.

Assume the camera is StarDot NetCam SC series, with /jpeg.cgi HTTP request to query for JPEG image and /nph-mjpeg.cgi HTTP request to query for M-JPEG video feed.

To construct the graph in GraphEdit (GraphStudio), first it is required to manually add Alax.Info HTTP Stream Source Filter and provide HTTP URL for the request in the property page:

Then an Alax.Info JPEG HTTP Stream Parser Filter is added and connected downstream to the previously added source filter:

A this point it is possible to automatically render output pin and DirectShow intelligent connect will add the rest of required filters, Alax.Info JPEG Frame Decoder (I), Alax.Info JPEG Decoder (I) and Video Renderer.

However, in order for the graph to run in GraphEdit it is required to manually provide correct video resolution in the properties of JPEG Frame Decoder Filter and once applied reconnect all downstream connections up to video renderer in order to enforce new media type.

Running the graph will start streaming video.

Class Overview

HTTP Stream Source Filter

The filter sends one or series of HTTP requests to receive data from network location and stream received data into DirectShow environment.

Continue Reading »

Published by Roman on 05 Jan 2009

RegisterTypeLibrary to register type libraries the way regsvr32 registers COM servers

While regsvr32 tool is used to register COM servers, such as .DLL, .OCX, .AX files, sometimes you need an equivalent for type library files, such as .TLB, for example to automate software registration on a host. There has been no stock utility for this that I am aware of, so I have been using my own one (RegisterTypeLibrary):

C:\Program Files\Alax.Info\Media Tools>RegisterTypeLibrary.exe Acquisition.dll
Type Library Information:
  File Path: Acquisition.dll
  File Full Path: C:\Program Files\Alax.Info\Media Tools\Acquisition.dll
  Name: AlaxInfoMediaToolsAcquisition
  Documentation String: Alax.Info Media Tools Acquisition 1.0 Type Library
  GUID: {4C03D3C8-13AD-451F-9363-FAD08EF94A91}
  LCID: 0
  Platform: 1
  Version: 1.0
  Flags: 0x8

Registering...
Registered.

/U unregisters, /Q displays information about type library (may be useful as it also works for .DLL files where type library is embedded into binary).

A Visual C++ .NET 2008 source code is available from SVN, release binary included.