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