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