//------------------------------------------------------------------------------ // 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 #ifdef _BUILD_FROM_VS #include #endif #include 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::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::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; dwSRelease(); 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::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(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; }