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