Introduction
The article is a brief tutorial on how to design a directshow's source filter. Most of them reference the SDK's example. Download the demo and have a test using GraphEdit. It creates a video stream, add the "My Source Filter" to the GraphEdit, right click the "My Source OutPin" and select render pin. Okay, now it works! Press play and you'll see my photo floating on the video screen.
Principle of the Code
Before our development, be sure that the SDK is installed on your computer and configure it well in your VC or VS. After finishing the jobs, now going on:
Step 1
Build a project, for example we select the "Win32 Dynamic-Link Library" .
Step 2
Add the following libraries for your project: strmbasd.lib msvcrtd.lib quartz.lib vfw32.lib winmm.lib.
Include the header files we need:
#include <streams.h>
#include <olectl.h>
#include <initguid.h>
#include "MyStream.h" //This file is not system's ,its a class's header that we create.
Step 3
Find the tool "GUIDGEN.EXE" on your computer. (If you failed, look on the internet.) Start the tool, click the "New GUID" button, a new GUID will be created. Now, go to select the second option, in other words we want to "DEFINE_GUID(...)
" format, after that click "copy" button and paste it in your project.
Step 4
Set data types, major type and minor type.
const AMOVIESETUP_MEDIATYPE sudOpPinTypes =
{
&MEDIATYPE_Video, &MEDIASUBTYPE_NULL };
For our filter, we need an output pin. Do as follows. Get to set the pin's properties.
const AMOVIESETUP_PIN sudOpPin =
{
L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, NULL, 1, &sudOpPinTypes };
Setup the filter's properties:
const AMOVIESETUP_FILTER sudMyax =
{
&CLSID_MySourceFilter, L"My Source Filter", MERIT_DO_NOT_USE, 1, &sudOpPin };
Step 5
Create a class that inherits from CSource
. Why do we choose this class? Because we are going to develop a source filter. So, we must choose reasonable base classes.
class CMySourceFilter:public CSource
{
public:
static CUnknown * WINAPI CreateInstance(LPUNKNOWN lpunk, HRESULT *phr);
private:
CMySourceFilter(LPUNKNOWN lpunk, HRESULT *phr);
};
CMySourceFilter::CMySourceFilter(LPUNKNOWN lpunk, HRESULT *phr):
CSource(NAME("MySource"),lpunk,CLSID_MySourceFilter)
{
CAutoLock cAutoLock(&m_cStateLock);
m_paStreams = (CSourceStream **) new CMyStream*[1];
if (m_paStreams == NULL) {
*phr = E_OUTOFMEMORY;
return;
}
m_paStreams[0] = new CMyStream(phr, this, L"My Source OutPin");
if (m_paStreams[0] == NULL) {
*phr = E_OUTOFMEMORY;
return;
}
}
CUnknown * WINAPI CMySourceFilter::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr)
{
CUnknown *punk = new CMySourceFilter(lpunk, phr);
if (punk == NULL) {
*phr = E_OUTOFMEMORY;
}
return punk;
}
Having finished our source filter class, how could we create our new instances. Perhaps an array, please look at the following snippets:
CFactoryTemplate g_Templates[] = {
{ L"My Source Filter"
, &CLSID_MySourceFilter
, CMySourceFilter::CreateInstance
, NULL
, &sudMyax }
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
Step 6
Add the two functions for register and unregister:
STDAPI DllRegisterServer()
{
return AMovieDllRegisterServer2( TRUE );
} STDAPI DllUnregisterServer()
{
return AMovieDllRegisterServer2( FALSE );
}
Step 7
For creating our filter in a DLL, we also need to export some functions. Create a new DEF file:
LIBRARY MySourceFilter.ax
EXPORTS
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
Okay, so far. Our filter has finished without pins. Of course, you could test it in the GraphEdit.exe. You only look at its information, it can do nothing now. We want to add an output pin, the pin output video we want. Moving on...
Step 8
Now we need insert a new class inherited from CSourceStream
. It could generate samples and push it to the next filters.
class CMyStream : public CSourceStream
{
public:
STDMETHOD(Notify)(IBaseFilter * pSender, Quality q);
HRESULT CheckMediaType(const CMediaType *pMediaType);
HRESULT GetMediaType(int iPosition,CMediaType *pMediaType);
HRESULT DecideBufferSize(IMemAllocator *pAlloc,ALLOCATOR_PROPERTIES *pProperties);
HRESULT FillBuffer(IMediaSample *pms);
CMyStream(HRESULT *phr,CSource *pms,LPCWSTR pName);
virtual ~CMyStream();
private:
CRefTime m_rtSampleTime; int m_iRepeatTime;
BITMAPFILEHEADER * pbfh;
int xPos,yPos;
BOOL xLock,yLock;
HBITMAP hBmp;
};
How does the pin work? Look for more on MSDN for more details, click here!
The streaming thread runs a loop with the following structure:
until (stopped)
1. Get a media sample from the allocator.
2. Fill the sample with data.
3. Time stamp the sample.
4. Deliver the sample downstream.
Okay, want more details? Download the resource. Tired!
Points of Interest
I am so tired after writing the article, but I'm very glad. Hope it will be useful to you.
History
- 22nd February, 2010: Initial post