DirectShow custom transform filter skeleton
After my last struggles with building a DirectShow video filter, I've decided to publish a skeleton application for a video transform filter.
This skeleton application is using the CTransformFilter approach, it means that it performs per frame transformations on a copy of the frame.
While you might think that transforming a copy of the video frame is not effective as you are using double the size of the memory for each frame, but in fact - this approach is quite more performance-effective for the project that I was building as accessing and modifying the data in the video card memory consumes more time than working on your local memory in some cases (when your video cards is not fast i.e an embedded devide) and my project was the case.
Of course, you should consult the MSDN documentation upon writing your filters and I would suggest you to write it from scratch if it's a sophisticated one, if you might need more than one input and output pin, or in general you need to customize it to meet your needs. This code is by no means a silver bullet.
I would like you to excuse me for leaving the classes and methods unchanged in the source code. As this skeleton is a crippled-down version of my original project that was really a pain in the ass.
Using the attached source code to create a new filter is limited to implementing one method - the Transform method in the OculusVideoFilter class:
HRESULT OculusVideoFilter::Transform(IMediaSample *pMedia)
{
BYTE *pData;
long lDataLen;
int iPixel;
int temp,x,y;
RGBTRIPLE *pRGB;
AM_MEDIA_TYPE* pType = &m_pInput->CurrentMediaType();
VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER *) pType->pbFormat;
ASSERT(pvi);
CheckPointer(pMedia,E_POINTER);
pMedia->GetPointer(&pData);
lDataLen = pMedia->GetSize();
int cxImage = pvi->bmiHeader.biWidth;
int cyImage = pvi->bmiHeader.biHeight;
int numPixels = cxImage * cyImage;
pRGB = (RGBTRIPLE*) pData;
for (iPixel=0; iPixel < numPixels; iPixel++, pRGB++) {
/*
here you implement your per-pixel operation on the currently processed frame.
*/
}
return NOERROR;
}
This skeleton app is COM-ready (It's required for DirectShow filters to be COM ready), so all you need after implementing your filtering logic is to compile the library, it will generate a .DLL file for you and use regsvr32.exe to register it.
One more note: Please remember to change the CLSID of the class, as it will conflict with other registered interfaces. If you would leave the CLSID:
// {5F1E81B9-9669-444D-A007-63EBB5CE92E3} static const GUID CLSID_OculusFilter = { 0x5f1e81b9, 0x9669, 0x444d, { 0xa0, 0x7, 0x63, 0xeb, 0xb5, 0xce, 0x92, 0xe3 } };
Unchanged and one day it might happen that you'll use my piece of software (or two people using this skeleton will not change this CLSID) you will end up with conflicts in your registry.
Now as an example I'll show you how to create a video filter that will grayscale your output video.
The grayscale calculation is: grey = (30 * red + 59 * green + 11 * blue) / 100
A faster (but less precise) method would be: grey = (red + green) / 2
Now, with this navigate to the filter class to line number 137 and in your loop should look similar to this:
for (iPixel=0; iPixel < numPixels; iPixel++, pRGB++) { grey = (prgb->rgbtRed + prgb->rgbtGreen) >> 1; pRGB->rgbtRed = prgb->rgbtGreen = pRGB->rgbtBlue = (BYTE) grey; }
Have fun with your filters!