Advertisement Jump to content
Sign in to follow this  
mrmrcoleman

DirectShow Custom Renderer Question.

This topic is 5179 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello. I have implemented a program which uses DirectShow to draw to a Direct3D surface (as in the Texture3D9 example in the SDK). However I want to be able to uses animations with a format of ARGB32 instead of the RGB24 that is used in the example but i don't really understand the code that copies the DirectShow Sample onto the Direct3D surface. Has anybody implemented this type of thing who would be willing to help me out? I basically just need to know how to copy the data in the DirectShow sample (ARGB32) to the Direct3D surface (ARGB32), taking into account the pitch so that the drawing is performed correctly... Big thanks in advance for any help. Kind regards. Mark Coleman

Share this post


Link to post
Share on other sites
Advertisement
Sure. That sample in the SDK, I don't even know how it works. It seems horribly and unneccessarily complex. I found what I think is a much simpler way. Basically, I figured this out by using GraphEdit and observing how graphs were created. As long as your video does not have sound, this should work. First, create your graph manager (open GraphEdit). Then add a Null Renderer filter and Sample Grabber filter to the graph. Next, call RenderFile on the video file (in GraphEdit this is like going to File...Render Media File) and the graph manger will build the graph automatically. Since it will see the sample grabber and null renderer filters, it will use them instead of the default video renderer as it builds the graph. You can confirm this by running GraphEdit and following those exact steps. In code that process translates as follows:


//create graph manager
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&m_pGraph);
//you can uncomment this line and DirectShow will make a log file for you as the graph is built.
// m_pGraph->SetLogFile((unsigned long)dslogfile);

//create null renderer
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&m_pNULLRenderer);
//add null renderer to graph
hr = m_pGraph->AddFilter(m_pNULLRenderer, L"Null Renderer");

//create sample grabber
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&m_pGrabberBase);
hr = m_pGrabberBase->QueryInterface(IID_ISampleGrabber, (void**)&m_pVideoGrabber);

//setup sample grabber
//set media type of sample grabber
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Video;
mt.subtype = GUID_NULL;//MEDIASUBTYPE_RGB32;
mt.formattype = FORMAT_VideoInfo;

hr = m_pVideoGrabber->SetMediaType(&mt);
hr = m_pVideoGrabber->SetBufferSamples(TRUE);
hr = m_pVideoGrabber->SetOneShot(FALSE);

//add grabber to graph
hr = m_pGraph->AddFilter(m_pGrabberBase, L"Sample Grabber");

//automatically build graph based on filename
hr = m_pGraph->RenderFile(file_name,L"");

//get media interfaces
hr = m_pGraph->QueryInterface(IID_IMediaControl, (void**)&m_pMediaControl);
hr = m_pGraph->QueryInterface(IID_IMediaEvent, (void**)&m_pMediaEvent);
hr = m_pGraph->QueryInterface(IID_IMediaPosition, (void**)&m_pMediaPosition);










Now you just poll your Sample GRabber for the video frame:

m_pVideoGrabber->GetCurrentBuffer(&video_buffer_size, (long*)video_buffer);
if(!FAILED(dx8tex->LockRect(0,&d3dlr,0,D3DLOCK_DISCARD)))
{
memcpy((byte*)(d3dlr.pBits),(byte*)video_buffer,video_buffer_size);
dx8tex->UnlockRect(0);
}










If you have sound in your media file, it gets a bit more complicated because the graph manager will add the sample grabber and null renderer to the sound stream instead. So what you have to do is add them after you RenderFile, find the default video renderer, remove it, and manually connect your sample grabber and null renderer. Let me know if you need that code.
GraphEdit is a great tool because if you understand how the graph was constructed in GraphEdit, you can easily convert to code. At first, I did not understand why my Sample Grabber did not seem to be getting added to the graph in my code. Then I repeated my steps in GraphEdit and realized that the Sample Grabber was being added to the sound stream despite my having set the media type to video. I was able to try different ways of building the graph in GraphEdit until I found a process that did what I wanted. Then I just translated that process into code. I highly recommend spending some time with GraphEdit as you will gain a deeper understanding of DirectShow.

[Edited by - CodeMunkie on October 21, 2004 9:18:58 AM]

Share this post


Link to post
Share on other sites
I never thought of doing that!! That's amazing although unfortunately I do need sound support...;(

Also, will the sample grabber be able to grab me a ARGB32 frame?

Big Thanks.

Mark Coleman

Share this post


Link to post
Share on other sites
Setting the media subtype to GUID_NULL will allow it to accept any supported format, including ARGB32. As to the sound, like I said it gets a bit tricky, but if you can follow that last bit of code it is not too bad. First thing to change, move the RenderFile call before the part where you add the Sample Grabber and Null Renderer, otherwise the graph manager will afix them to the sound stream which is not what you want. Ok, so now your code should read, create graph, render file, add sample grabber and null renderer. Here is where it gets a bit complex. Now you must find the default Video Renderer and Color Space Converter that was added automagically by RenderFile and remove them:

//locate default video renderer
IBaseFilter* pVidRenderer = NULL;
m_pGraph->FindFilterByName(L"Video Renderer", &pVidRenderer);
if(pVidRenderer)
{
//get input pin of video renderer
IPin* ipin = GetPin(pVidRenderer, PINDIR_INPUT);
IPin* opin = NULL;
//find out who the renderer is connected to and disconnect from them
ipin->ConnectedTo(&opin);
ipin->Disconnect();
opin->Disconnect();

SAFE_RELEASE(ipin);

//remove the default renderer from the graph
m_pGraph->RemoveFilter(pVidRenderer);
SAFE_RELEASE(pVidRenderer);

//see if the video renderer was originally connected to
//a color space converter
IBaseFilter* pColorConverter = NULL;
m_pGraph->FindFilterByName(L"Color Space Converter", &pColorConverter);
if(pColorConverter)
{
SAFE_RELEASE(opin);

//remove the converter from the graph as well
ipin = GetPin(pColorConverter, PINDIR_INPUT);

ipin->ConnectedTo(&opin);
ipin->Disconnect();
opin->Disconnect();

SAFE_RELEASE(ipin);

m_pGraph->RemoveFilter(pColorConverter);
SAFE_RELEASE(pColorConverter);
}

//get the input pin of the sample grabber
ipin = GetPin(m_pGrabberBase, PINDIR_INPUT);

//connect the filter that was originally connected to the default renderer
//to the sample grabber
m_pGraph->Connect(opin, ipin);
SAFE_RELEASE(ipin);
SAFE_RELEASE(opin);

//get output pin of sample grabber
opin = GetPin(m_pGrabberBase, PINDIR_OUTPUT);
//get input pin of null renderer
ipin = GetPin(m_pNULLRenderer, PINDIR_INPUT);

//connect them
m_pGraph->Connect(opin, ipin);
SAFE_RELEASE(ipin);
SAFE_RELEASE(opin);
}





Let me know if that is not straight forward. The GetPin function looks like this:


IPin* GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir)
{
BOOL bFound = FALSE;
IEnumPins *pEnum;
IPin *pPin;

HRESULT hr = pFilter->EnumPins(&pEnum);
if (FAILED(hr))
{
return NULL;
}
int i = 0;
while(pEnum->Next(1, &pPin, 0) == S_OK)
{
PIN_DIRECTION PinDirThis;
pPin->QueryDirection(&PinDirThis);
bFound = (PinDir == PinDirThis);
if(bFound)
break;
pPin->Release();
}
pEnum->Release();
return (bFound ? pPin : NULL);
}



Share this post


Link to post
Share on other sites
Thanks for your replies, they are extremely useful but I have a problem.

According to the DirectX SDK Documentation the Include for the ISampleGrabber interface is Qedit.h, which apparently is 'not compatible with Direct 3d headers later than version 7'.

So I went to my headers and found the following three which mention Direct3D9.

D3D9.h which can become just D3D.h
D3DX9.h which can become just D3DX.h
D3DX9TEX.h but this one doesn't seem to have a Direct3D7 equivalent.

Does anybody know which headers I have to include to get the same functionality but with Direct3D7 header files? I don't think that I require any of the complex features that may have been added in later versions so can I just go ahead and change the header files?

Thanks in advance.

Mark Coleman

Share this post


Link to post
Share on other sites
Oh yes I forgot about that little issue. The simple but not immediately obvious answer is to extract the ISampleGrabber interface from qedit.h and put it in its own header. This is exactly what I have done:

EXTERN_C const IID IID_ISampleGrabberCB;

MIDL_INTERFACE("0579154A-2B53-4994-B0D0-E773148EFF85")
ISampleGrabberCB : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE SampleCB(
double SampleTime,
IMediaSample *pSample) = 0;

virtual HRESULT STDMETHODCALLTYPE BufferCB(
double SampleTime,
BYTE *pBuffer,
long BufferLen) = 0;

};

EXTERN_C const CLSID CLSID_NullRenderer;
EXTERN_C const CLSID CLSID_SampleGrabber;
EXTERN_C const IID IID_ISampleGrabber;

MIDL_INTERFACE("6B652FFF-11FE-4fce-92AD-0266B5D7C78F")
ISampleGrabber : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE SetOneShot(
BOOL OneShot) = 0;

virtual HRESULT STDMETHODCALLTYPE SetMediaType(
const AM_MEDIA_TYPE *pType) = 0;

virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType(
AM_MEDIA_TYPE *pType) = 0;

virtual HRESULT STDMETHODCALLTYPE SetBufferSamples(
BOOL BufferThem) = 0;

virtual HRESULT STDMETHODCALLTYPE GetCurrentBuffer(
/* [out][in] */ long *pBufferSize,
/* [out] */ long *pBuffer) = 0;

virtual HRESULT STDMETHODCALLTYPE GetCurrentSample(
/* [retval][out] */ IMediaSample **ppSample) = 0;

virtual HRESULT STDMETHODCALLTYPE SetCallback(
ISampleGrabberCB *pCallback,
long WhichMethodToCallback) = 0;

};





Just create ISampleGrabber.h (or some other suitably named header), paste the code in and include it in your project.

Share this post


Link to post
Share on other sites
Hello again, just implementing the code here and I am having a compilation problem.

If we are using our own header file, won't it be necesary to put something in their about the NullRenderer other than:

EXTERN_C const CLSID CLSID_NullRenderer;?

It doesm't seem to like it for some reason...?

Thanks in advance for any help.

Mark Coleman

[Edited by - mrmrcoleman on October 19, 2004 12:10:41 PM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!