1 //------------------------------------------------------------------------------
4 // Desc: DirectShow base classes.
6 // Copyright (c) 1992-2002 Microsoft Corporation. All rights reserved.
7 //------------------------------------------------------------------------------
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
20 // 'bool' non standard reserved word
21 #pragma warning(disable:4237)
24 // --- CBaseDispatch implementation ----------
25 CBaseDispatch::~CBaseDispatch()
33 // return 1 if we support GetTypeInfo
36 CBaseDispatch::GetTypeInfoCount(UINT
* pctinfo
)
38 CheckPointer(pctinfo
,E_POINTER
);
39 ValidateReadWritePtr(pctinfo
,sizeof(UINT
*));
45 typedef HRESULT (STDAPICALLTYPE
*LPLOADTYPELIB
)(
46 const OLECHAR FAR
*szFile
,
47 ITypeLib FAR
* FAR
* pptlib
);
49 typedef HRESULT (STDAPICALLTYPE
*LPLOADREGTYPELIB
)(REFGUID rguid
,
53 ITypeLib FAR
* FAR
* pptlib
);
55 // attempt to find our type library
58 CBaseDispatch::GetTypeInfo(
64 CheckPointer(pptinfo
,E_POINTER
);
65 ValidateReadWritePtr(pptinfo
,sizeof(ITypeInfo
*));
70 // we only support one type element
72 return TYPE_E_ELEMENTNOTFOUND
;
75 if (NULL
== pptinfo
) {
79 // always look for neutral
82 LPLOADTYPELIB lpfnLoadTypeLib
;
83 LPLOADREGTYPELIB lpfnLoadRegTypeLib
;
87 static const char szTypeLib
[] = "LoadTypeLib";
88 static const char szRegTypeLib
[] = "LoadRegTypeLib";
89 static const WCHAR szControl
[] = L
"control.tlb";
92 // Try to get the Ole32Aut.dll module handle.
95 hInst
= LoadOLEAut32();
97 DWORD dwError
= GetLastError();
98 return AmHresultFromWin32(dwError
);
100 lpfnLoadRegTypeLib
= (LPLOADREGTYPELIB
)GetProcAddress(hInst
,
102 if (lpfnLoadRegTypeLib
== NULL
) {
103 DWORD dwError
= GetLastError();
104 return AmHresultFromWin32(dwError
);
107 hr
= (*lpfnLoadRegTypeLib
)(LIBID_QuartzTypeLib
, 1, 0, // version 1.0
112 // attempt to load directly - this will fill the
113 // registry in if it finds it
115 lpfnLoadTypeLib
= (LPLOADTYPELIB
)GetProcAddress(hInst
, szTypeLib
);
116 if (lpfnLoadTypeLib
== NULL
) {
117 DWORD dwError
= GetLastError();
118 return AmHresultFromWin32(dwError
);
121 hr
= (*lpfnLoadTypeLib
)(szControl
, &ptlib
);
127 hr
= ptlib
->GetTypeInfoOfGuid(
145 CBaseDispatch::GetIDsOfNames(
147 OLECHAR
** rgszNames
,
152 // although the IDispatch riid is dead, we use this to pass from
153 // the interface implementation class to us the iid we are talking about.
156 HRESULT hr
= GetTypeInfo(riid
, 0, lcid
, &pti
);
159 hr
= pti
->GetIDsOfNames(rgszNames
, cNames
, rgdispid
);
167 // --- CMediaControl implementation ---------
169 CMediaControl::CMediaControl(const TCHAR
* name
,LPUNKNOWN pUnk
) :
174 // expose our interfaces IMediaControl and IUnknown
177 CMediaControl::NonDelegatingQueryInterface(REFIID riid
, void **ppv
)
179 ValidateReadWritePtr(ppv
,sizeof(PVOID
));
180 if (riid
== IID_IMediaControl
) {
181 return GetInterface( (IMediaControl
*) this, ppv
);
183 return CUnknown::NonDelegatingQueryInterface(riid
, ppv
);
188 // return 1 if we support GetTypeInfo
191 CMediaControl::GetTypeInfoCount(UINT
* pctinfo
)
193 return m_basedisp
.GetTypeInfoCount(pctinfo
);
197 // attempt to find our type library
200 CMediaControl::GetTypeInfo(
203 ITypeInfo
** pptinfo
)
205 return m_basedisp
.GetTypeInfo(
214 CMediaControl::GetIDsOfNames(
216 OLECHAR
** rgszNames
,
221 return m_basedisp
.GetIDsOfNames(
231 CMediaControl::Invoke(
236 DISPPARAMS
* pdispparams
,
237 VARIANT
* pvarResult
,
238 EXCEPINFO
* pexcepinfo
,
241 // this parameter is a dead leftover from an earlier interface
242 if (IID_NULL
!= riid
) {
243 return DISP_E_UNKNOWNINTERFACE
;
247 HRESULT hr
= GetTypeInfo(0, lcid
, &pti
);
254 (IMediaControl
*)this,
267 // --- CMediaEvent implementation ----------
270 CMediaEvent::CMediaEvent(const TCHAR
* name
,LPUNKNOWN pUnk
) :
276 // expose our interfaces IMediaEvent and IUnknown
279 CMediaEvent::NonDelegatingQueryInterface(REFIID riid
, void **ppv
)
281 ValidateReadWritePtr(ppv
,sizeof(PVOID
));
282 if (riid
== IID_IMediaEvent
|| riid
== IID_IMediaEventEx
) {
283 return GetInterface( (IMediaEventEx
*) this, ppv
);
285 return CUnknown::NonDelegatingQueryInterface(riid
, ppv
);
290 // return 1 if we support GetTypeInfo
293 CMediaEvent::GetTypeInfoCount(UINT
* pctinfo
)
295 return m_basedisp
.GetTypeInfoCount(pctinfo
);
299 // attempt to find our type library
302 CMediaEvent::GetTypeInfo(
305 ITypeInfo
** pptinfo
)
307 return m_basedisp
.GetTypeInfo(
316 CMediaEvent::GetIDsOfNames(
318 OLECHAR
** rgszNames
,
323 return m_basedisp
.GetIDsOfNames(
338 DISPPARAMS
* pdispparams
,
339 VARIANT
* pvarResult
,
340 EXCEPINFO
* pexcepinfo
,
343 // this parameter is a dead leftover from an earlier interface
344 if (IID_NULL
!= riid
) {
345 return DISP_E_UNKNOWNINTERFACE
;
349 HRESULT hr
= GetTypeInfo(0, lcid
, &pti
);
369 // --- CMediaPosition implementation ----------
372 CMediaPosition::CMediaPosition(const TCHAR
* name
,LPUNKNOWN pUnk
) :
377 CMediaPosition::CMediaPosition(const TCHAR
* name
,
382 UNREFERENCED_PARAMETER(phr
);
386 // expose our interfaces IMediaPosition and IUnknown
389 CMediaPosition::NonDelegatingQueryInterface(REFIID riid
, void **ppv
)
391 ValidateReadWritePtr(ppv
,sizeof(PVOID
));
392 if (riid
== IID_IMediaPosition
) {
393 return GetInterface( (IMediaPosition
*) this, ppv
);
395 return CUnknown::NonDelegatingQueryInterface(riid
, ppv
);
400 // return 1 if we support GetTypeInfo
403 CMediaPosition::GetTypeInfoCount(UINT
* pctinfo
)
405 return m_basedisp
.GetTypeInfoCount(pctinfo
);
409 // attempt to find our type library
412 CMediaPosition::GetTypeInfo(
415 ITypeInfo
** pptinfo
)
417 return m_basedisp
.GetTypeInfo(
426 CMediaPosition::GetIDsOfNames(
428 OLECHAR
** rgszNames
,
433 return m_basedisp
.GetIDsOfNames(
443 CMediaPosition::Invoke(
448 DISPPARAMS
* pdispparams
,
449 VARIANT
* pvarResult
,
450 EXCEPINFO
* pexcepinfo
,
453 // this parameter is a dead leftover from an earlier interface
454 if (IID_NULL
!= riid
) {
455 return DISP_E_UNKNOWNINTERFACE
;
459 HRESULT hr
= GetTypeInfo(0, lcid
, &pti
);
466 (IMediaPosition
*)this,
479 // --- IMediaPosition and IMediaSeeking pass through class ----------
482 CPosPassThru::CPosPassThru(const TCHAR
*pName
,
486 CMediaPosition(pName
,pUnk
),
496 // Expose our IMediaSeeking and IMediaPosition interfaces
499 CPosPassThru::NonDelegatingQueryInterface(REFIID riid
,void **ppv
)
501 CheckPointer(ppv
,E_POINTER
);
504 if (riid
== IID_IMediaSeeking
) {
505 return GetInterface( static_cast<IMediaSeeking
*>(this), ppv
);
507 return CMediaPosition::NonDelegatingQueryInterface(riid
,ppv
);
511 // Return the IMediaPosition interface from our peer
514 CPosPassThru::GetPeer(IMediaPosition
** ppMP
)
519 HRESULT hr
= m_pPin
->ConnectedTo(&pConnected
);
523 IMediaPosition
* pMP
;
524 hr
= pConnected
->QueryInterface(IID_IMediaPosition
, (void **) &pMP
);
525 pConnected
->Release();
535 // Return the IMediaSeeking interface from our peer
538 CPosPassThru::GetPeerSeeking(IMediaSeeking
** ppMS
)
543 HRESULT hr
= m_pPin
->ConnectedTo(&pConnected
);
548 hr
= pConnected
->QueryInterface(IID_IMediaSeeking
, (void **) &pMS
);
549 pConnected
->Release();
559 // --- IMediaSeeking methods ----------
563 CPosPassThru::GetCapabilities(DWORD
* pCaps
)
566 HRESULT hr
= GetPeerSeeking(&pMS
);
571 hr
= pMS
->GetCapabilities(pCaps
);
577 CPosPassThru::CheckCapabilities(DWORD
* pCaps
)
580 HRESULT hr
= GetPeerSeeking(&pMS
);
585 hr
= pMS
->CheckCapabilities(pCaps
);
591 CPosPassThru::IsFormatSupported(const GUID
* pFormat
)
594 HRESULT hr
= GetPeerSeeking(&pMS
);
599 hr
= pMS
->IsFormatSupported(pFormat
);
606 CPosPassThru::QueryPreferredFormat(GUID
*pFormat
)
609 HRESULT hr
= GetPeerSeeking(&pMS
);
614 hr
= pMS
->QueryPreferredFormat(pFormat
);
621 CPosPassThru::SetTimeFormat(const GUID
* pFormat
)
624 HRESULT hr
= GetPeerSeeking(&pMS
);
629 hr
= pMS
->SetTimeFormat(pFormat
);
636 CPosPassThru::GetTimeFormat(GUID
*pFormat
)
639 HRESULT hr
= GetPeerSeeking(&pMS
);
644 hr
= pMS
->GetTimeFormat(pFormat
);
651 CPosPassThru::IsUsingTimeFormat(const GUID
* pFormat
)
654 HRESULT hr
= GetPeerSeeking(&pMS
);
659 hr
= pMS
->IsUsingTimeFormat(pFormat
);
666 CPosPassThru::ConvertTimeFormat(LONGLONG
* pTarget
, const GUID
* pTargetFormat
,
667 LONGLONG Source
, const GUID
* pSourceFormat
)
670 HRESULT hr
= GetPeerSeeking(&pMS
);
675 hr
= pMS
->ConvertTimeFormat(pTarget
, pTargetFormat
, Source
, pSourceFormat
);
682 CPosPassThru::SetPositions( LONGLONG
* pCurrent
, DWORD CurrentFlags
683 , LONGLONG
* pStop
, DWORD StopFlags
)
686 HRESULT hr
= GetPeerSeeking(&pMS
);
691 hr
= pMS
->SetPositions(pCurrent
, CurrentFlags
, pStop
, StopFlags
);
697 CPosPassThru::GetPositions(LONGLONG
*pCurrent
, LONGLONG
* pStop
)
700 HRESULT hr
= GetPeerSeeking(&pMS
);
705 hr
= pMS
->GetPositions(pCurrent
,pStop
);
711 CPosPassThru::GetSeekingLongLong
712 ( HRESULT (__stdcall
IMediaSeeking::*pMethod
)( LONGLONG
* )
717 HRESULT hr
= GetPeerSeeking(&pMS
);
720 hr
= (pMS
->*pMethod
)(pll
);
726 // If we don't have a current position then ask upstream
729 CPosPassThru::GetCurrentPosition(LONGLONG
*pCurrent
)
731 // Can we report the current position
732 HRESULT hr
= GetMediaTime(pCurrent
,NULL
);
733 if (SUCCEEDED(hr
)) hr
= NOERROR
;
734 else hr
= GetSeekingLongLong( &IMediaSeeking::GetCurrentPosition
, pCurrent
);
740 CPosPassThru::GetStopPosition(LONGLONG
*pStop
)
742 return GetSeekingLongLong( &IMediaSeeking::GetStopPosition
, pStop
);;
746 CPosPassThru::GetDuration(LONGLONG
*pDuration
)
748 return GetSeekingLongLong( &IMediaSeeking::GetDuration
, pDuration
);;
753 CPosPassThru::GetPreroll(LONGLONG
*pllPreroll
)
755 return GetSeekingLongLong( &IMediaSeeking::GetPreroll
, pllPreroll
);;
760 CPosPassThru::GetAvailable( LONGLONG
*pEarliest
, LONGLONG
*pLatest
)
763 HRESULT hr
= GetPeerSeeking(&pMS
);
768 hr
= pMS
->GetAvailable( pEarliest
, pLatest
);
775 CPosPassThru::GetRate(double * pdRate
)
778 HRESULT hr
= GetPeerSeeking(&pMS
);
782 hr
= pMS
->GetRate(pdRate
);
789 CPosPassThru::SetRate(double dRate
)
796 HRESULT hr
= GetPeerSeeking(&pMS
);
800 hr
= pMS
->SetRate(dRate
);
808 // --- IMediaPosition methods ----------
812 CPosPassThru::get_Duration(REFTIME
* plength
)
815 HRESULT hr
= GetPeer(&pMP
);
820 hr
= pMP
->get_Duration(plength
);
827 CPosPassThru::get_CurrentPosition(REFTIME
* pllTime
)
830 HRESULT hr
= GetPeer(&pMP
);
834 hr
= pMP
->get_CurrentPosition(pllTime
);
841 CPosPassThru::put_CurrentPosition(REFTIME llTime
)
844 HRESULT hr
= GetPeer(&pMP
);
848 hr
= pMP
->put_CurrentPosition(llTime
);
855 CPosPassThru::get_StopTime(REFTIME
* pllTime
)
858 HRESULT hr
= GetPeer(&pMP
);
862 hr
= pMP
->get_StopTime(pllTime
);
869 CPosPassThru::put_StopTime(REFTIME llTime
)
872 HRESULT hr
= GetPeer(&pMP
);
876 hr
= pMP
->put_StopTime(llTime
);
883 CPosPassThru::get_PrerollTime(REFTIME
* pllTime
)
886 HRESULT hr
= GetPeer(&pMP
);
890 hr
= pMP
->get_PrerollTime(pllTime
);
897 CPosPassThru::put_PrerollTime(REFTIME llTime
)
900 HRESULT hr
= GetPeer(&pMP
);
904 hr
= pMP
->put_PrerollTime(llTime
);
911 CPosPassThru::get_Rate(double * pdRate
)
914 HRESULT hr
= GetPeer(&pMP
);
918 hr
= pMP
->get_Rate(pdRate
);
925 CPosPassThru::put_Rate(double dRate
)
932 HRESULT hr
= GetPeer(&pMP
);
936 hr
= pMP
->put_Rate(dRate
);
943 CPosPassThru::CanSeekForward(LONG
*pCanSeekForward
)
946 HRESULT hr
= GetPeer(&pMP
);
950 hr
= pMP
->CanSeekForward(pCanSeekForward
);
957 CPosPassThru::CanSeekBackward(LONG
*pCanSeekBackward
)
960 HRESULT hr
= GetPeer(&pMP
);
964 hr
= pMP
->CanSeekBackward(pCanSeekBackward
);
970 // --- Implements the CRendererPosPassThru class ----------
973 // Media times (eg current frame, field, sample etc) are passed through the
974 // filtergraph in media samples. When a renderer gets a sample with media
975 // times in it, it will call one of the RegisterMediaTime methods we expose
976 // (one takes an IMediaSample, the other takes the media times direct). We
977 // store the media times internally and return them in GetCurrentPosition.
979 CRendererPosPassThru::CRendererPosPassThru(const TCHAR
*pName
,
983 CPosPassThru(pName
,pUnk
,phr
,pPin
),
991 // Sets the media times the object should report
994 CRendererPosPassThru::RegisterMediaTime(IMediaSample
*pMediaSample
)
996 ASSERT(pMediaSample
);
1000 CAutoLock
cAutoLock(&m_PositionLock
);
1002 // Get the media times from the sample
1004 HRESULT hr
= pMediaSample
->GetTime(&StartMedia
,&EndMedia
);
1007 ASSERT(hr
== VFW_E_SAMPLE_TIME_NOT_SET
);
1011 m_StartMedia
= StartMedia
;
1012 m_EndMedia
= EndMedia
;
1018 // Sets the media times the object should report
1021 CRendererPosPassThru::RegisterMediaTime(LONGLONG StartTime
,LONGLONG EndTime
)
1023 CAutoLock
cAutoLock(&m_PositionLock
);
1024 m_StartMedia
= StartTime
;
1025 m_EndMedia
= EndTime
;
1031 // Return the current media times registered in the object
1034 CRendererPosPassThru::GetMediaTime(LONGLONG
*pStartTime
,LONGLONG
*pEndTime
)
1038 CAutoLock
cAutoLock(&m_PositionLock
);
1039 if (m_bReset
== TRUE
) {
1043 // We don't have to return the end time
1045 HRESULT hr
= ConvertTimeFormat( pStartTime
, 0, m_StartMedia
, &TIME_FORMAT_MEDIA_TIME
);
1046 if (pEndTime
&& SUCCEEDED(hr
)) {
1047 hr
= ConvertTimeFormat( pEndTime
, 0, m_EndMedia
, &TIME_FORMAT_MEDIA_TIME
);
1053 // Resets the media times we hold
1056 CRendererPosPassThru::ResetMediaTime()
1058 CAutoLock
cAutoLock(&m_PositionLock
);
1065 // Intended to be called by the owing filter during EOS processing so
1066 // that the media times can be adjusted to the stop time. This ensures
1067 // that the GetCurrentPosition will actully get to the stop position.
1069 CRendererPosPassThru::EOS()
1073 if ( m_bReset
== TRUE
) hr
= E_FAIL
;
1077 if SUCCEEDED(hr
=GetStopPosition(&llStop
))
1079 CAutoLock
cAutoLock(&m_PositionLock
);
1081 m_EndMedia
= llStop
;
1087 // -- CSourceSeeking implementation ------------
1089 CSourceSeeking::CSourceSeeking(
1090 const TCHAR
* pName
,
1094 CUnknown(pName
, pUnk
),
1098 m_rtStop
= _I64_MAX
/ 2;
1099 m_rtDuration
= m_rtStop
;
1100 m_dRateSeeking
= 1.0;
1102 m_dwSeekingCaps
= AM_SEEKING_CanSeekForwards
1103 | AM_SEEKING_CanSeekBackwards
1104 | AM_SEEKING_CanSeekAbsolute
1105 | AM_SEEKING_CanGetStopPos
1106 | AM_SEEKING_CanGetDuration
;
1109 HRESULT
CSourceSeeking::NonDelegatingQueryInterface(REFIID riid
, void **ppv
)
1111 if(riid
== IID_IMediaSeeking
) {
1112 CheckPointer(ppv
, E_POINTER
);
1113 return GetInterface(static_cast<IMediaSeeking
*>(this), ppv
);
1116 return CUnknown::NonDelegatingQueryInterface(riid
, ppv
);
1121 HRESULT
CSourceSeeking::IsFormatSupported(const GUID
* pFormat
)
1123 CheckPointer(pFormat
, E_POINTER
);
1124 // only seeking in time (REFERENCE_TIME units) is supported
1125 return *pFormat
== TIME_FORMAT_MEDIA_TIME
? S_OK
: S_FALSE
;
1128 HRESULT
CSourceSeeking::QueryPreferredFormat(GUID
*pFormat
)
1130 CheckPointer(pFormat
, E_POINTER
);
1131 *pFormat
= TIME_FORMAT_MEDIA_TIME
;
1135 HRESULT
CSourceSeeking::SetTimeFormat(const GUID
* pFormat
)
1137 CheckPointer(pFormat
, E_POINTER
);
1139 // nothing to set; just check that it's TIME_FORMAT_TIME
1140 return *pFormat
== TIME_FORMAT_MEDIA_TIME
? S_OK
: E_INVALIDARG
;
1143 HRESULT
CSourceSeeking::IsUsingTimeFormat(const GUID
* pFormat
)
1145 CheckPointer(pFormat
, E_POINTER
);
1146 return *pFormat
== TIME_FORMAT_MEDIA_TIME
? S_OK
: S_FALSE
;
1149 HRESULT
CSourceSeeking::GetTimeFormat(GUID
*pFormat
)
1151 CheckPointer(pFormat
, E_POINTER
);
1152 *pFormat
= TIME_FORMAT_MEDIA_TIME
;
1156 HRESULT
CSourceSeeking::GetDuration(LONGLONG
*pDuration
)
1158 CheckPointer(pDuration
, E_POINTER
);
1159 CAutoLock
lock(m_pLock
);
1160 *pDuration
= m_rtDuration
;
1164 HRESULT
CSourceSeeking::GetStopPosition(LONGLONG
*pStop
)
1166 CheckPointer(pStop
, E_POINTER
);
1167 CAutoLock
lock(m_pLock
);
1172 HRESULT
CSourceSeeking::GetCurrentPosition(LONGLONG
*pCurrent
)
1174 // GetCurrentPosition is typically supported only in renderers and
1175 // not in source filters.
1179 HRESULT
CSourceSeeking::GetCapabilities( DWORD
* pCapabilities
)
1181 CheckPointer(pCapabilities
, E_POINTER
);
1182 *pCapabilities
= m_dwSeekingCaps
;
1186 HRESULT
CSourceSeeking::CheckCapabilities( DWORD
* pCapabilities
)
1188 CheckPointer(pCapabilities
, E_POINTER
);
1190 // make sure all requested capabilities are in our mask
1191 return (~m_dwSeekingCaps
& *pCapabilities
) ? S_FALSE
: S_OK
;
1194 HRESULT
CSourceSeeking::ConvertTimeFormat( LONGLONG
* pTarget
, const GUID
* pTargetFormat
,
1195 LONGLONG Source
, const GUID
* pSourceFormat
)
1197 CheckPointer(pTarget
, E_POINTER
);
1198 // format guids can be null to indicate current format
1200 // since we only support TIME_FORMAT_MEDIA_TIME, we don't really
1201 // offer any conversions.
1202 if(pTargetFormat
== 0 || *pTargetFormat
== TIME_FORMAT_MEDIA_TIME
)
1204 if(pSourceFormat
== 0 || *pSourceFormat
== TIME_FORMAT_MEDIA_TIME
)
1211 return E_INVALIDARG
;
1215 HRESULT
CSourceSeeking::SetPositions( LONGLONG
* pCurrent
, DWORD CurrentFlags
1216 , LONGLONG
* pStop
, DWORD StopFlags
)
1218 DWORD StopPosBits
= StopFlags
& AM_SEEKING_PositioningBitsMask
;
1219 DWORD StartPosBits
= CurrentFlags
& AM_SEEKING_PositioningBitsMask
;
1222 CheckPointer(pStop
, E_POINTER
);
1224 // accept only relative, incremental, or absolute positioning
1225 if(StopPosBits
!= StopFlags
) {
1226 return E_INVALIDARG
;
1231 CheckPointer(pCurrent
, E_POINTER
);
1232 if(StartPosBits
!= AM_SEEKING_AbsolutePositioning
&&
1233 StartPosBits
!= AM_SEEKING_RelativePositioning
) {
1234 return E_INVALIDARG
;
1239 // scope for autolock
1241 CAutoLock
lock(m_pLock
);
1243 // set start position
1244 if(StartPosBits
== AM_SEEKING_AbsolutePositioning
)
1246 m_rtStart
= *pCurrent
;
1248 else if(StartPosBits
== AM_SEEKING_RelativePositioning
)
1250 m_rtStart
+= *pCurrent
;
1253 // set stop position
1254 if(StopPosBits
== AM_SEEKING_AbsolutePositioning
)
1258 else if(StopPosBits
== AM_SEEKING_IncrementalPositioning
)
1260 m_rtStop
= m_rtStart
+ *pStop
;
1262 else if(StopPosBits
== AM_SEEKING_RelativePositioning
)
1264 m_rtStop
= m_rtStop
+ *pStop
;
1270 if(SUCCEEDED(hr
) && StopPosBits
) {
1281 HRESULT
CSourceSeeking::GetPositions( LONGLONG
* pCurrent
, LONGLONG
* pStop
)
1284 *pCurrent
= m_rtStart
;
1294 HRESULT
CSourceSeeking::GetAvailable( LONGLONG
* pEarliest
, LONGLONG
* pLatest
)
1300 CAutoLock
lock(m_pLock
);
1301 *pLatest
= m_rtDuration
;
1306 HRESULT
CSourceSeeking::SetRate( double dRate
)
1309 CAutoLock
lock(m_pLock
);
1310 m_dRateSeeking
= dRate
;
1312 return ChangeRate();
1315 HRESULT
CSourceSeeking::GetRate( double * pdRate
)
1317 CheckPointer(pdRate
, E_POINTER
);
1318 CAutoLock
lock(m_pLock
);
1319 *pdRate
= m_dRateSeeking
;
1323 HRESULT
CSourceSeeking::GetPreroll(LONGLONG
*pPreroll
)
1325 CheckPointer(pPreroll
, E_POINTER
);
1334 // --- CSourcePosition implementation ----------
1337 CSourcePosition::CSourcePosition(const TCHAR
* pName
,
1341 CMediaPosition(pName
, pUnk
),
1343 m_Start(CRefTime((LONGLONG
)0))
1351 CSourcePosition::get_Duration(REFTIME
* plength
)
1353 CheckPointer(plength
,E_POINTER
);
1354 ValidateReadWritePtr(plength
,sizeof(REFTIME
));
1355 CAutoLock
lock(m_pLock
);
1357 *plength
= m_Duration
;
1363 CSourcePosition::put_CurrentPosition(REFTIME llTime
)
1369 return ChangeStart();
1374 CSourcePosition::get_StopTime(REFTIME
* pllTime
)
1376 CheckPointer(pllTime
,E_POINTER
);
1377 ValidateReadWritePtr(pllTime
,sizeof(REFTIME
));
1378 CAutoLock
lock(m_pLock
);
1386 CSourcePosition::put_StopTime(REFTIME llTime
)
1392 return ChangeStop();
1397 CSourcePosition::get_PrerollTime(REFTIME
* pllTime
)
1399 CheckPointer(pllTime
,E_POINTER
);
1400 ValidateReadWritePtr(pllTime
,sizeof(REFTIME
));
1406 CSourcePosition::put_PrerollTime(REFTIME llTime
)
1413 CSourcePosition::get_Rate(double * pdRate
)
1415 CheckPointer(pdRate
,E_POINTER
);
1416 ValidateReadWritePtr(pdRate
,sizeof(double));
1417 CAutoLock
lock(m_pLock
);
1425 CSourcePosition::put_Rate(double dRate
)
1431 return ChangeRate();
1435 // By default we can seek forwards
1438 CSourcePosition::CanSeekForward(LONG
*pCanSeekForward
)
1440 CheckPointer(pCanSeekForward
,E_POINTER
);
1441 *pCanSeekForward
= OATRUE
;
1446 // By default we can seek backwards
1449 CSourcePosition::CanSeekBackward(LONG
*pCanSeekBackward
)
1451 CheckPointer(pCanSeekBackward
,E_POINTER
);
1452 *pCanSeekBackward
= OATRUE
;
1457 // --- Implementation of CBasicAudio class ----------
1460 CBasicAudio::CBasicAudio(const TCHAR
* pName
,LPUNKNOWN punk
) :
1461 CUnknown(pName
, punk
)
1465 // overriden to publicise our interfaces
1468 CBasicAudio::NonDelegatingQueryInterface(REFIID riid
, void **ppv
)
1470 ValidateReadWritePtr(ppv
,sizeof(PVOID
));
1471 if (riid
== IID_IBasicAudio
) {
1472 return GetInterface( (IBasicAudio
*) this, ppv
);
1474 return CUnknown::NonDelegatingQueryInterface(riid
, ppv
);
1480 CBasicAudio::GetTypeInfoCount(UINT
* pctinfo
)
1482 return m_basedisp
.GetTypeInfoCount(pctinfo
);
1487 CBasicAudio::GetTypeInfo(
1490 ITypeInfo
** pptinfo
)
1492 return m_basedisp
.GetTypeInfo(
1501 CBasicAudio::GetIDsOfNames(
1503 OLECHAR
** rgszNames
,
1508 return m_basedisp
.GetIDsOfNames(
1518 CBasicAudio::Invoke(
1519 DISPID dispidMember
,
1523 DISPPARAMS
* pdispparams
,
1524 VARIANT
* pvarResult
,
1525 EXCEPINFO
* pexcepinfo
,
1528 // this parameter is a dead leftover from an earlier interface
1529 if (IID_NULL
!= riid
) {
1530 return DISP_E_UNKNOWNINTERFACE
;
1534 HRESULT hr
= GetTypeInfo(0, lcid
, &pti
);
1541 (IBasicAudio
*)this,
1554 // --- IVideoWindow implementation ----------
1556 CBaseVideoWindow::CBaseVideoWindow(const TCHAR
* pName
,LPUNKNOWN punk
) :
1557 CUnknown(pName
, punk
)
1562 // overriden to publicise our interfaces
1565 CBaseVideoWindow::NonDelegatingQueryInterface(REFIID riid
, void **ppv
)
1567 ValidateReadWritePtr(ppv
,sizeof(PVOID
));
1568 if (riid
== IID_IVideoWindow
) {
1569 return GetInterface( (IVideoWindow
*) this, ppv
);
1571 return CUnknown::NonDelegatingQueryInterface(riid
, ppv
);
1577 CBaseVideoWindow::GetTypeInfoCount(UINT
* pctinfo
)
1579 return m_basedisp
.GetTypeInfoCount(pctinfo
);
1584 CBaseVideoWindow::GetTypeInfo(
1587 ITypeInfo
** pptinfo
)
1589 return m_basedisp
.GetTypeInfo(
1598 CBaseVideoWindow::GetIDsOfNames(
1600 OLECHAR
** rgszNames
,
1605 return m_basedisp
.GetIDsOfNames(
1615 CBaseVideoWindow::Invoke(
1616 DISPID dispidMember
,
1620 DISPPARAMS
* pdispparams
,
1621 VARIANT
* pvarResult
,
1622 EXCEPINFO
* pexcepinfo
,
1625 // this parameter is a dead leftover from an earlier interface
1626 if (IID_NULL
!= riid
) {
1627 return DISP_E_UNKNOWNINTERFACE
;
1631 HRESULT hr
= GetTypeInfo(0, lcid
, &pti
);
1638 (IVideoWindow
*)this,
1651 // --- IBasicVideo implementation ----------
1654 CBaseBasicVideo::CBaseBasicVideo(const TCHAR
* pName
,LPUNKNOWN punk
) :
1655 CUnknown(pName
, punk
)
1660 // overriden to publicise our interfaces
1663 CBaseBasicVideo::NonDelegatingQueryInterface(REFIID riid
, void **ppv
)
1665 ValidateReadWritePtr(ppv
,sizeof(PVOID
));
1666 if (riid
== IID_IBasicVideo
|| riid
== IID_IBasicVideo2
) {
1667 return GetInterface( static_cast<IBasicVideo2
*>(this), ppv
);
1669 return CUnknown::NonDelegatingQueryInterface(riid
, ppv
);
1675 CBaseBasicVideo::GetTypeInfoCount(UINT
* pctinfo
)
1677 return m_basedisp
.GetTypeInfoCount(pctinfo
);
1682 CBaseBasicVideo::GetTypeInfo(
1685 ITypeInfo
** pptinfo
)
1687 return m_basedisp
.GetTypeInfo(
1696 CBaseBasicVideo::GetIDsOfNames(
1698 OLECHAR
** rgszNames
,
1703 return m_basedisp
.GetIDsOfNames(
1713 CBaseBasicVideo::Invoke(
1714 DISPID dispidMember
,
1718 DISPPARAMS
* pdispparams
,
1719 VARIANT
* pvarResult
,
1720 EXCEPINFO
* pexcepinfo
,
1723 // this parameter is a dead leftover from an earlier interface
1724 if (IID_NULL
!= riid
) {
1725 return DISP_E_UNKNOWNINTERFACE
;
1729 HRESULT hr
= GetTypeInfo(0, lcid
, &pti
);
1736 (IBasicVideo
*)this,
1749 // --- Implementation of Deferred Commands ----------
1752 CDispParams::CDispParams(UINT nArgs
, VARIANT
* pArgs
, HRESULT
*phr
)
1755 rgdispidNamedArgs
= NULL
;
1759 rgvarg
= new VARIANT
[cArgs
];
1760 if (NULL
== rgvarg
) {
1763 *phr
= E_OUTOFMEMORY
;
1768 for (UINT i
= 0; i
< cArgs
; i
++) {
1770 VARIANT
* pDest
= &rgvarg
[i
];
1771 VARIANT
* pSrc
= &pArgs
[i
];
1773 pDest
->vt
= pSrc
->vt
;
1777 pDest
->lVal
= pSrc
->lVal
;
1781 pDest
->bVal
= pSrc
->bVal
;
1785 pDest
->iVal
= pSrc
->iVal
;
1789 pDest
->fltVal
= pSrc
->fltVal
;
1793 pDest
->dblVal
= pSrc
->dblVal
;
1797 pDest
->boolVal
= pSrc
->boolVal
;
1801 pDest
->scode
= pSrc
->scode
;
1805 pDest
->cyVal
= pSrc
->cyVal
;
1809 pDest
->date
= pSrc
->date
;
1813 if (pSrc
->bstrVal
== NULL
) {
1814 pDest
->bstrVal
= NULL
;
1817 // a BSTR is a WORD followed by a UNICODE string.
1818 // the pointer points just after the WORD
1820 WORD len
= * (WORD
*) (pSrc
->bstrVal
- (sizeof(WORD
) / sizeof(OLECHAR
)));
1821 OLECHAR
* pch
= new OLECHAR
[len
+ (sizeof(WORD
)/sizeof(OLECHAR
))];
1823 WORD
*pui
= (WORD
*)pch
;
1825 pDest
->bstrVal
= pch
+ (sizeof(WORD
)/sizeof(OLECHAR
));
1826 CopyMemory(pDest
->bstrVal
, pSrc
->bstrVal
, len
*sizeof(OLECHAR
));
1830 *phr
= E_OUTOFMEMORY
;
1834 pDest
->bstrVal
= pSrc
->bstrVal
;
1838 pDest
->punkVal
= pSrc
->punkVal
;
1839 pDest
->punkVal
->AddRef();
1843 pDest
->pdispVal
= pSrc
->pdispVal
;
1844 pDest
->pdispVal
->AddRef();
1848 // a type we haven't got round to adding yet!
1861 CDispParams::~CDispParams()
1863 for (UINT i
= 0; i
< cArgs
; i
++) {
1864 switch(rgvarg
[i
].vt
) {
1866 if (rgvarg
[i
].bstrVal
!= NULL
) {
1867 OLECHAR
* pch
= rgvarg
[i
].bstrVal
- (sizeof(WORD
)/sizeof(OLECHAR
));
1873 rgvarg
[i
].punkVal
->Release();
1877 rgvarg
[i
].pdispVal
->Release();
1885 // lifetime is controlled by refcounts (see defer.h)
1887 CDeferredCommand::CDeferredCommand(
1891 LPUNKNOWN pUnkExecutor
,
1897 VARIANT
* pDispParams
,
1898 VARIANT
* pvarResult
,
1902 CUnknown(NAME("DeferredCommand"), pUnk
),
1904 m_pUnk(pUnkExecutor
),
1906 m_dispidMethod(dispidMethod
),
1908 m_DispParams(nArgs
, pDispParams
, phr
),
1909 m_pvarResult(pvarResult
),
1914 // convert REFTIME to REFERENCE_TIME
1915 COARefTime
convertor(time
);
1918 // no check of time validity - it's ok to queue a command that's
1921 // check iid is supportable on pUnk by QueryInterface for it
1922 IUnknown
* pInterface
;
1923 HRESULT hr
= m_pUnk
->QueryInterface(GetIID(), (void**) &pInterface
);
1928 pInterface
->Release();
1931 // !!! check dispidMethod and param/return types using typelib
1933 hr
= m_Dispatch
.GetTypeInfo(*iid
, 0, 0, &pti
);
1938 // !!! some sort of ITypeInfo validity check here
1942 // Fix up the dispid for put and get
1943 if (wFlags
== DISPATCH_PROPERTYPUT
) {
1944 m_DispParams
.cNamedArgs
= 1;
1945 m_DispId
= DISPID_PROPERTYPUT
;
1946 m_DispParams
.rgdispidNamedArgs
= &m_DispId
;
1949 // all checks ok - add to queue
1950 hr
= pQ
->Insert(this);
1957 // refcounts are held by caller of InvokeAt... and by list. So if
1958 // we get here, we can't be on the list
1961 CDeferredCommand::~CDeferredCommand()
1963 // this assert is invalid since if the queue is deleted while we are
1964 // still on the queue, we will have been removed by the queue and this
1965 // m_pQueue will not have been modified.
1966 // ASSERT(m_pQueue == NULL);
1968 // we don't hold a ref count on pUnk, which is the object that should
1969 // execute the command.
1970 // This is because there would otherwise be a circular refcount problem
1971 // since pUnk probably owns the CmdQueue object that has a refcount
1973 // The lifetime of pUnk is guaranteed by it being part of, or lifetime
1974 // controlled by, our parent object. As long as we are on the list, pUnk
1975 // must be valid. Once we are off the list, we do not use pUnk.
1981 // overriden to publicise our interfaces
1984 CDeferredCommand::NonDelegatingQueryInterface(REFIID riid
, void **ppv
)
1986 ValidateReadWritePtr(ppv
,sizeof(PVOID
));
1987 if (riid
== IID_IDeferredCommand
) {
1988 return GetInterface( (IDeferredCommand
*) this, ppv
);
1990 return CUnknown::NonDelegatingQueryInterface(riid
, ppv
);
1995 // remove from q. this will reduce the refcount by one (since the q
1996 // holds a count) but can't make us go away since he must have a
1997 // refcount in order to call this method.
2000 CDeferredCommand::Cancel()
2002 if (m_pQueue
== NULL
) {
2003 return VFW_E_ALREADY_CANCELLED
;
2006 HRESULT hr
= m_pQueue
->Remove(this);
2017 CDeferredCommand::Confidence(LONG
* pConfidence
)
2024 CDeferredCommand::GetHResult(HRESULT
* phrResult
)
2026 CheckPointer(phrResult
,E_POINTER
);
2027 ValidateReadWritePtr(phrResult
,sizeof(HRESULT
));
2029 if (m_pQueue
!= NULL
) {
2032 *phrResult
= m_hrResult
;
2037 // set the time to be a new time (checking that it is valid) and
2041 CDeferredCommand::Postpone(REFTIME newtime
)
2044 // check that this time is not past
2045 // convert REFTIME to REFERENCE_TIME
2046 COARefTime
convertor(newtime
);
2048 // check that the time has not passed
2049 if (m_pQueue
->CheckTime(convertor
, IsStreamTime())) {
2050 return VFW_E_TIME_ALREADY_PASSED
;
2053 // extract from list
2054 HRESULT hr
= m_pQueue
->Remove(this);
2063 hr
= m_pQueue
->Insert(this);
2070 CDeferredCommand::Invoke()
2072 // check that we are still outstanding
2073 if (m_pQueue
== NULL
) {
2074 return VFW_E_ALREADY_CANCELLED
;
2077 // get the type info
2079 HRESULT hr
= m_Dispatch
.GetTypeInfo(GetIID(), 0, 0, &pti
);
2084 // qi for the expected interface and then invoke it. Note that we have to
2085 // treat the returned interface as IUnknown since we don't know its type.
2086 IUnknown
* pInterface
;
2088 hr
= m_pUnk
->QueryInterface(GetIID(), (void**) &pInterface
);
2096 m_hrResult
= pti
->Invoke(
2105 // release the interface we QI'd for
2106 pInterface
->Release();
2110 // remove from list whether or not successful
2111 // or we loop indefinitely
2112 hr
= m_pQueue
->Remove(this);
2119 // --- CCmdQueue methods ----------
2122 CCmdQueue::CCmdQueue() :
2123 m_listPresentation(NAME("Presentation time command list")),
2124 m_listStream(NAME("Stream time command list")),
2125 m_evDue(TRUE
), // manual reset
2133 CCmdQueue::~CCmdQueue()
2135 // empty all our lists
2137 // we hold a refcount on each, so traverse and Release each
2138 // entry then RemoveAll to empty the list
2139 POSITION pos
= m_listPresentation
.GetHeadPosition();
2142 CDeferredCommand
* pCmd
= m_listPresentation
.GetNext(pos
);
2145 m_listPresentation
.RemoveAll();
2147 pos
= m_listStream
.GetHeadPosition();
2150 CDeferredCommand
* pCmd
= m_listStream
.GetNext(pos
);
2153 m_listStream
.RemoveAll();
2157 m_pClock
->Unadvise(m_dwAdvise
);
2160 m_pClock
->Release();
2165 // returns a new CDeferredCommand object that will be initialised with
2166 // the parameters and will be added to the queue during construction.
2167 // returns S_OK if successfully created otherwise an error and
2168 // no object has been queued.
2172 CDeferredCommand
**ppCmd
,
2173 LPUNKNOWN pUnk
, // this object will execute command
2179 VARIANT
* pDispParams
,
2180 VARIANT
* pvarResult
,
2185 CAutoLock
lock(&m_Lock
);
2190 CDeferredCommand
* pCmd
;
2191 pCmd
= new CDeferredCommand(
2193 NULL
, // not aggregated
2195 pUnk
, // this guy will execute
2216 CCmdQueue::Insert(CDeferredCommand
* pCmd
)
2218 CAutoLock
lock(&m_Lock
);
2223 CGenericList
<CDeferredCommand
> * pList
;
2224 if (pCmd
->IsStreamTime()) {
2225 pList
= &m_listStream
;
2227 pList
= &m_listPresentation
;
2229 POSITION pos
= pList
->GetHeadPosition();
2231 // seek past all items that are before us
2233 (pList
->Get(pos
)->GetTime() <= pCmd
->GetTime())) {
2235 pList
->GetNext(pos
);
2238 // now at end of list or in front of items that come later
2240 pList
->AddTail(pCmd
);
2242 pList
->AddBefore(pos
, pCmd
);
2251 CCmdQueue::Remove(CDeferredCommand
* pCmd
)
2253 CAutoLock
lock(&m_Lock
);
2256 CGenericList
<CDeferredCommand
> * pList
;
2257 if (pCmd
->IsStreamTime()) {
2258 pList
= &m_listStream
;
2260 pList
= &m_listPresentation
;
2262 POSITION pos
= pList
->GetHeadPosition();
2264 // traverse the list
2265 while (pos
&& (pList
->Get(pos
) != pCmd
)) {
2266 pList
->GetNext(pos
);
2269 // did we drop off the end?
2271 hr
= VFW_E_NOT_FOUND
;
2274 // found it - now take off list
2277 // Insert did an AddRef, so release it
2280 // check that timer request is still for earliest time
2287 // set the clock used for timing
2290 CCmdQueue::SetSyncSource(IReferenceClock
* pClock
)
2292 CAutoLock
lock(&m_Lock
);
2294 // addref the new clock first in case they are the same
2299 // kill any advise on the old clock
2302 m_pClock
->Unadvise(m_dwAdvise
);
2305 m_pClock
->Release();
2309 // set up a new advise
2315 // set up a timer event with the reference clock
2318 CCmdQueue::SetTimeAdvise(void)
2320 // make sure we have a clock to use
2325 // reset the event whenever we are requesting a new signal
2328 // time 0 is earliest
2331 // find the earliest presentation time
2332 if (m_listPresentation
.GetCount() > 0) {
2334 POSITION pos
= m_listPresentation
.GetHeadPosition();
2335 current
= m_listPresentation
.Get(pos
)->GetTime();
2338 // if we're running, check the stream times too
2343 if (m_listStream
.GetCount() > 0) {
2345 POSITION pos
= m_listStream
.GetHeadPosition();
2346 t
= m_listStream
.Get(pos
)->GetTime();
2348 // add on stream time offset to get presentation time
2349 t
+= m_StreamTimeOffset
;
2352 if ((current
== TimeZero
) || (t
< current
)) {
2359 if ((current
> TimeZero
) && (current
!= m_tCurrentAdvise
)) {
2361 m_pClock
->Unadvise(m_dwAdvise
);
2362 // reset the event whenever we are requesting a new signal
2366 // ask for time advice - the first two params are either
2367 // stream time offset and stream time or
2368 // presentation time and 0. we always use the latter
2369 HRESULT hr
= m_pClock
->AdviseTime(
2370 (REFERENCE_TIME
)current
,
2372 (HEVENT
) HANDLE(m_evDue
),
2375 ASSERT(SUCCEEDED(hr
));
2376 m_tCurrentAdvise
= current
;
2381 // switch to run mode. Streamtime to Presentation time mapping known.
2384 CCmdQueue::Run(REFERENCE_TIME tStreamTimeOffset
)
2386 CAutoLock
lock(&m_Lock
);
2388 m_StreamTimeOffset
= tStreamTimeOffset
;
2391 // ensure advise is accurate
2397 // switch to Stopped or Paused mode. Time mapping not known.
2402 CAutoLock
lock(&m_Lock
);
2406 // check timer setting - stream times
2412 // return a pointer to the next due command. Blocks for msTimeout
2413 // milliseconds until there is a due command.
2414 // Stream-time commands will only become due between Run and Endrun calls.
2415 // The command remains queued until invoked or cancelled.
2416 // Returns E_ABORT if timeout occurs, otherwise S_OK (or other error).
2418 // returns an AddRef'd object
2421 CCmdQueue::GetDueCommand(CDeferredCommand
** ppCmd
, long msTimeout
)
2423 // loop until we timeout or find a due command
2427 CAutoLock
lock(&m_Lock
);
2430 // find the earliest command
2431 CDeferredCommand
* pCmd
= NULL
;
2433 // check the presentation time and the
2434 // stream time list to find the earliest
2436 if (m_listPresentation
.GetCount() > 0) {
2437 POSITION pos
= m_listPresentation
.GetHeadPosition();
2438 pCmd
= m_listPresentation
.Get(pos
);
2441 if (m_bRunning
&& (m_listStream
.GetCount() > 0)) {
2442 POSITION pos
= m_listStream
.GetHeadPosition();
2443 CDeferredCommand
* pStrm
= m_listStream
.Get(pos
);
2445 CRefTime t
= pStrm
->GetTime() + m_StreamTimeOffset
;
2446 if (!pCmd
|| (t
< pCmd
->GetTime())) {
2451 // if we have found one, is it due?
2453 if (CheckTime(pCmd
->GetTime(), pCmd
->IsStreamTime())) {
2455 // yes it's due - addref it
2463 // block until the advise is signalled
2464 if (WaitForSingleObject(m_evDue
, msTimeout
) != WAIT_OBJECT_0
) {
2471 // return a pointer to a command that will be due for a given time.
2472 // Pass in a stream time here. The stream time offset will be passed
2473 // in via the Run method.
2474 // Commands remain queued until invoked or cancelled.
2475 // This method will not block. It will report E_ABORT if there are no
2476 // commands due yet.
2478 // returns an AddRef'd object
2481 CCmdQueue::GetCommandDueFor(REFERENCE_TIME rtStream
, CDeferredCommand
**ppCmd
)
2483 CAutoLock
lock(&m_Lock
);
2485 CRefTime
tStream(rtStream
);
2487 // find the earliest stream and presentation time commands
2488 CDeferredCommand
* pStream
= NULL
;
2489 if (m_listStream
.GetCount() > 0) {
2490 POSITION pos
= m_listStream
.GetHeadPosition();
2491 pStream
= m_listStream
.Get(pos
);
2493 CDeferredCommand
* pPresent
= NULL
;
2494 if (m_listPresentation
.GetCount() > 0) {
2495 POSITION pos
= m_listPresentation
.GetHeadPosition();
2496 pPresent
= m_listPresentation
.Get(pos
);
2499 // is there a presentation time that has passed already
2500 if (pPresent
&& CheckTime(pPresent
->GetTime(), FALSE
)) {
2506 // is there a stream time command due before this stream time
2507 if (pStream
&& (pStream
->GetTime() <= tStream
)) {
2513 // if we are running, we can map presentation times to
2514 // stream time. In this case, is there a presentation time command
2515 // that will be due before this stream time is presented?
2516 if (m_bRunning
&& pPresent
) {
2518 // this stream time will appear at...
2519 tStream
+= m_StreamTimeOffset
;
2522 if (pPresent
->GetTime() <= tStream
) {
2528 // no commands due yet
2529 return VFW_E_NOT_FOUND
;