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