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