I'm involved in a project where we want to create a virtual webcam
that can be used as in Skype/MSN and alike.
Google search tells me that there's been a heated discussion about
this virtual webcam topic years ago here; and the vcam demo by
rep_movsd does a similar thing with what I have in mind~
As I'm totally new to DirectShow, there's still questions that you
seasoned DS experts can make it clearer for me before I fully plunge
in. So here are my questions:
1. seems source filter is the one I need to create, implementing
proper interfaces (IAMStreamconfig and IKSPropertySet) to make it
behave like a capture source (http://groups.google.com/group/
microsoft.public.win32.programmer.directx.video/browse_thread/thread/
5356761826c726de). Then how do I make the filter/virtual_cam visible
in Skype/MSN?
2. instead of capture the image from desktop or playback some video
in the virtual webcam, I actually want to capture the video from a
real webcam, and do some post-processing to finally stream it through
the virtual webcam. But from what I read, source filter only has
output pin but no input pin?
3. I have some OpenGL/CUDA post-processing module already working.
Is it possible to integrate existing OpenGL/CUDA codes into a
DirectShow filter?
Thanks for your time and have a nice day!
----------------------------------------------
Cyrus
Computing Science Department
University of Alberta
> 1. seems source filter is the one I need to create, implementing
> proper interfaces (IAMStreamconfig and IKSPropertySet) to make it
> behave like a capture source (http://groups.google.com/group/
> microsoft.public.win32.programmer.directx.video/browse_thread/thread/
> 5356761826c726de). Then how do I make the filter/virtual_cam visible
> in Skype/MSN?
You need to register the filter in the "Video Capture Sources" category.
It will only show up in apps that use DirectShow, if they use WDM directly
or the old win32 API it will not show.
> 2. instead of capture the image from desktop or playback some video
> in the virtual webcam, I actually want to capture the video from a
> real webcam, and do some post-processing to finally stream it through
> the virtual webcam. But from what I read, source filter only has
> output pin but no input pin?
You need a graph inside your filter. The internal graph will be used to
capture frames and buffer, you can then process them and copy them to the
output pin of the outer graph. The internal graph could consist of:
capture source -> samplegrabber -> NULL renderer
The samplegrabber delivers frames to you via a callback.
> 3. I have some OpenGL/CUDA post-processing module already working.
> Is it possible to integrate existing OpenGL/CUDA codes into a
> DirectShow filter?
Theoretically I guess this is possible, although I would leave this part
out until you get the basics working.
--
http://www.chrisnet.net/code.htm
[MS MVP for DirectShow / MediaFoundation]
Thx for the lead Chris!
I've been trying to get a normal graph Capture->SampleGrabber-
>NullRenderer working. When I was trying to register callback function
with the SampleGrabber, the compiler keeps complaining. Cannot locate
any discussions on this topic; maybe it is too simple a mistake? But I
just cannot figure out why...
To my understanding, I have to get a class implement the
ISampleGrabberCB interface, and plug in my SampleCB code;
class CSampleGrabberCB : public ISampleGrabberCB
{
public:
HRESULT SampleCB(double SampleTime, IMediaSample* pSample)
{
// some processing code here
}
};
but the compiler keeps getting error message about
error C2695: 'CSampleGrabberCB::SampleCB': overriding virtual
function differs from 'ISampleGrabberCB::SampleCB' only by calling
convention
1> c:\program files\microsoft sdks\windows\v6.1\include\qedit.h
(5972) : see declaration of 'ISampleGrabberCB::SampleCB'
Any idea how to get rid of this? Thx
I'm programming on Windows Vista, Visual Studio 2005, Windows SDK v6.1
__________________________
> HRESULT SampleCB(double SampleTime, IMediaSample* pSample)
> error C2695: 'CSampleGrabberCB::SampleCB': overriding
> virtual function differs from
> 'ISampleGrabberCB::SampleCB' only by calling convention
VC++ defaults to __cdecl while WinAPIs and COM default to
__stdcall. The docs are crap and never specify the calling
convention so you have to look at the definition in the .h
file. You'll find out that ISampleGrabberCB::SampleCB() must
be a __stdcall so you need to write your implementation as:
HRESULT STDMETHODCALLTYPE SampleCB(...) ...
--
// Alessandro Angeli
// MVP :: DirectShow / MediaFoundation
// mvpnews at riseoftheants dot com
// http://www.riseoftheants.com/mmx/faq.htm
ha~~ Thx a bunch, Alessandro~~
What can I say, SDK/docs don't even bother to give out a example of
using SampleBC, as long as I can tell...
http://msdn.microsoft.com/en-us/library/ms787867(VS.85).aspx simply
refers you to ISampleGrabber::SetCallback man page about how to use
SampleCB...
And I get easily overwhelmed as a newbie to DirectShow...
To define CSampleGrabberCB, I still need to write codes for BufferCB
(ISampleGrabberCB) and QueryInterface/AddRe/Release (IUnknown). Looks
like SampleCB is all I want, so do I just leave the bodies of these
functions empty, or they have some fixed functionality that I have to
fulfill?
cyrus
> To define CSampleGrabberCB, I still need to write codes
> for BufferCB (ISampleGrabberCB) and
If you are configuring the SG to only call your SampleCB(),
your BufferCB() can just return E_NOTIMPL without doing
anything.
> QueryInterface/AddRe/Release (IUnknown). Looks like
> SampleCB is all I want, so do I just leave the bodies of
> these functions empty, or they have some fixed
> functionality that I have to fulfill?
You must implement those 3 IUnknown methods:
class MySampleGrabberCB : public ISampleGrabberCB
{
private: LONG lRefCount;
public: MySampleGrabberCB(void) : lRefCount(0)
{
}
public: virtual ~MySampleGrabberCB(void)
{
}
public: HRESULT STDMETHODCALLTYPE QueryInterface(REFIID
riid, void** ppv)
{
if(ppv == NULL) return E_POINTER; else *ppv = NULL;
if(riid == __uuidof(ISampleGrabberCB)) *ppv =
(ISampleGrabberCB*)this;
else if(riid == __uuidof(IUnknown)) *ppv =
(IUnknown*)this;
else return E_NOINTERFACE;
AddRef(); return S_OK;
}
public: ULONG STDMETHODCALLTYPE AddRef(void)
{
return (ULONG)InterlockedIncrement(&lRefCount);
}
public: ULONG STDMETHODCALLTYPE Release(void)
{
LONG lRefCount = InterlockedDecrement(&lRefCount);
if(lRefCount < 1) delete this;
return (ULONG)lRefCount;
}
public: HRESULT STDMETHODCALLTYPE BufferCB(...)
{
return E_NOTIMPL;
}
public: HRESULT STDMETHODCALLTYPE SampleCB(...)
{
/// do something
}
};
After creating a MySampleGrabberCB object you should call
its QueryInterface(__uuidof(ISampleGrabberCB)) to get a
reference to ISampleGrabberCB and AddRef() it. If you just
cast it to (ISampleGrabberCB*) you will not AddRef() it. To
destroy it, call Release(), not delete (since the object is
self-deleting, you can Release() it right after passing it
to the SG, since the SG will AddRef() it).
If you can manage the lifetime making sure that the object
is destroyed after it is no longer in use anywhere (e.g.
after the SG is destroyed, which happens when the graph is
destroyed unless you hold a reference to it), your AddRef()
and Release() can just always return 1, and you can just use
a cast and delete. You can even allocate it on the stack.
If you don't want to create your own implementation of
IUnknown, ATL, MFC and the DirectShow BaseClasses will
provide one. If you use the BaseClasses, you will need to
compile and link them and your code will be:
/// INCLUDE = BaseClasses
#include <streams.h>
#pragma comment(lib,"winmm")
class MySampleGrabberCB : public CUnknown, public
ISampleGrabberCB
{
public: MySampleGrabberCB(HRESULT* phr)
: CUnknown(NAME("MySampleGrabberCB"),NULL,phr)
{
}
public: virtual ~MySampleGrabberCB(void)
{
}
public: DECLARE_IUNKNOWN
public: HRESULT STDMETHODCALLTYPE
NonDelegatingQueryInterface(REFIID riid, void** ppv)
{
if(riid == __uuidof(ISampleGrabberCB)) return
GetInterface((ISampleGrabberCB*)this,ppv);
return CUnknown::NonDelegatingQueryInterface(riid,ppv);
}
public: HRESULT STDMETHODCALLTYPE BufferCB(...)
{
return E_NOTIMPL;
}
public: HRESULT STDMETHODCALLTYPE SampleCB(...)
{
/// do something
}
};
You will need to check *phr after invoking the ctor in case
there were errors.
Great! I'll be looking into this better impl!
At the same time, I found a little problem when building the graph
with SampleGrabber and NullRenderer
With a normal Renderer, the graph builder automatically takes in
SampleGrabber, so callback are working just fine; The graph looks like
this:
http://lh5.ggpht.com/_O8tm2-T1dqc/SW_ewsBz0bI/AAAAAAAAFBM/jvC0NIJRUw4/s912/Renderer.jpg
But when I throw in NullRenderer instead, the graph will ignore
SampleGrabber, getting the final graph like this:
http://lh5.ggpht.com/_O8tm2-T1dqc/SW_e2UQIAqI/AAAAAAAAFBs/gd5yEWBkL74/s912/NullRenderer.jpg
I guess it is because NullRenderer can suck in any media type, so the
graph builder just went for the easier way without integrating the
SampleGrabber?
Or is it because the media type I set for the SampleGrabber is not
consistent with the output pin data type in the graph? The media type
I used for the SampleGrabber is like this:
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = pSampleGrabber->SetMediaType(&mt);
And the source file is an AVI file.
What do I have to do in order to avoid manually connecting filters one
by one?
____________________________
> I guess it is because NullRenderer can suck in any media
> type, so the
> graph builder just went for the easier way without
> integrating the
> SampleGrabber?
If more than one filter in a graph accepts the connection,
the one that is used depends on the unpredictable order in
which they are tried.
Try rendering the graph without the NullRenderer, then find
the SampleGrabber's output pin and call
IGraphBuilder::Render() on it.
Or you can use ICaptureGraphBuilder2::RenderStream() to
connect the filters the way you want.
hmm... its become more and more clear to me, thanks!
Lemme recapture the idea to see if I get it right:
1st way:
Build the graph with SG(SampleGrabber) but without NR(NullRenderer);
I believe you need to get the output pin P of the capture source
filter, and do IGraphBuilder:::Render(P) to build it?
Then I need to identify the output pin of SG in this graph; I haven't
done any graph traversal yet, but I guess you can use CLSID of the
filter along with pin type to get the output pin of SG?
Then you need to build a new graph with NR added; by render the output
pin of SG you guarantee SG is included in the final graph?
2nd way:
ICaptureGraphBuilder2::RenderStream( &PIN_CATEGORY_PREVIEW,
&MEDIATYPE_Video, pCaptureSrcFilter, pSampleGrabber, pNullRenderer)
I tried this but kept getting access violation. I'll figure out
whether some initialization is left out
Thanks
I found out the reason why it keeps crashing (Access violation)
I created the MySampleGrabberCB object on the stack during
initialization; and the object should be alive throughout the program
as it has to be used everytime a call back happens... So when the init
function returns, the MySampleGrabberCB object is destroyed and
nowhere to be found...
By making it global, things are working fine now. RenderStream works
perfectly
Stupid and very simple mistake
cyrus
computing science department
university of alberat