Sign in to follow this  
jesse121

VMR9 Renderless Mode Help!

Recommended Posts

Hello everyone. I am trying to implement the VMR9 in nightmare mode, I mean Renderless mode. I managed to get my code to the point where everything is created without fail, but I still can't get anything rendering. For some reason the IVMRImagePresenter9::PresentImage callback function is never called. When I make the call to m_pGraph->RenderFile(), the only callbacks that are called by the application are InitializeDevice(), GetSurface(), and TerminateDevice(). When I call IVMRFilterConfig9::SetNumberOfStreams() with a zero the following calls are made: InitializeDevice() GetSurface() TerminateDevice() InitializeDevice() GetSurface() TerminateDevice() InitializeDevice() GetSurface() TerminateDevice() and then the video is rendered in a pop up window not in my main window. When I call IVMRFilterConfig9::SetNumberOfStreams() with a one the following calls are made: TerminateDevice() TerminateDevice() TerminateDevice() InitializeDevice() GetSurface() and then nothing happens at all, just a blank screen, no audio. Is there a specific reason why IVMRImagePresenter9::PresentImage never gets called? When I call IMediaControl::Run() shouldn't the IVMRImagePresenter9::PresentImage function be called? I am trying to play a movie that has both video and sound should I SetNumberOfStreams() to 0, 1, or 2? I setup the VMR9AllocationInfo with the following flags: D3DDISPLAYMODE dm; m_pd3dDevice->GetDisplayMode( 0, &dm ); pAllocInfo->dwFlags = VMR9AllocFlag_3DRenderTarget; pAllocInfo->Format = dm.Format; pAllocInfo->Pool = D3DPOOL_DEFAULT; Should I be using a different flag such as VMR9AllocFlag_OffscreenSurface or VMR9AllocFlag_TextureSurface? I am sorry if this post is kind of confusing but VMR9 is confusing, there is just to many things that I could be doing wrong so, I don't really know where to look to solve this problem. By the way I have read all the posts on msdn, and tutorials I found on the internet referring to VMR9 Renderless mode.

Share this post


Link to post
Share on other sites
Any chance you could upload your source somewhere? I'm familiar with DirectShow, even though I've not written anything using the VMR9 renderless mode before, so perhaps I can come up with something by tinkering with it a bit.

Oh, and kudos for trying out the renderless mode. You're one brave man indeed [grin] (The closest I got to that mode is skimming the docs, then deciding I didn't want to waste my life just yet)

Share this post


Link to post
Share on other sites
VMR9Allocator.h

// Class VMR9Allocator inherits from IVMRSurfaceAllocator9 and IVMRImagePresenter9
class CVMR9Allocator : public IVMRSurfaceAllocator9, private IVMRImagePresenter9
{
public:
CVMR9Allocator();
~CVMR9Allocator();

// VMR9 mutators
HRESULT STDMETHODCALLTYPE InitializeDevice( DWORD_PTR dwUserID, VMR9AllocationInfo* pAllocInfo, DWORD* pNumBuffers );
HRESULT STDMETHODCALLTYPE TerminateDevice( DWORD_PTR dwID );
HRESULT STDMETHODCALLTYPE GetSurface( DWORD_PTR dwUserID, DWORD dwSurfaceIndex, DWORD dwSurfaceFlags, IDirect3DSurface9** lplpSurface );
HRESULT STDMETHODCALLTYPE AdviseNotify( IVMRSurfaceAllocatorNotify9* lpIVMRSurfAllocNotify );
HRESULT STDMETHODCALLTYPE StartPresenting( DWORD_PTR dwUserID );
HRESULT STDMETHODCALLTYPE StopPresenting( DWORD_PTR dwUserID );
HRESULT STDMETHODCALLTYPE PresentImage( DWORD_PTR dwUserID, VMR9PresentationInfo* lpPresInfo );
HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject );
ULONG STDMETHODCALLTYPE AddRef();
ULONG STDMETHODCALLTYPE Release();


// User mutators
void SetDevice( HWND hWnd, IDirect3DDevice9* pd3dDevice );
void SetVideoWindow( float x, float y, float fWidth, float fHeight );

// Engine accessors
DWORD GetMovieWidth() const;
DWORD GetMovieHeight() const;

protected:

// Support member functions
void DeleteSurfaces();
HRESULT PresentHelper( VMR9PresentationInfo *lpPresInfo );

private:
ULONG m_lRefCount;
DWORD m_dwSurfaceNum;
DWORD m_dwMovieWidth;
DWORD m_dwMovieHeight;
LPDIRECT3DTEXTURE9 m_pTexture;
CPolygon<CTDVertex>* m_pVideoWindow;
IVMRSurfaceAllocatorNotify9* m_pIVMRSurfAllocNotify;
vector<IDirect3DSurface9*> m_vpSurfaces;
HWND m_hWnd;
IDirect3DDevice9* m_pd3dDeviceInst;
};



VMR9Allocator.cpp

// CVMR9Allocator constructor
CVMR9Allocator::CVMR9Allocator()
: m_lRefCount(0),
m_dwSurfaceNum(0),
m_dwMovieWidth(0),
m_dwMovieHeight(0),
m_pTexture(NULL),
m_pVideoWindow(NULL),
m_pIVMRSurfAllocNotify(NULL),
m_hWnd(0),
m_pd3dDeviceInst(NULL)
{
}

// CVMR9Allocator destructor
CVMR9Allocator::~CVMR9Allocator()
{
SafeRelease( m_pTexture );
SafeDelete( m_pVideoWindow );
DeleteSurfaces();
}



// InitializeDevice callback handler
HRESULT STDMETHODCALLTYPE CVMR9Allocator::InitializeDevice( DWORD_PTR dwUserID, VMR9AllocationInfo* pAllocInfo, DWORD* pNumBuffers )
{
if( !pNumBuffers ) return E_POINTER;
if( !m_pIVMRSurfAllocNotify ) return E_FAIL;
if( !m_pd3dDeviceInst ) return E_FAIL;

// Adjust the surface size to powers of two
m_dwMovieWidth = pAllocInfo->dwWidth;
m_dwMovieHeight = pAllocInfo->dwHeight;
DWORD dwWidth(1), dwHeight(1);
while( dwWidth < pAllocInfo->dwWidth ) dwWidth <<= 1;
while( dwHeight < pAllocInfo->dwHeight ) dwHeight <<= 1;
pAllocInfo->dwWidth = dwWidth;
pAllocInfo->dwHeight = dwHeight;

// Setup the allocator to render to a back buffer
D3DDISPLAYMODE dm; m_pd3dDeviceInst->GetDisplayMode( 0, &dm );
pAllocInfo->dwFlags = VMR9AllocFlag_3DRenderTarget;
pAllocInfo->Format = dm.Format;
pAllocInfo->Pool = D3DPOOL_DEFAULT;

// Resize the array of surfaces
DeleteSurfaces();
m_dwSurfaceNum = *pNumBuffers;
m_vpSurfaces.resize( m_dwSurfaceNum );

// Allocate the array of surfaces
if( FAILED( m_pIVMRSurfAllocNotify->AllocateSurfaceHelper( pAllocInfo, &m_dwSurfaceNum, &m_vpSurfaces[0] ) ) )
return E_FAIL;

// Create the texture as a render target
if( FAILED( m_pd3dDeviceInst->CreateTexture( pAllocInfo->dwWidth, pAllocInfo->dwHeight, 1, D3DUSAGE_RENDERTARGET, dm.Format, D3DPOOL_DEFAULT, &m_pTexture, NULL ) ) )
return E_FAIL;

// Create a textured diffused polygon to map the texture to
m_pVideoWindow = new CPolygon<CTDVertex>;
if( m_pVideoWindow->CreatePolygon( m_pd3dDeviceInst, 2, 6, 4, 0, D3DPOOL_MANAGED, D3DPT_TRIANGLELIST ) )
{ // Scale the Quad to the texture size by default
D3DXVECTOR2 vPolySize((float)m_dwMovieWidth, (float)m_dwMovieHeight);
D3DXVECTOR2 vPolyPos(0.0f, 0.0f);

// Create four vertice's for the quad
vector<CTDVertex> vVertices( 4 );
vVertices[0].SetData( vPolyPos.x, vPolyPos.y - vPolySize.y, 0.0f, 0xffffffff, 0.0f, 0.0f ); // Top left
vVertices[1].SetData( vPolyPos.x + vPolySize.x, vPolyPos.y - vPolySize.y, 0.0f, 0xffffffff, 1.0f, 0.0f ); // Top right
vVertices[2].SetData( vPolyPos.x + vPolySize.x, vPolyPos.y, 0.0f, 0xffffffff, 1.0f, 1.0f ); // Bottom right
vVertices[3].SetData( vPolyPos.x, vPolyPos.y, 0.0f, 0xffffffff, 0.0f, 1.0f ); // Bottom left
m_pVideoWindow->SetVertices( vVertices );

// Create the indice's
vector<WORD> vIndices( 6 );
vIndices[0] = 0; vIndices[1] = 1; vIndices[2] = 2; // Triangle one
vIndices[3] = 3; vIndices[4] = 0; vIndices[5] = 2; // Triangle two
m_pVideoWindow->SetIndices( vIndices );
}
else return E_FAIL;

MessageBox( NULL, "Success", "InitializeDevice", MB_OK );
return S_OK;
}

// TerminateDevice callback handler
HRESULT STDMETHODCALLTYPE CVMR9Allocator::TerminateDevice( DWORD_PTR dwID )
{
SafeRelease( m_pTexture );
SafeDelete( m_pVideoWindow );
DeleteSurfaces();
MessageBox( NULL, "Success", "TerminateDevice", MB_OK );
return S_OK;
}

// GetSurface callback handler
HRESULT STDMETHODCALLTYPE CVMR9Allocator::GetSurface( DWORD_PTR dwUserID, DWORD dwSurfaceIndex, DWORD dwSurfaceFlags, IDirect3DSurface9** ppSurface )
{
if( !ppSurface ) return E_POINTER;
if( dwSurfaceIndex >= m_dwSurfaceNum ) return E_FAIL;
if( !m_vpSurfaces[dwSurfaceIndex] ) return E_FAIL;

// Create a reference to the surface
*ppSurface = m_vpSurfaces[dwSurfaceIndex];
m_vpSurfaces[dwSurfaceIndex]->AddRef();
MessageBox( NULL, "Success", "GetSurface", MB_OK );
return S_OK;
}

// AdviseNotify callback handler
HRESULT STDMETHODCALLTYPE CVMR9Allocator::AdviseNotify( IVMRSurfaceAllocatorNotify9* pIVMRSurfAllocNotify )
{
if( !m_pd3dDeviceInst ) return E_FAIL;

// Set the device
m_pIVMRSurfAllocNotify = pIVMRSurfAllocNotify;
if( FAILED( m_pIVMRSurfAllocNotify->SetD3DDevice( m_pd3dDeviceInst, MonitorFromWindow( m_hWnd, MONITOR_DEFAULTTOPRIMARY ) ) ) )
return E_FAIL;
return S_OK;
}

// Start presenting
HRESULT STDMETHODCALLTYPE CVMR9Allocator::StartPresenting( DWORD_PTR dwUserID )
{
return S_OK;
}

// Stop presenting
HRESULT STDMETHODCALLTYPE CVMR9Allocator::StopPresenting( DWORD_PTR dwUserID )
{
return S_OK;
}

// Present the image
HRESULT STDMETHODCALLTYPE CVMR9Allocator::PresentImage( DWORD_PTR dwUserID, VMR9PresentationInfo* pPresInfo )
{
MessageBox( NULL, "", "PresentImage", MB_OK );
return PresentHelper( pPresInfo );
}

// Present helper
HRESULT CVMR9Allocator::PresentHelper( VMR9PresentationInfo *pPresInfo )
{
if( !pPresInfo ) return E_POINTER;
if( !pPresInfo->lpSurf ) return E_POINTER;
if( !m_pd3dDeviceInst ) return E_FAIL;
if( !m_pTexture ) return E_FAIL;
if( !m_pVideoWindow ) return E_FAIL;

// Blit the surface onto the texture
IDirect3DSurface9* pSurface(NULL);
if( SUCCEEDED( m_pTexture->GetSurfaceLevel( 0, &pSurface ) ) )
{ m_pd3dDeviceInst->StretchRect( pPresInfo->lpSurf, NULL, pSurface, NULL, D3DTEXF_NONE );
SafeRelease( pSurface );

m_pd3dDeviceInst->Clear( 0, 0, D3DCLEAR_TARGET, 0xff000000, 1.0f, 0 );
if( SUCCEEDED( m_pd3dDeviceInst->BeginScene() ) )
{ // Render the texture onto the quad
m_pd3dDeviceInst->SetTexture( 0, m_pTexture );
m_pVideoWindow->OnRender( m_pd3dDeviceInst );

m_pd3dDeviceInst->EndScene();
m_pd3dDeviceInst->Present( 0, 0, 0, 0 );
}
}
MessageBox( NULL, "Success", "PresentHelper", MB_OK );
return S_OK;
}

// Query the interface
HRESULT STDMETHODCALLTYPE CVMR9Allocator::QueryInterface( REFIID riid, void** ppvObject )
{
if( !ppvObject ) return E_POINTER;

if( riid == IID_IVMRSurfaceAllocator9 )
{ *ppvObject = (IVMRSurfaceAllocator9*)this;
AddRef();
}
else if( riid == IID_IVMRImagePresenter9 )
{ *ppvObject = (IVMRImagePresenter9*)this;
AddRef();
}
else if( riid == IID_IUnknown )
{ *ppvObject = (IUnknown*)((IVMRSurfaceAllocator9*)this);
AddRef();
}
return S_OK;
}

// Add a reference to the interface
ULONG STDMETHODCALLTYPE CVMR9Allocator::AddRef( void )
{
return ++m_lRefCount;
}

// Release interface
ULONG STDMETHODCALLTYPE CVMR9Allocator::Release( void )
{
if( m_lRefCount > 0 ) return --m_lRefCount;
return m_lRefCount;
}



// Set the device
void CVMR9Allocator::SetDevice( HWND hWnd, IDirect3DDevice9* pd3dDevice )
{
m_hWnd = hWnd;
m_pd3dDeviceInst = pd3dDevice;
}

// Set the size of the video window
void CVMR9Allocator::SetVideoWindow( float x, float y, float fWidth, float fHeight )
{
if( m_pVideoWindow )
{ // Get the vertice's
vector<CTDVertex> vVertices;
m_pVideoWindow->GetVertices( vVertices );
if( vVertices.size() < 4 ) return;

// Adjust the vertice's positions
vVertices[0].m_vPosition.x = x;
vVertices[0].m_vPosition.y = y - fHeight;
vVertices[1].m_vPosition.x = x + fWidth;
vVertices[1].m_vPosition.y = y - fHeight;
vVertices[2].m_vPosition.x = x + fWidth;
vVertices[2].m_vPosition.y = y;
vVertices[3].m_vPosition.x = x;
vVertices[3].m_vPosition.y = y;

// Set the vertice's
m_pVideoWindow->SetVertices( vVertices );
}
}


// Get the movie width
DWORD CVMR9Allocator::GetMovieWidth() const
{
return m_dwMovieWidth;
}

// Get the movie height
DWORD CVMR9Allocator::GetMovieHeight() const
{
return m_dwMovieHeight;
}



// Delete surfaces
void CVMR9Allocator::DeleteSurfaces()
{
for ( DWORD i(0); i < m_dwSurfaceNum; ++i )
SafeRelease( m_vpSurfaces[i] );
m_vpSurfaces.clear();
m_dwSurfaceNum = 0;
}



VMR9.h

// Class VMR9
class CVMR9
{
public:
CVMR9();
~CVMR9();

// User mutators
bool LoadMedia( HWND hWnd, IDirect3DDevice9* pd3dDevice, const string& strFilePath, const D3DXVECTOR2& vResolution );
void SetVideoWindow( float x, float y, float fWidth, float fHeight );
void OnRender( IDirect3DDevice9* pd3dDevice );
void Play();
void Stop();

// User accessors
bool IsPlaying();
DWORD GetMovieWidth() const;
DWORD GetMovieHeight() const;

private:

// Support member functions
HRESULT SetAllocatorPresenter( HWND hWnd, IDirect3DDevice9* pd3dDevice, IBaseFilter *pFilter );
bool CleanUp();

// VMR9 data
IGraphBuilder* m_pGraph;
IBaseFilter* m_pFilter;
IMediaControl* m_pMediaControl;
CVMR9Allocator* m_pAllocator;
};



VMR9.cpp

// VMR9 class constructor
CVMR9::CVMR9()
: m_pGraph(NULL),
m_pFilter(NULL),
m_pMediaControl(NULL),
m_pAllocator(NULL)
{
}

// VMR9 class destructor
CVMR9::~CVMR9()
{
CleanUp();
}



// Load media from file
bool CVMR9::LoadMedia( HWND hWnd, IDirect3DDevice9* pd3dDevice, const string& strFilePath, const D3DXVECTOR2& vResolution )
{
// Create the graph
if( FAILED( ::CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_ALL, IID_IGraphBuilder, (void**)&m_pGraph ) ) )
return CleanUp();

// Create the VMR9 filter
if( FAILED( ::CoCreateInstance( CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&m_pFilter ) ) )
return CleanUp();

// Create a filter config
IVMRFilterConfig9* pFilterConfig(NULL);
if( FAILED( m_pFilter->QueryInterface( IID_IVMRFilterConfig9, (void**)&pFilterConfig ) ) )
return CleanUp();

// Set VMR9 rendering mode
pFilterConfig->SetRenderingMode( VMR9Mode_Renderless );
pFilterConfig->SetNumberOfStreams( 1 );
SafeRelease( pFilterConfig );

// Set the allocator presenter
if( FAILED( SetAllocatorPresenter( hWnd, pd3dDevice, m_pFilter ) ) )
return CleanUp();

// Add the VMR9 to the graph
if( FAILED( m_pGraph->AddFilter( m_pFilter, L"Video Mixing Renderer 9" ) ) )
return CleanUp();

// Create the media control
if( FAILED( m_pGraph->QueryInterface( IID_IMediaControl, (void**)&m_pMediaControl ) ) )
return CleanUp();

// Render the file
if( FAILED( m_pGraph->RenderFile( stows( strFilePath ).c_str(), NULL ) ) )
return CleanUp();
return true;
}

// Set the size of the video window
void CVMR9::SetVideoWindow( float x, float y, float fWidth, float fHeight )
{
if( m_pAllocator ) m_pAllocator->SetVideoWindow( x, y, fWidth, fHeight );
}

// Render the file
void CVMR9::OnRender( IDirect3DDevice9* pd3dDevice )
{
}

// Play the currently loaded file
void CVMR9::Play()
{
if( m_pMediaControl ) m_pMediaControl->Run();
}

// Stop playing the file
void CVMR9::Stop()
{
if( m_pMediaControl )
{ OAFilterState filterState;
do
{ m_pMediaControl->Stop();
m_pMediaControl->GetState( 0, &filterState );
} while( filterState != State_Stopped );
}
}


// Check if the file is playing
bool CVMR9::IsPlaying()
{
if( m_pMediaControl )
{ OAFilterState filterState;
m_pMediaControl->GetState( 0, &filterState );
return filterState != State_Stopped;
}
return false;
}

// Get the movie width
DWORD CVMR9::GetMovieWidth() const
{
if( m_pAllocator ) return m_pAllocator->GetMovieWidth();
return 0;
}

// Get the movie height
DWORD CVMR9::GetMovieHeight() const
{
if( m_pAllocator ) return m_pAllocator->GetMovieHeight();
return 0;
}



// Set the allocator presenter
HRESULT CVMR9::SetAllocatorPresenter( HWND hWnd, IDirect3DDevice9* pd3dDevice, IBaseFilter* pFilter )
{
// Create the surface allocator notify
IVMRSurfaceAllocatorNotify9* pIVMRSurfAllocNotify(NULL);
if( FAILED( pFilter->QueryInterface( IID_IVMRSurfaceAllocatorNotify9, (void**)&pIVMRSurfAllocNotify ) ) )
return E_FAIL;

// Establish a communication link with the allocator
m_pAllocator = new CVMR9Allocator;
m_pAllocator->SetDevice( hWnd, pd3dDevice );
if( FAILED( pIVMRSurfAllocNotify->AdviseSurfaceAllocator( 0xACDCACDC, m_pAllocator ) ) )
return E_FAIL;

// Advise the allocator
if( FAILED( m_pAllocator->AdviseNotify( pIVMRSurfAllocNotify ) ) )
return E_FAIL;

return S_OK;
}

// Clean up
bool CVMR9::CleanUp()
{
Stop();
SafeRelease( m_pGraph );
SafeRelease( m_pFilter );
SafeRelease( m_pMediaControl );
SafeRelease( m_pAllocator );
return false;
}

Share this post


Link to post
Share on other sites
You should be setting your mixing prefs just before you add your filter to the graph.

// Here you need to SetMixingPrefs (try RenderTargetYUV, NoDecimation, ARAdjustXorY and BiLinearFiltering

// Add the VMR9 to the graph
if( FAILED( m_pGraph->AddFilter( m_pFilter, L"Video Mixing Renderer 9" ) ) )
return CleanUp();


Quote:
Original post by jesse121
I am trying to play a movie that has both video and sound should I SetNumberOfStreams() to 0, 1, or 2?


Just try setting 1 stream for now

Quote:
Original post by jesse121
I setup the VMR9AllocationInfo with the following flags:
D3DDISPLAYMODE dm; m_pd3dDevice->GetDisplayMode( 0, &dm );
pAllocInfo->dwFlags = VMR9AllocFlag_3DRenderTarget;
pAllocInfo->Format = dm.Format;
pAllocInfo->Pool = D3DPOOL_DEFAULT;

Should I be using a different flag such as VMR9AllocFlag_OffscreenSurface or VMR9AllocFlag_TextureSurface?


You should be using an offscreen surface then in PresentImage() use StretchRectangle to do the pixel format conversion to the surface of your texture.

Sorry I can't be more specific with help as I code in C#.

Share this post


Link to post
Share on other sites
I did what you said, and set the IVMRFilterConfig9::SetNumberOfStreams( 1 ) to one.
I set the allocInfo flag to VMR9AllocFlag_OffscreenSurface.
I also placed this code just before adding the VMR9 to the graph:


// Create the mixer control
IVMRMixerControl9* pMixerControl(NULL);
if( FAILED( m_pFilter->QueryInterface( IID_IVMRMixerControl9, (void**)&pMixerControl ) ) )
return CleanUp();

// Set VMR9 mixing preferences
DWORD dwPrefs(0);
dwPrefs |= MixerPref_RenderTargetYUV;
dwPrefs |= MixerPref9_NoDecimation;
dwPrefs |= MixerPref9_BiLinearFiltering;
dwPrefs |= MixerPref9_ARAdjustXorY;
pMixerControl->SetMixingPrefs( dwPrefs );
SafeRelease( pMixerControl );



Now when I run it I get about 10 calls to TerminateDevice(), and once again the video is rendered in a pop up window and not in my main window. I tried different combinations of mixing preferences but got the same result every time.
Any ideas why this isn't working?

Share this post


Link to post
Share on other sites
I still haven't tried your code, but I've noticed there are several VMR samples that come with the platform SDK. Did you check them out? I assume one would cover the renderless mode.

They should be in (Samples\Multimedia\DirectShow\VMR9). If you don't have them in your version of the SDK, I can upload them somewhere.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this