xref: /reactos/dll/directx/ksproxy/allocator.cpp (revision 40462c92)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS WDM Streaming ActiveMovie Proxy
4  * FILE:            dll/directx/ksproxy/allocator.cpp
5  * PURPOSE:         IKsAllocator interface
6  *
7  * PROGRAMMERS:     Johannes Anderwald (johannes.anderwald@reactos.org)
8  */
9 #include "precomp.h"
10 
11 const GUID IID_IKsAllocatorEx = {0x091bb63a, 0x603f, 0x11d1, {0xb0, 0x67, 0x00, 0xa0, 0xc9, 0x06, 0x28, 0x02}};
12 const GUID IID_IKsAllocator   = {0x8da64899, 0xc0d9, 0x11d0, {0x84, 0x13, 0x00, 0x00, 0xf8, 0x22, 0xfe, 0x8a}};
13 
14 class CKsAllocator : public IKsAllocatorEx,
15                      public IMemAllocatorCallbackTemp
16 {
17 public:
18     typedef std::stack<IMediaSample *>MediaSampleStack;
19     typedef std::list<IMediaSample *>MediaSampleList;
20 
21     STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
22 
23     STDMETHODIMP_(ULONG) AddRef()
24     {
25         InterlockedIncrement(&m_Ref);
26         return m_Ref;
27     }
28     STDMETHODIMP_(ULONG) Release()
29     {
30         InterlockedDecrement(&m_Ref);
31 
32         if (!m_Ref)
33         {
34             delete this;
35             return 0;
36         }
37         return m_Ref;
38     }
39     //IKsAllocator
40     HANDLE STDMETHODCALLTYPE KsGetAllocatorHandle();
41     KSALLOCATORMODE STDMETHODCALLTYPE KsGetAllocatorMode();
42     HRESULT STDMETHODCALLTYPE KsGetAllocatorStatus(PKSSTREAMALLOCATOR_STATUS AllocatorStatus);
43     VOID STDMETHODCALLTYPE KsSetAllocatorMode(KSALLOCATORMODE Mode);
44 
45     //IKsAllocatorEx
46     PALLOCATOR_PROPERTIES_EX STDMETHODCALLTYPE KsGetProperties();
47     VOID STDMETHODCALLTYPE KsSetProperties(PALLOCATOR_PROPERTIES_EX Properties);
48     VOID STDMETHODCALLTYPE KsSetAllocatorHandle(HANDLE AllocatorHandle);
49     HANDLE STDMETHODCALLTYPE KsCreateAllocatorAndGetHandle(IKsPin* KsPin);
50 
51     //IMemAllocator
52     HRESULT STDMETHODCALLTYPE SetProperties(ALLOCATOR_PROPERTIES *pRequest, ALLOCATOR_PROPERTIES *pActual);
53     HRESULT STDMETHODCALLTYPE GetProperties(ALLOCATOR_PROPERTIES *pProps);
54     HRESULT STDMETHODCALLTYPE Commit();
55     HRESULT STDMETHODCALLTYPE Decommit();
56     HRESULT STDMETHODCALLTYPE GetBuffer(IMediaSample **ppBuffer, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime, DWORD dwFlags);
57     HRESULT STDMETHODCALLTYPE ReleaseBuffer(IMediaSample *pBuffer);
58 
59     //IMemAllocatorCallbackTemp
60     HRESULT STDMETHODCALLTYPE SetNotify(IMemAllocatorNotifyCallbackTemp *pNotify);
61     HRESULT STDMETHODCALLTYPE GetFreeCount(LONG *plBuffersFree);
62 
63 
64     CKsAllocator();
65     virtual ~CKsAllocator(){}
66     VOID STDMETHODCALLTYPE FreeMediaSamples();
67 protected:
68     LONG m_Ref;
69     HANDLE m_hAllocator;
70     KSALLOCATORMODE m_Mode;
71     ALLOCATOR_PROPERTIES_EX m_Properties;
72     IMemAllocatorNotifyCallbackTemp *m_Notify;
73     ULONG m_Allocated;
74     LONG m_cbBuffer;
75     LONG m_cBuffers;
76     LONG m_cbAlign;
77     LONG m_cbPrefix;
78     BOOL m_Committed;
79     CRITICAL_SECTION m_CriticalSection;
80     MediaSampleStack m_FreeList;
81     MediaSampleList m_UsedList;
82     LPVOID m_Buffer;
83     BOOL m_FreeSamples;
84 };
85 
86 
87 HRESULT
88 STDMETHODCALLTYPE
89 CKsAllocator::QueryInterface(
90     IN  REFIID refiid,
91     OUT PVOID* Output)
92 {
93     if (IsEqualGUID(refiid, IID_IUnknown) ||
94         IsEqualGUID(refiid, IID_IKsAllocator) ||
95         IsEqualGUID(refiid, IID_IKsAllocatorEx))
96     {
97         *Output = PVOID(this);
98         reinterpret_cast<IUnknown*>(*Output)->AddRef();
99         return NOERROR;
100     }
101     if (IsEqualGUID(refiid, IID_IMemAllocator) ||
102         IsEqualGUID(refiid, IID_IMemAllocatorCallbackTemp))
103     {
104         *Output = (IMemAllocatorCallbackTemp*)(this);
105         reinterpret_cast<IMemAllocatorCallbackTemp*>(*Output)->AddRef();
106         return NOERROR;
107     }
108 
109     return E_NOINTERFACE;
110 }
111 
112 CKsAllocator::CKsAllocator() : m_Ref(0),
113                                m_hAllocator(0),
114                                m_Mode(KsAllocatorMode_User),
115                                m_Notify(0),
116                                m_Allocated(0),
117                                m_cbBuffer(0),
118                                m_cBuffers(0),
119                                m_cbAlign(0),
120                                m_cbPrefix(0),
121                                m_Committed(FALSE),
122                                m_FreeList(),
123                                m_UsedList(),
124                                m_Buffer(0),
125                                m_FreeSamples(FALSE)
126 {
127    InitializeCriticalSection(&m_CriticalSection);
128 
129 }
130 
131 //-------------------------------------------------------------------
132 // IMemAllocator
133 //
134 HRESULT
135 STDMETHODCALLTYPE
136 CKsAllocator::SetProperties(
137     ALLOCATOR_PROPERTIES *pRequest,
138     ALLOCATOR_PROPERTIES *pActual)
139 {
140     SYSTEM_INFO SystemInfo;
141 
142     EnterCriticalSection(&m_CriticalSection);
143 
144 #ifdef KSPROXY_TRACE
145     OutputDebugStringW(L"CKsAllocator::SetProperties\n");
146 #endif
147 
148     if (!pRequest || !pActual)
149         return E_POINTER;
150 
151     // zero output properties
152     ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));
153 
154     // get system info
155     GetSystemInfo(&SystemInfo);
156 
157     if (!pRequest->cbAlign || (pRequest->cbAlign - 1) & SystemInfo.dwAllocationGranularity)
158     {
159         // bad alignment
160         LeaveCriticalSection(&m_CriticalSection);
161         return VFW_E_BADALIGN;
162     }
163 
164     if (m_Mode == KsAllocatorMode_Kernel)
165     {
166         // u can't change a kernel allocator
167         LeaveCriticalSection(&m_CriticalSection);
168         return VFW_E_ALREADY_COMMITTED;
169     }
170 
171     if (m_Committed)
172     {
173         // need to decommit first
174         LeaveCriticalSection(&m_CriticalSection);
175         return VFW_E_ALREADY_COMMITTED;
176     }
177 
178     if (m_Allocated != m_FreeList.size())
179     {
180         // outstanding buffers
181         LeaveCriticalSection(&m_CriticalSection);
182         return VFW_E_BUFFERS_OUTSTANDING;
183     }
184 
185     pActual->cbAlign = m_cbAlign = pRequest->cbAlign;
186     pActual->cbBuffer = m_cbBuffer = pRequest->cbBuffer;
187     pActual->cbPrefix = m_cbPrefix = pRequest->cbPrefix;
188     pActual->cBuffers = m_cBuffers = pRequest->cBuffers;
189 
190     LeaveCriticalSection(&m_CriticalSection);
191     return NOERROR;
192 }
193 
194 HRESULT
195 STDMETHODCALLTYPE
196 CKsAllocator::GetProperties(
197     ALLOCATOR_PROPERTIES *pProps)
198 {
199     if (!pProps)
200         return E_POINTER;
201 
202     pProps->cbBuffer = m_cbBuffer;
203     pProps->cBuffers = m_cBuffers;
204     pProps->cbAlign = m_cbAlign;
205     pProps->cbPrefix = m_cbPrefix;
206 
207     return NOERROR;
208 }
209 
210 HRESULT
211 STDMETHODCALLTYPE
212 CKsAllocator::Commit()
213 {
214     LONG Index;
215     PUCHAR CurrentBuffer;
216     IMediaSample * Sample;
217     HRESULT hr;
218 
219     //TODO integer overflow checks
220     EnterCriticalSection(&m_CriticalSection);
221 
222 #ifdef KSPROXY_TRACE
223     OutputDebugStringW(L"CKsAllocator::Commit\n");
224 #endif
225 
226     if (m_Mode == KsAllocatorMode_Kernel)
227     {
228         /* no-op for kernel allocator */
229        LeaveCriticalSection(&m_CriticalSection);
230        return NOERROR;
231     }
232 
233     if (m_Committed)
234     {
235         // already committed
236         LeaveCriticalSection(&m_CriticalSection);
237         return NOERROR;
238     }
239 
240     if (m_cbBuffer < 0 || m_cBuffers < 0 || m_cbPrefix < 0)
241     {
242         // invalid parameter
243         LeaveCriticalSection(&m_CriticalSection);
244         return E_OUTOFMEMORY;
245     }
246 
247     LONG Size = m_cbBuffer + m_cbPrefix;
248 
249     if (m_cbAlign > 1)
250     {
251         //check alignment
252         LONG Mod = Size % m_cbAlign;
253         if (Mod)
254         {
255             // calculate aligned size
256             Size += m_cbAlign - Mod;
257         }
258     }
259 
260     LONG TotalSize = Size * m_cBuffers;
261 
262     assert(TotalSize);
263     assert(m_cBuffers);
264     assert(Size);
265 
266     // now allocate buffer
267     m_Buffer = VirtualAlloc(NULL, TotalSize, MEM_COMMIT, PAGE_READWRITE);
268     if (!m_Buffer)
269     {
270         LeaveCriticalSection(&m_CriticalSection);
271         return E_OUTOFMEMORY;
272     }
273 
274     ZeroMemory(m_Buffer, TotalSize);
275 
276     CurrentBuffer = (PUCHAR)m_Buffer;
277 
278     for (Index = 0; Index < m_cBuffers; Index++)
279     {
280         // construct media sample
281         hr = CMediaSample_Constructor((IMemAllocator*)this, CurrentBuffer + m_cbPrefix, m_cbBuffer, IID_IMediaSample, (void**)&Sample);
282         if (FAILED(hr))
283         {
284             LeaveCriticalSection(&m_CriticalSection);
285             return E_OUTOFMEMORY;
286         }
287 
288         // add to free list
289         m_FreeList.push(Sample);
290         m_Allocated++;
291 
292         //next sample buffer
293         CurrentBuffer += Size;
294     }
295 
296     // we are now committed
297     m_Committed = true;
298 
299     LeaveCriticalSection(&m_CriticalSection);
300     return S_OK;
301 }
302 
303 HRESULT
304 STDMETHODCALLTYPE
305 CKsAllocator::Decommit()
306 {
307     EnterCriticalSection(&m_CriticalSection);
308 
309 #ifdef KSPROXY_TRACE
310     OutputDebugStringW(L"CKsAllocator::Decommit\n");
311 #endif
312 
313     if (m_Mode == KsAllocatorMode_Kernel)
314     {
315         /* no-op for kernel allocator */
316         LeaveCriticalSection(&m_CriticalSection);
317         return NOERROR;
318     }
319 
320     m_Committed = false;
321 
322     if (m_Allocated != m_FreeList.size())
323     {
324         // outstanding buffers
325         m_FreeSamples = true;
326         LeaveCriticalSection(&m_CriticalSection);
327         return NOERROR;
328     }
329     else
330     {
331         // no outstanding buffers
332         // free to free them
333         FreeMediaSamples();
334     }
335 
336     LeaveCriticalSection(&m_CriticalSection);
337     return NOERROR;
338 }
339 
340 
341 HRESULT
342 STDMETHODCALLTYPE
343 CKsAllocator::GetBuffer(
344     IMediaSample **ppBuffer,
345     REFERENCE_TIME *pStartTime,
346     REFERENCE_TIME *pEndTime,
347     DWORD dwFlags)
348 {
349     IMediaSample * Sample = NULL;
350 
351     if (!m_Committed)
352         return VFW_E_NOT_COMMITTED;
353 
354     do
355     {
356         EnterCriticalSection(&m_CriticalSection);
357 
358         if (!m_FreeList.empty())
359         {
360             OutputDebugStringW(L"CKsAllocator::GetBuffer HACK\n");
361             Sample = m_FreeList.top();
362             m_FreeList.pop();
363         }
364 
365         LeaveCriticalSection(&m_CriticalSection);
366 
367         if (dwFlags & AM_GBF_NOWAIT)
368         {
369             // never wait untill a buffer becomes available
370             break;
371         }
372     }
373     while(Sample == NULL);
374 
375     if (!Sample)
376     {
377         // no sample acquired
378         //HACKKKKKKK
379         Sample = m_UsedList.back();
380         m_UsedList.pop_back();
381 
382         if (!Sample)
383             return VFW_E_TIMEOUT;
384     }
385 
386     // store result
387     *ppBuffer = Sample;
388 
389    // store sample in used list
390    m_UsedList.push_front(Sample);
391 
392     // done
393     return NOERROR;
394 }
395 
396 HRESULT
397 STDMETHODCALLTYPE
398 CKsAllocator::ReleaseBuffer(
399     IMediaSample *pBuffer)
400 {
401     EnterCriticalSection(&m_CriticalSection);
402 
403 #ifdef KSPROXY_TRACE
404     OutputDebugStringW(L"CKsAllocator::ReleaseBuffer\n");
405 #endif
406 
407     // media sample always 1 ref count in free list
408     pBuffer->AddRef();
409 
410     // add the sample to the free list
411     m_FreeList.push(pBuffer);
412 
413 
414     if (m_FreeSamples)
415     {
416         // pending de-commit
417         if (m_FreeList.size () == m_Allocated)
418         {
419             FreeMediaSamples();
420         }
421     }
422 
423     if (m_Notify)
424     {
425         //notify caller of an available buffer
426         m_Notify->NotifyRelease();
427     }
428 
429     LeaveCriticalSection(&m_CriticalSection);
430     return S_OK;
431 }
432 
433 //-------------------------------------------------------------------
434 // IMemAllocatorCallbackTemp
435 //
436 HRESULT
437 STDMETHODCALLTYPE
438 CKsAllocator::SetNotify(
439     IMemAllocatorNotifyCallbackTemp *pNotify)
440 {
441     EnterCriticalSection(&m_CriticalSection);
442 
443 #ifdef KSPROXY_TRACE
444     OutputDebugStringW(L"CKsAllocator::SetNotify\n");
445 #endif
446 
447     if (pNotify)
448         pNotify->AddRef();
449 
450     if (m_Notify)
451         m_Notify->Release();
452 
453     m_Notify = pNotify;
454 
455     LeaveCriticalSection(&m_CriticalSection);
456     return NOERROR;
457 }
458 
459 HRESULT
460 STDMETHODCALLTYPE
461 CKsAllocator::GetFreeCount(
462     LONG *plBuffersFree)
463 {
464     *plBuffersFree = m_Allocated - m_FreeList.size();
465     return S_OK;
466 }
467 
468 //-------------------------------------------------------------------
469 // IKsAllocator
470 //
471 HANDLE
472 STDMETHODCALLTYPE
473 CKsAllocator::KsGetAllocatorHandle()
474 {
475     return m_hAllocator;
476 }
477 
478 KSALLOCATORMODE
479 STDMETHODCALLTYPE
480 CKsAllocator::KsGetAllocatorMode()
481 {
482     return m_Mode;
483 }
484 
485 HRESULT
486 STDMETHODCALLTYPE
487 CKsAllocator::KsGetAllocatorStatus(
488     PKSSTREAMALLOCATOR_STATUS AllocatorStatus)
489 {
490     return NOERROR;
491 }
492 VOID
493 STDMETHODCALLTYPE
494 CKsAllocator::KsSetAllocatorMode(
495     KSALLOCATORMODE Mode)
496 {
497     m_Mode = Mode;
498 }
499 
500 //-------------------------------------------------------------------
501 // IKsAllocatorEx
502 //
503 PALLOCATOR_PROPERTIES_EX
504 STDMETHODCALLTYPE
505 CKsAllocator::KsGetProperties()
506 {
507     return &m_Properties;
508 }
509 
510 VOID
511 STDMETHODCALLTYPE
512 CKsAllocator::KsSetProperties(
513     PALLOCATOR_PROPERTIES_EX Properties)
514 {
515     CopyMemory(&m_Properties, Properties, sizeof(ALLOCATOR_PROPERTIES_EX));
516 }
517 
518 VOID
519 STDMETHODCALLTYPE
520 CKsAllocator::KsSetAllocatorHandle(
521     HANDLE AllocatorHandle)
522 {
523     m_hAllocator = AllocatorHandle;
524 }
525 
526 
527 HANDLE
528 STDMETHODCALLTYPE
529 CKsAllocator::KsCreateAllocatorAndGetHandle(
530     IKsPin* KsPin)
531 {
532     HRESULT hr;
533     IKsObject * pObject;
534     KSALLOCATOR_FRAMING AllocatorFraming;
535     HANDLE hPin;
536 
537 #ifdef KSPROXY_TRACE
538     OutputDebugStringW(L"CKsAllocator::KsCreateAllocatorAndGetHandle\n");
539 #endif
540 
541     if (m_hAllocator)
542     {
543         CloseHandle(m_hAllocator);
544         m_hAllocator = NULL;
545     }
546 
547     // get pin IKsObject interface
548     hr = KsPin->QueryInterface(IID_IKsObject, (void**)&pObject);
549     if (FAILED(hr))
550         return NULL;
551 
552     // get pin handle
553     hPin = pObject->KsGetObjectHandle();
554 
555     //release IKsObject interface
556     pObject->Release();
557 
558     if (!hPin || hPin == INVALID_HANDLE_VALUE)
559         return NULL;
560 
561     //setup allocator framing
562     AllocatorFraming.Frames = m_Properties.cBuffers;
563     AllocatorFraming.FrameSize = m_Properties.cbBuffer;
564     AllocatorFraming.FileAlignment = (m_Properties.cbAlign -1);
565     AllocatorFraming.OptionsFlags = KSALLOCATOR_OPTIONF_SYSTEM_MEMORY;
566     AllocatorFraming.PoolType = (m_Properties.LogicalMemoryType == KS_MemoryTypeKernelPaged);
567 
568     DWORD dwError = KsCreateAllocator(hPin, &AllocatorFraming, &m_hAllocator);
569     if (dwError)
570         return NULL;
571 
572     return m_hAllocator;
573 }
574 
575 //-------------------------------------------------------------------
576 VOID
577 STDMETHODCALLTYPE
578 CKsAllocator::FreeMediaSamples()
579 {
580     ULONG Index;
581 
582     for(Index = 0; Index < m_FreeList.size(); Index++)
583     {
584         IMediaSample * Sample = m_FreeList.top();
585         m_FreeList.pop();
586         Sample->Release();
587     }
588 
589     m_FreeSamples = false;
590     m_Allocated = 0;
591 
592     if (m_Buffer)
593     {
594         // release buffer
595         VirtualFree(m_Buffer, 0, MEM_RELEASE);
596 
597         m_Buffer = NULL;
598     }
599 }
600 
601 HRESULT
602 WINAPI
603 CKsAllocator_Constructor(
604     IUnknown * pUnkOuter,
605     REFIID riid,
606     LPVOID * ppv)
607 {
608 #ifdef KSPROXY_TRACE
609     OutputDebugStringW(L"CKsAllocator_Constructor\n");
610 #endif
611 
612     CKsAllocator * handler = new CKsAllocator();
613 
614     if (!handler)
615         return E_OUTOFMEMORY;
616 
617     if (FAILED(handler->QueryInterface(riid, ppv)))
618     {
619         /* not supported */
620         delete handler;
621         return E_NOINTERFACE;
622     }
623 
624     return NOERROR;
625 }
626