xref: /reactos/dll/directx/ksproxy/interface.cpp (revision c2c66aff)
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 (johannes.anderwald@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 
AddRef()18     STDMETHODIMP_(ULONG) AddRef()
19     {
20         InterlockedIncrement(&m_Ref);
21         return m_Ref;
22     }
Release()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 
CKsInterfaceHandler()38     CKsInterfaceHandler() : m_Ref(0), m_Handle(NULL), m_Pin(0) {m_PinName[0] = L'\0';};
~CKsInterfaceHandler()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
QueryInterface(IN REFIID refiid,OUT PVOID * Output)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
KsSetPin(IKsPin * KsPin)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
KsProcessMediaSamples(IKsDataTypeHandler * KsDataTypeHandler,IMediaSample ** SampleList,PLONG SampleCount,KSIOOPERATION IoOperation,PKSSTREAM_SEGMENT * OutStreamSegment)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
KsCompleteIo(PKSSTREAM_SEGMENT InStreamSegment)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
CKsInterfaceHandler_Constructor(IUnknown * pUnkOuter,REFIID riid,LPVOID * ppv)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 }
501