e7c4b1f24365b1f6b977dbe614004dc8f93644b5
[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
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_Commited)
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_Commited)
234 {
235 // already commited
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 commited
297 m_Commited = 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_Commited = 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_Commited)
352 return VFW_E_NOT_COMMITTED;
353
354 do
355 {
356 EnterCriticalSection(&m_CriticalSection);
357
358 if (!m_FreeList.empty())
359 {
360 Sample = m_FreeList.top();
361 m_FreeList.pop();
362 }
363
364 LeaveCriticalSection(&m_CriticalSection);
365
366 if (dwFlags & AM_GBF_NOWAIT)
367 {
368 // never wait untill a buffer becomes available
369 break;
370 }
371 }
372 while(Sample == NULL);
373
374 if (!Sample)
375 {
376 // no sample acquired
377 //HACKKKKKKK
378 Sample = m_UsedList.back();
379 m_UsedList.pop_back();
380
381 if (!Sample)
382 return VFW_E_TIMEOUT;
383 }
384
385 // store result
386 *ppBuffer = Sample;
387
388 // store sample in used list
389 m_UsedList.push_front(Sample);
390
391 // done
392 return NOERROR;
393 }
394
395 HRESULT
396 STDMETHODCALLTYPE
397 CKsAllocator::ReleaseBuffer(
398 IMediaSample *pBuffer)
399 {
400 EnterCriticalSection(&m_CriticalSection);
401
402 #ifdef KSPROXY_TRACE
403 OutputDebugStringW(L"CKsAllocator::ReleaseBuffer\n");
404 #endif
405
406 // media sample always 1 ref count in free list
407 pBuffer->AddRef();
408
409 // add the sample to the free list
410 m_FreeList.push(pBuffer);
411
412
413 if (m_FreeSamples)
414 {
415 // pending de-commit
416 if (m_FreeList.size () == m_Allocated)
417 {
418 FreeMediaSamples();
419 }
420 }
421
422 if (m_Notify)
423 {
424 //notify caller of an available buffer
425 m_Notify->NotifyRelease();
426 }
427
428 LeaveCriticalSection(&m_CriticalSection);
429 return S_OK;
430 }
431
432 //-------------------------------------------------------------------
433 // IMemAllocatorCallbackTemp
434 //
435 HRESULT
436 STDMETHODCALLTYPE
437 CKsAllocator::SetNotify(
438 IMemAllocatorNotifyCallbackTemp *pNotify)
439 {
440 EnterCriticalSection(&m_CriticalSection);
441
442 #ifdef KSPROXY_TRACE
443 OutputDebugStringW(L"CKsAllocator::SetNotify\n");
444 #endif
445
446 if (pNotify)
447 pNotify->AddRef();
448
449 if (m_Notify)
450 m_Notify->Release();
451
452 m_Notify = pNotify;
453
454 LeaveCriticalSection(&m_CriticalSection);
455 return NOERROR;
456 }
457
458 HRESULT
459 STDMETHODCALLTYPE
460 CKsAllocator::GetFreeCount(
461 LONG *plBuffersFree)
462 {
463 *plBuffersFree = m_Allocated - m_FreeList.size();
464 return S_OK;
465 }
466
467 //-------------------------------------------------------------------
468 // IKsAllocator
469 //
470 HANDLE
471 STDMETHODCALLTYPE
472 CKsAllocator::KsGetAllocatorHandle()
473 {
474 return m_hAllocator;
475 }
476
477 KSALLOCATORMODE
478 STDMETHODCALLTYPE
479 CKsAllocator::KsGetAllocatorMode()
480 {
481 return m_Mode;
482 }
483
484 HRESULT
485 STDMETHODCALLTYPE
486 CKsAllocator::KsGetAllocatorStatus(
487 PKSSTREAMALLOCATOR_STATUS AllocatorStatus)
488 {
489 return NOERROR;
490 }
491 VOID
492 STDMETHODCALLTYPE
493 CKsAllocator::KsSetAllocatorMode(
494 KSALLOCATORMODE Mode)
495 {
496 m_Mode = Mode;
497 }
498
499 //-------------------------------------------------------------------
500 // IKsAllocatorEx
501 //
502 PALLOCATOR_PROPERTIES_EX
503 STDMETHODCALLTYPE
504 CKsAllocator::KsGetProperties()
505 {
506 return &m_Properties;
507 }
508
509 VOID
510 STDMETHODCALLTYPE
511 CKsAllocator::KsSetProperties(
512 PALLOCATOR_PROPERTIES_EX Properties)
513 {
514 CopyMemory(&m_Properties, Properties, sizeof(ALLOCATOR_PROPERTIES_EX));
515 }
516
517 VOID
518 STDMETHODCALLTYPE
519 CKsAllocator::KsSetAllocatorHandle(
520 HANDLE AllocatorHandle)
521 {
522 m_hAllocator = AllocatorHandle;
523 }
524
525
526 HANDLE
527 STDMETHODCALLTYPE
528 CKsAllocator::KsCreateAllocatorAndGetHandle(
529 IKsPin* KsPin)
530 {
531 HRESULT hr;
532 IKsObject * pObject;
533 KSALLOCATOR_FRAMING AllocatorFraming;
534 HANDLE hPin;
535
536 #ifdef KSPROXY_TRACE
537 OutputDebugStringW(L"CKsAllocator::KsCreateAllocatorAndGetHandle\n");
538 #endif
539
540 if (m_hAllocator)
541 {
542 CloseHandle(m_hAllocator);
543 m_hAllocator = NULL;
544 }
545
546 // get pin IKsObject interface
547 hr = KsPin->QueryInterface(IID_IKsObject, (void**)&pObject);
548 if (FAILED(hr))
549 return NULL;
550
551 // get pin handle
552 hPin = pObject->KsGetObjectHandle();
553
554 //release IKsObject interface
555 pObject->Release();
556
557 if (!hPin || hPin == INVALID_HANDLE_VALUE)
558 return NULL;
559
560 //setup allocator framing
561 AllocatorFraming.Frames = m_Properties.cBuffers;
562 AllocatorFraming.FrameSize = m_Properties.cbBuffer;
563 AllocatorFraming.FileAlignment = (m_Properties.cbAlign -1);
564 AllocatorFraming.OptionsFlags = KSALLOCATOR_OPTIONF_SYSTEM_MEMORY;
565 AllocatorFraming.PoolType = (m_Properties.LogicalMemoryType == KS_MemoryTypeKernelPaged);
566
567 DWORD dwError = KsCreateAllocator(hPin, &AllocatorFraming, &m_hAllocator);
568 if (dwError)
569 return NULL;
570
571 return m_hAllocator;
572 }
573
574 //-------------------------------------------------------------------
575 VOID
576 STDMETHODCALLTYPE
577 CKsAllocator::FreeMediaSamples()
578 {
579 ULONG Index;
580
581 for(Index = 0; Index < m_FreeList.size(); Index++)
582 {
583 IMediaSample * Sample = m_FreeList.top();
584 m_FreeList.pop();
585 delete Sample;
586 }
587
588 m_FreeSamples = false;
589 m_Allocated = 0;
590
591 if (m_Buffer)
592 {
593 // release buffer
594 VirtualFree(m_Buffer, 0, MEM_RELEASE);
595
596 m_Buffer = NULL;
597 }
598 }
599
600 HRESULT
601 WINAPI
602 CKsAllocator_Constructor(
603 IUnknown * pUnkOuter,
604 REFIID riid,
605 LPVOID * ppv)
606 {
607 #ifdef KSPROXY_TRACE
608 OutputDebugStringW(L"CKsAllocator_Constructor\n");
609 #endif
610
611 CKsAllocator * handler = new CKsAllocator();
612
613 if (!handler)
614 return E_OUTOFMEMORY;
615
616 if (FAILED(handler->QueryInterface(riid, ppv)))
617 {
618 /* not supported */
619 delete handler;
620 return E_NOINTERFACE;
621 }
622
623 return NOERROR;
624 }