45cc04342bef19001c7f18621fc17bed7e64a0d7
[reactos.git] / reactos / dll / directx / ksproxy / allocator.cpp
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 (janderwald@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_Commited;
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_Commited(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 OutputDebugStringW(L"CKsAllocator::SetProperties\n");
144
145 if (!pRequest || !pActual)
146 return E_POINTER;
147
148 // zero output properties
149 ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));
150
151 // get system info
152 GetSystemInfo(&SystemInfo);
153
154 if (!pRequest->cbAlign || (pRequest->cbAlign - 1) & SystemInfo.dwAllocationGranularity)
155 {
156 // bad alignment
157 LeaveCriticalSection(&m_CriticalSection);
158 return VFW_E_BADALIGN;
159 }
160
161 if (m_Mode == KsAllocatorMode_Kernel)
162 {
163 // u can't change a kernel allocator
164 LeaveCriticalSection(&m_CriticalSection);
165 return VFW_E_ALREADY_COMMITTED;
166 }
167
168 if (m_Commited)
169 {
170 // need to decommit first
171 LeaveCriticalSection(&m_CriticalSection);
172 return VFW_E_ALREADY_COMMITTED;
173 }
174
175 if (m_Allocated != m_FreeList.size())
176 {
177 // outstanding buffers
178 LeaveCriticalSection(&m_CriticalSection);
179 return VFW_E_BUFFERS_OUTSTANDING;
180 }
181
182 pActual->cbAlign = m_cbAlign = pRequest->cbAlign;
183 pActual->cbBuffer = m_cbBuffer = pRequest->cbBuffer;
184 pActual->cbPrefix = m_cbPrefix = pRequest->cbPrefix;
185 pActual->cBuffers = m_cBuffers = pRequest->cBuffers;
186
187 LeaveCriticalSection(&m_CriticalSection);
188 return NOERROR;
189 }
190
191 HRESULT
192 STDMETHODCALLTYPE
193 CKsAllocator::GetProperties(
194 ALLOCATOR_PROPERTIES *pProps)
195 {
196 if (!pProps)
197 return E_POINTER;
198
199 pProps->cbBuffer = m_cbBuffer;
200 pProps->cBuffers = m_cBuffers;
201 pProps->cbAlign = m_cbAlign;
202 pProps->cbPrefix = m_cbPrefix;
203
204 return NOERROR;
205 }
206
207 HRESULT
208 STDMETHODCALLTYPE
209 CKsAllocator::Commit()
210 {
211 LONG Index;
212 PUCHAR CurrentBuffer;
213 IMediaSample * Sample;
214 HRESULT hr;
215
216 //TODO integer overflow checks
217 EnterCriticalSection(&m_CriticalSection);
218
219 OutputDebugStringW(L"CKsAllocator::Commit\n");
220
221 if (m_Mode == KsAllocatorMode_Kernel)
222 {
223 /* no-op for kernel allocator */
224 LeaveCriticalSection(&m_CriticalSection);
225 return NOERROR;
226 }
227
228 if (m_Commited)
229 {
230 // already commited
231 LeaveCriticalSection(&m_CriticalSection);
232 return NOERROR;
233 }
234
235 if (m_cbBuffer < 0 || m_cBuffers < 0 || m_cbPrefix < 0)
236 {
237 // invalid parameter
238 LeaveCriticalSection(&m_CriticalSection);
239 return E_OUTOFMEMORY;
240 }
241
242 LONG Size = m_cbBuffer + m_cbPrefix;
243
244 if (m_cbAlign > 1)
245 {
246 //check alignment
247 LONG Mod = Size % m_cbAlign;
248 if (Mod)
249 {
250 // calculate aligned size
251 Size += m_cbAlign - Mod;
252 }
253 }
254
255 LONG TotalSize = Size * m_cBuffers;
256
257 assert(TotalSize);
258 assert(m_cBuffers);
259 assert(Size);
260
261 // now allocate buffer
262 m_Buffer = VirtualAlloc(NULL, TotalSize, MEM_COMMIT, PAGE_READWRITE);
263 if (!m_Buffer)
264 {
265 LeaveCriticalSection(&m_CriticalSection);
266 return E_OUTOFMEMORY;
267 }
268
269 ZeroMemory(m_Buffer, TotalSize);
270
271 CurrentBuffer = (PUCHAR)m_Buffer;
272
273 for (Index = 0; Index < m_cBuffers; Index++)
274 {
275 // construct media sample
276 hr = CMediaSample_Constructor((IMemAllocator*)this, CurrentBuffer + m_cbPrefix, m_cbBuffer, IID_IMediaSample, (void**)&Sample);
277 if (FAILED(hr))
278 {
279 LeaveCriticalSection(&m_CriticalSection);
280 return E_OUTOFMEMORY;
281 }
282
283 // add to free list
284 m_FreeList.push(Sample);
285 m_Allocated++;
286
287 //next sample buffer
288 CurrentBuffer += Size;
289 }
290
291 // we are now commited
292 m_Commited = true;
293
294 LeaveCriticalSection(&m_CriticalSection);
295 return S_OK;
296 }
297
298 HRESULT
299 STDMETHODCALLTYPE
300 CKsAllocator::Decommit()
301 {
302 EnterCriticalSection(&m_CriticalSection);
303
304 OutputDebugStringW(L"CKsAllocator::Decommit\n");
305
306 if (m_Mode == KsAllocatorMode_Kernel)
307 {
308 /* no-op for kernel allocator */
309 LeaveCriticalSection(&m_CriticalSection);
310 return NOERROR;
311 }
312
313 m_Commited = false;
314
315 if (m_Allocated != m_FreeList.size())
316 {
317 // outstanding buffers
318 m_FreeSamples = true;
319 LeaveCriticalSection(&m_CriticalSection);
320 return NOERROR;
321 }
322 else
323 {
324 // no outstanding buffers
325 // free to free them
326 FreeMediaSamples();
327 }
328
329 LeaveCriticalSection(&m_CriticalSection);
330 return NOERROR;
331 }
332
333
334 HRESULT
335 STDMETHODCALLTYPE
336 CKsAllocator::GetBuffer(
337 IMediaSample **ppBuffer,
338 REFERENCE_TIME *pStartTime,
339 REFERENCE_TIME *pEndTime,
340 DWORD dwFlags)
341 {
342 IMediaSample * Sample = NULL;
343
344 if (!m_Commited)
345 return VFW_E_NOT_COMMITTED;
346
347 do
348 {
349 EnterCriticalSection(&m_CriticalSection);
350
351 if (!m_FreeList.empty())
352 {
353 Sample = m_FreeList.top();
354 m_FreeList.pop();
355 }
356
357 LeaveCriticalSection(&m_CriticalSection);
358
359 if (dwFlags & AM_GBF_NOWAIT)
360 {
361 // never wait untill a buffer becomes available
362 break;
363 }
364 }
365 while(Sample == NULL);
366
367 if (!Sample)
368 {
369 // no sample acquired
370 //HACKKKKKKK
371 Sample = m_UsedList.back();
372 m_UsedList.pop_back();
373
374 if (!Sample)
375 return VFW_E_TIMEOUT;
376 }
377
378 // store result
379 *ppBuffer = Sample;
380
381 // store sample in used list
382 m_UsedList.push_front(Sample);
383
384 // done
385 return NOERROR;
386 }
387
388 HRESULT
389 STDMETHODCALLTYPE
390 CKsAllocator::ReleaseBuffer(
391 IMediaSample *pBuffer)
392 {
393 EnterCriticalSection(&m_CriticalSection);
394
395 OutputDebugStringW(L"CKsAllocator::ReleaseBuffer\n");
396
397 // media sample always 1 ref count in free list
398 pBuffer->AddRef();
399
400 // add the sample to the free list
401 m_FreeList.push(pBuffer);
402
403
404 if (m_FreeSamples)
405 {
406 // pending de-commit
407 if (m_FreeList.size () == m_Allocated)
408 {
409 FreeMediaSamples();
410 }
411 }
412
413 if (m_Notify)
414 {
415 //notify caller of an available buffer
416 m_Notify->NotifyRelease();
417 }
418
419 LeaveCriticalSection(&m_CriticalSection);
420 return S_OK;
421 }
422
423 //-------------------------------------------------------------------
424 // IMemAllocatorCallbackTemp
425 //
426 HRESULT
427 STDMETHODCALLTYPE
428 CKsAllocator::SetNotify(
429 IMemAllocatorNotifyCallbackTemp *pNotify)
430 {
431 EnterCriticalSection(&m_CriticalSection);
432 OutputDebugStringW(L"CKsAllocator::SetNotify\n");
433
434 if (pNotify)
435 pNotify->AddRef();
436
437 if (m_Notify)
438 m_Notify->Release();
439
440 m_Notify = pNotify;
441
442 LeaveCriticalSection(&m_CriticalSection);
443 return NOERROR;
444 }
445
446 HRESULT
447 STDMETHODCALLTYPE
448 CKsAllocator::GetFreeCount(
449 LONG *plBuffersFree)
450 {
451 *plBuffersFree = m_Allocated - m_FreeList.size();
452 return S_OK;
453 }
454
455 //-------------------------------------------------------------------
456 // IKsAllocator
457 //
458 HANDLE
459 STDMETHODCALLTYPE
460 CKsAllocator::KsGetAllocatorHandle()
461 {
462 return m_hAllocator;
463 }
464
465 KSALLOCATORMODE
466 STDMETHODCALLTYPE
467 CKsAllocator::KsGetAllocatorMode()
468 {
469 return m_Mode;
470 }
471
472 HRESULT
473 STDMETHODCALLTYPE
474 CKsAllocator::KsGetAllocatorStatus(
475 PKSSTREAMALLOCATOR_STATUS AllocatorStatus)
476 {
477 return NOERROR;
478 }
479 VOID
480 STDMETHODCALLTYPE
481 CKsAllocator::KsSetAllocatorMode(
482 KSALLOCATORMODE Mode)
483 {
484 m_Mode = Mode;
485 }
486
487 //-------------------------------------------------------------------
488 // IKsAllocatorEx
489 //
490 PALLOCATOR_PROPERTIES_EX
491 STDMETHODCALLTYPE
492 CKsAllocator::KsGetProperties()
493 {
494 return &m_Properties;
495 }
496
497 VOID
498 STDMETHODCALLTYPE
499 CKsAllocator::KsSetProperties(
500 PALLOCATOR_PROPERTIES_EX Properties)
501 {
502 CopyMemory(&m_Properties, Properties, sizeof(ALLOCATOR_PROPERTIES_EX));
503 }
504
505 VOID
506 STDMETHODCALLTYPE
507 CKsAllocator::KsSetAllocatorHandle(
508 HANDLE AllocatorHandle)
509 {
510 m_hAllocator = AllocatorHandle;
511 }
512
513
514 HANDLE
515 STDMETHODCALLTYPE
516 CKsAllocator::KsCreateAllocatorAndGetHandle(
517 IKsPin* KsPin)
518 {
519 HRESULT hr;
520 IKsObject * pObject;
521 KSALLOCATOR_FRAMING AllocatorFraming;
522 HANDLE hPin;
523
524 OutputDebugStringW(L"CKsAllocator::KsCreateAllocatorAndGetHandle\n");
525
526 if (m_hAllocator)
527 {
528 CloseHandle(m_hAllocator);
529 m_hAllocator = NULL;
530 }
531
532 // get pin IKsObject interface
533 hr = KsPin->QueryInterface(IID_IKsObject, (void**)&pObject);
534 if (FAILED(hr))
535 return NULL;
536
537 // get pin handle
538 hPin = pObject->KsGetObjectHandle();
539
540 //release IKsObject interface
541 pObject->Release();
542
543 if (!hPin || hPin == INVALID_HANDLE_VALUE)
544 return NULL;
545
546 //setup allocator framing
547 AllocatorFraming.Frames = m_Properties.cBuffers;
548 AllocatorFraming.FrameSize = m_Properties.cbBuffer;
549 AllocatorFraming.FileAlignment = (m_Properties.cbAlign -1);
550 AllocatorFraming.OptionsFlags = KSALLOCATOR_OPTIONF_SYSTEM_MEMORY;
551 AllocatorFraming.PoolType = (m_Properties.LogicalMemoryType == KS_MemoryTypeKernelPaged);
552
553 DWORD dwError = KsCreateAllocator(hPin, &AllocatorFraming, &m_hAllocator);
554 if (dwError)
555 return NULL;
556
557 return m_hAllocator;
558 }
559
560 //-------------------------------------------------------------------
561 VOID
562 STDMETHODCALLTYPE
563 CKsAllocator::FreeMediaSamples()
564 {
565 ULONG Index;
566
567 for(Index = 0; Index < m_FreeList.size(); Index++)
568 {
569 IMediaSample * Sample = m_FreeList.top();
570 m_FreeList.pop();
571 delete Sample;
572 }
573
574 m_FreeSamples = false;
575 m_Allocated = 0;
576
577 if (m_Buffer)
578 {
579 // release buffer
580 VirtualFree(m_Buffer, 0, MEM_RELEASE);
581
582 m_Buffer = NULL;
583 }
584 }
585
586 HRESULT
587 WINAPI
588 CKsAllocator_Constructor(
589 IUnknown * pUnkOuter,
590 REFIID riid,
591 LPVOID * ppv)
592 {
593 OutputDebugStringW(L"CKsAllocator_Constructor\n");
594
595 CKsAllocator * handler = new CKsAllocator();
596
597 if (!handler)
598 return E_OUTOFMEMORY;
599
600 if (FAILED(handler->QueryInterface(riid, ppv)))
601 {
602 /* not supported */
603 delete handler;
604 return E_NOINTERFACE;
605 }
606
607 return NOERROR;
608 }