Jump to content
  • Advertisement
Sign in to follow this  
sipickles

Congratulations Microsoft!

This topic is 4823 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

I'd like to congratulate microsoft for making the directshow SDK samples so very easy to follow. They are always clearly laid out, consistent and easily learned. Just have a quick look at the VRM9Allocator sample for a taster! yum! Anyone got any aspirin??? Simon :(

Share this post


Link to post
Share on other sites
Advertisement
ah, codemunkie!

I am experimenting with various video rendering techniques for fullscreen apps.

I have a class which follows your technique of samplegrabbing and null rendering, then copying to a texture. It works, but my FPS of 750+ drops to about 30fps on video playback, and the quality is reduced.

I was therefore exploring other methods, and have succeeded in giving myself a headache! :)

VMR9Allocator and Texture3D9 are so convoluted, they seem to loop around and leave me tangled up in the middle!



Simon

Share this post


Link to post
Share on other sites
Quote:
Original post by sipickles
VMR9Allocator and Texture3D9 are so convoluted, they seem to loop around and leave me tangled up in the middle!
I remember those days. I spend a long time figuring out what I actually needed. Now that I understand the process, I can get by with far less code, so I'll see what I can do to help you narrow in on all the important parts.

Here's roughly what I do:

I create a class that inherits from IVMRSurfaceAllocator9 and IVMRImagePresenter9. Its job is to look like a surface allocator and presenter to the VMR9 object, and to look like a class that contains all the things needed for rendering a video to the rest of your app. It looks something like this:

class c_Texture_VMR9 : public IVMRSurfaceAllocator9, public IVMRImagePresenter9
{
public:
//IVMRSurfaceAllocator9 Functions
virtual HRESULT STDMETHODCALLTYPE InitializeDevice(DWORD_PTR dwUserID, VMR9AllocationInfo *lpAllocInfo, DWORD *lpNumBuffers);
virtual HRESULT STDMETHODCALLTYPE TerminateDevice(DWORD_PTR dwID);
virtual HRESULT STDMETHODCALLTYPE GetSurface(DWORD_PTR dwUserID, DWORD SurfaceIndex, DWORD SurfaceFlags, LPDIRECT3DSURFACE9 *lplpSurface);
virtual HRESULT STDMETHODCALLTYPE AdviseNotify(IVMRSurfaceAllocatorNotify9 *lpIVMRSurfAllocNotify);

//IVMRImagePresenter9 Functions
virtual HRESULT STDMETHODCALLTYPE StartPresenting(DWORD_PTR dwUserID);
virtual HRESULT STDMETHODCALLTYPE StopPresenting(DWORD_PTR dwUserID);
virtual HRESULT STDMETHODCALLTYPE PresentImage(DWORD_PTR dwUserID, VMR9PresentationInfo *lpPresInfo);

//IUnknown Functions
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();

//c_Texture_VMR9 Functions
c_Texture_VMR9(LPDIRECT3DDEVICE9 pDevice, const std::string& FileName);
~c_Texture_VMR9();

void GetDirect3DTexture(LPDIRECT3DTEXTURE9* ppTexture);

protected:
IFilterGraph* mpFilterGraph;
IGraphBuilder* mpGraphBuilder;

IBaseFilter* mpVMR;
IVMRFilterConfig9* mpVMRConfig;
IVMRSurfaceAllocatorNotify9* mpVMRSurfaceAllocatorNotify;

std::vector<LPDIRECT3DSURFACE9> mSurfaces;

LPDIRECT3DDEVICE9 mpDevice;
LPDIRECT3DTEXTURE9 mpTexture;

ULONG mRefCount;
};



The IVMRSurfaceAllocator9 functions are responsible for creating surfaces for the VMR9 to render to. The main function here is InitializeDevice(). It first figures out what size the surfaces need to be, based on power of 2 rules, and then it creates the surfaces. It then also creates a single texture, which will be the texture you'll use for all your Direct3D rendering. TerminateDevice() just frees these surfaces and this texture. GetSurface() is simply used by the VMR9 to request a surface from you. And AdviseNotify() [partially] sets up the VMR9 to be able to work properly with this allocator/presenter. These four functions look roughly like this:

HRESULT STDMETHODCALLTYPE c_Texture_VMR9::InitializeDevice(DWORD_PTR dwUserID, VMR9AllocationInfo *lpAllocInfo, DWORD *lpNumBuffers)
{
HRESULT Result;

D3DCAPS9 Capabilities;

if (lpNumBuffers == NULL)
return E_POINTER;

if (mpVMRSurfaceAllocatorNotify == NULL)
return E_FAIL;

if (mpDevice == NULL)
return E_FAIL;

if (mpParent == NULL)
return E_FAIL;

UINT Width = lpAllocInfo->dwWidth;
UINT Height = lpAllocInfo->dwHeight;

mpDevice->GetDeviceCaps(&Capabilities);
if (Capabilities.TextureCaps & D3DPTEXTURECAPS_POW2)
{
lpAllocInfo->dwWidth = 1;
lpAllocInfo->dwHeight = 1;

while(lpAllocInfo->dwWidth < Width )
lpAllocInfo->dwWidth = lpAllocInfo->dwWidth << 1;
while(lpAllocInfo->dwHeight < Height)
lpAllocInfo->dwHeight = lpAllocInfo->dwHeight << 1;
}

lpAllocInfo->dwFlags = VMR9AllocFlag_OffscreenSurface;
lpAllocInfo->Format = D3DFMT_R5G6B5;

int i;
for (i = 0; i < mSurfaces.size(); ++i)
{
if (mSurfaces != NULL)
{
mSurfaces->Release();
mSurfaces = NULL;
}
}
mSurfaces.resize(*lpNumBuffers);
for (i = 0; i < mSurfaces.size(); ++i)
{
mSurfaces = NULL;
}
Result = mpVMRSurfaceAllocatorNotify->AllocateSurfaceHelper(lpAllocInfo, lpNumBuffers, &(mSurfaces[0]));

mpDevice->CreateTexture(lpAllocInfo->dwWidth, lpAllocInfo->dwHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R5G6B5, D3DPOOL_DEFAULT, &mpTexture, NULL);

if (mpTexture != NULL)
{
RECT SrcRect;
LPDIRECT3DSURFACE9 pSurface;
mpTexture->GetSurfaceLevel(0, &pSurface);
D3DCOLOR BackgroundColor = 0xFF000000;
SrcRect.left = 0;
SrcRect.top = 0;
SrcRect.right = 1;
SrcRect.bottom = 1;
D3DXLoadSurfaceFromMemory(pSurface, NULL, NULL, &BackgroundColor, D3DFMT_A8R8G8B8, 4, NULL, &SrcRect, D3DX_FILTER_LINEAR, 0x00000000);
SafeRelease(pSurface);
}

return S_OK;
}

HRESULT STDMETHODCALLTYPE c_Texture_VMR9::TerminateDevice(DWORD_PTR dwID)
{
int i;
for (i = 0; i < mSurfaces.size(); ++i)
{
if (mSurfaces != NULL)
{
mSurfaces->Release();
mSurfaces = NULL;
}
}
mSurfaces.clear();

if (mpTexture != NULL)
{
mpTexture->Release();
mpTexture = NULL;
}

return S_OK;
}

HRESULT STDMETHODCALLTYPE c_Texture_VMR9::GetSurface(DWORD_PTR dwUserID, DWORD SurfaceIndex, DWORD SurfaceFlags, PDIRECT3DSURFACE9 *lplpSurface)
{
if (lplpSurface == NULL)
return E_POINTER;

if (SurfaceIndex >= mSurfaces.size())
return E_FAIL;

if (mSurfaces[SurfaceIndex] != NULL)
{
mSurfaces[SurfaceIndex]->AddRef();
}
*lplpSurface = mSurfaces[SurfaceIndex];

return S_OK;
}

HRESULT STDMETHODCALLTYPE c_Texture_VMR9::AdviseNotify(IVMRSurfaceAllocatorNotify9 *lpIVMRSurfAllocNotify)
{
LPDIRECT3D9 pDirect3D;

mpVMRSurfaceAllocatorNotify = lpIVMRSurfAllocNotify;

mpDevice->GetDirect3D(&pDirect3D);
if (pDirect3D != NULL)
{
HMONITOR hMonitor = pDirect3D->GetAdapterMonitor(D3DADAPTER_DEFAULT);
pDirect3D->Release();
pDirect3D = NULL;
mpVMRSurfaceAllocatorNotify->SetD3DDevice(mpDevice, hMonitor);
}

return S_OK;
}



The IVMRImagePresenter9 functions are responsible for actually getting the rendered surfaces displayed however you want. In our case, they copy the surface to the texture. For speed, you can use IDirect3DDevice9::UpdateSurface() or IDirect3DDevice9::StretchRect() to do the copy. This is typically a ton faster than locking the surfaces and copying from one to the other. StartPresenting() and StopPresenting() don't need to do anything special in our case. PresentImage() is where we do the copy.

HRESULT STDMETHODCALLTYPE c_Texture_VMR9::StartPresenting(DWORD_PTR dwUserID)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE c_Texture_VMR9::StopPresenting(DWORD_PTR dwUserID)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE c_Texture_VMR9::PresentImage(DWORD_PTR dwUserID, VMR9PresentationInfo *lpPresInfo)
{
if (lpPresInfo == NULL)
return E_POINTER;
else if (lpPresInfo->lpSurf == NULL)
return E_POINTER;
else if (mpDevice == NULL)
return E_FAIL;
else if (mpParent == NULL)
return E_FAIL;

RECT Rectangle = { 0, 0, mWidth, mHeight };

LPDIRECT3DSURFACE9 pSurfaceSrc, pSurfaceDest;
mpTexture->GetSurfaceLevel(0, &pSurfaceDest);
pSurfaceSrc = lpPresInfo->lpSurf;
HRESULT Result = mpDevice->StretchRect(pSurfaceSrc, &Rectangle, pSurfaceDest, &Rectangle, D3DTEXF_NONE);
SafeRelease(pSurfaceDest);

return S_OK;
}



Then there are the IUnknown functions. In this sample code I'm giving you, I don't actually properly handle the reference count, so you'll have to figure out how you want to arrange the reference counting stuff for yourself. It all depends on how you fit it into your program. I had problems with it, because my object was already reference counted, and then adding COM reference counts to it complicated things, since certain objects inside it (the VMR9 object) held references counts to the object, and it was kind of a circular thingy. I now have a seperate class for just the VMR9 object and allocator/presenter, which is reference counted like normal, and another class for representinga texture, which contains the VMR9/Allocator/Presenter as a member. This VMR9/A/P object can be reused over and over as texture objects come and go, because I found that the creation of a new VMR9 object seemed to stall the program temporarily, regardless of how I had threads set up. So now I save all my VMR9/A/P objects after they're used, and keep reusing them rather than creating new ones when they're needed. Anyway, here's a bit of code, QueryInterface being the main function to focus on:

HRESULT STDMETHODCALLTYPE c_Texture_VMR9::QueryInterface(REFIID riid, void** ppvObject)
{
HRESULT Result = E_NOINTERFACE;

if(ppvObject == NULL)
{
Result = E_POINTER;
}
else if(riid == IID_IVMRSurfaceAllocator9)
{
*ppvObject = static_cast<IVMRSurfaceAllocator9*>(this);
AddRef();
Result = S_OK;
}
else if(riid == IID_IVMRImagePresenter9)
{
*ppvObject = static_cast<IVMRImagePresenter9*>(this);
AddRef();
Result = S_OK;
}

return Result;
}

ULONG STDMETHODCALLTYPE c_Texture_VMR9::AddRef()
{
++mRefCount;
return mRefCount;
}

ULONG STDMETHODCALLTYPE c_Texture_VMR9::Release()
{
if (mRefCount > 0)
{
--mRefCount;
return mRefCount;
}
}



Finally there are the functions for the texture object itself. To keep it simple, I'm just allowing the constructor to set up all the video stuff. After creating all that junk, you can query the filter graph, graph builder, or VMR for other interfaces that you might need, as you would with any DirectShow filter graph setup.

c_Texture_VMR9::c_Texture_VMR9(LPDIRECT3DDEVICE9 pDevice, const std::string& FileName)
{
if (pDevice != NULL)
{
pDevice->AddRef();
mpDevice = pDevice;

CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IFilterGraph, (void**)(&mpFilterGraph));
mpFilterGraph->QueryInterface(IID_IGraphBuilder, (void**)(&mpGraphBuilder));

CoCreateInstance(CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void**)(&mpVMR));
mpVMR->QueryInterface(IID_IVMRFilterConfig9, (void**)(&mpVMRConfig));
mpVMRConfig->SetRenderingMode(VMRMode_Renderless);
mpVMRConfig->SetNumberOfStreams(0);
mpVMR->QueryInterface(IID_IVMRSurfaceAllocatorNotify9, (void**)(&mpVMRSurfaceAllocatorNotify));
mpVMRSurfaceAllocatorNotify->AdviseSurfaceAllocator(0x00000000, this);
AdviseNotify(mpVMRSurfaceAllocatorNotify);

mpGraphBuilder->AddFilter(mpVMR, L"Video Mixing Renderer 9");

WCHAR wFileName[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, FileName.c_str(), -1, wFileName, MAX_PATH);
mpGraphBuilder->RenderFile(wFileName, NULL);
}
}

c_Texture_VMR9::~c_Texture_VMR9()
{
if (mpVMR != NULL)
{
mpVMR->Release();
mpVMR = NULL;
}
if (mpVMRConfig != NULL)
{
mpVMRConfig->Release();
mpVMRConfig= NULL;
}
if (mpVMRSurfaceAllocatorNotify != NULL)
{
mpVMRSurfaceAllocatorNotify->Release();
mpVMRSurfaceAllocatorNotify= NULL;
}

if (mpGraphBuilder != NULL)
{
mpGraphBuilder->Release();
mpGraphBuilder= NULL;
}
if (mpFilterGraph != NULL)
{
mpFilterGraph->Release();
mpFilterGraph= NULL;
}

while (!mSurfaces.empty())
{
if (mSurfaces.back() != NULL)
{
mSurfaces.back()->Release();
mSurfaces.back() = NULL;
mSurfaces.pop_back();
}
}

if (mpDevice != NULL)
{
mpDevice->Release();
mpDevice = NULL;
}
if (mpTexture != NULL)
{
mpTexture->Release();
mpTexture = NULL;
}
}

void c_Texture_VMR9::GetDirect3DTexture(LPDIRECT3DTEXTURE9* ppTexture)
{
if (ppTexture != NULL)
{
if (mpTexture != NULL)
{
mpTexture->AddRef();
*ppTexture = mpTexture;
}
else
{
*ppTexture = NULL;
}
}
}



Hopefully that'll help you figure out specifically what you need to focus on, and what things in the DirectShow examples you can ignore. I'm not going to guarantee that this code actually works, as I copied, pasted, and modified it to be as simple and straightforward as possible. But maybe it'll be somewhat useful at least.

Share this post


Link to post
Share on other sites
Quote:
Original post by sipickles
I'd like to congratulate microsoft for making the directshow SDK samples so very easy to follow. They are always clearly laid out, consistent and easily learned.

Just have a quick look at the VRM9Allocator sample for a taster! yum!

Anyone got any aspirin???

Simon :(


[lol] I feel your pain. Best bit is, with DShow disappearing/merging with something else (whatever that is) I doubt the samples are getting any attention from the DX team.

At least there are some decent people kicking around these forums to help out though [smile]

Jack

Share this post


Link to post
Share on other sites
Agony, what can I say?

Thanks for taking so much time out to help me! wow!

Thats a lot of code to digest so I'll have to take my time.

First glance, I can see many areas where I was quite close to the mark with my own efforts,...... and many areas where I was not!

I'll PM when I have some news....

SimoN

Share this post


Link to post
Share on other sites
Quote:
Original post by sipickles
Agony, what can I say?

"here, have some painkillers..." ?? [lol]

Alternatively, a ++rating I suppose... [rolleyes]

Jack

Share this post


Link to post
Share on other sites
Quote:
Original post by jollyjeffers
I feel your pain. Best bit is, with DShow disappearing/merging with something else (whatever that is) I doubt the samples are getting any attention from the DX team.

[lol] These samples haven't got any attention for 2-3 years, not to mention this month [wink]

DirectShow can be a tremendous tool - it's just too bad that the documentation and samples are so cloudy. I wish they wouldn't have taken the Graph Edit tool away either - it could literally convert any media format to any other.

Share this post


Link to post
Share on other sites
Hi,
I tried once to understand DShow and got frustrated. Instead I picked the Video for Windows API (vfw.lib). I render the stream to a texture and thats all. You can get a DC to the texture just using the DIRECT3DTEXTURE9::GetSurfaceLevel method. After that you get the DC using: Direct3DSurface9::GetDC. Provide that DC to vfw rendering method.

Way easier that DShow and easier to integrate to your engine too.

Luck!
Guimo

Share this post


Link to post
Share on other sites
Quote:
Original post by Guimo
Hi,
I tried once to understand DShow and got frustrated. Instead I picked the Video for Windows API (vfw.lib). I render the stream to a texture and thats all. You can get a DC to the texture just using the DIRECT3DTEXTURE9::GetSurfaceLevel method. After that you get the DC using: Direct3DSurface9::GetDC. Provide that DC to vfw rendering method.

Way easier that DShow and easier to integrate to your engine too.


What about any alternatives for decoding and playing MP3s or OGGs?

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.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!