topical media & game development

talk show tell print

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.