[KSPROXY]
[reactos.git] / reactos / 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 WCHAR Buffer[200];
124
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
128 m_Time = 0;
129 if (m_ThreadStarted)
130 {
131 // signal pending stop
132 m_PendingStop = true;
133
134 assert(m_hThread);
135 assert(m_hEvent);
136
137 // set stop event
138 SetEvent(m_hEvent);
139
140 // wait untill the thread has finished
141 WaitForSingleObject(m_hThread, INFINITE);
142
143 // close thread handle
144 CloseHandle(m_hThread);
145
146 // zero handle
147 m_hThread = NULL;
148 }
149
150 if (m_hEvent)
151 {
152 // close stop event
153 CloseHandle(m_hEvent);
154 m_hEvent = NULL;
155 }
156
157 m_PendingStop = false;
158
159 SetClockState(KSSTATE_STOP);
160 return NOERROR;
161 }
162
163 HRESULT
164 STDMETHODCALLTYPE
165 CKsClockForwarder::Pause()
166 {
167 OutputDebugString("CKsClockForwarder::Pause\n");
168
169 if (!m_hEvent)
170 {
171 m_hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
172 if (!m_hEvent)
173 return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
174 }
175
176 if (m_State <= KSSTATE_PAUSE)
177 {
178 if (m_State == KSSTATE_STOP)
179 SetClockState(KSSTATE_ACQUIRE);
180
181 if (m_State == KSSTATE_ACQUIRE)
182 SetClockState(KSSTATE_PAUSE);
183 }
184 else
185 {
186 if (!m_ForceStart)
187 {
188 SetClockState(KSSTATE_PAUSE);
189 }
190 }
191
192 if (!m_hThread)
193 {
194 m_hThread = CreateThread(NULL, 0, CKsClockForwarder_ThreadStartup, (LPVOID)this, 0, NULL);
195 if (!m_hThread)
196 return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
197 }
198
199 return NOERROR;
200 }
201
202 HRESULT
203 STDMETHODCALLTYPE
204 CKsClockForwarder::Run(
205 REFERENCE_TIME tStart)
206 {
207 OutputDebugString("CKsClockForwarder::Run\n");
208
209 m_Time = tStart;
210
211 if (!m_hEvent || !m_hThread)
212 {
213 m_ForceStart = TRUE;
214 HRESULT hr = Pause();
215 m_ForceStart = FALSE;
216
217 if (FAILED(hr))
218 return hr;
219 }
220
221 assert(m_hThread);
222
223 SetClockState(KSSTATE_RUN);
224 SetEvent(m_hEvent);
225
226 return NOERROR;
227 }
228
229 HRESULT
230 STDMETHODCALLTYPE
231 CKsClockForwarder::SetSyncSource(
232 IReferenceClock *pClock)
233 {
234 OutputDebugString("CKsClockForwarder::SetSyncSource\n");
235
236 if (pClock)
237 pClock->AddRef();
238
239 if (m_Clock)
240 m_Clock->Release();
241
242
243 m_Clock = pClock;
244 return NOERROR;
245 }
246
247 HRESULT
248 STDMETHODCALLTYPE
249 CKsClockForwarder::NotifyGraphChange()
250 {
251 OutputDebugString("CKsClockForwarder::NotifyGraphChange\n");
252 return NOERROR;
253 }
254
255 //-------------------------------------------------------------------
256 // IKsObject interface
257 //
258
259 HANDLE
260 STDMETHODCALLTYPE
261 CKsClockForwarder::KsGetObjectHandle()
262 {
263 return m_Handle;
264 }
265
266 //-------------------------------------------------------------------
267 HRESULT
268 STDMETHODCALLTYPE
269 CKsClockForwarder::SetClockState(KSSTATE State)
270 {
271 KSPROPERTY Property;
272 ULONG BytesReturned;
273
274 Property.Set = KSPROPSETID_Clock;
275 Property.Id = KSPROPERTY_CLOCK_STATE;
276 Property.Flags = KSPROPERTY_TYPE_SET;
277
278 HRESULT hr = KsSynchronousDeviceControl(m_Handle, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), &State, sizeof(KSSTATE), &BytesReturned);
279 if (SUCCEEDED(hr))
280 m_State = State;
281
282 WCHAR Buffer[100];
283 swprintf(Buffer, L"CKsClockForwarder::SetClockState m_State %u State %u hr %lx\n", m_State, State, hr);
284 OutputDebugStringW(Buffer);
285
286 return hr;
287 }
288
289 DWORD
290 WINAPI
291 CKsClockForwarder_ThreadStartup(LPVOID lpParameter)
292 {
293 REFERENCE_TIME Time;
294 ULONG BytesReturned;
295
296 CKsClockForwarder * Fwd = (CKsClockForwarder*)lpParameter;
297
298 Fwd->m_ThreadStarted = TRUE;
299
300 do
301 {
302 if (Fwd->m_PendingStop)
303 break;
304
305 if (Fwd->m_State != KSSTATE_RUN)
306 WaitForSingleObject(Fwd->m_hEvent, INFINITE);
307
308 KSPROPERTY Property;
309 Property.Set = KSPROPSETID_Clock;
310 Property.Id = KSPROPERTY_CLOCK_TIME;
311 Property.Flags = KSPROPERTY_TYPE_SET;
312
313 Fwd->m_Clock->GetTime(&Time);
314 Time -= Fwd->m_Time;
315
316 KsSynchronousDeviceControl(Fwd->m_Handle, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), &Time, sizeof(REFERENCE_TIME), &BytesReturned);
317 }
318 while(TRUE);
319
320 Fwd->m_ThreadStarted = FALSE;
321 return NOERROR;
322 }
323
324 HRESULT
325 WINAPI
326 CKsClockForwarder_Constructor(
327 IUnknown * pUnkOuter,
328 REFIID riid,
329 LPVOID * ppv)
330 {
331 HRESULT hr;
332 HANDLE handle;
333
334 OutputDebugStringW(L"CKsClockForwarder_Constructor\n");
335
336 // open default clock
337 hr = KsOpenDefaultDevice(KSCATEGORY_CLOCK, GENERIC_READ | GENERIC_WRITE, &handle);
338
339 if (hr != NOERROR)
340 {
341 OutputDebugString("CKsClockForwarder_Constructor failed to open device\n");
342 return hr;
343 }
344
345 CKsClockForwarder * clock = new CKsClockForwarder(handle);
346
347 if (!clock)
348 {
349 // free clock handle
350 CloseHandle(handle);
351 return E_OUTOFMEMORY;
352 }
353
354 if (FAILED(clock->QueryInterface(riid, ppv)))
355 {
356 /* not supported */
357 delete clock;
358 return E_NOINTERFACE;
359 }
360
361 return NOERROR;
362 }