home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / BaseClasses / ctlutil.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  55.3 KB  |  2,379 lines

  1. //------------------------------------------------------------------------------
  2. // File: CtlUtil.cpp
  3. //
  4. // Desc: DirectShow base classes.
  5. //
  6. // Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.
  7. //------------------------------------------------------------------------------
  8.  
  9.  
  10. // Base classes implementing IDispatch parsing for the basic control dual
  11. // interfaces. Derive from these and implement just the custom method and
  12. // property methods. We also implement CPosPassThru that can be used by
  13. // renderers and transforms to pass by IMediaPosition and IMediaSeeking
  14.  
  15.  
  16. #include <streams.h>
  17. #include <limits.h>
  18. #include "seekpt.h"
  19.  
  20. // 'bool' non standard reserved word
  21. #pragma warning(disable:4237)
  22.  
  23.  
  24. // --- CBaseDispatch implementation ----------
  25. CBaseDispatch::~CBaseDispatch() {
  26.     if(m_pti) {
  27.         m_pti->Release();
  28.     }
  29. }
  30.  
  31.  
  32. // return 1 if we support GetTypeInfo
  33.  
  34. STDMETHODIMP
  35. CBaseDispatch::GetTypeInfoCount(UINT * pctinfo) {
  36.     CheckPointer(pctinfo,E_POINTER);
  37.     ValidateReadWritePtr(pctinfo,sizeof(UINT *));
  38.     *pctinfo = 1;
  39.     return S_OK;
  40. }
  41.  
  42.  
  43. typedef HRESULT(STDAPICALLTYPE *LPLOADTYPELIB)(
  44.                 const OLECHAR FAR *szFile,
  45.                 ITypeLib FAR* FAR* pptlib);
  46.  
  47. typedef HRESULT(STDAPICALLTYPE *LPLOADREGTYPELIB)(REFGUID rguid,
  48.                 WORD wVerMajor,
  49.                 WORD wVerMinor,
  50.                 LCID lcid,
  51.                 ITypeLib FAR* FAR* pptlib);
  52.  
  53. // attempt to find our type library
  54.  
  55. STDMETHODIMP
  56. CBaseDispatch::GetTypeInfo(
  57.   REFIID riid,
  58.   UINT itinfo,
  59.   LCID lcid,
  60.   ITypeInfo ** pptinfo) {
  61.     CheckPointer(pptinfo,E_POINTER);
  62.     ValidateReadWritePtr(pptinfo,sizeof(ITypeInfo *));
  63.     HRESULT hr;
  64.  
  65.     *pptinfo = NULL;
  66.  
  67.     // we only support one type element
  68.     if(0 != itinfo) {
  69.         return TYPE_E_ELEMENTNOTFOUND;
  70.     }
  71.  
  72.     // always look for neutral
  73.     if(NULL == m_pti) {
  74.  
  75.         LPLOADTYPELIB       lpfnLoadTypeLib;
  76.         LPLOADREGTYPELIB    lpfnLoadRegTypeLib;
  77.         ITypeLib        *ptlib;
  78.         HINSTANCE       hInst;
  79.  
  80.         static const char  szTypeLib[]    = "LoadTypeLib";
  81.         static const char  szRegTypeLib[] = "LoadRegTypeLib";
  82.         static const WCHAR szControl[]    = L"control.tlb";
  83.  
  84.         //
  85.         // Try to get the Ole32Aut.dll module handle.
  86.         //
  87.  
  88.         hInst = LoadOLEAut32();
  89.         if(hInst == NULL) {
  90.             DWORD dwError = GetLastError();
  91.             return AmHresultFromWin32(dwError);
  92.         }
  93.         lpfnLoadRegTypeLib = (LPLOADREGTYPELIB)GetProcAddress(hInst,
  94.             szRegTypeLib);
  95.         if(lpfnLoadRegTypeLib == NULL) {
  96.             DWORD dwError = GetLastError();
  97.             return AmHresultFromWin32(dwError);
  98.         }
  99.  
  100.         hr = (*lpfnLoadRegTypeLib)(LIBID_QuartzTypeLib, 1, 0, // version 1.0
  101.             lcid, &ptlib);
  102.  
  103.         if(FAILED(hr)) {
  104.  
  105.             // attempt to load directly - this will fill the
  106.             // registry in if it finds it
  107.  
  108.             lpfnLoadTypeLib = (LPLOADTYPELIB)GetProcAddress(hInst, szTypeLib);
  109.             if(lpfnLoadTypeLib == NULL) {
  110.                 DWORD dwError = GetLastError();
  111.                 return AmHresultFromWin32(dwError);
  112.             }
  113.  
  114.             hr = (*lpfnLoadTypeLib)(szControl, &ptlib);
  115.             if(FAILED(hr)) {
  116.                 return hr;
  117.             }
  118.         }
  119.  
  120.         hr = ptlib->GetTypeInfoOfGuid(riid, &m_pti);
  121.  
  122.         ptlib->Release();
  123.  
  124.         if(FAILED(hr)) {
  125.             return hr;
  126.         }
  127.     }
  128.  
  129.     *pptinfo = m_pti;
  130.     m_pti->AddRef();
  131.     return S_OK;
  132. }
  133.  
  134.  
  135. STDMETHODIMP
  136. CBaseDispatch::GetIDsOfNames(
  137.   REFIID riid,
  138.   OLECHAR  ** rgszNames,
  139.   UINT cNames,
  140.   LCID lcid,
  141.   DISPID * rgdispid) {
  142.     // although the IDispatch riid is dead, we use this to pass from
  143.     // the interface implementation class to us the iid we are talking about.
  144.  
  145.     ITypeInfo * pti;
  146.     HRESULT hr = GetTypeInfo(riid, 0, lcid, &pti);
  147.  
  148.     if(SUCCEEDED(hr)) {
  149.         hr = pti->GetIDsOfNames(rgszNames, cNames, rgdispid);
  150.  
  151.         pti->Release();
  152.     }
  153.     return hr;
  154. }
  155.  
  156.  
  157. // --- CMediaControl implementation ---------
  158.  
  159. CMediaControl::CMediaControl(const TCHAR * name,LPUNKNOWN pUnk) :
  160.     CUnknown(name, pUnk) {
  161. }
  162.  
  163. // expose our interfaces IMediaControl and IUnknown
  164.  
  165. STDMETHODIMP
  166. CMediaControl::NonDelegatingQueryInterface(REFIID riid, void **ppv) {
  167.     ValidateReadWritePtr(ppv,sizeof(PVOID));
  168.     if(riid == IID_IMediaControl) {
  169.         return GetInterface((IMediaControl *) this, ppv);
  170.     }
  171.     else {
  172.         return CUnknown::NonDelegatingQueryInterface(riid, ppv);
  173.     }
  174. }
  175.  
  176.  
  177. // return 1 if we support GetTypeInfo
  178.  
  179. STDMETHODIMP
  180. CMediaControl::GetTypeInfoCount(UINT * pctinfo) {
  181.     return m_basedisp.GetTypeInfoCount(pctinfo);
  182. }
  183.  
  184.  
  185. // attempt to find our type library
  186.  
  187. STDMETHODIMP
  188. CMediaControl::GetTypeInfo(
  189.   UINT itinfo,
  190.   LCID lcid,
  191.   ITypeInfo ** pptinfo) {
  192.     return m_basedisp.GetTypeInfo(IID_IMediaControl,
  193.         itinfo,
  194.         lcid,
  195.         pptinfo);
  196. }
  197.  
  198.  
  199. STDMETHODIMP
  200. CMediaControl::GetIDsOfNames(
  201.   REFIID riid,
  202.   OLECHAR  ** rgszNames,
  203.   UINT cNames,
  204.   LCID lcid,
  205.   DISPID * rgdispid) {
  206.     return m_basedisp.GetIDsOfNames(IID_IMediaControl,
  207.         rgszNames,
  208.         cNames,
  209.         lcid,
  210.         rgdispid);
  211. }
  212.  
  213.  
  214. STDMETHODIMP
  215. CMediaControl::Invoke(
  216.   DISPID dispidMember,
  217.   REFIID riid,
  218.   LCID lcid,
  219.   WORD wFlags,
  220.   DISPPARAMS * pdispparams,
  221.   VARIANT * pvarResult,
  222.   EXCEPINFO * pexcepinfo,
  223.   UINT * puArgErr) {
  224.     // this parameter is a dead leftover from an earlier interface
  225.     if(IID_NULL != riid) {
  226.         return DISP_E_UNKNOWNINTERFACE;
  227.     }
  228.  
  229.     ITypeInfo * pti;
  230.     HRESULT hr = GetTypeInfo(0, lcid, &pti);
  231.  
  232.     if(FAILED(hr)) {
  233.         return hr;
  234.     }
  235.  
  236.     hr = pti->Invoke((IMediaControl *)this,
  237.         dispidMember,
  238.         wFlags,
  239.         pdispparams,
  240.         pvarResult,
  241.         pexcepinfo,
  242.         puArgErr);
  243.  
  244.     pti->Release();
  245.     return hr;
  246. }
  247.  
  248.  
  249. // --- CMediaEvent implementation ----------
  250.  
  251.  
  252. CMediaEvent::CMediaEvent(const TCHAR * name,LPUNKNOWN pUnk) :
  253.     CUnknown(name, pUnk) {
  254. }
  255.  
  256.  
  257. // expose our interfaces IMediaEvent and IUnknown
  258.  
  259. STDMETHODIMP
  260. CMediaEvent::NonDelegatingQueryInterface(REFIID riid, void **ppv) {
  261.     ValidateReadWritePtr(ppv,sizeof(PVOID));
  262.     if(riid == IID_IMediaEvent || riid == IID_IMediaEventEx) {
  263.         return GetInterface((IMediaEventEx *) this, ppv);
  264.     }
  265.     else {
  266.         return CUnknown::NonDelegatingQueryInterface(riid, ppv);
  267.     }
  268. }
  269.  
  270.  
  271. // return 1 if we support GetTypeInfo
  272.  
  273. STDMETHODIMP
  274. CMediaEvent::GetTypeInfoCount(UINT * pctinfo) {
  275.     return m_basedisp.GetTypeInfoCount(pctinfo);
  276. }
  277.  
  278.  
  279. // attempt to find our type library
  280.  
  281. STDMETHODIMP
  282. CMediaEvent::GetTypeInfo(
  283.   UINT itinfo,
  284.   LCID lcid,
  285.   ITypeInfo ** pptinfo) {
  286.     return m_basedisp.GetTypeInfo(IID_IMediaEvent,
  287.         itinfo,
  288.         lcid,
  289.         pptinfo);
  290. }
  291.  
  292.  
  293. STDMETHODIMP
  294. CMediaEvent::GetIDsOfNames(
  295.   REFIID riid,
  296.   OLECHAR  ** rgszNames,
  297.   UINT cNames,
  298.   LCID lcid,
  299.   DISPID * rgdispid) {
  300.     return m_basedisp.GetIDsOfNames(IID_IMediaEvent,
  301.         rgszNames,
  302.         cNames,
  303.         lcid,
  304.         rgdispid);
  305. }
  306.  
  307.  
  308. STDMETHODIMP
  309. CMediaEvent::Invoke(
  310.   DISPID dispidMember,
  311.   REFIID riid,
  312.   LCID lcid,
  313.   WORD wFlags,
  314.   DISPPARAMS * pdispparams,
  315.   VARIANT * pvarResult,
  316.   EXCEPINFO * pexcepinfo,
  317.   UINT * puArgErr) {
  318.     // this parameter is a dead leftover from an earlier interface
  319.     if(IID_NULL != riid) {
  320.         return DISP_E_UNKNOWNINTERFACE;
  321.     }
  322.  
  323.     ITypeInfo * pti;
  324.     HRESULT hr = GetTypeInfo(0, lcid, &pti);
  325.  
  326.     if(FAILED(hr)) {
  327.         return hr;
  328.     }
  329.  
  330.     hr = pti->Invoke((IMediaEvent *)this,
  331.         dispidMember,
  332.         wFlags,
  333.         pdispparams,
  334.         pvarResult,
  335.         pexcepinfo,
  336.         puArgErr);
  337.  
  338.     pti->Release();
  339.     return hr;
  340. }
  341.  
  342.  
  343. // --- CMediaPosition implementation ----------
  344.  
  345.  
  346. CMediaPosition::CMediaPosition(const TCHAR * name,LPUNKNOWN pUnk) :
  347.     CUnknown(name, pUnk) {
  348. }
  349.  
  350. CMediaPosition::CMediaPosition(const TCHAR * name,
  351.                                LPUNKNOWN pUnk,
  352.                                HRESULT * phr) :
  353.     CUnknown(name, pUnk) {
  354.     UNREFERENCED_PARAMETER(phr);
  355. }
  356.  
  357.  
  358. // expose our interfaces IMediaPosition and IUnknown
  359.  
  360. STDMETHODIMP
  361. CMediaPosition::NonDelegatingQueryInterface(REFIID riid, void **ppv) {
  362.     ValidateReadWritePtr(ppv,sizeof(PVOID));
  363.     if(riid == IID_IMediaPosition) {
  364.         return GetInterface((IMediaPosition *) this, ppv);
  365.     }
  366.     else {
  367.         return CUnknown::NonDelegatingQueryInterface(riid, ppv);
  368.     }
  369. }
  370.  
  371.  
  372. // return 1 if we support GetTypeInfo
  373.  
  374. STDMETHODIMP
  375. CMediaPosition::GetTypeInfoCount(UINT * pctinfo) {
  376.     return m_basedisp.GetTypeInfoCount(pctinfo);
  377. }
  378.  
  379.  
  380. // attempt to find our type library
  381.  
  382. STDMETHODIMP
  383. CMediaPosition::GetTypeInfo(
  384.   UINT itinfo,
  385.   LCID lcid,
  386.   ITypeInfo ** pptinfo) {
  387.     return m_basedisp.GetTypeInfo(IID_IMediaPosition,
  388.         itinfo,
  389.         lcid,
  390.         pptinfo);
  391. }
  392.  
  393.  
  394. STDMETHODIMP
  395. CMediaPosition::GetIDsOfNames(
  396.   REFIID riid,
  397.   OLECHAR  ** rgszNames,
  398.   UINT cNames,
  399.   LCID lcid,
  400.   DISPID * rgdispid) {
  401.     return m_basedisp.GetIDsOfNames(IID_IMediaPosition,
  402.         rgszNames,
  403.         cNames,
  404.         lcid,
  405.         rgdispid);
  406. }
  407.  
  408.  
  409. STDMETHODIMP
  410. CMediaPosition::Invoke(
  411.   DISPID dispidMember,
  412.   REFIID riid,
  413.   LCID lcid,
  414.   WORD wFlags,
  415.   DISPPARAMS * pdispparams,
  416.   VARIANT * pvarResult,
  417.   EXCEPINFO * pexcepinfo,
  418.   UINT * puArgErr) {
  419.     // this parameter is a dead leftover from an earlier interface
  420.     if(IID_NULL != riid) {
  421.         return DISP_E_UNKNOWNINTERFACE;
  422.     }
  423.  
  424.     ITypeInfo * pti;
  425.     HRESULT hr = GetTypeInfo(0, lcid, &pti);
  426.  
  427.     if(FAILED(hr)) {
  428.         return hr;
  429.     }
  430.  
  431.     hr = pti->Invoke((IMediaPosition *)this,
  432.         dispidMember,
  433.         wFlags,
  434.         pdispparams,
  435.         pvarResult,
  436.         pexcepinfo,
  437.         puArgErr);
  438.  
  439.     pti->Release();
  440.     return hr;
  441. }
  442.  
  443.  
  444. // --- IMediaPosition and IMediaSeeking pass through class ----------
  445.  
  446.  
  447. CPosPassThru::CPosPassThru(const TCHAR *pName,
  448.                LPUNKNOWN pUnk,
  449.                HRESULT *phr,
  450.                IPin *pPin) :
  451.     CMediaPosition(pName,pUnk),
  452.     m_pPin(pPin) {
  453.     if(pPin == NULL) {
  454.         *phr = E_POINTER;
  455.         return;
  456.     }
  457. }
  458.  
  459.  
  460. // Expose our IMediaSeeking and IMediaPosition interfaces
  461.  
  462. STDMETHODIMP
  463. CPosPassThru::NonDelegatingQueryInterface(REFIID riid,void **ppv) {
  464.     CheckPointer(ppv,E_POINTER);
  465.     *ppv = NULL;
  466.  
  467.     if(riid == IID_IMediaSeeking) {
  468.         return GetInterface(static_cast<IMediaSeeking *>(this), ppv);
  469.     }
  470.     return CMediaPosition::NonDelegatingQueryInterface(riid,ppv);
  471. }
  472.  
  473.  
  474. // Return the IMediaPosition interface from our peer
  475.  
  476. HRESULT
  477. CPosPassThru::GetPeer(IMediaPosition ** ppMP) {
  478.     *ppMP = NULL;
  479.  
  480.     IPin *pConnected;
  481.     HRESULT hr = m_pPin->ConnectedTo(&pConnected);
  482.     if(FAILED(hr)) {
  483.         return E_NOTIMPL;
  484.     }
  485.  
  486.     IMediaPosition * pMP;
  487.     hr = pConnected->QueryInterface(IID_IMediaPosition, (void **) &pMP);
  488.     pConnected->Release();
  489.     if(FAILED(hr)) {
  490.         return E_NOTIMPL;
  491.     }
  492.  
  493.     *ppMP = pMP;
  494.     return S_OK;
  495. }
  496.  
  497.  
  498. // Return the IMediaSeeking interface from our peer
  499.  
  500. HRESULT
  501. CPosPassThru::GetPeerSeeking(IMediaSeeking ** ppMS) {
  502.     *ppMS = NULL;
  503.  
  504.     IPin *pConnected;
  505.     HRESULT hr = m_pPin->ConnectedTo(&pConnected);
  506.     if(FAILED(hr)) {
  507.         return E_NOTIMPL;
  508.     }
  509.  
  510.     IMediaSeeking * pMS;
  511.     hr = pConnected->QueryInterface(IID_IMediaSeeking, (void **) &pMS);
  512.     pConnected->Release();
  513.     if(FAILED(hr)) {
  514.         return E_NOTIMPL;
  515.     }
  516.  
  517.     *ppMS = pMS;
  518.     return S_OK;
  519. }
  520.  
  521.  
  522. // --- IMediaSeeking methods ----------
  523.  
  524.  
  525. STDMETHODIMP
  526. CPosPassThru::GetCapabilities(DWORD * pCaps) {
  527.     IMediaSeeking* pMS;
  528.     HRESULT hr = GetPeerSeeking(&pMS);
  529.     if(FAILED(hr)) {
  530.         return hr;
  531.     }
  532.  
  533.     hr = pMS->GetCapabilities(pCaps);
  534.     pMS->Release();
  535.     return hr;
  536. }
  537.  
  538. STDMETHODIMP
  539. CPosPassThru::CheckCapabilities(DWORD * pCaps) {
  540.     IMediaSeeking* pMS;
  541.     HRESULT hr = GetPeerSeeking(&pMS);
  542.     if(FAILED(hr)) {
  543.         return hr;
  544.     }
  545.  
  546.     hr = pMS->CheckCapabilities(pCaps);
  547.     pMS->Release();
  548.     return hr;
  549. }
  550.  
  551. STDMETHODIMP
  552. CPosPassThru::IsFormatSupported(const GUID * pFormat) {
  553.     IMediaSeeking* pMS;
  554.     HRESULT hr = GetPeerSeeking(&pMS);
  555.     if(FAILED(hr)) {
  556.         return hr;
  557.     }
  558.  
  559.     hr = pMS->IsFormatSupported(pFormat);
  560.     pMS->Release();
  561.     return hr;
  562. }
  563.  
  564.  
  565. STDMETHODIMP
  566. CPosPassThru::QueryPreferredFormat(GUID *pFormat) {
  567.     IMediaSeeking* pMS;
  568.     HRESULT hr = GetPeerSeeking(&pMS);
  569.     if(FAILED(hr)) {
  570.         return hr;
  571.     }
  572.  
  573.     hr = pMS->QueryPreferredFormat(pFormat);
  574.     pMS->Release();
  575.     return hr;
  576. }
  577.  
  578.  
  579. STDMETHODIMP
  580. CPosPassThru::SetTimeFormat(const GUID * pFormat) {
  581.     IMediaSeeking* pMS;
  582.     HRESULT hr = GetPeerSeeking(&pMS);
  583.     if(FAILED(hr)) {
  584.         return hr;
  585.     }
  586.  
  587.     hr = pMS->SetTimeFormat(pFormat);
  588.     pMS->Release();
  589.     return hr;
  590. }
  591.  
  592.  
  593. STDMETHODIMP
  594. CPosPassThru::GetTimeFormat(GUID *pFormat) {
  595.     IMediaSeeking* pMS;
  596.     HRESULT hr = GetPeerSeeking(&pMS);
  597.     if(FAILED(hr)) {
  598.         return hr;
  599.     }
  600.  
  601.     hr = pMS->GetTimeFormat(pFormat);
  602.     pMS->Release();
  603.     return hr;
  604. }
  605.  
  606.  
  607. STDMETHODIMP
  608. CPosPassThru::IsUsingTimeFormat(const GUID * pFormat) {
  609.     IMediaSeeking* pMS;
  610.     HRESULT hr = GetPeerSeeking(&pMS);
  611.     if(FAILED(hr)) {
  612.         return hr;
  613.     }
  614.  
  615.     hr = pMS->IsUsingTimeFormat(pFormat);
  616.     pMS->Release();
  617.     return hr;
  618. }
  619.  
  620.  
  621. STDMETHODIMP
  622. CPosPassThru::ConvertTimeFormat(LONGLONG * pTarget, const GUID * pTargetFormat,
  623.                 LONGLONG    Source, const GUID * pSourceFormat ) {
  624.     IMediaSeeking* pMS;
  625.     HRESULT hr = GetPeerSeeking(&pMS);
  626.     if(FAILED(hr)) {
  627.         return hr;
  628.     }
  629.  
  630.     hr = pMS->ConvertTimeFormat(pTarget, pTargetFormat, Source, pSourceFormat);
  631.     pMS->Release();
  632.     return hr;
  633. }
  634.  
  635.  
  636. STDMETHODIMP
  637. CPosPassThru::SetPositions( LONGLONG * pCurrent, DWORD CurrentFlags
  638.               , LONGLONG * pStop, DWORD StopFlags ) {
  639.     IMediaSeeking* pMS;
  640.     HRESULT hr = GetPeerSeeking(&pMS);
  641.     if(FAILED(hr)) {
  642.         return hr;
  643.     }
  644.  
  645.     hr = pMS->SetPositions(pCurrent, CurrentFlags, pStop, StopFlags);
  646.     pMS->Release();
  647.     return hr;
  648. }
  649.  
  650. STDMETHODIMP
  651. CPosPassThru::GetPositions(LONGLONG *pCurrent, LONGLONG * pStop) {
  652.     IMediaSeeking* pMS;
  653.     HRESULT hr = GetPeerSeeking(&pMS);
  654.     if(FAILED(hr)) {
  655.         return hr;
  656.     }
  657.  
  658.     hr = pMS->GetPositions(pCurrent,pStop);
  659.     pMS->Release();
  660.     return hr;
  661. }
  662.  
  663. HRESULT
  664. CPosPassThru::GetSeekingLongLong
  665. ( HRESULT(__stdcall IMediaSeeking::*pMethod)( LONGLONG * )
  666. , LONGLONG * pll
  667. ) {
  668.     IMediaSeeking* pMS;
  669.     HRESULT hr = GetPeerSeeking(&pMS);
  670.     if(SUCCEEDED(hr)) {
  671.         hr = (pMS->*pMethod)(pll);
  672.         pMS->Release();
  673.     }
  674.     return hr;
  675. }
  676.  
  677. // If we don't have a current position then ask upstream
  678.  
  679. STDMETHODIMP
  680. CPosPassThru::GetCurrentPosition(LONGLONG *pCurrent) {
  681.     // Can we report the current position
  682.     HRESULT hr = GetMediaTime(pCurrent,NULL);
  683.     if(SUCCEEDED(hr)) 
  684.         hr = NOERROR;
  685.     else 
  686.         hr = GetSeekingLongLong(&IMediaSeeking::GetCurrentPosition, pCurrent);
  687.     return hr;
  688. }
  689.  
  690.  
  691. STDMETHODIMP
  692. CPosPassThru::GetStopPosition(LONGLONG *pStop) {
  693.     return GetSeekingLongLong(&IMediaSeeking::GetStopPosition, pStop);;
  694. }
  695.  
  696. STDMETHODIMP
  697. CPosPassThru::GetDuration(LONGLONG *pDuration) {
  698.     return GetSeekingLongLong(&IMediaSeeking::GetDuration, pDuration);;
  699. }
  700.  
  701.  
  702. STDMETHODIMP
  703. CPosPassThru::GetPreroll(LONGLONG *pllPreroll) {
  704.     return GetSeekingLongLong(&IMediaSeeking::GetPreroll, pllPreroll);;
  705. }
  706.  
  707.  
  708. STDMETHODIMP
  709. CPosPassThru::GetAvailable( LONGLONG *pEarliest, LONGLONG *pLatest ) {
  710.     IMediaSeeking* pMS;
  711.     HRESULT hr = GetPeerSeeking(&pMS);
  712.     if(FAILED(hr)) {
  713.         return hr;
  714.     }
  715.  
  716.     hr = pMS->GetAvailable(pEarliest, pLatest);
  717.     pMS->Release();
  718.     return hr;
  719. }
  720.  
  721.  
  722. STDMETHODIMP
  723. CPosPassThru::GetRate(double * pdRate) {
  724.     IMediaSeeking* pMS;
  725.     HRESULT hr = GetPeerSeeking(&pMS);
  726.     if(FAILED(hr)) {
  727.         return hr;
  728.     }
  729.     hr = pMS->GetRate(pdRate);
  730.     pMS->Release();
  731.     return hr;
  732. }
  733.  
  734.  
  735. STDMETHODIMP
  736. CPosPassThru::SetRate(double dRate) {
  737.     if(0.0 == dRate) {
  738.         return E_INVALIDARG;
  739.     }
  740.  
  741.     IMediaSeeking* pMS;
  742.     HRESULT hr = GetPeerSeeking(&pMS);
  743.     if(FAILED(hr)) {
  744.         return hr;
  745.     }
  746.     hr = pMS->SetRate(dRate);
  747.     pMS->Release();
  748.     return hr;
  749. }
  750.  
  751.  
  752.  
  753.  
  754. // --- IMediaPosition methods ----------
  755.  
  756.  
  757. STDMETHODIMP
  758. CPosPassThru::get_Duration(REFTIME * plength) {
  759.     IMediaPosition* pMP;
  760.     HRESULT hr = GetPeer(&pMP);
  761.     if(FAILED(hr)) {
  762.         return hr;
  763.     }
  764.  
  765.     hr = pMP->get_Duration(plength);
  766.     pMP->Release();
  767.     return hr;
  768. }
  769.  
  770.  
  771. STDMETHODIMP
  772. CPosPassThru::get_CurrentPosition(REFTIME * pllTime) {
  773.     IMediaPosition* pMP;
  774.     HRESULT hr = GetPeer(&pMP);
  775.     if(FAILED(hr)) {
  776.         return hr;
  777.     }
  778.     hr = pMP->get_CurrentPosition(pllTime);
  779.     pMP->Release();
  780.     return hr;
  781. }
  782.  
  783.  
  784. STDMETHODIMP
  785. CPosPassThru::put_CurrentPosition(REFTIME llTime) {
  786.     IMediaPosition* pMP;
  787.     HRESULT hr = GetPeer(&pMP);
  788.     if(FAILED(hr)) {
  789.         return hr;
  790.     }
  791.     hr = pMP->put_CurrentPosition(llTime);
  792.     pMP->Release();
  793.     return hr;
  794. }
  795.  
  796.  
  797. STDMETHODIMP
  798. CPosPassThru::get_StopTime(REFTIME * pllTime) {
  799.     IMediaPosition* pMP;
  800.     HRESULT hr = GetPeer(&pMP);
  801.     if(FAILED(hr)) {
  802.         return hr;
  803.     }
  804.     hr = pMP->get_StopTime(pllTime);
  805.     pMP->Release();
  806.     return hr;
  807. }
  808.  
  809.  
  810. STDMETHODIMP
  811. CPosPassThru::put_StopTime(REFTIME llTime) {
  812.     IMediaPosition* pMP;
  813.     HRESULT hr = GetPeer(&pMP);
  814.     if(FAILED(hr)) {
  815.         return hr;
  816.     }
  817.     hr = pMP->put_StopTime(llTime);
  818.     pMP->Release();
  819.     return hr;
  820. }
  821.  
  822.  
  823. STDMETHODIMP
  824. CPosPassThru::get_PrerollTime(REFTIME * pllTime) {
  825.     IMediaPosition* pMP;
  826.     HRESULT hr = GetPeer(&pMP);
  827.     if(FAILED(hr)) {
  828.         return hr;
  829.     }
  830.     hr = pMP->get_PrerollTime(pllTime);
  831.     pMP->Release();
  832.     return hr;
  833. }
  834.  
  835.  
  836. STDMETHODIMP
  837. CPosPassThru::put_PrerollTime(REFTIME llTime) {
  838.     IMediaPosition* pMP;
  839.     HRESULT hr = GetPeer(&pMP);
  840.     if(FAILED(hr)) {
  841.         return hr;
  842.     }
  843.     hr = pMP->put_PrerollTime(llTime);
  844.     pMP->Release();
  845.     return hr;
  846. }
  847.  
  848.  
  849. STDMETHODIMP
  850. CPosPassThru::get_Rate(double * pdRate) {
  851.     IMediaPosition* pMP;
  852.     HRESULT hr = GetPeer(&pMP);
  853.     if(FAILED(hr)) {
  854.         return hr;
  855.     }
  856.     hr = pMP->get_Rate(pdRate);
  857.     pMP->Release();
  858.     return hr;
  859. }
  860.  
  861.  
  862. STDMETHODIMP
  863. CPosPassThru::put_Rate(double dRate) {
  864.     if(0.0 == dRate) {
  865.         return E_INVALIDARG;
  866.     }
  867.  
  868.     IMediaPosition* pMP;
  869.     HRESULT hr = GetPeer(&pMP);
  870.     if(FAILED(hr)) {
  871.         return hr;
  872.     }
  873.     hr = pMP->put_Rate(dRate);
  874.     pMP->Release();
  875.     return hr;
  876. }
  877.  
  878.  
  879. STDMETHODIMP
  880. CPosPassThru::CanSeekForward(LONG *pCanSeekForward) {
  881.     IMediaPosition* pMP;
  882.     HRESULT hr = GetPeer(&pMP);
  883.     if(FAILED(hr)) {
  884.         return hr;
  885.     }
  886.     hr = pMP->CanSeekForward(pCanSeekForward);
  887.     pMP->Release();
  888.     return hr;
  889. }
  890.  
  891.  
  892. STDMETHODIMP
  893. CPosPassThru::CanSeekBackward(LONG *pCanSeekBackward) {
  894.     IMediaPosition* pMP;
  895.     HRESULT hr = GetPeer(&pMP);
  896.     if(FAILED(hr)) {
  897.         return hr;
  898.     }
  899.     hr = pMP->CanSeekBackward(pCanSeekBackward);
  900.     pMP->Release();
  901.     return hr;
  902. }
  903.  
  904.  
  905. // --- Implements the CRendererPosPassThru class ----------
  906.  
  907.  
  908. // Media times (eg current frame, field, sample etc) are passed through the
  909. // filtergraph in media samples. When a renderer gets a sample with media
  910. // times in it, it will call one of the RegisterMediaTime methods we expose
  911. // (one takes an IMediaSample, the other takes the media times direct). We
  912. // store the media times internally and return them in GetCurrentPosition.
  913.  
  914. CRendererPosPassThru::CRendererPosPassThru(const TCHAR *pName,
  915.                        LPUNKNOWN pUnk,
  916.                        HRESULT *phr,
  917.                        IPin *pPin) :
  918.     CPosPassThru(pName,pUnk,phr,pPin),
  919.     m_StartMedia(0),
  920.     m_EndMedia(0),
  921.     m_bReset(TRUE) {
  922. }
  923.  
  924.  
  925. // Sets the media times the object should report
  926.  
  927. HRESULT
  928. CRendererPosPassThru::RegisterMediaTime(IMediaSample *pMediaSample) {
  929.     ASSERT(pMediaSample);
  930.     LONGLONG StartMedia;
  931.     LONGLONG EndMedia;
  932.  
  933.     CAutoLock cAutoLock(&m_PositionLock);
  934.  
  935.     // Get the media times from the sample
  936.  
  937.     HRESULT hr = pMediaSample->GetTime(&StartMedia,&EndMedia);
  938.     if(FAILED(hr)) {
  939.         ASSERT(hr == VFW_E_SAMPLE_TIME_NOT_SET);
  940.         return hr;
  941.     }
  942.  
  943.     m_StartMedia = StartMedia;
  944.     m_EndMedia = EndMedia;
  945.     m_bReset = FALSE;
  946.     return NOERROR;
  947. }
  948.  
  949.  
  950. // Sets the media times the object should report
  951.  
  952. HRESULT
  953. CRendererPosPassThru::RegisterMediaTime(LONGLONG StartTime,LONGLONG EndTime) {
  954.     CAutoLock cAutoLock(&m_PositionLock);
  955.     m_StartMedia = StartTime;
  956.     m_EndMedia = EndTime;
  957.     m_bReset = FALSE;
  958.     return NOERROR;
  959. }
  960.  
  961.  
  962. // Return the current media times registered in the object
  963.  
  964. HRESULT
  965. CRendererPosPassThru::GetMediaTime(LONGLONG *pStartTime,LONGLONG *pEndTime) {
  966.     ASSERT(pStartTime);
  967.  
  968.     CAutoLock cAutoLock(&m_PositionLock);
  969.     if(m_bReset == TRUE) {
  970.         return E_FAIL;
  971.     }
  972.  
  973.     // We don't have to return the end time
  974.  
  975.     HRESULT hr = ConvertTimeFormat(pStartTime, 0, m_StartMedia, &TIME_FORMAT_MEDIA_TIME);
  976.     if(pEndTime && SUCCEEDED(hr)) {
  977.         hr = ConvertTimeFormat(pEndTime, 0, m_EndMedia, &TIME_FORMAT_MEDIA_TIME);
  978.     }
  979.     return hr;
  980. }
  981.  
  982.  
  983. // Resets the media times we hold
  984.  
  985. HRESULT
  986. CRendererPosPassThru::ResetMediaTime() {
  987.     CAutoLock cAutoLock(&m_PositionLock);
  988.     m_StartMedia = 0;
  989.     m_EndMedia = 0;
  990.     m_bReset = TRUE;
  991.     return NOERROR;
  992. }
  993.  
  994. // Intended to be called by the owing filter during EOS processing so
  995. // that the media times can be adjusted to the stop time.  This ensures
  996. // that the GetCurrentPosition will actully get to the stop position.
  997. HRESULT
  998. CRendererPosPassThru::EOS() {
  999.     HRESULT hr;
  1000.  
  1001.     if(m_bReset == TRUE) hr = E_FAIL;
  1002.     else {
  1003.         LONGLONG llStop;
  1004.         if (SUCCEEDED(hr=GetStopPosition(&llStop))) {
  1005.             CAutoLock cAutoLock(&m_PositionLock);
  1006.             m_StartMedia = m_EndMedia   = llStop;
  1007.         }
  1008.     }
  1009.     return hr;
  1010. }
  1011.  
  1012. // -- CSourceSeeking implementation ------------
  1013.  
  1014. CSourceSeeking::CSourceSeeking(
  1015.     const TCHAR * pName,
  1016.     LPUNKNOWN pUnk,
  1017.     HRESULT* phr,
  1018.     CCritSec * pLock) :
  1019.         CUnknown(pName, pUnk),
  1020.         m_pLock(pLock),
  1021.         m_rtStart((long)0) {
  1022.     m_rtStop = _I64_MAX / 2;
  1023.     m_rtDuration = m_rtStop;
  1024.     m_dRateSeeking = 1.0;
  1025.  
  1026.     m_dwSeekingCaps = AM_SEEKING_CanSeekForwards
  1027.         | AM_SEEKING_CanSeekBackwards
  1028.         | AM_SEEKING_CanSeekAbsolute
  1029.         | AM_SEEKING_CanGetStopPos
  1030.         | AM_SEEKING_CanGetDuration;
  1031. }
  1032.  
  1033. HRESULT CSourceSeeking::NonDelegatingQueryInterface(REFIID riid, void **ppv) {
  1034.     if(riid == IID_IMediaSeeking) {
  1035.         CheckPointer(ppv, E_POINTER);
  1036.         return GetInterface(static_cast<IMediaSeeking *>(this), ppv);
  1037.     }
  1038.     else {
  1039.         return CUnknown::NonDelegatingQueryInterface(riid, ppv);
  1040.     }
  1041. }
  1042.  
  1043.  
  1044. HRESULT CSourceSeeking::IsFormatSupported(const GUID * pFormat) {
  1045.     CheckPointer(pFormat, E_POINTER);
  1046.     // only seeking in time (REFERENCE_TIME units) is supported
  1047.     return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;
  1048. }
  1049.  
  1050. HRESULT CSourceSeeking::QueryPreferredFormat(GUID *pFormat) {
  1051.     CheckPointer(pFormat, E_POINTER);
  1052.     *pFormat = TIME_FORMAT_MEDIA_TIME;
  1053.     return S_OK;
  1054. }
  1055.  
  1056. HRESULT CSourceSeeking::SetTimeFormat(const GUID * pFormat) {
  1057.     CheckPointer(pFormat, E_POINTER);
  1058.  
  1059.     // nothing to set; just check that it's TIME_FORMAT_TIME
  1060.     return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : E_INVALIDARG;
  1061. }
  1062.  
  1063. HRESULT CSourceSeeking::IsUsingTimeFormat(const GUID * pFormat) {
  1064.     CheckPointer(pFormat, E_POINTER);
  1065.     return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;
  1066. }
  1067.  
  1068. HRESULT CSourceSeeking::GetTimeFormat(GUID *pFormat) {
  1069.     CheckPointer(pFormat, E_POINTER);
  1070.     *pFormat = TIME_FORMAT_MEDIA_TIME;
  1071.     return S_OK;
  1072. }
  1073.  
  1074. HRESULT CSourceSeeking::GetDuration(LONGLONG *pDuration) {
  1075.     CheckPointer(pDuration, E_POINTER);
  1076.     CAutoLock lock(m_pLock);
  1077.     *pDuration = m_rtDuration;
  1078.     return S_OK;
  1079. }
  1080.  
  1081. HRESULT CSourceSeeking::GetStopPosition(LONGLONG *pStop) {
  1082.     CheckPointer(pStop, E_POINTER);
  1083.     CAutoLock lock(m_pLock);
  1084.     *pStop = m_rtStop;
  1085.     return S_OK;
  1086. }
  1087.  
  1088. HRESULT CSourceSeeking::GetCurrentPosition(LONGLONG *pCurrent) {
  1089.     // GetCurrentPosition is typically supported only in renderers and
  1090.     // not in source filters.
  1091.     return E_NOTIMPL;
  1092. }
  1093.  
  1094. HRESULT CSourceSeeking::GetCapabilities( DWORD * pCapabilities ) {
  1095.     CheckPointer(pCapabilities, E_POINTER);
  1096.     *pCapabilities = m_dwSeekingCaps;
  1097.     return S_OK;
  1098. }
  1099.  
  1100. HRESULT CSourceSeeking::CheckCapabilities( DWORD * pCapabilities ) {
  1101.     CheckPointer(pCapabilities, E_POINTER);
  1102.  
  1103.     // make sure all requested capabilities are in our mask
  1104.     return (~m_dwSeekingCaps & *pCapabilities) ? S_FALSE : S_OK;
  1105. }
  1106.  
  1107. HRESULT CSourceSeeking::ConvertTimeFormat( LONGLONG * pTarget, const GUID * pTargetFormat,
  1108.                            LONGLONG    Source, const GUID * pSourceFormat ) {
  1109.     CheckPointer(pTarget, E_POINTER);
  1110.     // format guids can be null to indicate current format
  1111.  
  1112.     // since we only support TIME_FORMAT_MEDIA_TIME, we don't really
  1113.     // offer any conversions.
  1114.     if(pTargetFormat == 0 || *pTargetFormat == TIME_FORMAT_MEDIA_TIME) {
  1115.         if(pSourceFormat == 0 || *pSourceFormat == TIME_FORMAT_MEDIA_TIME) {
  1116.             *pTarget = Source;
  1117.             return S_OK;
  1118.         }
  1119.     }
  1120.  
  1121.     return E_INVALIDARG;
  1122. }
  1123.  
  1124.  
  1125. HRESULT CSourceSeeking::SetPositions( LONGLONG * pCurrent,  DWORD CurrentFlags
  1126.                       , LONGLONG * pStop,  DWORD StopFlags ) {
  1127.     DWORD StopPosBits = StopFlags & AM_SEEKING_PositioningBitsMask;
  1128.     DWORD StartPosBits = CurrentFlags & AM_SEEKING_PositioningBitsMask;
  1129.  
  1130.     if(StopFlags) {
  1131.         CheckPointer(pStop, E_POINTER);
  1132.  
  1133.         // accept only relative, incremental, or absolute positioning
  1134.         if(StopPosBits != StopFlags) {
  1135.             return E_INVALIDARG;
  1136.         }
  1137.     }
  1138.  
  1139.     if(CurrentFlags) {
  1140.         CheckPointer(pCurrent, E_POINTER);
  1141.         if(StartPosBits != AM_SEEKING_AbsolutePositioning &&
  1142.             StartPosBits != AM_SEEKING_RelativePositioning) {
  1143.             return E_INVALIDARG;
  1144.         }
  1145.     }
  1146.  
  1147.  
  1148.     // scope for autolock
  1149.     {
  1150.         CAutoLock lock(m_pLock);
  1151.  
  1152.         // set start position
  1153.         if(StartPosBits == AM_SEEKING_AbsolutePositioning) {
  1154.             m_rtStart = *pCurrent;
  1155.         }
  1156.         else if(StartPosBits == AM_SEEKING_RelativePositioning) {
  1157.             m_rtStart += *pCurrent;
  1158.         }
  1159.  
  1160.         // set stop position
  1161.         if(StopPosBits == AM_SEEKING_AbsolutePositioning) {
  1162.             m_rtStop = *pStop;
  1163.         }
  1164.         else if(StopPosBits == AM_SEEKING_IncrementalPositioning) {
  1165.             m_rtStop = m_rtStart + *pStop;
  1166.         }
  1167.         else if(StopPosBits == AM_SEEKING_RelativePositioning) {
  1168.             m_rtStop = m_rtStop + *pStop;
  1169.         }
  1170.     }
  1171.  
  1172.  
  1173.     HRESULT hr = S_OK;
  1174.     if(SUCCEEDED(hr) && StopPosBits) {
  1175.         hr = ChangeStop();
  1176.     }
  1177.     if(StartPosBits) {
  1178.         hr = ChangeStart();
  1179.     }
  1180.  
  1181.     return hr;
  1182. }
  1183.  
  1184.  
  1185. HRESULT CSourceSeeking::GetPositions( LONGLONG * pCurrent, LONGLONG * pStop ) {
  1186.     if(pCurrent) {
  1187.         *pCurrent = m_rtStart;
  1188.     }
  1189.     if(pStop) {
  1190.         *pStop = m_rtStop;
  1191.     }
  1192.  
  1193.     return S_OK;;
  1194. }
  1195.  
  1196.  
  1197. HRESULT CSourceSeeking::GetAvailable( LONGLONG * pEarliest, LONGLONG * pLatest ) {
  1198.     if(pEarliest) {
  1199.         *pEarliest = 0;
  1200.     }
  1201.     if(pLatest) {
  1202.         CAutoLock lock(m_pLock);
  1203.         *pLatest = m_rtDuration;
  1204.     }
  1205.     return S_OK;
  1206. }
  1207.  
  1208. HRESULT CSourceSeeking::SetRate( double dRate) { {
  1209.         CAutoLock lock(m_pLock);
  1210.         m_dRateSeeking = dRate;
  1211.     }
  1212.     return ChangeRate();
  1213. }
  1214.  
  1215. HRESULT CSourceSeeking::GetRate( double * pdRate) {
  1216.     CheckPointer(pdRate, E_POINTER);
  1217.     CAutoLock lock(m_pLock);
  1218.     *pdRate = m_dRateSeeking;
  1219.     return S_OK;
  1220. }
  1221.  
  1222. HRESULT CSourceSeeking::GetPreroll(LONGLONG *pPreroll) {
  1223.     CheckPointer(pPreroll, E_POINTER);
  1224.     *pPreroll = 0;
  1225.     return S_OK;
  1226. }
  1227.  
  1228.  
  1229.  
  1230.  
  1231.  
  1232. // --- CSourcePosition implementation ----------
  1233.  
  1234.  
  1235. CSourcePosition::CSourcePosition(const TCHAR * pName,
  1236.                  LPUNKNOWN pUnk,
  1237.                  HRESULT* phr,
  1238.                  CCritSec * pLock) :
  1239.     CMediaPosition(pName, pUnk),
  1240.     m_pLock(pLock),
  1241.     m_Start(CRefTime((LONGLONG)0)) {
  1242.     m_Stop = _I64_MAX;
  1243.     m_Rate = 1.0;
  1244. }
  1245.  
  1246.  
  1247. STDMETHODIMP
  1248. CSourcePosition::get_Duration(REFTIME * plength) {
  1249.     CheckPointer(plength,E_POINTER);
  1250.     ValidateReadWritePtr(plength,sizeof(REFTIME));
  1251.     CAutoLock lock(m_pLock);
  1252.  
  1253.     *plength = m_Duration;
  1254.     return S_OK;
  1255. }
  1256.  
  1257.  
  1258. STDMETHODIMP
  1259. CSourcePosition::put_CurrentPosition(REFTIME llTime) {
  1260.     m_pLock->Lock();
  1261.     m_Start = llTime;
  1262.     m_pLock->Unlock();
  1263.  
  1264.     return ChangeStart();
  1265. }
  1266.  
  1267.  
  1268. STDMETHODIMP
  1269. CSourcePosition::get_StopTime(REFTIME * pllTime) {
  1270.     CheckPointer(pllTime,E_POINTER);
  1271.     ValidateReadWritePtr(pllTime,sizeof(REFTIME));
  1272.     CAutoLock lock(m_pLock);
  1273.  
  1274.     *pllTime = m_Stop;
  1275.     return S_OK;
  1276. }
  1277.  
  1278.  
  1279. STDMETHODIMP
  1280. CSourcePosition::put_StopTime(REFTIME llTime) {
  1281.     m_pLock->Lock();
  1282.     m_Stop = llTime;
  1283.     m_pLock->Unlock();
  1284.  
  1285.     return ChangeStop();
  1286. }
  1287.  
  1288.  
  1289. STDMETHODIMP
  1290. CSourcePosition::get_PrerollTime(REFTIME * pllTime) {
  1291.     CheckPointer(pllTime,E_POINTER);
  1292.     ValidateReadWritePtr(pllTime,sizeof(REFTIME));
  1293.     return E_NOTIMPL;
  1294. }
  1295.  
  1296.  
  1297. STDMETHODIMP
  1298. CSourcePosition::put_PrerollTime(REFTIME llTime) {
  1299.     return E_NOTIMPL;
  1300. }
  1301.  
  1302.  
  1303. STDMETHODIMP
  1304. CSourcePosition::get_Rate(double * pdRate) {
  1305.     CheckPointer(pdRate,E_POINTER);
  1306.     ValidateReadWritePtr(pdRate,sizeof(double));
  1307.     CAutoLock lock(m_pLock);
  1308.  
  1309.     *pdRate = m_Rate;
  1310.     return S_OK;
  1311. }
  1312.  
  1313.  
  1314. STDMETHODIMP
  1315. CSourcePosition::put_Rate(double dRate) {
  1316.     m_pLock->Lock();
  1317.     m_Rate = dRate;
  1318.     m_pLock->Unlock();
  1319.  
  1320.     return ChangeRate();
  1321. }
  1322.  
  1323.  
  1324. // By default we can seek forwards
  1325.  
  1326. STDMETHODIMP
  1327. CSourcePosition::CanSeekForward(LONG *pCanSeekForward) {
  1328.     CheckPointer(pCanSeekForward,E_POINTER);
  1329.     *pCanSeekForward = OATRUE;
  1330.     return S_OK;
  1331. }
  1332.  
  1333.  
  1334. // By default we can seek backwards
  1335.  
  1336. STDMETHODIMP
  1337. CSourcePosition::CanSeekBackward(LONG *pCanSeekBackward) {
  1338.     CheckPointer(pCanSeekBackward,E_POINTER);
  1339.     *pCanSeekBackward = OATRUE;
  1340.     return S_OK;
  1341. }
  1342.  
  1343.  
  1344. // --- Implementation of CBasicAudio class ----------
  1345.  
  1346.  
  1347. CBasicAudio::CBasicAudio(const TCHAR * pName,LPUNKNOWN punk) :
  1348.     CUnknown(pName, punk) {
  1349. }
  1350.  
  1351. // overriden to publicise our interfaces
  1352.  
  1353. STDMETHODIMP
  1354. CBasicAudio::NonDelegatingQueryInterface(REFIID riid, void **ppv) {
  1355.     ValidateReadWritePtr(ppv,sizeof(PVOID));
  1356.     if(riid == IID_IBasicAudio) {
  1357.         return GetInterface((IBasicAudio *) this, ppv);
  1358.     }
  1359.     else {
  1360.         return CUnknown::NonDelegatingQueryInterface(riid, ppv);
  1361.     }
  1362. }
  1363.  
  1364.  
  1365. STDMETHODIMP
  1366. CBasicAudio::GetTypeInfoCount(UINT * pctinfo) {
  1367.     return m_basedisp.GetTypeInfoCount(pctinfo);
  1368. }
  1369.  
  1370.  
  1371. STDMETHODIMP
  1372. CBasicAudio::GetTypeInfo(
  1373.   UINT itinfo,
  1374.   LCID lcid,
  1375.   ITypeInfo ** pptinfo) {
  1376.     return m_basedisp.GetTypeInfo(IID_IBasicAudio,
  1377.         itinfo,
  1378.         lcid,
  1379.         pptinfo);
  1380. }
  1381.  
  1382.  
  1383. STDMETHODIMP
  1384. CBasicAudio::GetIDsOfNames(
  1385.   REFIID riid,
  1386.   OLECHAR  ** rgszNames,
  1387.   UINT cNames,
  1388.   LCID lcid,
  1389.   DISPID * rgdispid) {
  1390.     return m_basedisp.GetIDsOfNames(IID_IBasicAudio,
  1391.         rgszNames,
  1392.         cNames,
  1393.         lcid,
  1394.         rgdispid);
  1395. }
  1396.  
  1397.  
  1398. STDMETHODIMP
  1399. CBasicAudio::Invoke(
  1400.   DISPID dispidMember,
  1401.   REFIID riid,
  1402.   LCID lcid,
  1403.   WORD wFlags,
  1404.   DISPPARAMS * pdispparams,
  1405.   VARIANT * pvarResult,
  1406.   EXCEPINFO * pexcepinfo,
  1407.   UINT * puArgErr) {
  1408.     // this parameter is a dead leftover from an earlier interface
  1409.     if(IID_NULL != riid) {
  1410.         return DISP_E_UNKNOWNINTERFACE;
  1411.     }
  1412.  
  1413.     ITypeInfo * pti;
  1414.     HRESULT hr = GetTypeInfo(0, lcid, &pti);
  1415.  
  1416.     if(FAILED(hr)) {
  1417.         return hr;
  1418.     }
  1419.  
  1420.     hr = pti->Invoke((IBasicAudio *)this,
  1421.         dispidMember,
  1422.         wFlags,
  1423.         pdispparams,
  1424.         pvarResult,
  1425.         pexcepinfo,
  1426.         puArgErr);
  1427.  
  1428.     pti->Release();
  1429.     return hr;
  1430. }
  1431.  
  1432.  
  1433. // --- IVideoWindow implementation ----------
  1434.  
  1435. CBaseVideoWindow::CBaseVideoWindow(const TCHAR * pName,LPUNKNOWN punk) :
  1436.     CUnknown(pName, punk) {
  1437. }
  1438.  
  1439.  
  1440. // overriden to publicise our interfaces
  1441.  
  1442. STDMETHODIMP
  1443. CBaseVideoWindow::NonDelegatingQueryInterface(REFIID riid, void **ppv) {
  1444.     ValidateReadWritePtr(ppv,sizeof(PVOID));
  1445.     if(riid == IID_IVideoWindow) {
  1446.         return GetInterface((IVideoWindow *) this, ppv);
  1447.     }
  1448.     else {
  1449.         return CUnknown::NonDelegatingQueryInterface(riid, ppv);
  1450.     }
  1451. }
  1452.  
  1453.  
  1454. STDMETHODIMP
  1455. CBaseVideoWindow::GetTypeInfoCount(UINT * pctinfo) {
  1456.     return m_basedisp.GetTypeInfoCount(pctinfo);
  1457. }
  1458.  
  1459.  
  1460. STDMETHODIMP
  1461. CBaseVideoWindow::GetTypeInfo(
  1462.   UINT itinfo,
  1463.   LCID lcid,
  1464.   ITypeInfo ** pptinfo) {
  1465.     return m_basedisp.GetTypeInfo(IID_IVideoWindow,
  1466.         itinfo,
  1467.         lcid,
  1468.         pptinfo);
  1469. }
  1470.  
  1471.  
  1472. STDMETHODIMP
  1473. CBaseVideoWindow::GetIDsOfNames(
  1474.   REFIID riid,
  1475.   OLECHAR  ** rgszNames,
  1476.   UINT cNames,
  1477.   LCID lcid,
  1478.   DISPID * rgdispid) {
  1479.     return m_basedisp.GetIDsOfNames(IID_IVideoWindow,
  1480.         rgszNames,
  1481.         cNames,
  1482.         lcid,
  1483.         rgdispid);
  1484. }
  1485.  
  1486.  
  1487. STDMETHODIMP
  1488. CBaseVideoWindow::Invoke(
  1489.   DISPID dispidMember,
  1490.   REFIID riid,
  1491.   LCID lcid,
  1492.   WORD wFlags,
  1493.   DISPPARAMS * pdispparams,
  1494.   VARIANT * pvarResult,
  1495.   EXCEPINFO * pexcepinfo,
  1496.   UINT * puArgErr) {
  1497.     // this parameter is a dead leftover from an earlier interface
  1498.     if(IID_NULL != riid) {
  1499.         return DISP_E_UNKNOWNINTERFACE;
  1500.     }
  1501.  
  1502.     ITypeInfo * pti;
  1503.     HRESULT hr = GetTypeInfo(0, lcid, &pti);
  1504.  
  1505.     if(FAILED(hr)) {
  1506.         return hr;
  1507.     }
  1508.  
  1509.     hr = pti->Invoke((IVideoWindow *)this,
  1510.         dispidMember,
  1511.         wFlags,
  1512.         pdispparams,
  1513.         pvarResult,
  1514.         pexcepinfo,
  1515.         puArgErr);
  1516.  
  1517.     pti->Release();
  1518.     return hr;
  1519. }
  1520.  
  1521.  
  1522. // --- IBasicVideo implementation ----------
  1523.  
  1524.  
  1525. CBaseBasicVideo::CBaseBasicVideo(const TCHAR * pName,LPUNKNOWN punk) :
  1526.     CUnknown(pName, punk) {
  1527. }
  1528.  
  1529.  
  1530. // overriden to publicise our interfaces
  1531.  
  1532. STDMETHODIMP
  1533. CBaseBasicVideo::NonDelegatingQueryInterface(REFIID riid, void **ppv) {
  1534.     ValidateReadWritePtr(ppv,sizeof(PVOID));
  1535.     if(riid == IID_IBasicVideo || riid == IID_IBasicVideo2) {
  1536.         return GetInterface(static_cast<IBasicVideo2 *>(this), ppv);
  1537.     }
  1538.     else {
  1539.         return CUnknown::NonDelegatingQueryInterface(riid, ppv);
  1540.     }
  1541. }
  1542.  
  1543.  
  1544. STDMETHODIMP
  1545. CBaseBasicVideo::GetTypeInfoCount(UINT * pctinfo) {
  1546.     return m_basedisp.GetTypeInfoCount(pctinfo);
  1547. }
  1548.  
  1549.  
  1550. STDMETHODIMP
  1551. CBaseBasicVideo::GetTypeInfo(
  1552.   UINT itinfo,
  1553.   LCID lcid,
  1554.   ITypeInfo ** pptinfo) {
  1555.     return m_basedisp.GetTypeInfo(IID_IBasicVideo,
  1556.         itinfo,
  1557.         lcid,
  1558.         pptinfo);
  1559. }
  1560.  
  1561.  
  1562. STDMETHODIMP
  1563. CBaseBasicVideo::GetIDsOfNames(
  1564.   REFIID riid,
  1565.   OLECHAR  ** rgszNames,
  1566.   UINT cNames,
  1567.   LCID lcid,
  1568.   DISPID * rgdispid) {
  1569.     return m_basedisp.GetIDsOfNames(IID_IBasicVideo,
  1570.         rgszNames,
  1571.         cNames,
  1572.         lcid,
  1573.         rgdispid);
  1574. }
  1575.  
  1576.  
  1577. STDMETHODIMP
  1578. CBaseBasicVideo::Invoke(
  1579.   DISPID dispidMember,
  1580.   REFIID riid,
  1581.   LCID lcid,
  1582.   WORD wFlags,
  1583.   DISPPARAMS * pdispparams,
  1584.   VARIANT * pvarResult,
  1585.   EXCEPINFO * pexcepinfo,
  1586.   UINT * puArgErr) {
  1587.     // this parameter is a dead leftover from an earlier interface
  1588.     if(IID_NULL != riid) {
  1589.         return DISP_E_UNKNOWNINTERFACE;
  1590.     }
  1591.  
  1592.     ITypeInfo * pti;
  1593.     HRESULT hr = GetTypeInfo(0, lcid, &pti);
  1594.  
  1595.     if(FAILED(hr)) {
  1596.         return hr;
  1597.     }
  1598.  
  1599.     hr = pti->Invoke((IBasicVideo *)this,
  1600.         dispidMember,
  1601.         wFlags,
  1602.         pdispparams,
  1603.         pvarResult,
  1604.         pexcepinfo,
  1605.         puArgErr);
  1606.  
  1607.     pti->Release();
  1608.     return hr;
  1609. }
  1610.  
  1611.  
  1612. // --- Implementation of Deferred Commands ----------
  1613.  
  1614.  
  1615. CDispParams::CDispParams(UINT nArgs, VARIANT* pArgs, HRESULT *phr) {
  1616.     cNamedArgs = 0;
  1617.     rgdispidNamedArgs = NULL;
  1618.     cArgs = nArgs;
  1619.  
  1620.     if(cArgs) {
  1621.         rgvarg = new VARIANT[cArgs];
  1622.         if(NULL == rgvarg) {
  1623.             cArgs = 0;
  1624.             if(phr) {
  1625.                 *phr = E_OUTOFMEMORY;
  1626.             }
  1627.             return;
  1628.         }
  1629.  
  1630.         for(UINT i = 0; i < cArgs; i++) {
  1631.  
  1632.             VARIANT * pDest = &rgvarg[i];
  1633.             VARIANT * pSrc = &pArgs[i];
  1634.  
  1635.             pDest->vt = pSrc->vt;
  1636.             switch(pDest->vt) {
  1637.  
  1638.                 case VT_I4:
  1639.                     pDest->lVal = pSrc->lVal;
  1640.                     break;
  1641.  
  1642.                 case VT_UI1:
  1643.                     pDest->bVal = pSrc->bVal;
  1644.                     break;
  1645.  
  1646.                 case VT_I2:
  1647.                     pDest->iVal = pSrc->iVal;
  1648.                     break;
  1649.  
  1650.                 case VT_R4:
  1651.                     pDest->fltVal = pSrc->fltVal;
  1652.                     break;
  1653.  
  1654.                 case VT_R8:
  1655.                     pDest->dblVal = pSrc->dblVal;
  1656.                     break;
  1657.  
  1658.                 case VT_BOOL:
  1659.                     pDest->boolVal = pSrc->boolVal;
  1660.                     break;
  1661.  
  1662.                 case VT_ERROR:
  1663.                     pDest->scode = pSrc->scode;
  1664.                     break;
  1665.  
  1666.                 case VT_CY:
  1667.                     pDest->cyVal = pSrc->cyVal;
  1668.                     break;
  1669.  
  1670.                 case VT_DATE:
  1671.                     pDest->date = pSrc->date;
  1672.                     break;
  1673.  
  1674.                 case VT_BSTR:
  1675.                     if(pSrc->bstrVal == NULL) {
  1676.                         pDest->bstrVal = NULL;
  1677.                     }
  1678.                     else {
  1679.  
  1680.                             // a BSTR is a WORD followed by a UNICODE string.
  1681.                             // the pointer points just after the WORD
  1682.  
  1683.                             WORD len = * (WORD*) (pSrc->bstrVal - (sizeof(WORD) / sizeof(OLECHAR)));
  1684.                             OLECHAR* pch = new OLECHAR[len + (sizeof(WORD)/sizeof(OLECHAR))];
  1685.                             if(pch) {
  1686.                                 WORD *pui = (WORD*)pch;
  1687.                                 *pui = len;
  1688.                                 pDest->bstrVal = pch + (sizeof(WORD)/sizeof(OLECHAR));
  1689.                                 CopyMemory(pDest->bstrVal, pSrc->bstrVal, len*sizeof(OLECHAR));
  1690.                             }
  1691.                             else {
  1692.                                 cArgs = i;
  1693.                                 if(phr) {
  1694.                                     *phr = E_OUTOFMEMORY;
  1695.                                 }
  1696.                             }
  1697.                     }
  1698.                     pDest->bstrVal = pSrc->bstrVal;
  1699.                     break;
  1700.  
  1701.                 case VT_UNKNOWN:
  1702.                     pDest->punkVal = pSrc->punkVal;
  1703.                     pDest->punkVal->AddRef();
  1704.                     break;
  1705.  
  1706.                 case VT_DISPATCH:
  1707.                     pDest->pdispVal = pSrc->pdispVal;
  1708.                     pDest->pdispVal->AddRef();
  1709.                     break;
  1710.  
  1711.                 default:
  1712.                     // a type we haven't got round to adding yet!
  1713.                     ASSERT(0);
  1714.                     break;
  1715.             }
  1716.         }
  1717.  
  1718.     }
  1719.     else {
  1720.         rgvarg = NULL;
  1721.     }
  1722.  
  1723. }
  1724.  
  1725.  
  1726. CDispParams::~CDispParams() {
  1727.     for(UINT i = 0; i < cArgs; i++) {
  1728.         switch(rgvarg[i].vt) {
  1729.             case VT_BSTR:
  1730.                 if(rgvarg[i].bstrVal != NULL) {
  1731.                     OLECHAR * pch = rgvarg[i].bstrVal - (sizeof(WORD)/sizeof(OLECHAR));
  1732.                     delete pch;
  1733.                 }
  1734.                 break;
  1735.  
  1736.             case VT_UNKNOWN:
  1737.                 rgvarg[i].punkVal->Release();
  1738.                 break;
  1739.  
  1740.             case VT_DISPATCH:
  1741.                 rgvarg[i].pdispVal->Release();
  1742.                 break;
  1743.         }
  1744.     }
  1745.     delete[] rgvarg;
  1746. }
  1747.  
  1748.  
  1749. // lifetime is controlled by refcounts (see defer.h)
  1750.  
  1751. CDeferredCommand::CDeferredCommand(
  1752.     CCmdQueue * pQ,
  1753.     LPUNKNOWN   pUnk,
  1754.     HRESULT *   phr,
  1755.     LPUNKNOWN   pUnkExecutor,
  1756.     REFTIME time,
  1757.     GUID*   iid,
  1758.     long    dispidMethod,
  1759.     short   wFlags,
  1760.     long    nArgs,
  1761.     VARIANT*    pDispParams,
  1762.     VARIANT*    pvarResult,
  1763.     short*  puArgErr,
  1764.     BOOL    bStream
  1765.     ) :
  1766.     CUnknown(NAME("DeferredCommand"), pUnk),
  1767.     m_pQueue(pQ),
  1768.     m_pUnk(pUnkExecutor),
  1769.     m_iid(iid),
  1770.     m_dispidMethod(dispidMethod),
  1771.     m_wFlags(wFlags),
  1772.     m_DispParams(nArgs, pDispParams, phr),
  1773.     m_pvarResult(pvarResult),
  1774.     m_bStream(bStream),
  1775.     m_hrResult(E_ABORT) {
  1776.  
  1777.     // convert REFTIME to REFERENCE_TIME
  1778.     COARefTime convertor(time);
  1779.     m_time = convertor;
  1780.  
  1781.     // no check of time validity - it's ok to queue a command that's
  1782.     // already late
  1783.  
  1784.     // check iid is supportable on pUnk by QueryInterface for it
  1785.     IUnknown * pInterface;
  1786.     HRESULT hr = m_pUnk->QueryInterface(GetIID(), (void**) &pInterface);
  1787.     if(FAILED(hr)) {
  1788.         *phr = hr;
  1789.         return;
  1790.     }
  1791.     pInterface->Release();
  1792.  
  1793.  
  1794.     // !!! check dispidMethod and param/return types using typelib
  1795.     ITypeInfo *pti;
  1796.     hr = m_Dispatch.GetTypeInfo(*iid, 0, 0, &pti);
  1797.     if(FAILED(hr)) {
  1798.         *phr = hr;
  1799.         return;
  1800.     }
  1801.     // !!! some sort of ITypeInfo validity check here
  1802.     pti->Release();
  1803.  
  1804.  
  1805.     // Fix up the dispid for put and get
  1806.     if(wFlags == DISPATCH_PROPERTYPUT) {
  1807.         m_DispParams.cNamedArgs = 1;
  1808.         m_DispId = DISPID_PROPERTYPUT;
  1809.         m_DispParams.rgdispidNamedArgs = &m_DispId;
  1810.     }
  1811.  
  1812.     // all checks ok - add to queue
  1813.     hr = pQ->Insert(this);
  1814.     if(FAILED(hr)) {
  1815.         *phr = hr;
  1816.     }
  1817. }
  1818.  
  1819.  
  1820. // refcounts are held by caller of InvokeAt... and by list. So if
  1821. // we get here, we can't be on the list
  1822.  
  1823. #if 0
  1824. CDeferredCommand::~CDeferredCommand() {
  1825.     // this assert is invalid since if the queue is deleted while we are
  1826.     // still on the queue, we will have been removed by the queue and this
  1827.     // m_pQueue will not have been modified.
  1828.     // ASSERT(m_pQueue == NULL);
  1829.  
  1830.     // we don't hold a ref count on pUnk, which is the object that should
  1831.     // execute the command.
  1832.     // This is because there would otherwise be a circular refcount problem
  1833.     // since pUnk probably owns the CmdQueue object that has a refcount
  1834.     // on us.
  1835.     // The lifetime of pUnk is guaranteed by it being part of, or lifetime
  1836.     // controlled by, our parent object. As long as we are on the list, pUnk
  1837.     // must be valid. Once we are off the list, we do not use pUnk.
  1838.  
  1839. }
  1840. #endif
  1841.  
  1842.  
  1843. // overriden to publicise our interfaces
  1844.  
  1845. STDMETHODIMP
  1846. CDeferredCommand::NonDelegatingQueryInterface(REFIID riid, void **ppv) {
  1847.     ValidateReadWritePtr(ppv,sizeof(PVOID));
  1848.     if(riid == IID_IDeferredCommand) {
  1849.         return GetInterface((IDeferredCommand *) this, ppv);
  1850.     }
  1851.     else {
  1852.         return CUnknown::NonDelegatingQueryInterface(riid, ppv);
  1853.     }
  1854. }
  1855.  
  1856.  
  1857. // remove from q. this will reduce the refcount by one (since the q
  1858. // holds a count) but can't make us go away since he must have a
  1859. // refcount in order to call this method.
  1860.  
  1861. STDMETHODIMP
  1862. CDeferredCommand::Cancel() {
  1863.     if(m_pQueue == NULL) {
  1864.         return VFW_E_ALREADY_CANCELLED;
  1865.     }
  1866.  
  1867.     HRESULT hr = m_pQueue->Remove(this);
  1868.     if(FAILED(hr)) {
  1869.         return hr;
  1870.     }
  1871.  
  1872.     m_pQueue = NULL;
  1873.     return S_OK;
  1874. }
  1875.  
  1876.  
  1877. STDMETHODIMP
  1878. CDeferredCommand::Confidence(LONG* pConfidence) {
  1879.     return E_NOTIMPL;
  1880. }
  1881.  
  1882.  
  1883. STDMETHODIMP
  1884. CDeferredCommand::GetHResult(HRESULT * phrResult) {
  1885.     CheckPointer(phrResult,E_POINTER);
  1886.     ValidateReadWritePtr(phrResult,sizeof(HRESULT));
  1887.  
  1888.     if(m_pQueue != NULL) {
  1889.         return E_ABORT;
  1890.     }
  1891.     *phrResult = m_hrResult;
  1892.     return S_OK;
  1893. }
  1894.  
  1895.  
  1896. // set the time to be a new time (checking that it is valid) and
  1897. // then requeue
  1898.  
  1899. STDMETHODIMP
  1900. CDeferredCommand::Postpone(REFTIME newtime) {
  1901.  
  1902.     // check that this time is not past
  1903.     // convert REFTIME to REFERENCE_TIME
  1904.     COARefTime convertor(newtime);
  1905.  
  1906.     // check that the time has not passed
  1907.     if(m_pQueue->CheckTime(convertor, IsStreamTime())) {
  1908.         return VFW_E_TIME_ALREADY_PASSED;
  1909.     }
  1910.  
  1911.     // extract from list
  1912.     HRESULT hr = m_pQueue->Remove(this);
  1913.     if(FAILED(hr)) {
  1914.         return hr;
  1915.     }
  1916.  
  1917.     // change time
  1918.     m_time = convertor;
  1919.  
  1920.     // requeue
  1921.     hr = m_pQueue->Insert(this);
  1922.  
  1923.     return hr;
  1924. }
  1925.  
  1926.  
  1927. HRESULT
  1928. CDeferredCommand::Invoke() {
  1929.     // check that we are still outstanding
  1930.     if(m_pQueue == NULL) {
  1931.         return VFW_E_ALREADY_CANCELLED;
  1932.     }
  1933.  
  1934.     // get the type info
  1935.     ITypeInfo* pti;
  1936.     HRESULT hr = m_Dispatch.GetTypeInfo(GetIID(), 0, 0, &pti);
  1937.     if(FAILED(hr)) {
  1938.         return hr;
  1939.     }
  1940.  
  1941.     // qi for the expected interface and then invoke it. Note that we have to
  1942.     // treat the returned interface as IUnknown since we don't know its type.
  1943.     IUnknown* pInterface;
  1944.  
  1945.     hr = m_pUnk->QueryInterface(GetIID(), (void**) &pInterface);
  1946.     if(FAILED(hr)) {
  1947.         pti->Release();
  1948.         return hr;
  1949.     }
  1950.  
  1951.     EXCEPINFO expinfo;
  1952.     UINT uArgErr;
  1953.     m_hrResult = pti->Invoke(pInterface,
  1954.         GetMethod(),
  1955.         GetFlags(),
  1956.         GetParams(),
  1957.         GetResult(),
  1958.         &expinfo,
  1959.         &uArgErr);
  1960.  
  1961.     // release the interface we QI'd for
  1962.     pInterface->Release();
  1963.     pti->Release();
  1964.  
  1965.  
  1966.     // remove from list whether or not successful
  1967.     // or we loop indefinitely
  1968.     hr = m_pQueue->Remove(this);
  1969.     m_pQueue = NULL;
  1970.     return hr;
  1971. }
  1972.  
  1973.  
  1974.  
  1975. // --- CCmdQueue methods ----------
  1976.  
  1977.  
  1978. CCmdQueue::CCmdQueue() :
  1979.     m_listPresentation(NAME("Presentation time command list")),
  1980.     m_listStream(NAME("Stream time command list")),
  1981.     m_evDue(TRUE),    // manual reset
  1982.     m_dwAdvise(0),
  1983.     m_pClock(NULL),
  1984.     m_bRunning(FALSE) {
  1985. }
  1986.  
  1987.  
  1988. CCmdQueue::~CCmdQueue() {
  1989.     // empty all our lists
  1990.  
  1991.     // we hold a refcount on each, so traverse and Release each
  1992.     // entry then RemoveAll to empty the list
  1993.     POSITION pos = m_listPresentation.GetHeadPosition();
  1994.  
  1995.     while(pos) {
  1996.         CDeferredCommand* pCmd = m_listPresentation.GetNext(pos);
  1997.         pCmd->Release();
  1998.     }
  1999.     m_listPresentation.RemoveAll();
  2000.  
  2001.     pos = m_listStream.GetHeadPosition();
  2002.  
  2003.     while(pos) {
  2004.         CDeferredCommand* pCmd = m_listStream.GetNext(pos);
  2005.         pCmd->Release();
  2006.     }
  2007.     m_listStream.RemoveAll();
  2008.  
  2009.     if(m_pClock) {
  2010.         if(m_dwAdvise) {
  2011.             m_pClock->Unadvise(m_dwAdvise);
  2012.             m_dwAdvise = 0;
  2013.         }
  2014.         m_pClock->Release();
  2015.     }
  2016. }
  2017.  
  2018.  
  2019. // returns a new CDeferredCommand object that will be initialised with
  2020. // the parameters and will be added to the queue during construction.
  2021. // returns S_OK if successfully created otherwise an error and
  2022. // no object has been queued.
  2023.  
  2024. HRESULT
  2025. CCmdQueue::New(
  2026.     CDeferredCommand **ppCmd,
  2027.     LPUNKNOWN   pUnk,       // this object will execute command
  2028.     REFTIME time,
  2029.     GUID*   iid,
  2030.     long    dispidMethod,
  2031.     short   wFlags,
  2032.     long    cArgs,
  2033.     VARIANT*    pDispParams,
  2034.     VARIANT*    pvarResult,
  2035.     short*  puArgErr,
  2036.     BOOL    bStream
  2037. ) {
  2038.     CAutoLock lock(&m_Lock);
  2039.  
  2040.     HRESULT hr = S_OK;
  2041.     *ppCmd = NULL;
  2042.  
  2043.     CDeferredCommand* pCmd;
  2044.     pCmd = new CDeferredCommand(this,
  2045.         NULL,       // not aggregated
  2046.         &hr,
  2047.         pUnk,       // this guy will execute
  2048.         time,
  2049.         iid,
  2050.         dispidMethod,
  2051.         wFlags,
  2052.         cArgs,
  2053.         pDispParams,
  2054.         pvarResult,
  2055.         puArgErr,
  2056.         bStream);
  2057.  
  2058.     if(pCmd == NULL) {
  2059.         hr = E_OUTOFMEMORY;
  2060.     }
  2061.     else {
  2062.         *ppCmd = pCmd;
  2063.     }
  2064.     return hr;
  2065. }
  2066.  
  2067.  
  2068. HRESULT
  2069. CCmdQueue::Insert(CDeferredCommand* pCmd) {
  2070.     CAutoLock lock(&m_Lock);
  2071.  
  2072.     // addref the item
  2073.     pCmd->AddRef();
  2074.  
  2075.     CGenericList<CDeferredCommand> * pList;
  2076.     if(pCmd->IsStreamTime()) {
  2077.         pList = &m_listStream;
  2078.     }
  2079.     else {
  2080.         pList = &m_listPresentation;
  2081.     }
  2082.     POSITION pos = pList->GetHeadPosition();
  2083.  
  2084.     // seek past all items that are before us
  2085.     while(pos &&
  2086.         (pList->Get(pos)->GetTime() <= pCmd->GetTime())) {
  2087.  
  2088.         pList->GetNext(pos);
  2089.     }
  2090.  
  2091.     // now at end of list or in front of items that come later
  2092.     if(!pos) {
  2093.         pList->AddTail(pCmd);
  2094.     }
  2095.     else {
  2096.         pList->AddBefore(pos, pCmd);
  2097.     }
  2098.  
  2099.     SetTimeAdvise();
  2100.     return S_OK;
  2101. }
  2102.  
  2103.  
  2104. HRESULT
  2105. CCmdQueue::Remove(CDeferredCommand* pCmd) {
  2106.     CAutoLock lock(&m_Lock);
  2107.     HRESULT hr = S_OK;
  2108.  
  2109.     CGenericList<CDeferredCommand> * pList;
  2110.     if(pCmd->IsStreamTime()) {
  2111.         pList = &m_listStream;
  2112.     }
  2113.     else {
  2114.         pList = &m_listPresentation;
  2115.     }
  2116.     POSITION pos = pList->GetHeadPosition();
  2117.  
  2118.     // traverse the list
  2119.     while(pos && (pList->Get(pos) != pCmd)) {
  2120.         pList->GetNext(pos);
  2121.     }
  2122.  
  2123.     // did we drop off the end?
  2124.     if(!pos) {
  2125.         hr = VFW_E_NOT_FOUND;
  2126.     }
  2127.     else {
  2128.  
  2129.         // found it - now take off list
  2130.         pList->Remove(pos);
  2131.  
  2132.         // Insert did an AddRef, so release it
  2133.         pCmd->Release();
  2134.  
  2135.         // check that timer request is still for earliest time
  2136.         SetTimeAdvise();
  2137.     }
  2138.     return hr;
  2139. }
  2140.  
  2141.  
  2142. // set the clock used for timing
  2143.  
  2144. HRESULT
  2145. CCmdQueue::SetSyncSource(IReferenceClock* pClock) {
  2146.     CAutoLock lock(&m_Lock);
  2147.  
  2148.     // addref the new clock first in case they are the same
  2149.     if(pClock) {
  2150.         pClock->AddRef();
  2151.     }
  2152.  
  2153.     // kill any advise on the old clock
  2154.     if(m_pClock) {
  2155.         if(m_dwAdvise) {
  2156.             m_pClock->Unadvise(m_dwAdvise);
  2157.             m_dwAdvise = 0;
  2158.         }
  2159.         m_pClock->Release();
  2160.     }
  2161.     m_pClock = pClock;
  2162.  
  2163.     // set up a new advise
  2164.     SetTimeAdvise();
  2165.     return S_OK;
  2166. }
  2167.  
  2168.  
  2169. // set up a timer event with the reference clock
  2170.  
  2171. void
  2172. CCmdQueue::SetTimeAdvise(void) {
  2173.     // make sure we have a clock to use
  2174.     if(!m_pClock) {
  2175.         return;
  2176.     }
  2177.  
  2178.     // reset the event whenever we are requesting a new signal
  2179.     m_evDue.Reset();
  2180.  
  2181.     // time 0 is earliest
  2182.     CRefTime current;
  2183.  
  2184.     // find the earliest presentation time
  2185.     if(m_listPresentation.GetCount() > 0) {
  2186.  
  2187.         POSITION pos = m_listPresentation.GetHeadPosition();
  2188.         current = m_listPresentation.Get(pos)->GetTime();
  2189.     }
  2190.  
  2191.     // if we're running, check the stream times too
  2192.     if(m_bRunning) {
  2193.  
  2194.         CRefTime t;
  2195.  
  2196.         if(m_listStream.GetCount() > 0) {
  2197.  
  2198.             POSITION pos = m_listStream.GetHeadPosition();
  2199.             t = m_listStream.Get(pos)->GetTime();
  2200.  
  2201.             // add on stream time offset to get presentation time
  2202.             t += m_StreamTimeOffset;
  2203.  
  2204.             // is this earlier?
  2205.             if((current == TimeZero) || (t < current)) {
  2206.                 current = t;
  2207.             }
  2208.         }
  2209.     }
  2210.  
  2211.     // need to change?
  2212.     if((current > TimeZero) && (current != m_tCurrentAdvise)) {
  2213.         if(m_dwAdvise) {
  2214.             m_pClock->Unadvise(m_dwAdvise);
  2215.             // reset the event whenever we are requesting a new signal
  2216.             m_evDue.Reset();
  2217.         }
  2218.  
  2219.         // ask for time advice - the first two params are either
  2220.         // stream time offset and stream time or
  2221.         // presentation time and 0. we always use the latter
  2222.         HRESULT hr = m_pClock->AdviseTime((REFERENCE_TIME)current,
  2223.             TimeZero,
  2224.             (HEVENT) HANDLE(m_evDue),
  2225.             &m_dwAdvise);
  2226.  
  2227.         ASSERT(SUCCEEDED(hr));
  2228.         m_tCurrentAdvise = current;
  2229.     }
  2230. }
  2231.  
  2232.  
  2233. // switch to run mode. Streamtime to Presentation time mapping known.
  2234.  
  2235. HRESULT
  2236. CCmdQueue::Run(REFERENCE_TIME tStreamTimeOffset) {
  2237.     CAutoLock lock(&m_Lock);
  2238.  
  2239.     m_StreamTimeOffset = tStreamTimeOffset;
  2240.     m_bRunning = TRUE;
  2241.  
  2242.     // ensure advise is accurate
  2243.     SetTimeAdvise();
  2244.     return S_OK;
  2245. }
  2246.  
  2247.  
  2248. // switch to Stopped or Paused mode. Time mapping not known.
  2249.  
  2250. HRESULT
  2251. CCmdQueue::EndRun() {
  2252.     CAutoLock lock(&m_Lock);
  2253.  
  2254.     m_bRunning = FALSE;
  2255.  
  2256.     // check timer setting - stream times
  2257.     SetTimeAdvise();
  2258.     return S_OK;
  2259. }
  2260.  
  2261.  
  2262. // return a pointer to the next due command. Blocks for msTimeout
  2263. // milliseconds until there is a due command.
  2264. // Stream-time commands will only become due between Run and Endrun calls.
  2265. // The command remains queued until invoked or cancelled.
  2266. // Returns E_ABORT if timeout occurs, otherwise S_OK (or other error).
  2267. //
  2268. // returns an AddRef'd object
  2269.  
  2270. HRESULT
  2271. CCmdQueue::GetDueCommand(CDeferredCommand ** ppCmd, long msTimeout) {
  2272.     // loop until we timeout or find a due command
  2273.     for(;;) { {
  2274.             CAutoLock lock(&m_Lock);
  2275.  
  2276.  
  2277.             // find the earliest command
  2278.             CDeferredCommand * pCmd = NULL;
  2279.  
  2280.             // check the presentation time and the
  2281.             // stream time list to find the earliest
  2282.  
  2283.             if(m_listPresentation.GetCount() > 0) {
  2284.                 POSITION pos = m_listPresentation.GetHeadPosition();
  2285.                 pCmd = m_listPresentation.Get(pos);
  2286.             }
  2287.  
  2288.             if(m_bRunning && (m_listStream.GetCount() > 0)) {
  2289.                 POSITION pos = m_listStream.GetHeadPosition();
  2290.                 CDeferredCommand* pStrm = m_listStream.Get(pos);
  2291.  
  2292.                 CRefTime t = pStrm->GetTime() + m_StreamTimeOffset;
  2293.                 if(!pCmd || (t < pCmd->GetTime())) {
  2294.                     pCmd = pStrm;
  2295.                 }
  2296.             }
  2297.  
  2298.             //  if we have found one, is it due?
  2299.             if(pCmd) {
  2300.                 if(CheckTime(pCmd->GetTime(), pCmd->IsStreamTime())) {
  2301.  
  2302.                     // yes it's due - addref it
  2303.                     pCmd->AddRef();
  2304.                     *ppCmd = pCmd;
  2305.                     return S_OK;
  2306.                 }
  2307.             }
  2308.         }
  2309.  
  2310.         // block until the advise is signalled
  2311.         if(WaitForSingleObject(m_evDue, msTimeout) != WAIT_OBJECT_0) {
  2312.             return E_ABORT;
  2313.         }
  2314.     }
  2315. }
  2316.  
  2317.  
  2318. // return a pointer to a command that will be due for a given time.
  2319. // Pass in a stream time here. The stream time offset will be passed
  2320. // in via the Run method.
  2321. // Commands remain queued until invoked or cancelled.
  2322. // This method will not block. It will report E_ABORT if there are no
  2323. // commands due yet.
  2324. //
  2325. // returns an AddRef'd object
  2326.  
  2327. HRESULT
  2328. CCmdQueue::GetCommandDueFor(REFERENCE_TIME rtStream, CDeferredCommand**ppCmd) {
  2329.     CAutoLock lock(&m_Lock);
  2330.  
  2331.     CRefTime tStream(rtStream);
  2332.  
  2333.     // find the earliest stream and presentation time commands
  2334.     CDeferredCommand* pStream = NULL;
  2335.     if(m_listStream.GetCount() > 0) {
  2336.         POSITION pos = m_listStream.GetHeadPosition();
  2337.         pStream = m_listStream.Get(pos);
  2338.     }
  2339.     CDeferredCommand* pPresent = NULL;
  2340.     if(m_listPresentation.GetCount() > 0) {
  2341.         POSITION pos = m_listPresentation.GetHeadPosition();
  2342.         pPresent = m_listPresentation.Get(pos);
  2343.     }
  2344.  
  2345.     // is there a presentation time that has passed already
  2346.     if(pPresent && CheckTime(pPresent->GetTime(), FALSE)) {
  2347.         pPresent->AddRef();
  2348.         *ppCmd = pPresent;
  2349.         return S_OK;
  2350.     }
  2351.  
  2352.     // is there a stream time command due before this stream time
  2353.     if(pStream && (pStream->GetTime() <= tStream)) {
  2354.         pPresent->AddRef();
  2355.         *ppCmd = pStream;
  2356.         return S_OK;
  2357.     }
  2358.  
  2359.     // if we are running, we can map presentation times to
  2360.     // stream time. In this case, is there a presentation time command
  2361.     // that will be due before this stream time is presented?
  2362.     if(m_bRunning && pPresent) {
  2363.  
  2364.         // this stream time will appear at...
  2365.         tStream += m_StreamTimeOffset;
  2366.  
  2367.         // due before that?
  2368.         if(pPresent->GetTime() <= tStream) {
  2369.             *ppCmd = pPresent;
  2370.             return S_OK;
  2371.         }
  2372.     }
  2373.  
  2374.     // no commands due yet
  2375.     return VFW_E_NOT_FOUND;
  2376. }
  2377.  
  2378.  
  2379.