1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS WDM Streaming ActiveMovie Proxy
4  * FILE:            dll/directx/ksproxy/clockforward.cpp
5  * PURPOSE:         IKsClockForwarder interface
6  *
7  * PROGRAMMERS:     Johannes Anderwald (johannes.anderwald@reactos.org)
8  */
9 #include "precomp.h"
10 
11 const GUID IID_IKsClockForwarder              = {0x877e4352, 0x6fea, 0x11d0, {0xb8, 0x63, 0x00, 0xaa, 0x00, 0xa2, 0x16, 0xa1}};
12 
13 DWORD WINAPI CKsClockForwarder_ThreadStartup(LPVOID lpParameter);
14 
15 class CKsClockForwarder : public IDistributorNotify,
16                           public IKsObject
17 {
18 public:
19     STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
20 
AddRef()21     STDMETHODIMP_(ULONG) AddRef()
22     {
23         InterlockedIncrement(&m_Ref);
24         return m_Ref;
25     }
Release()26     STDMETHODIMP_(ULONG) Release()
27     {
28         InterlockedDecrement(&m_Ref);
29 
30         if (!m_Ref)
31         {
32             delete this;
33             return 0;
34         }
35         return m_Ref;
36     }
37 
38     // IDistributorNotify interface
39     HRESULT STDMETHODCALLTYPE Stop();
40     HRESULT STDMETHODCALLTYPE Pause();
41     HRESULT STDMETHODCALLTYPE Run(REFERENCE_TIME tStart);
42     HRESULT STDMETHODCALLTYPE SetSyncSource(IReferenceClock *pClock);
43     HRESULT STDMETHODCALLTYPE NotifyGraphChange();
44 
45     // IKsObject interface
46     HANDLE STDMETHODCALLTYPE KsGetObjectHandle();
47 
48     CKsClockForwarder(HANDLE handle);
~CKsClockForwarder()49     virtual ~CKsClockForwarder(){};
50     HRESULT STDMETHODCALLTYPE SetClockState(KSSTATE State);
51 protected:
52     LONG m_Ref;
53     HANDLE m_Handle;
54     IReferenceClock * m_Clock;
55     HANDLE m_hEvent;
56     HANDLE m_hThread;
57     BOOL m_ThreadStarted;
58     BOOL m_PendingStop;
59     BOOL m_ForceStart;
60     KSSTATE m_State;
61     REFERENCE_TIME m_Time;
62 
63     friend DWORD WINAPI CKsClockForwarder_ThreadStartup(LPVOID lpParameter);
64 };
65 
CKsClockForwarder(HANDLE handle)66 CKsClockForwarder::CKsClockForwarder(
67     HANDLE handle) : m_Ref(0),
68                      m_Handle(handle),
69                      m_Clock(0),
70                      m_hEvent(NULL),
71                      m_hThread(NULL),
72                      m_ThreadStarted(FALSE),
73                      m_PendingStop(FALSE),
74                      m_ForceStart(FALSE),
75                      m_State(KSSTATE_STOP),
76                      m_Time(0)
77 {
78 }
79 
80 HRESULT
81 STDMETHODCALLTYPE
QueryInterface(IN REFIID refiid,OUT PVOID * Output)82 CKsClockForwarder::QueryInterface(
83     IN  REFIID refiid,
84     OUT PVOID* Output)
85 {
86     if (IsEqualGUID(refiid, IID_IUnknown))
87     {
88         *Output = PVOID(this);
89         reinterpret_cast<IUnknown*>(*Output)->AddRef();
90         return NOERROR;
91     }
92     if (IsEqualGUID(refiid, IID_IKsObject) ||
93         IsEqualGUID(refiid, IID_IKsClockForwarder))
94     {
95         *Output = (IKsObject*)(this);
96         reinterpret_cast<IKsObject*>(*Output)->AddRef();
97         return NOERROR;
98     }
99 
100     if (IsEqualGUID(refiid, IID_IDistributorNotify))
101     {
102         *Output = (IDistributorNotify*)(this);
103         reinterpret_cast<IDistributorNotify*>(*Output)->AddRef();
104         return NOERROR;
105     }
106 
107     return E_NOINTERFACE;
108 }
109 
110 //-------------------------------------------------------------------
111 // IDistributorNotify interface
112 //
113 
114 
115 HRESULT
116 STDMETHODCALLTYPE
Stop()117 CKsClockForwarder::Stop()
118 {
119 #ifdef KSPROXY_TRACE
120     WCHAR Buffer[200];
121     swprintf(Buffer, L"CKsClockForwarder::Stop m_ThreadStarted %u m_PendingStop %u m_hThread %p m_hEvent %p m_Handle %p\n", m_ThreadStarted, m_PendingStop, m_hThread, m_hEvent, m_Handle);
122     OutputDebugStringW(Buffer);
123 #endif
124 
125     m_Time = 0;
126     if (m_ThreadStarted)
127     {
128         // signal pending stop
129         m_PendingStop = true;
130 
131         assert(m_hThread);
132         assert(m_hEvent);
133 
134         // set stop event
135         SetEvent(m_hEvent);
136 
137         // wait untill the thread has finished
138         WaitForSingleObject(m_hThread, INFINITE);
139 
140         // close thread handle
141         CloseHandle(m_hThread);
142 
143         // zero handle
144         m_hThread = NULL;
145     }
146 
147     if (m_hEvent)
148     {
149         // close stop event
150         CloseHandle(m_hEvent);
151         m_hEvent = NULL;
152     }
153 
154     m_PendingStop = false;
155 
156     SetClockState(KSSTATE_STOP);
157     return NOERROR;
158 }
159 
160 HRESULT
161 STDMETHODCALLTYPE
Pause()162 CKsClockForwarder::Pause()
163 {
164 #ifdef KSPROXY_TRACE
165     OutputDebugString("CKsClockForwarder::Pause\n");
166 #endif
167 
168     if (!m_hEvent)
169     {
170         m_hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
171         if (!m_hEvent)
172             return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
173     }
174 
175     if (m_State <= KSSTATE_PAUSE)
176     {
177         if (m_State == KSSTATE_STOP)
178             SetClockState(KSSTATE_ACQUIRE);
179 
180         if (m_State == KSSTATE_ACQUIRE)
181             SetClockState(KSSTATE_PAUSE);
182     }
183     else
184     {
185         if (!m_ForceStart)
186         {
187             SetClockState(KSSTATE_PAUSE);
188         }
189     }
190 
191     if (!m_hThread)
192     {
193         m_hThread = CreateThread(NULL, 0, CKsClockForwarder_ThreadStartup, (LPVOID)this, 0, NULL);
194         if (!m_hThread)
195             return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
196     }
197 
198     return NOERROR;
199 }
200 
201 HRESULT
202 STDMETHODCALLTYPE
Run(REFERENCE_TIME tStart)203 CKsClockForwarder::Run(
204     REFERENCE_TIME tStart)
205 {
206 #ifdef KSPROXY_TRACE
207     OutputDebugString("CKsClockForwarder::Run\n");
208 #endif
209 
210     m_Time = tStart;
211 
212     if (!m_hEvent || !m_hThread)
213     {
214         m_ForceStart = TRUE;
215         HRESULT hr = Pause();
216         m_ForceStart = FALSE;
217 
218         if (FAILED(hr))
219             return hr;
220     }
221 
222     assert(m_hThread);
223 
224     SetClockState(KSSTATE_RUN);
225     SetEvent(m_hEvent);
226 
227     return NOERROR;
228 }
229 
230 HRESULT
231 STDMETHODCALLTYPE
SetSyncSource(IReferenceClock * pClock)232 CKsClockForwarder::SetSyncSource(
233     IReferenceClock *pClock)
234 {
235 #ifdef KSPROXY_TRACE
236     OutputDebugString("CKsClockForwarder::SetSyncSource\n");
237 #endif
238 
239     if (pClock)
240         pClock->AddRef();
241 
242     if (m_Clock)
243         m_Clock->Release();
244 
245 
246     m_Clock = pClock;
247     return NOERROR;
248 }
249 
250 HRESULT
251 STDMETHODCALLTYPE
NotifyGraphChange()252 CKsClockForwarder::NotifyGraphChange()
253 {
254 #ifdef KSPROXY_TRACE
255     OutputDebugString("CKsClockForwarder::NotifyGraphChange\n");
256 #endif
257 
258     return NOERROR;
259 }
260 
261 //-------------------------------------------------------------------
262 // IKsObject interface
263 //
264 
265 HANDLE
266 STDMETHODCALLTYPE
KsGetObjectHandle()267 CKsClockForwarder::KsGetObjectHandle()
268 {
269     return m_Handle;
270 }
271 
272 //-------------------------------------------------------------------
273 HRESULT
274 STDMETHODCALLTYPE
SetClockState(KSSTATE State)275 CKsClockForwarder::SetClockState(KSSTATE State)
276 {
277     KSPROPERTY Property;
278     ULONG BytesReturned;
279 
280     Property.Set = KSPROPSETID_Clock;
281     Property.Id = KSPROPERTY_CLOCK_STATE;
282     Property.Flags = KSPROPERTY_TYPE_SET;
283 
284     HRESULT hr = KsSynchronousDeviceControl(m_Handle, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), &State, sizeof(KSSTATE), &BytesReturned);
285     if (SUCCEEDED(hr))
286         m_State = State;
287 
288 #ifdef KSPROXY_TRACE
289     WCHAR Buffer[100];
290     swprintf(Buffer, L"CKsClockForwarder::SetClockState m_State %u State %u hr %lx\n", m_State, State, hr);
291     OutputDebugStringW(Buffer);
292 #endif
293 
294     return hr;
295 }
296 
297 DWORD
298 WINAPI
CKsClockForwarder_ThreadStartup(LPVOID lpParameter)299 CKsClockForwarder_ThreadStartup(LPVOID lpParameter)
300 {
301     REFERENCE_TIME Time;
302     ULONG BytesReturned;
303 
304     CKsClockForwarder * Fwd = (CKsClockForwarder*)lpParameter;
305 
306     Fwd->m_ThreadStarted = TRUE;
307 
308     do
309     {
310         if (Fwd->m_PendingStop)
311             break;
312 
313         if (Fwd->m_State != KSSTATE_RUN)
314             WaitForSingleObject(Fwd->m_hEvent, INFINITE);
315 
316         KSPROPERTY Property;
317         Property.Set = KSPROPSETID_Clock;
318         Property.Id = KSPROPERTY_CLOCK_TIME;
319         Property.Flags = KSPROPERTY_TYPE_SET;
320 
321         Fwd->m_Clock->GetTime(&Time);
322         Time -= Fwd->m_Time;
323 
324         KsSynchronousDeviceControl(Fwd->m_Handle, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), &Time, sizeof(REFERENCE_TIME), &BytesReturned);
325     }
326     while(TRUE);
327 
328     Fwd->m_ThreadStarted = FALSE;
329     return NOERROR;
330 }
331 
332 HRESULT
333 WINAPI
CKsClockForwarder_Constructor(IUnknown * pUnkOuter,REFIID riid,LPVOID * ppv)334 CKsClockForwarder_Constructor(
335     IUnknown * pUnkOuter,
336     REFIID riid,
337     LPVOID * ppv)
338 {
339     HRESULT hr;
340     HANDLE handle;
341 
342 #ifdef KSPROXY_TRACE
343     OutputDebugStringW(L"CKsClockForwarder_Constructor\n");
344 #endif
345 
346     // open default clock
347     hr = KsOpenDefaultDevice(KSCATEGORY_CLOCK, GENERIC_READ | GENERIC_WRITE, &handle);
348 
349     if (hr != NOERROR)
350     {
351 #ifdef KSPROXY_TRACE
352          OutputDebugString("CKsClockForwarder_Constructor failed to open device\n");
353 #endif
354          return hr;
355     }
356 
357     CKsClockForwarder * clock = new CKsClockForwarder(handle);
358 
359     if (!clock)
360     {
361         // free clock handle
362         CloseHandle(handle);
363         return E_OUTOFMEMORY;
364     }
365 
366     if (FAILED(clock->QueryInterface(riid, ppv)))
367     {
368         /* not supported */
369         delete clock;
370         return E_NOINTERFACE;
371     }
372 
373     return NOERROR;
374 }
375