topical media & game development
hush-src-multi-DLL-Wizard.cpp / cpp
//------------------------------------------------------------------------------
// File: Wizard.cpp
//
// Desc: DirectShow sample code - Implements the CMultiVMR9Wizard class
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------
include <stdafx.h>
include <Wizard.h>
include <multimon.h>
ifdef _BUILD_FROM_VS
include <Windows.h>
endif
include <list>
using namespace std;
****************************Public*Routine******************************\
CMultiVMR9Wizard
constructor
\*************************************************************************
CMultiVMR9Wizard::CMultiVMR9Wizard(LPUNKNOWN pUnk, HRESULT *phr)
: CUnknown(NAME("MultiVMR9 Wizard"), pUnk)
, m_dwConfigFlags( NULL )
, m_pRenderEngine( NULL )
, m_pNotify( NULL )
, m_bInitialized (FALSE )
, m_RenderThreadStatus( eNotStarted )
, m_hwnd( NULL)
{
}
****************************Public*Routine******************************\
~CMultiVMR9Wizard
destructor
\*************************************************************************
CMultiVMR9Wizard::~CMultiVMR9Wizard()
{
HRESULT hr = Terminate();
}
/////////////////// IUnknown ///////////////////////////////////
****************************Public*Routine******************************\
CreateInstance
\*************************************************************************
CUnknown* CMultiVMR9Wizard::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
{
return new CMultiVMR9Wizard(pUnk, phr);
}
****************************Public*Routine******************************\
NonDelegatingQueryInterface
\*************************************************************************
STDMETHODIMP
CMultiVMR9Wizard::NonDelegatingQueryInterface(
REFIID riid,
void ** ppv)
{
HRESULT hr = E_NOINTERFACE;
*ppv = NULL;
if (riid == IID_IMultiVMR9Wizard)
{
hr = GetInterface((IMultiVMR9Wizard *)this, ppv);
}
else if (riid == IID_IVMRSurfaceAllocator9)
{
hr = GetInterface((IVMRSurfaceAllocator9 *)this, ppv);
}
else if (riid == IID_IVMRImagePresenter9)
{
hr = GetInterface((IVMRImagePresenter9 *)this, ppv );
}
else
{
hr = CUnknown::NonDelegatingQueryInterface(riid,ppv);
}
return hr;
}
/////////////////// IMultiVMR9Wizard ///////////////////////////
****************************Public*Routine******************************\
Initialize
Call this method right after IMultiVMR9Wizard object is created to configure
and initialize internal structures as well as D3D environment.
dwFlags - Configuration flags
hWnd - handle to valid video window
pRenderEngine - custom render engine (use NULL for default)
Return error codes: E_INVALIDARG (invalid config flags or hWnd),
VFW_E_WRONG_STATE (method was already called before)
E_FAIL (unexpected error),
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::Initialize(
DWORD dwFlags,
HWND hWnd,
IMultiVMR9RenderEngine *pRenderEngine)
{
HRESULT hr = S_OK;
HWND hwndRE = NULL;
if( m_bInitialized )
{
::DbgMsg("CMultiVMR9Wizard::Initialize: Wizard is already initialized");
return VFW_E_WRONG_STATE;
}
if( FALSE == IsWindow( hWnd ))
{
::DbgMsg("CMultiVMR9Wizard::Initialize: Invalid handle to the video window");
return E_INVALIDARG;
}
m_hwnd = hWnd;
// TODO: check flags
m_dwConfigFlags = dwFlags;
CAutoLock Lock(&m_ObjectLock);
try
{
// initialize render engine. We assume that if custom render engine is provided,
// it is already initialized
RELEASE( m_pRenderEngine );
if( pRenderEngine ) // user specified customized render engine
{
// check that pRenderEngine was initialized and they point to the same window
CHECK_HR(
hr = pRenderEngine->GetVideoWindow( &hwndRE ),
::DbgMsg("CMultiVMR9Wizard::Initialize: Failed to get window handler from the provided RenderEngine, hr = 0x%08x", hr));
CHECK_HR(
hwndRE != m_hwnd ? E_FAIL : S_OK,
::DbgMsg("CMultiVMR9Wizard::Initialize: specified render engine points to a different window than wizard"));
m_pRenderEngine = pRenderEngine;
m_pRenderEngine->AddRef();
m_pRenderEngine->SetWizardOwner(this);
}
else
{
// custom render engine was not provided; created one
hr = CoCreateInstance( CLSID_MultiVMR9RenderEngine, NULL, CLSCTX_INPROC_SERVER,
IID_IMultiVMR9RenderEngine, (void**)&m_pRenderEngine );
CHECK_HR(
FAILED(hr) ? hr : ( (!m_pRenderEngine) ? E_FAIL : S_OK),
::DbgMsg("CMultiVMR9Wizard::Initialize: Failed to create MultiVMR9RenderEngine object, error code %08x", hr));
if( FAILED(hr))
{
RELEASE( m_pRenderEngine );
}
CHECK_HR(
hr = m_pRenderEngine->Initialize( m_hwnd, NULL, NULL, NULL), // TODO: second parameter is flags
::DbgMsg("CMultiVMR9Wizard::Initialize: failed to initialize default render engine, hr = 0x%08x", hr));
m_pRenderEngine->SetWizardOwner(this);
}
m_bInitialized = TRUE;
CHECK_HR(
hr = StartRenderingThread_(),
::DbgMsg("CMultiVMR9Wizard::Initialize: failed in StartRenderingThread_, hr = 0x%08x", hr));
}// __try
catch( HRESULT hr1 )
{
hr = hr1;
}
return hr;
}
****************************Public*Routine******************************\
Terminate
This method must be called before destroying the wizard
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::Terminate()
{
HRESULT hr = S_OK;
// check that we do not have connected subgraphs
if( m_listVideoSources.size() > 0 )
{
return VFW_E_WRONG_STATE;
}
try
{
CHECK_HR(
hr = StopRenderingThread_(),
::DbgMsg("CMultiVMR9Wizard::Terminate: failed to stop rendering thread, hr = 0x%08x", hr));
// Unadvise render engine
if( m_pRenderEngine )
{
CHECK_HR(
hr = m_pRenderEngine->SetWizardOwner( NULL),
::DbgMsg("CMultiVMR9Wizard::Terminate: failed to unadvise WizardOwner for render engine, hr = 0x%08x", hr));
CHECK_HR(
hr = m_pRenderEngine->Terminate(),
::DbgMsg("CMultiVMR9Wizard::Terminate: failed to terminate render engine, hr = 0x%08x", hr));
RELEASE( m_pRenderEngine );
}
}
catch( HRESULT hr1 )
{
hr = hr1;
}
return hr;
}
****************************Public*Routine******************************\
Attach
Call this method to attach subgraph to MultiVMR9. Before this call,
correspondent VMR9 filter must be added to its graph and the graph must be
rendered and stopped. You must call Detach() method before destroying the graph.
If VMR was attached successfully, method will also
add MultiVMR9_VideoSource structure (associated with this video source)
to the list of active objects
pVMR - pointer to VMR9 filter of the correspondent subgraph
pdwID - return value, a DWORD_PTR cookie associated with attached subgraph
Return error codes: E_POINTER (pVMR is NULL or pdwID is NULL)
VFW_E_NOT_STOPPED (correspondent graph is not stopped
or pVMR was not added to the graph,
or pVMR is not in the renderless mode)
VFW_E_WRONG_STATE (method Initialize() was not called )
E_FAIL (unexpected error),
E_NOINTERFACE (pVMR does not expose IVMRSurfaceAllocatorNotify9)
E_OUTOFMEMORY (memory allocation error)
or error code from IVMRSurfaceAllocatorNotify9
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::Attach(
IBaseFilter* pVMR,
DWORD_PTR* pdwID)
{
HRESULT hr = S_OK;
FILTER_INFO fiVMR;
OAFilterState state;
DWORD dwVMRMode = 0L;;
MultiVMR9_VideoSource* pVideoSource = NULL;
IMediaControl* pMediaControl = NULL;
IVMRFilterConfig9* pFilterConfig = NULL;
IFilterGraph* pFilterGraph = NULL;
IDirect3DDevice9* pDevice = NULL;
IDirect3D9* pd3d9 = NULL;
HMONITOR hMonitor = NULL;
// check that pointers are valid
if( !pVMR || !pdwID )
{
::DbgMsg("CMultiVMR9Wizard::Attach received NULL pointer");
return E_POINTER;
}
if( !m_bInitialized )
{
::DbgMsg("CMultiVMR9Wizard::Attach: method Initialize() was not called!");
return VFW_E_WRONG_STATE;
}
try
{
// check that provided VMR is part of the graph
hr = pVMR->QueryFilterInfo( &fiVMR );
CHECK_HR(
(NULL == fiVMR.pGraph) ? E_FAIL : S_OK,
::DbgMsg("CMultiVMR9Wizard::Attach: provided VMR was not added to the graph"));
pFilterGraph = fiVMR.pGraph;
pFilterGraph->AddRef();
// check that provided VMR is in renderless mode
hr = pVMR->QueryInterface( IID_IVMRFilterConfig9,
(void**)&pFilterConfig );
CHECK_HR(
FAILED(hr) ? hr : ( !pFilterConfig ? E_FAIL : S_OK ),
::DbgMsg("CMultiVMR9Wizard::Attach: failed to QI IVMRFilterConfig9, hr = 0x%08x", hr));
CHECK_HR(
hr = pFilterConfig->GetRenderingMode( &dwVMRMode ),
::DbgMsg("CMultiVMR9Wizard::Attach: failed to get rendering mode, hr = 0x%08x", hr));
CHECK_HR(
(VMRMode_Renderless != dwVMRMode) ? VFW_E_WRONG_STATE : S_OK,
::DbgMsg("CMultiVMR9Wizard::Attach: provided VMR9 is not in renderless mode"));
pVideoSource = new MultiVMR9_VideoSource;
CHECK_HR(
!pVideoSource ? E_OUTOFMEMORY : S_OK,
::DbgMsg("CMultiVMR9Wizard::Attach: failed to allocate MultiVMR9_VideoSource structure"));
pVideoSource->dwID = (DWORD_PTR)pVideoSource;
pVideoSource->pVMR = pVMR;
pVideoSource->pVMR->AddRef();
pVideoSource->pGraph = pFilterGraph;
pVideoSource->pGraph->AddRef();
// check that provided pVMR exposes IVMRSurfaceAllocatorNotify9 interfaces
CHECK_HR(
pVMR->QueryInterface( IID_IVMRSurfaceAllocatorNotify9,
(void**)&pVideoSource->pDefaultNotify),
::DbgMsg("CMultiVMR9Wizard::Attach: cannot QI IVMRSurfaceAllocatorNotify9"));
CHECK_HR(
pVideoSource->pGraph->QueryInterface( IID_IMediaControl,
(void**)&pMediaControl),
::DbgMsg("CMultiVMR9Wizard::Attach: cannot QI IMediaControl"));
CHECK_HR(
hr = pMediaControl->GetState( 100, &state),
::DbgMsg("CMultiVMR9Wizard::Attach: failed to get state of IMediaControl, hr = 0x%08x", hr));
CHECK_HR(
( state != State_Stopped ) ? VFW_E_NOT_STOPPED : S_OK,
::DbgMsg("CMultiVMR9Wizard::Attach: graph is not stopped, state = \%ld", state));
// we have to be thread safe only here when we actually mess up with shared data
CAutoLock Lock(&m_ObjectLock);
// set device
CHECK_HR(
hr = m_pRenderEngine->Get3DDevice( &pDevice ),
::DbgMsg( "CMultiVMR9Wizard::Attach: failed to obtain Direct3D device "\
"from the render engine, hr = 0x%08x", hr));
// COMMENTED CODE works for VS.Net and PlatformSDK. If you use them, uncomment
// the following block and comment the next one
/*
{
CHECK_HR(
hr = pVideoSource->pDefaultNotify->SetD3DDevice(
pDevice,
MonitorFromWindow( m_hwnd, MONITOR_DEFAULTTOPRIMARY )),
::DbgMsg("CMultiVMR9Wizard::Attach: failed in SetD3DDevice() of IVMRSurfaceAllocatorNotify, "\
"hr = 0x%08x", hr));
}
*/
{
CHECK_HR(
hr = pDevice->GetDirect3D( &pd3d9 ),
::DbgMsg("CMultiVMR9Wizard::Attach: failed to retrieve IDirect3D9"));
HMONITOR hMonitor = pd3d9->GetAdapterMonitor( D3DADAPTER_DEFAULT );
CHECK_HR(
hr = pVideoSource->pDefaultNotify->SetD3DDevice(
pDevice,
hMonitor),
::DbgMsg("CMultiVMR9Wizard::Attach: failed in SetD3DDevice() of IVMRSurfaceAllocatorNotify, "\
"hr = 0x%08x", hr));
}
// try to advise 'this' custom allocator-presenter to the VMR
CHECK_HR(
hr = pVideoSource->pDefaultNotify->AdviseSurfaceAllocator(
pVideoSource->dwID,
(IVMRSurfaceAllocator9*)this),
::DbgMsg("CMultiVMR9Wizard::Attach: failed to advise A/P, hr = 0x%08x", hr));
CHECK_HR(
hr = StartPresenting( pVideoSource->dwID ),
::DbgMsg("CMultiVMR9Wizard::Attach: failed in StartPresenting(), hr = 0x%08x", hr));
// we successfully attached subgraph, last thing left is to save
// pVideoSource in the list
m_listVideoSources.push_back( pVideoSource );
*pdwID = pVideoSource->dwID;
} // try
catch( HRESULT hr1 )
{
hr = hr1;
if( pVideoSource )
{
delete pVideoSource;
pVideoSource = NULL;
}
}
RELEASE( pd3d9 );
RELEASE( fiVMR.pGraph );
RELEASE( pFilterGraph );
RELEASE( pFilterConfig );
RELEASE( pMediaControl );
RELEASE( pDevice );
return hr;
}
****************************Public*Routine******************************\
Detach
Detaches VMR of particular subgraph (identified by dwID) and
unadvises custom allocator-presenter. Call this method before destroying
correspondent subgraph. Graph must be stopped before calling this method.
Pins of the correspondent VMR filter must be disconnected
dwID -- Number that uniquely identifies subgraph in the multi-graph environment.
This number is assigned to the subgraph in Attach() method.
Return error codes: VFW_E_NOT_FOUND (unknown dwID),
VFW_E_WRONG_STATE (method Initalize() was never called)
VFW_E_NOT_STOPPED (graph was not stopped),
E_FAIL (unexpected),
or error code of IVMRSurfaceAllocatorNotify9.
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::Detach(DWORD_PTR dwID)
{
HRESULT hr = S_OK;
OAFilterState state;
bool bSourceWasDeleted = false;
IMediaControl* pMc = NULL;
IMultiVMR9MixerControl* pMixerControl = NULL;
MultiVMR9_VideoSource* pvideosource = NULL;
MultiVMR9_VideoSource* pcursource = NULL;
if( !m_bInitialized )
{
::DbgMsg("CMultiVMR9Wizard::Detach: method 'Initialize' was never called");
return VFW_E_WRONG_STATE;
}
hr = GetSourceInfo_( dwID, &pvideosource );
if( FAILED(hr) || !pvideosource )
{
::DbgMsg("CMultiVMR9Wizard::Detach: Failed in GetSourceInfo_()");
return ( FAILED(hr) ? hr : VFW_E_NOT_FOUND );
}
if( !m_pRenderEngine )
{
::DbgMsg("CMultiVMR9Wizard::Detach: FATAL IMultiVMR9RenderEngine pointer is NULL!");
return E_UNEXPECTED;
}
if( !pvideosource->pGraph )
{
::DbgMsg("CMultiVMR9Wizard::Detach: video source info does not contain pointer to IFilterGraph!");
return VFW_E_NOT_FOUND;
}
try
{
CHECK_HR(
hr = (pvideosource->pGraph)->QueryInterface(
IID_IMediaControl, (void**)&pMc ),
::DbgMsg("CMultiVMR9Wizard::Detach: cannot QI IMediaControl of the graph, hr = 0x%08x", hr));
CHECK_HR(
hr = pMc->GetState( 100, &state ),
::DbgMsg("CMultiVMR9Wizard::Detach: cannot obtain state from IMediaControl, hr = 0x%08x", hr));
CHECK_HR(
( State_Stopped != state ) ? VFW_E_NOT_STOPPED : S_OK,
::DbgMsg("CMultiVMR9Wizard::Detach: correspondent graph was not stopped"));
// advise NULL as A/P to VMR9 (this will return VMR9 to its default A/P)
CHECK_HR(
( !(pvideosource->pDefaultNotify)) ? VFW_E_NOT_FOUND : S_OK,
::DbgMsg("CMultiVMR9Wizard::Detach: video source info does not contain pointer to IVMRSurfaceAllocatorNotify9"));
CHECK_HR(
hr = m_pRenderEngine->GetMixerControl( &pMixerControl ),
::DbgMsg("CMultiVMR9Wizard::Detach: FATAL, cannot find currently active IMultiVMR9MixerControl!"));
// we have to be thread safe only here when we actually mess up with shared data
CAutoLock Lock(&m_ObjectLock);
CHECK_HR(
hr = StopPresenting( pvideosource->dwID ),
::DbgMsg("CMultiVMR9Wizard::Detach: failed in StopPresenting(), hr = 0x%08x", hr));
CHECK_HR(
pvideosource->DisconnectPins(),
::DbgMsg("CMultiVMR9Wizard::Detach: FATAL, failed to disconnect pins of VMR"));
CHECK_HR(
hr = (pvideosource->pDefaultNotify)->AdviseSurfaceAllocator(
dwID, NULL),
::DbgMsg("CMultiVMR9Wizard::Detach: failed to unadvise surface allocator, hr = 0x%08x", hr));
// we unadvised custom allocator-presenter successfully, let's delete
// video source structure from the list
list< MultiVMR9_VideoSource*>::iterator start, end, it;
start = m_listVideoSources.begin();
end = m_listVideoSources.end();
for( it=start; it!=end; it++)
{
pcursource = (MultiVMR9_VideoSource*)(*it);
CHECK_HR(
( NULL == pcursource ) ? E_UNEXPECTED : S_OK,
::DbgMsg("CMultiVMR9Wizard::Detach: FATAL, m_listVideoSources contains NULL pointer"));
if( dwID == pcursource->dwID )
{
// delete this video source from the mixer
CHECK_HR(
hr = pMixerControl->DeleteVideoSource( dwID ),
::DbgMsg("CMultiVMR9Wizard::Detach: failed to delete source from "\
"the mixer (ID=0x%08x, hr=0x%08x)", dwID, hr));
m_listVideoSources.remove( pcursource );
delete pcursource;
pcursource = NULL;
bSourceWasDeleted = true;
break;
}
}// for
CHECK_HR(
( false == bSourceWasDeleted ) ? VFW_E_NOT_FOUND : S_OK,
::DbgMsg("CMultiVMR9Wizard::Detach: FATAL, failed to delete source from the list (source was not found)"));
}// try
catch( HRESULT hr1 )
{
hr = hr1;
}
RELEASE( pMc );
RELEASE( pMixerControl );
return hr;
}
STDMETHODIMP
CMultiVMR9Wizard::BeginDeviceLoss( void )
{
HRESULT hr = S_OK;
CAutoLock Lock(&m_ObjectLock);
// go through all the connected sources and reset the device
list<MultiVMR9_VideoSource*>::iterator start, end, it;
start = m_listVideoSources.begin();
end = m_listVideoSources.end();
for( it=start; it!=end; it++)
{
MultiVMR9_VideoSource* pSource = (MultiVMR9_VideoSource*)(*it);
if( pSource )
{
pSource->DeleteSurfaces();
RELEASE( pSource->pTexturePriv );
}
}// for
return hr;
}
STDMETHODIMP
CMultiVMR9Wizard::EndDeviceLoss( IDirect3DDevice9* pDevice )
{
HRESULT hr = S_OK;
IDirect3D9* pd3d9 = NULL;
HMONITOR hMon = NULL;
if( !pDevice )
return E_POINTER;
hr = pDevice->GetDirect3D( &pd3d9 );
if( pd3d9 )
{
hMon = pd3d9->GetAdapterMonitor( D3DADAPTER_DEFAULT );
}
list<MultiVMR9_VideoSource*>::iterator start, end, it;
start = m_listVideoSources.begin();
end = m_listVideoSources.end();
for( it=start; it!=end; it++)
{
MultiVMR9_VideoSource* pSource = (MultiVMR9_VideoSource*)(*it);
if( pSource && pSource->pDefaultNotify )
{
hr = pSource->pDefaultNotify->ChangeD3DDevice( pDevice, hMon );
}
}
RELEASE( pd3d9 );
return hr;
}
****************************Public*Routine******************************\
VerifyID
Call this method to check if some dwID is registered with the wizard
dwID -- Number that uniquely identifies subgraph in the multi-graph environment.
This number is assigned to the subgraph in Attach() method.
Return error codes: VFW_E_NOT_FOUND (unknown dwID)
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::VerifyID( DWORD_PTR dwID )
{
HRESULT hr = S_OK;
MultiVMR9_VideoSource *pSrc = NULL;
hr = this->GetSourceInfo_( dwID, &pSrc);
if( SUCCEEDED( hr ) && pSrc )
{
return S_OK;
}
else
{
return VFW_E_NOT_FOUND;
}
}
****************************Public*Routine******************************\
GetGraph
Returns pointer to IFilterGraph interface of the sub-graph identified by dwID
dwID -- Number that uniquely identifies subgraph in the multi-graph environment.
This number is assigned to the subgraph in Attach() method.
ppGraph -- [out] pointer to IFilterGraph
Return error codes: E_POINTER (ppGraph is NULL),
VFW_E_WRONG_STATE (method Initialize() was never called)
VFW_E_NOT_FOUND (unknown dwID),
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::GetGraph(DWORD_PTR dwID, IFilterGraph** ppGraph)
{
HRESULT hr = S_OK;
MultiVMR9_VideoSource *pSrc = NULL;
if( !ppGraph )
{
::DbgMsg("CMultiVMR9Wizard::GetGraph: second argument is NULL");
return E_POINTER;
}
if( !m_bInitialized )
{
::DbgMsg("CMultiVMR9Wizard::GetGraph: Method 'Initialize' was never called");
return VFW_E_WRONG_STATE;
}
hr = this->GetSourceInfo_( dwID, &pSrc);
if( SUCCEEDED(hr))
{
if( pSrc->pGraph )
{
*ppGraph = pSrc->pGraph;
(*ppGraph)->AddRef();
hr = S_OK;
}
else
{
::DbgMsg("CMultiVMR9Wizard::GetGraph: FATAL: member of the list of video sources contain NULL IFilterGraph pointer");
hr = VFW_E_NOT_FOUND;
}
}
else
{
::DbgMsg("CMultiVMR9Wizard::GetGraph: Failed in GetSourceInfo_(), hr = 0x%08x", hr);
}
return hr;
}
****************************Public*Routine******************************\
GetRenderEngine
Call this method to obtain actively used render engine object
ppRenderEngine -- Pointer to IMultiVMR9RenderEngine
Return error codes: E_POINTER (ppGraph is NULL)
VFW_E_WRONG_STATE (method Initalize() was never called )
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::GetRenderEngine(
IMultiVMR9RenderEngine** ppRenderEngine
)
{
HRESULT hr = S_OK;
if( !ppRenderEngine )
{
::DbgMsg("CMultiVMR9Wizard::GetRenderEngine: first argument is NULL");
return E_POINTER;
}
if( !m_bInitialized )
{
::DbgMsg("CMultiVMR9Wizard::GetRenderEngine: Method 'Initialize' was never called");
return VFW_E_WRONG_STATE;
}
*ppRenderEngine = m_pRenderEngine;
(*ppRenderEngine)->AddRef();
return S_OK;
}
****************************Public*Routine******************************\
GetMixerControl
Call this method to obtain actively used mixer control object (IMultiVMR9MixerControl)
ppMixerControl -- Pointer to IMultiVMR9MixerControl
Return error codes: E_POINTER (ppGraph is NULL)
VFW_E_WRONG_STATE (method Initalize() was never called )
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::GetMixerControl(
IMultiVMR9MixerControl** ppMixerControl
)
{
HRESULT hr = S_OK;
if( !ppMixerControl )
{
::DbgMsg("CMultiVMR9Wizard::GetMixerControl: received NULL pointer");
return E_POINTER;
}
if( !m_bInitialized )
{
::DbgMsg("CMultiVMR9Wizard::GetMixerControl: Method 'Initialize' was never called");
return VFW_E_WRONG_STATE;
}
if( !m_pRenderEngine )
{
::DbgMsg("CMultiVMR9Wizard::GetMixerControl: FATAL, cannot find IMultiVMR9RenderEngine");
return E_UNEXPECTED;
}
hr = m_pRenderEngine->GetMixerControl( ppMixerControl );
return hr;
}
****************************Public*Routine******************************\
GetTexture
This method returns D3D9 texture of the video source identified by dwID
Render Engine calls this method.
dwID - ID of the video source assigned in Attach();
ppTexture - pointer to IDirect3DTexture9 that receives texture to be rendered
Return error codes: E_POINTER (ppTexture is NULL),
VFW_E_WRONG_STATE ( method Initialize() was never called )
VFW_E_NOT_FOUND (ID was not found),
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::GetTexture(
DWORD_PTR dwID,
LPDIRECT3DTEXTURE9* ppTexture
)
{
HRESULT hr = E_FAIL;
MultiVMR9_VideoSource *pSrc = NULL;
if( !ppTexture )
{
::DbgMsg("CMultiVMR9Wizard::GetRenderEngine: second argument is NULL");
return E_POINTER;
}
if( !m_bInitialized )
{
::DbgMsg("CMultiVMR9Wizard::GetRenderEngine: method 'Initialize' was never called");
return VFW_E_WRONG_STATE;
}
hr = GetSourceInfo_( dwID, &pSrc );
if( FAILED(hr))
{
::DbgMsg("CMultiVMR9Wizard::GetRenderEngine: failed in GetSourceInfo_() (video source was not found)");
return VFW_E_NOT_FOUND;
}
*ppTexture = pSrc->pTexturePriv;
if( pSrc->pTexturePriv )
{
(*ppTexture)->AddRef();
}
return S_OK;
}
****************************Public*Routine******************************\
GetVideoSize
Call this method to obtain native video size of the video source
identified with dwID. Sizes correspond to VMR9AllocationInfo::dwWidth,
VMR9AllocationInfo::dwHeight that is sent to CMultiVMR9Wizard::InitializeDevice
See description of VMR9AllocationInfo in the DirectShow SDK documentation for
other sizes that can be used
dwID - ID of the video source assigned in Attach();
plWidth - pointer to the variable that receives width
plHeight - pointer to the variable that receives height
Return error codes: E_POINTER (either plWidth or plHeight is NULL),
VFW_E_NOT_FOUND (ID was not found),
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::GetVideoSize(
DWORD_PTR dwID,
LONG* plWidth,
LONG* plHeight
)
{
HRESULT hr = S_OK;
MultiVMR9_VideoSource *pSrc = NULL;
if( !plWidth )
{
::DbgMsg("CMultiVMR9Wizard::GetVideoSize: received NULL for plWidth");
return E_POINTER;
}
if( !plHeight )
{
::DbgMsg("CMultiVMR9Wizard::GetVideoSize: received NULL for plHeight");
return E_POINTER;
}
hr = GetSourceInfo_( dwID, &pSrc );
if( FAILED(hr))
{
::DbgMsg("CMultiVMR9Wizard::GetVideoSize: failed in GetSourceInfo_() (video source was not found)");
*plWidth = 0L;
*plHeight = 0L;
return VFW_E_NOT_FOUND;
}
*plWidth = pSrc->lImageWidth;
*plHeight = pSrc->lImageHeight;
return S_OK;
}
/////////////////// IVMRSurfaceAllocator9 /////////////////////////
****************************Public*Routine******************************\
AdviseNotify
For usage, parameters and return codes
see DirectX SDK documentation, IVMRSurfaceAllocator9 interface
In custom implementation of this method, we do nothing. Why?
When VMR is created, it creates its own (default) allocator-presenter
and alltogether with IVMRSurfaceAllocatorNotify9::AdviseSurfaceAllocator,
method IVMRSurfaceAllocator9::AdviseNotify() is used to make two objects
talking to each other. We do not implement our custom IVMRSurfaceAllocatorNotify9
in this sample, and we have complete control over our custom A/P (this class
CMultiVMR9Wizard), so we do not have to tell A/P about VMR's notifier.
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::AdviseNotify(
IVMRSurfaceAllocatorNotify9* lpIVMRSurfAllocNotify
)
{
return S_OK;
}
****************************Public*Routine******************************\
InitializeDevice
For usage, parameters and return codes
see DirectX SDK documentation, IVMRSurfaceAllocator9 interface
In the custom implementation of this method, we provide Direct3D device
from CMultiVMR9RenderEngine that is responsible for actual Direct3D rendering.
InitializeDevice() is called by VMR to make a request for allocation of the swap chain.
We use Direct3DDevice9 from CMultiVMR9RenderEngine to allocate surfaces. Since
render engine uses Direct3D primitives (and video is applied on them as a texture),
we must make sure that
VMR9AllocationInfo::dwFlags contain VMR9AllocFlag_TextureSurface. For actual
surface allocation that works best on a particular device, we use
helper function IVMRSurfaceAllocatorNotify9::AllocateSurfaceHelper from
the default IVMRSurfaceAllocatorNotify9 of the VMR making request. (We saved
this pointer in MultiVMR9_VideoSource::pDefaultNotify in Attach(), while
connecting this VMR to our A/P).
Unlike VMR9Allocator sample of this SDK, we cannot use the same swap chain
to make actual rendering, because we have to accumulate PresentImage() calls
from many different sources and keep CMultiVMR9RenderEngine rendering
asynchronously from VMR calls (with the frame rate specified by client app).
So in addition to swap chain allocation we also allocate private texture for
each video source and will Blt on it every time correspondent VMR makes
PresentImage() call. Then render engine renders whatever was written last
to this private texture (i.e. some frames might be dropped and some will be
rendered several times, depending on the difference between native frame rate
and the frame rate our render engine is using).
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::InitializeDevice(
DWORD_PTR dwUserID,
VMR9AllocationInfo* lpAllocInfo,
DWORD* lpNumBuffers
)
{
HRESULT hr = S_OK;
D3DFORMAT format;
D3DSURFACE_DESC ddsd;
D3DCAPS9 ddcaps;
UINT width;
UINT height;
MultiVMR9_VideoSource* pSrc = NULL;
IDirect3DDevice9* pDevice = NULL;
IDirect3DSurface9* pS = NULL;
IMultiVMR9MixerControl* pMixerControl = NULL;
// first, make sure we got a call from a known VMR
hr = GetSourceInfo_( dwUserID, &pSrc );
if( FAILED(hr) || !pSrc )
{
::DbgMsg("CMultiVMR9Wizard::InitializeDevice: cannot get info on the calling VMR, "\
"dwUserID = 0x%08x, hr = 0x%08x, pSource = 0x%08x", dwUserID, hr, (void*)pSrc);
return ( FAILED(hr)? hr : E_FAIL );
}
// check we are provided valid parameters
if( !lpAllocInfo || !lpNumBuffers )
{
return E_POINTER;
}
if( *lpNumBuffers <1 )
{
*lpNumBuffers = 1;
}
// check we know about the default IVMRSurfaceAllocatorNotify9
if(!(pSrc->pDefaultNotify) )
{
::DbgMsg("CMultiVMR9Wizard::InitializeDevice: FATAL: video source contains NULL pointer to IVMRSurfaceAllocatorNotify9");
return E_FAIL;
}
try
{
// obtain device from the render engine
CHECK_HR(
hr = m_pRenderEngine->Get3DDevice( &pDevice ),
::DbgMsg("CMultiVMR9Wizard::InitializeDevice: failed to get Direct3D device from the render engine, "\
"hr = 0x%08x, pDevice = 0x%08x", hr, pDevice));
// we have to be thread safe only here when we actually mess up with shared data
CAutoLock Lock(&m_ObjectLock);
// just some insanity check
RELEASE( pSrc->pTexturePriv );
pSrc->DeleteSurfaces();
// allocate surface buffer
CHECK_HR(
hr = pSrc->AllocateSurfaceBuffer( *lpNumBuffers ),
::DbgMsg("CMultiVMR9Wizard::InitializeDevice: failed to allocate surface buffer, hr = 0x%08x, dwBuffers = \%ld",
hr, *lpNumBuffers));
// since we always copy data onto private textures, we create
// the swap chain as offscreen surface
//lpAllocInfo->Usage = 0; // not a render target
lpAllocInfo->dwFlags = VMR9AllocFlag_OffscreenSurface;//VMR9AllocFlag_3DRenderTarget | VMR9AllocFlag_TextureSurface;
CHECK_HR(
hr = pSrc->pDefaultNotify->AllocateSurfaceHelper(
lpAllocInfo,
lpNumBuffers,
pSrc->ppSurface ),
::DbgMsg("CMultiVMR9Wizard::InitializeDevice: failed in "\
"IVMRSurfaceAllocatorNotify9::AllocateSurfaceHelper, "\
"hr = 0x%08x, dwBuffers = \%ld", hr, *lpNumBuffers));
pSrc->dwNumBufActuallyAllocated = *lpNumBuffers;
// here we are creating private texture to be used by the render engine
if (lpAllocInfo->Format > '0000') // surface is YUV.
{
format = D3DFMT_X8R8G8B8;// TODO: get current display format
//format = D3DFMT_A8R8G8B8;// TODO: get current display format
}
else // RGB: use the same as original
{
format = lpAllocInfo->Format;
}
// first, check if we have to comply with pow2 requirement
ZeroMemory( &ddcaps, sizeof(D3DCAPS9));
CHECK_HR(
hr = pDevice->GetDeviceCaps( &ddcaps ),
::DbgMsg("CMultiVMR9Wizard::InitializeDevice: failed to get device caps"));
if( ddcaps.TextureCaps & D3DPTEXTURECAPS_POW2 ) // have to make it pow2 :-(
{
width = 2;
while( width < lpAllocInfo->dwWidth )
width = width << 1;
height = 2;
while( height < lpAllocInfo->dwHeight )
height = height << 1;
}
else
{
width = lpAllocInfo->dwWidth;
height = lpAllocInfo->dwHeight;
}
RELEASE( pSrc->pTexturePriv );
CHECK_HR(
hr = pDevice->CreateTexture(width, // width
height, // height
1, // levels
//D3DUSAGE_DYNAMIC,
D3DUSAGE_RENDERTARGET,// usage
format, // format
D3DPOOL_DEFAULT, // we are not going to get into surface bits, so we do not need managed
//D3DPOOL_SYSTEMMEM,
//D3DPOOL_MANAGED,
&(pSrc->pTexturePriv),
NULL),
::DbgMsg("CMultiVMR9Wizard::InitializeDevice: failed to create "\
"private texture, hr = 0x%08x", hr));
CHECK_HR(
hr = pSrc->pTexturePriv->GetSurfaceLevel(0, &pS),
::DbgMsg("CMultiVMR9Wizard::InitializeDevice: failed to get 0-level surface "\
"of the private texture, hr = 0x%08x", hr));
CHECK_HR(
hr = pDevice->ColorFill( pS, NULL, D3DCOLOR_XRGB(0x00,0x00,0x00)),
::DbgMsg("CMultiVMR9Wizard::InitializeDevice: failed to fill the surface "\
"of the private texture with solid color, hr = 0x%08x", hr));
CHECK_HR(
hr = pSrc->pTexturePriv->GetLevelDesc(0, &ddsd),
::DbgMsg("CMultiVMR9Wizard::InitializeDevice: failed to obtain "\
"surface description of the private texture, hr = 0x%08x", hr));
CHECK_HR(
hr = pSrc->SetVideoSize( lpAllocInfo->dwWidth, lpAllocInfo->dwHeight),
::DbgMsg("CMultiVMR9Wizard::InitializeDevice: failed to save "\
"video size, hr = 0x%08x", hr));
// tell the mixer we are "on"
CHECK_HR(
hr = this->GetMixerControl( &pMixerControl ),
::DbgMsg("CMultiVMR9Wizard::InitializeDevice: cannot find IMultiVMR9MixerControl!"));
CHECK_HR(
hr = pMixerControl->AddVideoSource(
pSrc->dwID,
pSrc->lImageWidth,
pSrc->lImageHeight,
ddsd.Width,
ddsd.Height),
::DbgMsg("CMultiVMR9Wizard::InitializeDevice: failed in "\
"IMultiVMR9MixerControl::AddVideoSource(), hr = 0x%08x", hr));
}// try
catch( HRESULT hr1 )
{
hr = hr1;
}
RELEASE( pS );
RELEASE( pDevice );
RELEASE( pMixerControl );
return hr;
}
****************************Public*Routine******************************\
TerminateDevice
For usage, parameters and return codes
see DirectX SDK documentation, IVMRSurfaceAllocator9 interface
In the custom implementation of this method, we just release the surface buffer
associated with correspondent video source
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::TerminateDevice(
DWORD_PTR dwID
)
{
HRESULT hr = S_OK;
MultiVMR9_VideoSource *pSrc = NULL;
hr = GetSourceInfo_( dwID, &pSrc );
if( FAILED(hr) || !pSrc)
{
::DbgMsg("CMultiVMR9Wizard::TerminateDevice: failed in GetSourceInfo_() (wrong dwID), "\
"hr = 0x%08x, pSource = 0x%08x", hr, pSrc);
return (FAILED(hr)? hr : E_FAIL);
}
// we have to be thread safe only here when we actually mess up with shared data
CAutoLock Lock(&m_ObjectLock);
/*
if( m_pRenderEngine )
{
IMultiVMR9MixerControl *pMixer = NULL;
hr = m_pRenderEngine->GetMixerControl( &pMixer );
if( SUCCEEDED(hr) && pMixer )
{
hr = pMixer->DeleteVideoSource( dwID );
}
RELEASE( pMixer );
}*/
return hr;
}
****************************Public*Routine******************************\
GetSurface
For usage, parameters and return codes
see DirectX SDK documentation, IVMRSurfaceAllocator9 interface
In the custom implementation of this method, we check that dwUserID is valid,
that Surface index does not exceed number of allocated buffers, and return
pointer to correspondent surface
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::GetSurface(
DWORD_PTR dwUserID,
DWORD SurfaceIndex,
DWORD SurfaceFlags,
IDirect3DSurface9** lplpSurface
)
{
HRESULT hr = S_OK;
MultiVMR9_VideoSource *pSrc = NULL;
// check for NULL pointers
if( !lplpSurface )
{
::DbgMsg("CMultiVMR9Wizard::GetSurface: fourth argument is NULL");
return E_POINTER;
}
// check that dwUserID points to a known VMR
hr = GetSourceInfo_( dwUserID, &pSrc );
if( FAILED(hr) || !pSrc )
{
::DbgMsg("CMultiVMR9Wizard::GetSurface, failed in GetSourceInfo_() (wrong dwID), "\
"hr = 0x%08x, pSource = 0x%08x", hr, pSrc);
return ( FAILED(hr) ? hr : E_FAIL );
}
// check that requested index does not exceed number of actually allocated buffers
if( SurfaceIndex >= pSrc->dwNumBufActuallyAllocated )
{
::DbgMsg("CMultiVMR9Wizard::GetSurface: requested surface index \%ld falls out of "\
"valid range [0, \%ld]", SurfaceIndex, pSrc->dwNumBufActuallyAllocated);
return E_INVALIDARG;
}
// check that requested surface is not null
if( NULL == pSrc->ppSurface[ SurfaceIndex ] )
{
::DbgMsg("CMultiVMR9Wizard::GetSurface: FATAL, requested surface of index \%ld is NULL!",
SurfaceIndex);
return E_UNEXPECTED;
}
// we have to be thread safe only here when we actually mess up with shared data
CAutoLock Lock(&m_ObjectLock);
// now we checked everything and can copy
*lplpSurface = pSrc->ppSurface[ SurfaceIndex ];
(*lplpSurface)->AddRef();
return S_OK;
}
/////////////////// IVMRImagePresenter9 /////////////////////////
****************************Public*Routine******************************\
StartPresenting
For usage, parameters and return codes
see DirectX SDK documentation, IVMRImagePresenter9 interface
In the custom implementation of this method, we check that dwUserID is valid
and Direct3Ddevice from the render engine is ready to go
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::StartPresenting(
DWORD_PTR dwUserID
)
{
HRESULT hr = S_OK;
MultiVMR9_VideoSource* pSrc = NULL;
IDirect3DDevice9* pDevice = NULL;
CAutoLock Lock(&m_ObjectLock);
hr = GetSourceInfo_( dwUserID, &pSrc );
if( FAILED(hr) || !pSrc )
{
::DbgMsg("CMultiVMR9Wizard::StartPresenting: failed in GetSourceInfo_() (invalid dwID) ,"\
"hr = 0x%08x, pSource = 0x%08x", hr, pSrc);
return ( FAILED(hr) ? hr : VFW_E_NOT_FOUND );
}
if( !m_pRenderEngine )
{
::DbgMsg("CMultiVMR9Wizard::StartPresenting: FATAL, Render engine is NULL");
return E_UNEXPECTED;
}
hr = m_pRenderEngine->Get3DDevice( &pDevice );
if( FAILED(hr) || !pDevice )
{
::DbgMsg("CMultiVMR9Wizard::StartPresenting: FATAL, Direct3DDevice in the render engine is NULL, "\
"hr = 0x%08x, pDirect3DDevice = 0x%08x", hr, pDevice);
RELEASE( pDevice );
return ( FAILED(hr) ? hr : E_UNEXPECTED);
}
RELEASE( pDevice );
return hr;
}
****************************Public*Routine******************************\
StopPresenting
For usage, parameters and return codes
see DirectX SDK documentation, IVMRImagePresenter9 interface
In the custom implementation of this method, we do nothing. By default, method
does not actually perform anything special, but one can use it to reflect on
the status of the rendering (vs. status of the graph) etc.
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::StopPresenting(
DWORD_PTR dwUserID
)
{
return S_OK;
}
****************************Public*Routine******************************\
PresentImage
For usage, parameters and return codes
see DirectX SDK documentation, IVMRImagePresenter9 interface
This is the place where all the fun happens. VMR calls this method every time
it is ready to render the image. From each VMR (they are identified with
dwUserID cookies) we obtain VMR9PresentationInfo structure (see SDK docs) that
contains the buffer to render, as well as presentation flags, timestamps
of the sample, aspect ratio, source and destination rectangles.
Here, in the custom implementation of the method, we just copy the sample
to the private texture we created in InitializeDevice() (See header to
InitializeDevice() to figure out why do we have to Blt onto private texture
rather than using surface from VMR9PresentationInfo directly).
Attention! This is the performance bottle neck of our library, because this
method is called by each VMR every time it delivers the picture. You do not
want to put a lot of computations here. Here is
the limitation we have to comply to not start losing frames:
Execution_time( PresentImage ) << 1000ms / SUM( FPS_i, i=0..N) ms,
where N in total number of VMRs attached and FPS_i is the frame rate
of i-th VMR (in frames per sec)
so if you have one mpeg 30fps and one DVD 50 mps, you have less that 12.5 ms
(in fact, you have much less time because you also spend resources in the render engine)
-- or give up on the promise to not lose frames
\*************************************************************************
STDMETHODIMP CMultiVMR9Wizard::PresentImage(
DWORD_PTR dwUserID,
VMR9PresentationInfo* lpPresInfo
)
{
HRESULT hr = S_OK;
MultiVMR9_VideoSource* pSrc = NULL;
IDirect3DDevice9* pSampleDevice = NULL;
IDirect3DSurface9* pTexturePrivSurf = NULL;
// first, check for NULLs
if( !lpPresInfo || !(lpPresInfo->lpSurf))
{
::DbgMsg("CMultiVMR9Wizard::PresentImage: received NULL pointer");
return E_POINTER;
}
// check that we know about dwUserID
hr = GetSourceInfo_( dwUserID, &pSrc );
if( FAILED(hr) || !pSrc )
{
::DbgMsg("CMultiVMR9Wizard::PresentImage: failed in GetSourceInfo_() (invalid dwID), "\
"hr = 0x%08x, pSource = 0x%08x", hr, pSrc);
return ( FAILED(hr) ? hr : VFW_E_NOT_FOUND);
}
if(!(pSrc->pTexturePriv) )
{
::DbgMsg("CMultiVMR9Wizard::PresentImage: FATAL, private texture of the source \%ld is NULL", dwUserID);
return E_UNEXPECTED;
}
// this is important to be thread safe here
CAutoLock Lock(&m_ObjectLock);
try
{
// now, get the device of the sample passed in (it is not necessarily the same
// device we created in the render engine
CHECK_HR(
hr = lpPresInfo->lpSurf->GetDevice( &pSampleDevice ),
::DbgMsg("CMultiVMR9Wizard::PresentImage: failed to get the device of the surface passed in, "\
"hr = 0x%08x, pSampleDevice = 0x%08x", hr, pSampleDevice));
CHECK_HR(
hr = pSrc->pTexturePriv->GetSurfaceLevel( 0, &pTexturePrivSurf),
::DbgMsg("CMultiVMR9Wizard::PresentImage: failed to get the 0-level surface from the private texture , "\
"hr = 0x%08x, pPrivTextureSurf = 0x%08x", hr, pTexturePrivSurf));
CHECK_HR(
hr = pSampleDevice->StretchRect( lpPresInfo->lpSurf,
NULL,
pTexturePrivSurf,
NULL,
D3DTEXF_NONE ),
::DbgMsg("CMultiVMR9Wizard::PresentImage: failed to StretchRect() from the video surface to the "\
"private texture, hr = 0x%08x", hr));
} // try
catch( HRESULT hr1)
{
hr = hr1;
}
RELEASE( pTexturePrivSurf );
RELEASE( pSampleDevice );
return hr;
}
///////////////// Private class CMultiVMR9_VideoSource /////////////////////////////////
****************************Private*Routine******************************\
MultiVMR9_VideoSource
constructor
\*************************************************************************
CMultiVMR9Wizard::MultiVMR9_VideoSource::MultiVMR9_VideoSource()
: dwTag( VIDEO_SOURCE_TAG )
, dwID( 0L )
, dwNumBuf( 0L)
, dwNumBufActuallyAllocated( 0L)
, lImageWidth( 0L)
, lImageHeight( 0L)
, pTexturePriv( NULL )
, ppSurface( NULL)
, pGraph( NULL )
, pDefaultNotify( NULL )
, pVMR (NULL)
{
}
****************************Private*Routine******************************\
~MultiVMR9_VideoSource
destructor
\*************************************************************************
CMultiVMR9Wizard::MultiVMR9_VideoSource::~MultiVMR9_VideoSource()
{
dwID = NULL;
dwTag = NULL;
if( pTexturePriv ) { pTexturePriv->Release(); pTexturePriv = NULL; }
if( pGraph ) { pGraph->Release(); pGraph = NULL; }
if( pDefaultNotify ) { pDefaultNotify->Release(); pDefaultNotify = NULL;}
if( pVMR ) { pVMR->Release(); pVMR = NULL; }
DeleteSurfaces();
lImageWidth = lImageHeight = 0L;
}
****************************Private*Routine******************************\
DisconnectPins
Disconnects pins of VMR
\*************************************************************************
HRESULT CMultiVMR9Wizard::MultiVMR9_VideoSource::DisconnectPins()
{
HRESULT hr = S_OK;
if( !pVMR )
{
return E_POINTER;
}
IEnumPins* pEnum = NULL;
IPin* pPin = NULL;
try
{
CHECK_HR(
hr = pVMR->EnumPins( &pEnum ),
::DbgMsg("MultiVMR9_VideoSource::DisconnectPins: failed to enumerate pins, hr = 0x%08x", hr));
hr = pEnum->Next(1, &pPin, NULL);
while( S_OK == hr && pPin)
{
CHECK_HR(
hr = pPin->Disconnect(),
::DbgMsg("MultiVMR9_VideoSource::DisconnectPins: failed to disconnect pin, hr = 0x%08x", hr));
RELEASE(pPin);
hr = pEnum->Next(1, &pPin, NULL);
}
}// try
catch( HRESULT hr1 )
{
hr = hr1;
}
RELEASE(pPin);
RELEASE(pEnum);
return hr;
}
****************************Private*Routine******************************\
DeleteSurfaces
deletes allocated surface buffers
\*************************************************************************
void CMultiVMR9Wizard::MultiVMR9_VideoSource::DeleteSurfaces()
{
if( ppSurface )
{
for( DWORD dwS = 0; dwS<dwNumBuf; dwS++)
{
if( ppSurface[dwS] )
{
(ppSurface[dwS])->Release();
ppSurface[dwS] = NULL;
}
}
delete[] ppSurface;
ppSurface = NULL;
}
dwNumBuf = 0L;
dwNumBufActuallyAllocated = 0L;
}
****************************Private*Routine******************************\
AllocateSurfaceBuffer
allocates buffer of dwN surfaces
\*************************************************************************
HRESULT CMultiVMR9Wizard::MultiVMR9_VideoSource::AllocateSurfaceBuffer(
DWORD dwN )
{
if( dwN < 1)
{
return E_INVALIDARG;
}
DeleteSurfaces();
dwNumBuf = dwN;
ppSurface = new IDirect3DSurface9*[dwNumBuf];
if( !ppSurface )
{
dwNumBuf = 0L;
return E_OUTOFMEMORY;
}
ZeroMemory( ppSurface, dwNumBuf * sizeof(IDirect3DSurface9*));
return S_OK;
}
****************************Private*Routine******************************\
SetVideoSize
saves data on the video source image size
\*************************************************************************
HRESULT CMultiVMR9Wizard::MultiVMR9_VideoSource::
SetVideoSize( LONG lImageW,
LONG lImageH )
{
if( lImageW < 1 ||
lImageH < 1 )
{
::DbgMsg("MultiVMR9_VideoSource::SetVideoSize: received invalid sizes: "\
"image width = \%ld, image height = \%ld",
lImageW, lImageH);
return E_INVALIDARG;
}
lImageWidth = lImageW;
lImageHeight = lImageH;
return S_OK;
}
///////////////// Private routine /////////////////////////////////
****************************Private*Routine******************************\
Clean_
clean all the data, release all interfaces
\*************************************************************************
void CMultiVMR9Wizard::Clean_()
{
HRESULT hr = S_OK;
DWORD_PTR dwID = 0L;
list<MultiVMR9_VideoSource*>::iterator it;
while( false == m_listVideoSources.empty())
{
it = m_listVideoSources.begin();
if( (MultiVMR9_VideoSource*)(*it) )
{
dwID = ((MultiVMR9_VideoSource*)(*it))->dwID;
hr = Detach( dwID );
DbgMsg( "Clean_: detaching \%ld, return code 0x%08x", dwID, hr);
}
}
RELEASE( m_pRenderEngine );
RELEASE( m_pNotify );
}
****************************Private*Routine******************************\
GetSourceInfo_
returns MultiVMR9_VideoSource associated with dwID
dwID -- sub-graph's cookie assigned in CMultiVMR9Wizard::Attach
source -- [out] reference to MultiVMR9_VideoSource to fill
Return error codes: VFW_E_NOT_FOUND -- source was not found
\*************************************************************************
HRESULT CMultiVMR9Wizard::GetSourceInfo_(DWORD_PTR dwID, MultiVMR9_VideoSource** ppsource)
{
HRESULT hr = S_OK;
MultiVMR9_VideoSource *pSrc = NULL;
if( !dwID )
{
return VFW_E_NOT_FOUND;
}
pSrc = reinterpret_cast<MultiVMR9_VideoSource*>(dwID);
if( !pSrc )
{
return VFW_E_NOT_FOUND;
}
// check that Tag is VIDEO_SOURCE_TAG and we do not have pSrc filled with trash
if( VIDEO_SOURCE_TAG != pSrc->dwTag )
{
return VFW_E_NOT_FOUND;
}
// ok, pSrc is a valid MultiVMR9_VideoSource; copy it to source
*ppsource = pSrc;
return S_OK;
}
****************************Public*Routine******************************\
RenderThreadProc_
spins off rendering thread
\*************************************************************************
HRESULT CMultiVMR9Wizard::StartRenderingThread_()
{
HANDLE hThread = NULL;
DWORD tid = NULL;
if( !m_bInitialized || m_RenderThreadStatus != eNotStarted )
{
::DbgMsg("CMultiVMR9Wizard::StartRenderingThread_: function called when wizard is not initialized or "\
"render thread is already running / closed");
return VFW_E_WRONG_STATE;
}
// since we initialized successfully, spin off rendering thread
hThread = CreateThread( NULL,
NULL,
RenderThreadProc_,
this,
NULL,
&tid);
if( INVALID_HANDLE_VALUE == hThread )
{
::DbgMsg("CMultiVMR9Wizard::Initialize: failed to create rendering thread");
return E_UNEXPECTED;
}
m_RenderThreadStatus = eRunning;
return S_OK;
}
****************************Public*Routine******************************\
RenderThreadProc_
fires the end of the rendering thread and waits untils render thread closes
\*************************************************************************
HRESULT CMultiVMR9Wizard::StopRenderingThread_()
{
if( m_RenderThreadStatus != eRunning )
{
return S_FALSE;
}
m_RenderThreadStatus = eWaitingToStop;
while( m_RenderThreadStatus != eFinished )
{
Sleep(50);
}
return S_OK;
}
****************************Public*Routine******************************\
RenderThreadProc_
ThreadProc processing rendering of the Render Engine: calls for Render
\*************************************************************************
DWORD WINAPI CMultiVMR9Wizard::RenderThreadProc_( LPVOID lpParameter )
{
CMultiVMR9Wizard* This = NULL;
IMultiVMR9RenderEngine* pRenderEngine = NULL;
HRESULT hr = S_OK;
This = (CMultiVMR9Wizard*)lpParameter;
if( !This )
{
::DbgMsg("CMultiVMR9RenderEngine::RenderThreadProc: parameter is NULL");
return 0;
}
This->AddRef();
hr = This->GetRenderEngine( &pRenderEngine );
if( FAILED(hr) || !pRenderEngine )
{
::DbgMsg("CMultiVMR9RenderEngine::RenderThreadProc: cannot find IMultiVMR9RenderEngine");
RELEASE( pRenderEngine );
RELEASE( This );
return 0;
}
while ( true )
{
hr = pRenderEngine->Render();
// check if we have to exit
{
CAutoLock Lock(&(This->m_ObjectLock));
if( eWaitingToStop == This->m_RenderThreadStatus )
{
break;
}
}
Sleep(10);
} // while true
{
CAutoLock Lock(&(This->m_ObjectLock));
This->m_RenderThreadStatus = eFinished;
}
RELEASE( This );
RELEASE( pRenderEngine );
return 0;
}
(C) Æliens
20/2/2008
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.