[KSPROXY]
[reactos.git] / reactos / dll / directx / ksproxy / interface.cpp
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS WDM Streaming ActiveMovie Proxy
4 * FILE: dll/directx/ksproxy/interface.cpp
5 * PURPOSE: IKsInterfaceHandler interface
6 *
7 * PROGRAMMERS: Johannes Anderwald (janderwald@reactos.org)
8 */
9 #include "precomp.h"
10
11 const GUID IID_IKsObject = {0x423c13a2, 0x2070, 0x11d0, {0x9e, 0xf7, 0x00, 0xaa, 0x00, 0xa2, 0x16, 0xa1}};
12
13 class CKsInterfaceHandler : public IKsInterfaceHandler
14 {
15 public:
16 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
17
18 STDMETHODIMP_(ULONG) AddRef()
19 {
20 InterlockedIncrement(&m_Ref);
21 return m_Ref;
22 }
23 STDMETHODIMP_(ULONG) Release()
24 {
25 InterlockedDecrement(&m_Ref);
26
27 if (!m_Ref)
28 {
29 delete this;
30 return 0;
31 }
32 return m_Ref;
33 }
34 HRESULT STDMETHODCALLTYPE KsSetPin(IKsPin *KsPin);
35 HRESULT STDMETHODCALLTYPE KsProcessMediaSamples(IKsDataTypeHandler *KsDataTypeHandler, IMediaSample** SampleList, PLONG SampleCount, KSIOOPERATION IoOperation, PKSSTREAM_SEGMENT *StreamSegment);
36 HRESULT STDMETHODCALLTYPE KsCompleteIo(PKSSTREAM_SEGMENT StreamSegment);
37
38 CKsInterfaceHandler() : m_Ref(0), m_Handle(NULL), m_Pin(0){};
39 virtual ~CKsInterfaceHandler(){};
40
41 protected:
42 LONG m_Ref;
43 HANDLE m_Handle;
44 IKsPinEx * m_Pin;
45 };
46
47 typedef struct
48 {
49 KSSTREAM_SEGMENT StreamSegment;
50 IMediaSample * MediaSample[64];
51
52 ULONG SampleCount;
53 ULONG ExtendedSize;
54 PKSSTREAM_HEADER StreamHeader;
55 OVERLAPPED Overlapped;
56 }KSSTREAM_SEGMENT_EXT, *PKSSTREAM_SEGMENT_EXT;
57
58
59 HRESULT
60 STDMETHODCALLTYPE
61 CKsInterfaceHandler::QueryInterface(
62 IN REFIID refiid,
63 OUT PVOID* Output)
64 {
65 if (IsEqualGUID(refiid, IID_IUnknown) ||
66 IsEqualGUID(refiid, IID_IKsInterfaceHandler))
67 {
68 *Output = PVOID(this);
69 reinterpret_cast<IUnknown*>(*Output)->AddRef();
70 return NOERROR;
71 }
72 return E_NOINTERFACE;
73 }
74
75 HRESULT
76 STDMETHODCALLTYPE
77 CKsInterfaceHandler::KsSetPin(
78 IKsPin *KsPin)
79 {
80 HRESULT hr;
81 IKsObject * KsObject;
82 IKsPinEx * Pin;
83
84 // get IKsPinEx interface
85 hr = KsPin->QueryInterface(IID_IKsPinEx, (void**)&Pin);
86 if (SUCCEEDED(hr))
87 {
88 // check if IKsObject is supported
89 hr = KsPin->QueryInterface(IID_IKsObject, (void**)&KsObject);
90
91 if (SUCCEEDED(hr))
92 {
93 // get pin handle
94 m_Handle = KsObject->KsGetObjectHandle();
95
96 // release IKsObject interface
97 KsObject->Release();
98
99 if (!m_Handle)
100 {
101 // expected a file handle
102 hr = E_UNEXPECTED;
103 Pin->Release();
104 }
105 else
106 {
107 if (m_Pin)
108 {
109 // release old interface
110 m_Pin->Release();
111 }
112 m_Pin = Pin;
113 }
114 }
115 else
116 {
117 //release IKsPinEx interface
118 Pin->Release();
119 }
120 }
121
122 // done
123 return hr;
124 }
125
126 HRESULT
127 STDMETHODCALLTYPE
128 CKsInterfaceHandler::KsProcessMediaSamples(
129 IKsDataTypeHandler *KsDataTypeHandler,
130 IMediaSample** SampleList,
131 PLONG SampleCount,
132 KSIOOPERATION IoOperation,
133 PKSSTREAM_SEGMENT *OutStreamSegment)
134 {
135 PKSSTREAM_SEGMENT_EXT StreamSegment;
136 ULONG ExtendedSize, Index, BytesReturned;
137 HRESULT hr = S_OK;
138
139 OutputDebugString("CKsInterfaceHandler::KsProcessMediaSamples\n");
140
141 // sanity check
142 assert(*SampleCount);
143
144 if (*SampleCount == 0 || *SampleCount < 0)
145 return E_FAIL;
146
147 // zero stream segment
148 *OutStreamSegment = NULL;
149
150 // allocate stream segment
151 StreamSegment = (PKSSTREAM_SEGMENT_EXT)CoTaskMemAlloc(sizeof(KSSTREAM_SEGMENT_EXT));
152 if (!StreamSegment)
153 return E_OUTOFMEMORY;
154
155 // zero stream segment
156 ZeroMemory(StreamSegment, sizeof(KSSTREAM_SEGMENT_EXT));
157
158 //allocate event
159 StreamSegment->StreamSegment.CompletionEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
160
161 if (!StreamSegment->StreamSegment.CompletionEvent)
162 {
163 // failed to create event
164 CoTaskMemFree(StreamSegment);
165 return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
166 }
167
168 // increase our own reference count
169 AddRef();
170
171 // setup stream segment
172 StreamSegment->StreamSegment.KsDataTypeHandler = KsDataTypeHandler;
173 StreamSegment->StreamSegment.KsInterfaceHandler = (IKsInterfaceHandler*)this;
174 StreamSegment->StreamSegment.IoOperation = IoOperation;
175 StreamSegment->Overlapped.hEvent = StreamSegment->StreamSegment.CompletionEvent;
176
177
178 // ge extension size
179 ExtendedSize = 0;
180 if (KsDataTypeHandler)
181 {
182 // query extension size
183 KsDataTypeHandler->KsQueryExtendedSize(&ExtendedSize);
184
185 if (ExtendedSize)
186 {
187 // increment reference count
188 KsDataTypeHandler->AddRef();
189 }
190 else
191 {
192 // no need for the datatype handler
193 StreamSegment->StreamSegment.KsDataTypeHandler = NULL;
194 }
195 }
196
197 StreamSegment->ExtendedSize = ExtendedSize;
198 StreamSegment->SampleCount = (ULONG)*SampleCount;
199
200 // calculate stream header size count
201 ULONG StreamHeaderSize = StreamSegment->SampleCount * (sizeof(KSSTREAM_HEADER) + ExtendedSize);
202
203 // allocate stream header
204 StreamSegment->StreamHeader = (PKSSTREAM_HEADER)CoTaskMemAlloc(StreamHeaderSize);
205 if (!StreamSegment->StreamHeader)
206 {
207 // not enough memory
208 CloseHandle(StreamSegment->StreamSegment.CompletionEvent);
209
210 if (StreamSegment->StreamSegment.KsDataTypeHandler)
211 StreamSegment->StreamSegment.KsDataTypeHandler->Release();
212
213 // free stream segment
214 CoTaskMemFree(StreamSegment);
215
216 //release our reference count
217 Release();
218 return E_OUTOFMEMORY;
219 }
220
221 // zero stream headers
222 ZeroMemory(StreamSegment->StreamHeader, StreamHeaderSize);
223
224 PKSSTREAM_HEADER CurStreamHeader = StreamSegment->StreamHeader;
225
226 // initialize all stream headers
227 for(Index = 0; Index < StreamSegment->SampleCount; Index++)
228 {
229 if (ExtendedSize)
230 {
231 // initialize extended size
232 hr = KsDataTypeHandler->KsPrepareIoOperation(SampleList[Index], (CurStreamHeader + 1), IoOperation);
233 // sanity check
234 assert(hr == NOERROR);
235 }
236
237 // query for IMediaSample2 interface
238 IMediaSample2 * MediaSample;
239 AM_SAMPLE2_PROPERTIES Properties;
240
241 hr = SampleList[Index]->QueryInterface(IID_IMediaSample2, (void**)&MediaSample);
242 if (SUCCEEDED(hr))
243 {
244 //get properties
245
246 hr = MediaSample->GetProperties(sizeof(AM_SAMPLE2_PROPERTIES), (BYTE*)&Properties);
247
248 //release IMediaSample2 interface
249 MediaSample->Release();
250 if (FAILED(hr))
251 OutputDebugStringW(L"CKsInterfaceHandler::KsProcessMediaSamples MediaSample::GetProperties failed\n");
252 }
253 else
254 {
255 OutputDebugStringW(L"CKsInterfaceHandler::KsProcessMediaSamples MediaSample:: only IMediaSample supported\n");
256 // get properties
257 hr = SampleList[Index]->GetPointer((BYTE**)&Properties.pbBuffer);
258 assert(hr == NOERROR);
259 hr = SampleList[Index]->GetTime(&Properties.tStart, &Properties.tStop);
260 assert(hr == NOERROR);
261
262 Properties.dwSampleFlags = 0;
263
264 if (SampleList[Index]->IsDiscontinuity() == S_OK)
265 Properties.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;
266
267 if (SampleList[Index]->IsPreroll() == S_OK)
268 Properties.dwSampleFlags |= AM_SAMPLE_PREROLL;
269
270 if (SampleList[Index]->IsSyncPoint() == S_OK)
271 Properties.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT;
272 }
273
274 WCHAR Buffer[100];
275 swprintf(Buffer, L"BufferLength %lu Property Buffer %p ExtendedSize %u lActual %u\n", Properties.cbBuffer, Properties.pbBuffer, ExtendedSize, Properties.lActual);
276 OutputDebugStringW(Buffer);
277
278 CurStreamHeader->Size = sizeof(KSSTREAM_HEADER) + ExtendedSize;
279 CurStreamHeader->PresentationTime.Denominator = 1;
280 CurStreamHeader->PresentationTime.Numerator = 1;
281 CurStreamHeader->FrameExtent = Properties.cbBuffer;
282 CurStreamHeader->Data = Properties.pbBuffer;
283
284 if (IoOperation == KsIoOperation_Write)
285 {
286 // set flags
287 CurStreamHeader->OptionsFlags = Properties.dwSampleFlags;
288 CurStreamHeader->DataUsed = Properties.lActual;
289 // increment reference count
290 SampleList[Index]->AddRef();
291 }
292
293 // store sample in stream segment
294 StreamSegment->MediaSample[Index] = SampleList[Index];
295
296 // move to next header
297 CurStreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)CurStreamHeader + CurStreamHeader->Size);
298 }
299
300 // submit to device
301 m_Pin->KsIncrementPendingIoCount();
302
303 if (DeviceIoControl(m_Handle,
304 IoOperation == KsIoOperation_Write ? IOCTL_KS_WRITE_STREAM : IOCTL_KS_READ_STREAM,
305 NULL, 0,
306 StreamSegment->StreamHeader,
307 StreamHeaderSize,
308 &BytesReturned,
309 &StreamSegment->Overlapped))
310 {
311 // signal completion
312 SetEvent(StreamSegment->StreamSegment.CompletionEvent);
313 hr = S_OK;
314 *OutStreamSegment = (PKSSTREAM_SEGMENT)StreamSegment;
315 }
316 else
317 {
318 if (GetLastError() == ERROR_IO_PENDING)
319 {
320 *OutStreamSegment = (PKSSTREAM_SEGMENT)StreamSegment;
321 hr = S_OK;
322 }
323 }
324 return hr;
325 }
326
327 HRESULT
328 STDMETHODCALLTYPE
329 CKsInterfaceHandler::KsCompleteIo(
330 PKSSTREAM_SEGMENT InStreamSegment)
331 {
332 PKSSTREAM_SEGMENT_EXT StreamSegment;
333 PKSSTREAM_HEADER CurStreamHeader;
334 DWORD dwError = ERROR_SUCCESS, BytesReturned;
335 BOOL bOverlapped;
336 ULONG Index;
337 HRESULT hr;
338 IMediaSample2 * MediaSample;
339 AM_SAMPLE2_PROPERTIES Properties;
340 REFERENCE_TIME Start, Stop;
341
342 OutputDebugStringW(L"CKsInterfaceHandler::KsCompleteIo\n");
343
344 // get private stream segment
345 StreamSegment = (PKSSTREAM_SEGMENT_EXT)InStreamSegment;
346
347 // get result
348 bOverlapped = GetOverlappedResult(m_Handle, &StreamSegment->Overlapped, &BytesReturned, FALSE);
349 dwError = GetLastError();
350
351 CurStreamHeader = StreamSegment->StreamHeader;
352
353 //iterate through all stream headers
354 for(Index = 0; Index < StreamSegment->SampleCount; Index++)
355 {
356 if (!bOverlapped)
357 {
358 // operation failed
359 m_Pin->KsNotifyError(StreamSegment->MediaSample[Index], MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwError));
360 }
361
362 // query IMediaSample2 interface
363 hr = StreamSegment->MediaSample[Index]->QueryInterface(IID_IMediaSample2, (void**)&MediaSample);
364 if (SUCCEEDED(hr))
365 {
366 // media sample properties
367 hr = MediaSample->GetProperties(sizeof(AM_SAMPLE2_PROPERTIES), (BYTE*)&Properties);
368 if (SUCCEEDED(hr))
369 {
370 //update media sample properties
371 Properties.dwTypeSpecificFlags = CurStreamHeader->TypeSpecificFlags;
372 Properties.dwSampleFlags |= (CurStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEDISCONTINUITY);
373
374 MediaSample->SetProperties(sizeof(AM_SAMPLE2_PROPERTIES), (BYTE*)&Properties);
375 }
376 // release IMediaSample2 interface
377 MediaSample->Release();
378 }
379
380 // was an extended header used
381 if (StreamSegment->ExtendedSize)
382 {
383 // unprepare stream header extension
384 StreamSegment->StreamSegment.KsDataTypeHandler->KsCompleteIoOperation(StreamSegment->MediaSample[Index], (CurStreamHeader + 1), StreamSegment->StreamSegment.IoOperation, bOverlapped == FALSE);
385 }
386
387 Start = 0;
388 Stop = 0;
389 if (bOverlapped && StreamSegment->StreamSegment.IoOperation == KsIoOperation_Read)
390 {
391 // update common media sample details
392 StreamSegment->MediaSample[Index]->SetSyncPoint((CurStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_SPLICEPOINT));
393 StreamSegment->MediaSample[Index]->SetPreroll((CurStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_PREROLL));
394 StreamSegment->MediaSample[Index]->SetDiscontinuity((CurStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY));
395
396 if (CurStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEVALID)
397 {
398 // use valid timestamp
399 Start = CurStreamHeader->PresentationTime.Time;
400
401 if (CurStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_DURATIONVALID)
402 {
403 Stop = CurStreamHeader->PresentationTime.Time + CurStreamHeader->Duration;
404 }
405 }
406 }
407
408 // now set time
409 hr = StreamSegment->MediaSample[Index]->SetTime(&Start, &Stop);
410 if (FAILED(hr))
411 {
412 // use start time
413 StreamSegment->MediaSample[Index]->SetTime(&Start, &Start);
414 }
415
416 // set valid data length
417 StreamSegment->MediaSample[Index]->SetActualDataLength(CurStreamHeader->DataUsed);
418
419 if (StreamSegment->StreamSegment.IoOperation == KsIoOperation_Read)
420 {
421 if (bOverlapped)
422 {
423 // deliver sample
424 m_Pin->KsDeliver(StreamSegment->MediaSample[Index], CurStreamHeader->OptionsFlags);
425 }
426 }
427 else if (StreamSegment->StreamSegment.IoOperation == KsIoOperation_Write)
428 {
429 // release media sample reference
430 StreamSegment->MediaSample[Index]->Release();
431 }
432
433 CurStreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)CurStreamHeader + CurStreamHeader->Size);
434 }
435
436 // delete stream headers
437 CoTaskMemFree(StreamSegment->StreamHeader);
438
439 if (StreamSegment->StreamSegment.KsDataTypeHandler)
440 {
441 // release reference
442 StreamSegment->StreamSegment.KsDataTypeHandler->Release();
443 }
444
445 // decrement pending i/o count
446 m_Pin->KsDecrementPendingIoCount();
447
448 //notify of completion
449 m_Pin->KsMediaSamplesCompleted(InStreamSegment);
450
451 //destroy stream segment
452 CoTaskMemFree(StreamSegment);
453
454 //release reference to ourselves
455 Release();
456
457 // done
458 // Event handle is closed by caller
459 return S_OK;
460 }
461
462 HRESULT
463 WINAPI
464 CKsInterfaceHandler_Constructor(
465 IUnknown * pUnkOuter,
466 REFIID riid,
467 LPVOID * ppv)
468 {
469 OutputDebugStringW(L"CKsInterfaceHandler_Constructor\n");
470
471 CKsInterfaceHandler * handler = new CKsInterfaceHandler();
472
473 if (!handler)
474 return E_OUTOFMEMORY;
475
476 if (FAILED(handler->QueryInterface(riid, ppv)))
477 {
478 /* not supported */
479 delete handler;
480 return E_NOINTERFACE;
481 }
482
483 return NOERROR;
484 }