[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 }
251 else
252 {
253 // get properties
254 hr = SampleList[Index]->GetPointer((BYTE**)&Properties.pbBuffer);
255 assert(hr == NOERROR);
256 hr = SampleList[Index]->GetTime(&Properties.tStart, &Properties.tStop);
257 assert(hr == NOERROR);
258
259 Properties.dwSampleFlags = 0;
260
261 if (SampleList[Index]->IsDiscontinuity() == S_OK)
262 Properties.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;
263
264 if (SampleList[Index]->IsPreroll() == S_OK)
265 Properties.dwSampleFlags |= AM_SAMPLE_PREROLL;
266
267 if (SampleList[Index]->IsSyncPoint() == S_OK)
268 Properties.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT;
269 }
270
271 WCHAR Buffer[100];
272 swprintf(Buffer, L"BufferLength %lu Property Buffer %p ExtendedSize %u lActual %u\n", Properties.cbBuffer, Properties.pbBuffer, ExtendedSize, Properties.lActual);
273 OutputDebugStringW(Buffer);
274
275 CurStreamHeader->Size = sizeof(KSSTREAM_HEADER) + ExtendedSize;
276 CurStreamHeader->PresentationTime.Denominator = 1;
277 CurStreamHeader->PresentationTime.Numerator = 1;
278 CurStreamHeader->FrameExtent = Properties.cbBuffer;
279 CurStreamHeader->Data = Properties.pbBuffer;
280
281 if (IoOperation == KsIoOperation_Write)
282 {
283 // set flags
284 CurStreamHeader->OptionsFlags = Properties.dwSampleFlags;
285 CurStreamHeader->DataUsed = Properties.lActual;
286 // increment reference count
287 SampleList[Index]->AddRef();
288 }
289
290 // store sample in stream segment
291 StreamSegment->MediaSample[Index] = SampleList[Index];
292
293 // move to next header
294 CurStreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)CurStreamHeader + CurStreamHeader->Size);
295 }
296
297 // submit to device
298 m_Pin->KsIncrementPendingIoCount();
299
300 if (DeviceIoControl(m_Handle,
301 IoOperation == KsIoOperation_Write ? IOCTL_KS_WRITE_STREAM : IOCTL_KS_READ_STREAM,
302 NULL, 0,
303 StreamSegment->StreamHeader,
304 StreamHeaderSize,
305 &BytesReturned,
306 &StreamSegment->Overlapped))
307 {
308 // signal completion
309 SetEvent(StreamSegment->StreamSegment.CompletionEvent);
310 hr = S_OK;
311 *OutStreamSegment = (PKSSTREAM_SEGMENT)StreamSegment;
312 }
313 else
314 {
315 if (GetLastError() == ERROR_IO_PENDING)
316 {
317 *OutStreamSegment = (PKSSTREAM_SEGMENT)StreamSegment;
318 hr = S_OK;
319 }
320 }
321 return hr;
322 }
323
324 HRESULT
325 STDMETHODCALLTYPE
326 CKsInterfaceHandler::KsCompleteIo(
327 PKSSTREAM_SEGMENT InStreamSegment)
328 {
329 PKSSTREAM_SEGMENT_EXT StreamSegment;
330 PKSSTREAM_HEADER CurStreamHeader;
331 DWORD dwError = ERROR_SUCCESS, BytesReturned;
332 BOOL bOverlapped;
333 ULONG Index;
334 HRESULT hr;
335 IMediaSample2 * MediaSample;
336 AM_SAMPLE2_PROPERTIES Properties;
337 REFERENCE_TIME Start, Stop;
338
339 OutputDebugStringW(L"CKsInterfaceHandler::KsCompleteIo\n");
340
341 // get private stream segment
342 StreamSegment = (PKSSTREAM_SEGMENT_EXT)InStreamSegment;
343
344 // get result
345 bOverlapped = GetOverlappedResult(m_Handle, &StreamSegment->Overlapped, &BytesReturned, FALSE);
346 dwError = GetLastError();
347
348 CurStreamHeader = StreamSegment->StreamHeader;
349
350 //iterate through all stream headers
351 for(Index = 0; Index < StreamSegment->SampleCount; Index++)
352 {
353 if (!bOverlapped)
354 {
355 // operation failed
356 m_Pin->KsNotifyError(StreamSegment->MediaSample[Index], MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwError));
357 }
358
359 // query IMediaSample2 interface
360 hr = StreamSegment->MediaSample[Index]->QueryInterface(IID_IMediaSample2, (void**)&MediaSample);
361 if (SUCCEEDED(hr))
362 {
363 // media sample properties
364 hr = MediaSample->GetProperties(sizeof(AM_SAMPLE2_PROPERTIES), (BYTE*)&Properties);
365 if (SUCCEEDED(hr))
366 {
367 //update media sample properties
368 Properties.dwTypeSpecificFlags = CurStreamHeader->TypeSpecificFlags;
369 Properties.dwSampleFlags |= (CurStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEDISCONTINUITY);
370
371 MediaSample->SetProperties(sizeof(AM_SAMPLE2_PROPERTIES), (BYTE*)&Properties);
372 }
373 // release IMediaSample2 interface
374 MediaSample->Release();
375 }
376
377 // was an extended header used
378 if (StreamSegment->ExtendedSize)
379 {
380 // unprepare stream header extension
381 StreamSegment->StreamSegment.KsDataTypeHandler->KsCompleteIoOperation(StreamSegment->MediaSample[Index], (CurStreamHeader + 1), StreamSegment->StreamSegment.IoOperation, bOverlapped == FALSE);
382 }
383
384 Start = 0;
385 Stop = 0;
386 if (bOverlapped && StreamSegment->StreamSegment.IoOperation == KsIoOperation_Read)
387 {
388 // update common media sample details
389 StreamSegment->MediaSample[Index]->SetSyncPoint((CurStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_SPLICEPOINT));
390 StreamSegment->MediaSample[Index]->SetPreroll((CurStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_PREROLL));
391 StreamSegment->MediaSample[Index]->SetDiscontinuity((CurStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY));
392
393 if (CurStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEVALID)
394 {
395 // use valid timestamp
396 Start = CurStreamHeader->PresentationTime.Time;
397
398 if (CurStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_DURATIONVALID)
399 {
400 Stop = CurStreamHeader->PresentationTime.Time + CurStreamHeader->Duration;
401 }
402 }
403 }
404
405 // now set time
406 hr = StreamSegment->MediaSample[Index]->SetTime(&Start, &Stop);
407 if (FAILED(hr))
408 {
409 // use start time
410 StreamSegment->MediaSample[Index]->SetTime(&Start, &Start);
411 }
412
413 // set valid data length
414 StreamSegment->MediaSample[Index]->SetActualDataLength(CurStreamHeader->DataUsed);
415
416 if (StreamSegment->StreamSegment.IoOperation == KsIoOperation_Read)
417 {
418 if (bOverlapped)
419 {
420 // deliver sample
421 m_Pin->KsDeliver(StreamSegment->MediaSample[Index], CurStreamHeader->OptionsFlags);
422 }
423 }
424 else if (StreamSegment->StreamSegment.IoOperation == KsIoOperation_Write)
425 {
426 // release media sample reference
427 StreamSegment->MediaSample[Index]->Release();
428 }
429
430 CurStreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)CurStreamHeader + CurStreamHeader->Size);
431 }
432
433 // delete stream headers
434 CoTaskMemFree(StreamSegment->StreamHeader);
435
436 if (StreamSegment->StreamSegment.KsDataTypeHandler)
437 {
438 // release reference
439 StreamSegment->StreamSegment.KsDataTypeHandler->Release();
440 }
441
442 // decrement pending i/o count
443 m_Pin->KsDecrementPendingIoCount();
444
445 //notify of completion
446 m_Pin->KsMediaSamplesCompleted(InStreamSegment);
447
448 //destroy stream segment
449 CoTaskMemFree(StreamSegment);
450
451 //release reference to ourselves
452 Release();
453
454 // done
455 // Event handle is closed by caller
456 return S_OK;
457 }
458
459 HRESULT
460 WINAPI
461 CKsInterfaceHandler_Constructor(
462 IUnknown * pUnkOuter,
463 REFIID riid,
464 LPVOID * ppv)
465 {
466 OutputDebugStringW(L"CKsInterfaceHandler_Constructor\n");
467
468 CKsInterfaceHandler * handler = new CKsInterfaceHandler();
469
470 if (!handler)
471 return E_OUTOFMEMORY;
472
473 if (FAILED(handler->QueryInterface(riid, ppv)))
474 {
475 /* not supported */
476 delete handler;
477 return E_NOINTERFACE;
478 }
479
480 return NOERROR;
481 }