This post is starting a step by step tutorial on writing a simple DirectShow filter using a simplified DirectX Media Objects (DMO) API. From the very scratch, the goal is to make a DirectShow/DMO video processing filter which implements video brightness and contrast correction.
DirectX Media Objects are COM-based components. To implement a COM object we will use Visual Studio .NET 2008 and Active Template Library (ATL).
We are starting creating a no thrills ATL DLL project following by adding a no thrills ATL Simple Object Class:
The only thing to make sure is to choose “Both” Threading Model since DirectX Media Object must function correctly in a free-threaded environment:
Visual Studio wizard creates a class which implements custom IDispatch-derived dual interface. We will not need it until we get to persistence and property pages.
First of all, we need to reference relevant SDK headers to be able to use DirectShow and DMO API:
#include < dshow.h > #include < dmo.h > #include < dmodshow.h > ... #pragma comment(lib, "strmiids.lib") #pragma comment(lib, "dmoguids.lib") #pragma comment(lib, "msdmo.lib")
We reference header files and immediately leave a comment to linker to link library files (to avoid specifying them explicitly in project settings.
From the minimum requirements for every DMO we implemented aggregation (ATL provides implementation by default) and threading model:
Every DMO should meet the following minimum requirements:
– It must support aggregation.
– It must expose the IMediaObject interface.
– The threading model must be ‘both’. DMOs must function correctly in a free-threaded environment.
We yet need to expose IMediaObject interface and add registration code to expose the object/filter to DirectShow-enabled applications and software components. While registration step is not necessary, it still allows visual graph construction with the filter being developed using GraphEdit utility.
To implement IMediaObject, we inherit from it the media object class:
class ATL_NO_VTABLE CBrightnessContrastObject : public CComObjectRootEx< CComMultiThreadModel >, public CComCoClass< CBrightnessContrastObject, &CLSID_BrightnessContrastObject >, public IDispatchImpl< IBrightnessContrastObject >, public IMediaObject
then add the interface into COM MAP:
BEGIN_COM_MAP(CBrightnessContrastObject) COM_INTERFACE_ENTRY(IMediaObject)
and add IMediaObject methods to the class.
Since IMediaObject contains Lock method, it is confused with CComObjectRootEx‘s Lock and breaks project from compilation. To resolve this ambiguity, we add another method to the media object class:
// CComObjectRootEx VOID Lock() { CComObjectRootEx::Lock(); }
To enable automatic registration of the media object with DirectShow/DMO infrastructure, we are putting registration as a part of COM registration of the DLL module. This way the object will be automatically exposed to DirectShow software once the DLL is installed and registered with the system.
First of all, we are adding GetObjectFriendlyName method to provide a friendly name for the object, to be used by both ATL to register the class as COM class and the same name will be available to DirectShow software:
static LPCTSTR GetObjectFriendlyName() throw() { return _T("Alax.Info BrightnessContrast Sample"); }
To intercept COM registration and register the object as DMO we are overriding UpdateRegistry method. The implementation for this method is added to th class through DECLARE_REGISTRY_RESOURCEID macro. We comment this out and add implementation for this method explicitly:
Except for default handling through CAtlModule::UpdateRegistryFromResource, we will add calls to our RegisterMediaObject and UnregisterMediaObject functions which in their turn will use DMORegister and DMOUnregister API.
static HRESULT WINAPI UpdateRegistry(BOOL bRegister) throw() { _ATLTRY { if(!bRegister) UnregisterMediaObject(); ATLASSERT(_pAtlModule); _pAtlModule->UpdateRegistryFromResource(IDR_BRIGHTNESSCONTRASTOBJECT, bRegister); if(bRegister) RegisterMediaObject(); } _ATLCATCH(Exception) { return Exception; } return S_OK; }
Our media object will expose one input and one output stream/pin both with media type MEDIATYPE_Video, MEDIASUBTYPE_YUY2.
Once the project is compiled, COM registration is performed as a part of build task and DMO registration is a subtask. So, the object is already exposed to DirectShow software and applications:
The framework part is done, we are ready to add real implementation for IMediaObject interface and video processing methods.
Source code: DmoBrightnessContrastSample.01.zip
Additional Notes:
- Platform SDK provides IMediaObjectImpl template class (dmoimpl.h) to inherit from instead of IMediaObject, which offer base implementation for IMediaObject interface.
- See also similar tutorial on vnet.uh.edu
Continued by:
Regarding the DirectShow filter routine: Using VS2005, the routine UpdateRegistryFromResource() is not available. Reverting to the standard method (DECLARE_REGISTRY_RESOURCEID) works to register the DLL, but it does not get registered as a DirectShow MediaObject (does not show up in GraphEdt), as seems to happen when using the sample UpdateRegistry routine. Can anyone tell me how to make the DirectShow MediaObject auto-registration work with VS2005?
Thanks for any help.
Jack,
I don’t think that registration in VS2005 is different from 2002, 2003 and 2008 versions where I have been using DECLARE_REGISTRY_RESOURCEID replacement. I would rather think it may be different for other reason such as a different project settings. Would you please mention, do you use attributed or non-attributed ATL, static or dynamic ATL linking. Basically if you send me the project, I can take a look and see what needs to be changed.
During the development process I upgraded from VS2005 to VS2008. I had previously commented out the
2008-specific code per your chat recommendation and uncommented the DECLARE_REGISTRY_RESOURCEID line. The DLL registered but there was no DirectShow MediaObject registry entry for my DLL. Since I had upgraded, I was able to revert the code back to use your UpdateRegistry() routine. The DLL now registers as a DirectShow MediaObject, and GraphEdt was able to find it.
I have signed a Non-Disclosure agreement regarding the codec development and cannot reveal anything of the actual code.
Thanks for your help.
Basically you need to change all UUIDs there. LIBID, CLSID, IID – just all of them, use CreateGUID utility to create new identifiers.
1 in dllmain.h, 2*2 in *.rgs, and all in DmoBrightnessContrastSample.idl but some of the mentioned are equal and new ones should also be equal in those places