1 /* 2 * Memory Allocator and Media Sample Implementation 3 * 4 * Copyright 2003 Robert Shearman 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #include <assert.h> 22 #include <limits.h> 23 #include <stdarg.h> 24 25 #include "windef.h" 26 #include "winbase.h" 27 #include "vfwmsgs.h" 28 29 #include "quartz_private.h" 30 #include "wine/debug.h" 31 32 WINE_DEFAULT_DEBUG_CHANNEL(quartz); 33 34 typedef struct StdMediaSample2 35 { 36 IMediaSample2 IMediaSample2_iface; 37 LONG ref; 38 AM_SAMPLE2_PROPERTIES props; 39 IMemAllocator * pParent; 40 struct list listentry; 41 LONGLONG tMediaStart; 42 LONGLONG tMediaEnd; 43 } StdMediaSample2; 44 45 typedef struct BaseMemAllocator 46 { 47 IMemAllocator IMemAllocator_iface; 48 49 LONG ref; 50 ALLOCATOR_PROPERTIES props; 51 HRESULT (* fnAlloc) (IMemAllocator *); 52 HRESULT (* fnFree)(IMemAllocator *); 53 HRESULT (* fnVerify)(IMemAllocator *, ALLOCATOR_PROPERTIES *); 54 HRESULT (* fnBufferPrepare)(IMemAllocator *, StdMediaSample2 *, DWORD flags); 55 HRESULT (* fnBufferReleased)(IMemAllocator *, StdMediaSample2 *); 56 void (* fnDestroyed)(IMemAllocator *); 57 HANDLE hSemWaiting; 58 BOOL bDecommitQueued; 59 BOOL bCommitted; 60 LONG lWaiting; 61 struct list free_list; 62 struct list used_list; 63 CRITICAL_SECTION *pCritSect; 64 } BaseMemAllocator; 65 66 static inline BaseMemAllocator *impl_from_IMemAllocator(IMemAllocator *iface) 67 { 68 return CONTAINING_RECORD(iface, BaseMemAllocator, IMemAllocator_iface); 69 } 70 71 static const IMemAllocatorVtbl BaseMemAllocator_VTable; 72 static const IMediaSample2Vtbl StdMediaSample2_VTable; 73 static inline StdMediaSample2 *unsafe_impl_from_IMediaSample(IMediaSample * iface); 74 75 #define AM_SAMPLE2_PROP_SIZE_WRITABLE FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, pbBuffer) 76 77 #define INVALID_MEDIA_TIME (((ULONGLONG)0x7fffffff << 32) | 0xffffffff) 78 79 static HRESULT BaseMemAllocator_Init(HRESULT (* fnAlloc)(IMemAllocator *), 80 HRESULT (* fnFree)(IMemAllocator *), 81 HRESULT (* fnVerify)(IMemAllocator *, ALLOCATOR_PROPERTIES *), 82 HRESULT (* fnBufferPrepare)(IMemAllocator *, StdMediaSample2 *, DWORD), 83 HRESULT (* fnBufferReleased)(IMemAllocator *, StdMediaSample2 *), 84 void (* fnDestroyed)(IMemAllocator *), 85 CRITICAL_SECTION *pCritSect, 86 BaseMemAllocator * pMemAlloc) 87 { 88 assert(fnAlloc && fnFree && fnDestroyed); 89 90 pMemAlloc->IMemAllocator_iface.lpVtbl = &BaseMemAllocator_VTable; 91 92 pMemAlloc->ref = 1; 93 ZeroMemory(&pMemAlloc->props, sizeof(pMemAlloc->props)); 94 list_init(&pMemAlloc->free_list); 95 list_init(&pMemAlloc->used_list); 96 pMemAlloc->fnAlloc = fnAlloc; 97 pMemAlloc->fnFree = fnFree; 98 pMemAlloc->fnVerify = fnVerify; 99 pMemAlloc->fnBufferPrepare = fnBufferPrepare; 100 pMemAlloc->fnBufferReleased = fnBufferReleased; 101 pMemAlloc->fnDestroyed = fnDestroyed; 102 pMemAlloc->bDecommitQueued = FALSE; 103 pMemAlloc->bCommitted = FALSE; 104 pMemAlloc->hSemWaiting = NULL; 105 pMemAlloc->lWaiting = 0; 106 pMemAlloc->pCritSect = pCritSect; 107 108 return S_OK; 109 } 110 111 static HRESULT WINAPI BaseMemAllocator_QueryInterface(IMemAllocator * iface, REFIID riid, LPVOID * ppv) 112 { 113 BaseMemAllocator *This = impl_from_IMemAllocator(iface); 114 TRACE("(%p)->(%s, %p)\n", This, qzdebugstr_guid(riid), ppv); 115 116 *ppv = NULL; 117 118 if (IsEqualIID(riid, &IID_IUnknown)) 119 *ppv = &This->IMemAllocator_iface; 120 else if (IsEqualIID(riid, &IID_IMemAllocator)) 121 *ppv = &This->IMemAllocator_iface; 122 123 if (*ppv) 124 { 125 IUnknown_AddRef((IUnknown *)(*ppv)); 126 return S_OK; 127 } 128 129 FIXME("No interface for %s!\n", qzdebugstr_guid(riid)); 130 131 return E_NOINTERFACE; 132 } 133 134 static ULONG WINAPI BaseMemAllocator_AddRef(IMemAllocator * iface) 135 { 136 BaseMemAllocator *This = impl_from_IMemAllocator(iface); 137 ULONG ref = InterlockedIncrement(&This->ref); 138 139 TRACE("(%p)->() AddRef from %d\n", iface, ref - 1); 140 141 return ref; 142 } 143 144 static ULONG WINAPI BaseMemAllocator_Release(IMemAllocator * iface) 145 { 146 BaseMemAllocator *This = impl_from_IMemAllocator(iface); 147 ULONG ref = InterlockedDecrement(&This->ref); 148 149 TRACE("(%p)->() Release from %d\n", iface, ref + 1); 150 151 if (!ref) 152 { 153 CloseHandle(This->hSemWaiting); 154 if (This->bCommitted) 155 This->fnFree(iface); 156 157 This->fnDestroyed(iface); 158 return 0; 159 } 160 return ref; 161 } 162 163 static HRESULT WINAPI BaseMemAllocator_SetProperties(IMemAllocator * iface, ALLOCATOR_PROPERTIES *pRequest, ALLOCATOR_PROPERTIES *pActual) 164 { 165 BaseMemAllocator *This = impl_from_IMemAllocator(iface); 166 HRESULT hr; 167 168 TRACE("(%p)->(%p, %p)\n", This, pRequest, pActual); 169 170 EnterCriticalSection(This->pCritSect); 171 { 172 if (!list_empty(&This->used_list)) 173 hr = VFW_E_BUFFERS_OUTSTANDING; 174 else if (This->bCommitted) 175 hr = VFW_E_ALREADY_COMMITTED; 176 else if (pRequest->cbAlign == 0) 177 hr = VFW_E_BADALIGN; 178 else 179 { 180 if (This->fnVerify) 181 hr = This->fnVerify(iface, pRequest); 182 else 183 hr = S_OK; 184 185 if (SUCCEEDED(hr)) 186 This->props = *pRequest; 187 188 *pActual = This->props; 189 } 190 } 191 LeaveCriticalSection(This->pCritSect); 192 193 return hr; 194 } 195 196 static HRESULT WINAPI BaseMemAllocator_GetProperties(IMemAllocator * iface, ALLOCATOR_PROPERTIES *pProps) 197 { 198 BaseMemAllocator *This = impl_from_IMemAllocator(iface); 199 HRESULT hr = S_OK; 200 201 TRACE("(%p)->(%p)\n", This, pProps); 202 203 EnterCriticalSection(This->pCritSect); 204 { 205 memcpy(pProps, &This->props, sizeof(*pProps)); 206 } 207 LeaveCriticalSection(This->pCritSect); 208 209 return hr; 210 } 211 212 static HRESULT WINAPI BaseMemAllocator_Commit(IMemAllocator * iface) 213 { 214 BaseMemAllocator *This = impl_from_IMemAllocator(iface); 215 HRESULT hr; 216 217 TRACE("(%p)->()\n", This); 218 219 EnterCriticalSection(This->pCritSect); 220 { 221 if (!This->props.cbAlign) 222 hr = VFW_E_BADALIGN; 223 else if (!This->props.cbBuffer) 224 hr = VFW_E_SIZENOTSET; 225 else if (!This->props.cBuffers) 226 hr = VFW_E_BUFFER_NOTSET; 227 else if (This->bDecommitQueued && This->bCommitted) 228 { 229 This->bDecommitQueued = FALSE; 230 hr = S_OK; 231 } 232 else if (This->bCommitted) 233 hr = S_OK; 234 else 235 { 236 if (!(This->hSemWaiting = CreateSemaphoreW(NULL, This->props.cBuffers, This->props.cBuffers, NULL))) 237 { 238 ERR("Couldn't create semaphore (error was %u)\n", GetLastError()); 239 hr = HRESULT_FROM_WIN32(GetLastError()); 240 } 241 else 242 { 243 hr = This->fnAlloc(iface); 244 if (SUCCEEDED(hr)) 245 This->bCommitted = TRUE; 246 else 247 ERR("fnAlloc failed with error 0x%x\n", hr); 248 } 249 } 250 } 251 LeaveCriticalSection(This->pCritSect); 252 253 return hr; 254 } 255 256 static HRESULT WINAPI BaseMemAllocator_Decommit(IMemAllocator * iface) 257 { 258 BaseMemAllocator *This = impl_from_IMemAllocator(iface); 259 HRESULT hr; 260 261 TRACE("(%p)->()\n", This); 262 263 EnterCriticalSection(This->pCritSect); 264 { 265 if (!This->bCommitted) 266 hr = S_OK; 267 else 268 { 269 if (!list_empty(&This->used_list)) 270 { 271 This->bDecommitQueued = TRUE; 272 /* notify ALL waiting threads that they cannot be allocated a buffer any more */ 273 ReleaseSemaphore(This->hSemWaiting, This->lWaiting, NULL); 274 275 hr = S_OK; 276 } 277 else 278 { 279 if (This->lWaiting != 0) 280 ERR("Waiting: %d\n", This->lWaiting); 281 282 This->bCommitted = FALSE; 283 CloseHandle(This->hSemWaiting); 284 This->hSemWaiting = NULL; 285 286 hr = This->fnFree(iface); 287 if (FAILED(hr)) 288 ERR("fnFree failed with error 0x%x\n", hr); 289 } 290 } 291 } 292 LeaveCriticalSection(This->pCritSect); 293 294 return hr; 295 } 296 297 static HRESULT WINAPI BaseMemAllocator_GetBuffer(IMemAllocator * iface, IMediaSample ** pSample, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime, DWORD dwFlags) 298 { 299 BaseMemAllocator *This = impl_from_IMemAllocator(iface); 300 HRESULT hr = S_OK; 301 302 /* NOTE: The pStartTime and pEndTime parameters are not applied to the sample. 303 * The allocator might use these values to determine which buffer it retrieves */ 304 305 TRACE("(%p)->(%p, %p, %p, %x)\n", This, pSample, pStartTime, pEndTime, dwFlags); 306 307 *pSample = NULL; 308 309 EnterCriticalSection(This->pCritSect); 310 if (!This->bCommitted || This->bDecommitQueued) 311 { 312 WARN("Not committed\n"); 313 hr = VFW_E_NOT_COMMITTED; 314 } 315 else 316 ++This->lWaiting; 317 LeaveCriticalSection(This->pCritSect); 318 if (FAILED(hr)) 319 return hr; 320 321 if (WaitForSingleObject(This->hSemWaiting, (dwFlags & AM_GBF_NOWAIT) ? 0 : INFINITE) != WAIT_OBJECT_0) 322 { 323 EnterCriticalSection(This->pCritSect); 324 --This->lWaiting; 325 LeaveCriticalSection(This->pCritSect); 326 WARN("Timed out\n"); 327 return VFW_E_TIMEOUT; 328 } 329 330 EnterCriticalSection(This->pCritSect); 331 { 332 --This->lWaiting; 333 if (!This->bCommitted) 334 hr = VFW_E_NOT_COMMITTED; 335 else if (This->bDecommitQueued) 336 hr = VFW_E_TIMEOUT; 337 else 338 { 339 StdMediaSample2 *ms; 340 struct list * free = list_head(&This->free_list); 341 list_remove(free); 342 list_add_head(&This->used_list, free); 343 344 ms = LIST_ENTRY(free, StdMediaSample2, listentry); 345 assert(ms->ref == 0); 346 *pSample = (IMediaSample *)&ms->IMediaSample2_iface; 347 IMediaSample_AddRef(*pSample); 348 } 349 } 350 LeaveCriticalSection(This->pCritSect); 351 352 if (hr != S_OK) 353 WARN("%08x\n", hr); 354 return hr; 355 } 356 357 static HRESULT WINAPI BaseMemAllocator_ReleaseBuffer(IMemAllocator * iface, IMediaSample * pSample) 358 { 359 BaseMemAllocator *This = impl_from_IMemAllocator(iface); 360 StdMediaSample2 * pStdSample = unsafe_impl_from_IMediaSample(pSample); 361 HRESULT hr = S_OK; 362 363 TRACE("(%p)->(%p)\n", This, pSample); 364 365 /* FIXME: make sure that sample is currently on the used list */ 366 367 /* FIXME: we should probably check the ref count on the sample before freeing 368 * it to make sure that it is not still in use */ 369 EnterCriticalSection(This->pCritSect); 370 { 371 if (!This->bCommitted) 372 ERR("Releasing a buffer when the allocator is not committed?!?\n"); 373 374 /* remove from used_list */ 375 list_remove(&pStdSample->listentry); 376 377 list_add_head(&This->free_list, &pStdSample->listentry); 378 379 if (list_empty(&This->used_list) && This->bDecommitQueued && This->bCommitted) 380 { 381 HRESULT hrfree; 382 383 if (This->lWaiting != 0) 384 ERR("Waiting: %d\n", This->lWaiting); 385 386 This->bCommitted = FALSE; 387 This->bDecommitQueued = FALSE; 388 389 CloseHandle(This->hSemWaiting); 390 This->hSemWaiting = NULL; 391 392 if (FAILED(hrfree = This->fnFree(iface))) 393 ERR("fnFree failed with error 0x%x\n", hrfree); 394 } 395 } 396 LeaveCriticalSection(This->pCritSect); 397 398 /* notify a waiting thread that there is now a free buffer */ 399 if (This->hSemWaiting && !ReleaseSemaphore(This->hSemWaiting, 1, NULL)) 400 { 401 ERR("ReleaseSemaphore failed with error %u\n", GetLastError()); 402 hr = HRESULT_FROM_WIN32(GetLastError()); 403 } 404 405 return hr; 406 } 407 408 static const IMemAllocatorVtbl BaseMemAllocator_VTable = 409 { 410 BaseMemAllocator_QueryInterface, 411 BaseMemAllocator_AddRef, 412 BaseMemAllocator_Release, 413 BaseMemAllocator_SetProperties, 414 BaseMemAllocator_GetProperties, 415 BaseMemAllocator_Commit, 416 BaseMemAllocator_Decommit, 417 BaseMemAllocator_GetBuffer, 418 BaseMemAllocator_ReleaseBuffer 419 }; 420 421 static HRESULT StdMediaSample2_Construct(BYTE * pbBuffer, LONG cbBuffer, IMemAllocator * pParent, StdMediaSample2 ** ppSample) 422 { 423 assert(pbBuffer && pParent && (cbBuffer > 0)); 424 425 if (!(*ppSample = CoTaskMemAlloc(sizeof(StdMediaSample2)))) 426 return E_OUTOFMEMORY; 427 428 (*ppSample)->IMediaSample2_iface.lpVtbl = &StdMediaSample2_VTable; 429 (*ppSample)->ref = 0; 430 ZeroMemory(&(*ppSample)->props, sizeof((*ppSample)->props)); 431 432 /* NOTE: no need to AddRef as the parent is guaranteed to be around 433 * at least as long as us and we don't want to create circular 434 * dependencies on the ref count */ 435 (*ppSample)->pParent = pParent; 436 (*ppSample)->props.cbData = sizeof(AM_SAMPLE2_PROPERTIES); 437 (*ppSample)->props.cbBuffer = (*ppSample)->props.lActual = cbBuffer; 438 (*ppSample)->props.pbBuffer = pbBuffer; 439 (*ppSample)->tMediaStart = INVALID_MEDIA_TIME; 440 (*ppSample)->tMediaEnd = 0; 441 442 return S_OK; 443 } 444 445 static void StdMediaSample2_Delete(StdMediaSample2 * This) 446 { 447 /* NOTE: does not remove itself from the list it belongs to */ 448 CoTaskMemFree(This); 449 } 450 451 static inline StdMediaSample2 *impl_from_IMediaSample2(IMediaSample2 * iface) 452 { 453 return CONTAINING_RECORD(iface, StdMediaSample2, IMediaSample2_iface); 454 } 455 456 static HRESULT WINAPI StdMediaSample2_QueryInterface(IMediaSample2 * iface, REFIID riid, void ** ppv) 457 { 458 TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv); 459 460 *ppv = NULL; 461 462 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IMediaSample) || 463 IsEqualIID(riid, &IID_IMediaSample2)) 464 { 465 *ppv = iface; 466 IMediaSample2_AddRef(iface); 467 return S_OK; 468 } 469 470 FIXME("No interface for %s!\n", qzdebugstr_guid(riid)); 471 return E_NOINTERFACE; 472 } 473 474 static ULONG WINAPI StdMediaSample2_AddRef(IMediaSample2 * iface) 475 { 476 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 477 ULONG ref = InterlockedIncrement(&This->ref); 478 479 TRACE("(%p)->(): new ref = %d\n", This, ref); 480 481 return ref; 482 } 483 484 static ULONG WINAPI StdMediaSample2_Release(IMediaSample2 * iface) 485 { 486 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 487 ULONG ref = InterlockedDecrement(&This->ref); 488 489 TRACE("(%p)->(): new ref = %d\n", This, ref); 490 491 if (!ref) 492 { 493 if (This->pParent) 494 IMemAllocator_ReleaseBuffer(This->pParent, (IMediaSample *)iface); 495 else 496 StdMediaSample2_Delete(This); 497 } 498 return ref; 499 } 500 501 static HRESULT WINAPI StdMediaSample2_GetPointer(IMediaSample2 * iface, BYTE ** ppBuffer) 502 { 503 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 504 505 TRACE("(%p)->(%p)\n", iface, ppBuffer); 506 507 *ppBuffer = This->props.pbBuffer; 508 509 if (!*ppBuffer) 510 { 511 ERR("Requested an unlocked surface and trying to lock regardless\n"); 512 return E_FAIL; 513 } 514 515 return S_OK; 516 } 517 518 static LONG WINAPI StdMediaSample2_GetSize(IMediaSample2 * iface) 519 { 520 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 521 522 TRACE("StdMediaSample2_GetSize()\n"); 523 524 return This->props.cbBuffer; 525 } 526 527 static HRESULT WINAPI StdMediaSample2_GetTime(IMediaSample2 * iface, REFERENCE_TIME * pStart, REFERENCE_TIME * pEnd) 528 { 529 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 530 HRESULT hr; 531 532 TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd); 533 534 if (!(This->props.dwSampleFlags & AM_SAMPLE_TIMEVALID)) 535 hr = VFW_E_SAMPLE_TIME_NOT_SET; 536 else if (!(This->props.dwSampleFlags & AM_SAMPLE_STOPVALID)) 537 { 538 *pStart = This->props.tStart; 539 *pEnd = This->props.tStart + 1; 540 541 hr = VFW_S_NO_STOP_TIME; 542 } 543 else 544 { 545 *pStart = This->props.tStart; 546 *pEnd = This->props.tStop; 547 548 hr = S_OK; 549 } 550 551 return hr; 552 } 553 554 static HRESULT WINAPI StdMediaSample2_SetTime(IMediaSample2 * iface, REFERENCE_TIME * pStart, REFERENCE_TIME * pEnd) 555 { 556 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 557 558 TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd); 559 560 if (pStart) 561 { 562 This->props.tStart = *pStart; 563 This->props.dwSampleFlags |= AM_SAMPLE_TIMEVALID; 564 } 565 else 566 This->props.dwSampleFlags &= ~AM_SAMPLE_TIMEVALID; 567 568 if (pEnd) 569 { 570 This->props.tStop = *pEnd; 571 This->props.dwSampleFlags |= AM_SAMPLE_STOPVALID; 572 } 573 else 574 This->props.dwSampleFlags &= ~AM_SAMPLE_STOPVALID; 575 576 return S_OK; 577 } 578 579 static HRESULT WINAPI StdMediaSample2_IsSyncPoint(IMediaSample2 * iface) 580 { 581 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 582 583 TRACE("(%p)->()\n", iface); 584 585 return (This->props.dwSampleFlags & AM_SAMPLE_SPLICEPOINT) ? S_OK : S_FALSE; 586 } 587 588 static HRESULT WINAPI StdMediaSample2_SetSyncPoint(IMediaSample2 * iface, BOOL bIsSyncPoint) 589 { 590 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 591 592 TRACE("(%p)->(%s)\n", iface, bIsSyncPoint ? "TRUE" : "FALSE"); 593 594 if (bIsSyncPoint) 595 This->props.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT; 596 else 597 This->props.dwSampleFlags &= ~AM_SAMPLE_SPLICEPOINT; 598 599 return S_OK; 600 } 601 602 static HRESULT WINAPI StdMediaSample2_IsPreroll(IMediaSample2 * iface) 603 { 604 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 605 606 TRACE("(%p)->()\n", iface); 607 608 return (This->props.dwSampleFlags & AM_SAMPLE_PREROLL) ? S_OK : S_FALSE; 609 } 610 611 static HRESULT WINAPI StdMediaSample2_SetPreroll(IMediaSample2 * iface, BOOL bIsPreroll) 612 { 613 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 614 615 TRACE("(%p)->(%s)\n", iface, bIsPreroll ? "TRUE" : "FALSE"); 616 617 if (bIsPreroll) 618 This->props.dwSampleFlags |= AM_SAMPLE_PREROLL; 619 else 620 This->props.dwSampleFlags &= ~AM_SAMPLE_PREROLL; 621 622 return S_OK; 623 } 624 625 static LONG WINAPI StdMediaSample2_GetActualDataLength(IMediaSample2 * iface) 626 { 627 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 628 629 TRACE("(%p)->()\n", iface); 630 631 return This->props.lActual; 632 } 633 634 static HRESULT WINAPI StdMediaSample2_SetActualDataLength(IMediaSample2 * iface, LONG len) 635 { 636 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 637 638 TRACE("(%p)->(%d)\n", iface, len); 639 640 if ((len > This->props.cbBuffer) || (len < 0)) 641 { 642 WARN("Tried to set length to %d, while max is %d\n", len, This->props.cbBuffer); 643 return VFW_E_BUFFER_OVERFLOW; 644 } 645 else 646 { 647 This->props.lActual = len; 648 return S_OK; 649 } 650 } 651 652 static HRESULT WINAPI StdMediaSample2_GetMediaType(IMediaSample2 * iface, AM_MEDIA_TYPE ** ppMediaType) 653 { 654 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 655 656 TRACE("(%p)->(%p)\n", iface, ppMediaType); 657 658 if (!This->props.pMediaType) { 659 /* Make sure we return a NULL pointer (required by native Quartz dll) */ 660 if (ppMediaType) 661 *ppMediaType = NULL; 662 return S_FALSE; 663 } 664 665 if (!(*ppMediaType = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)))) 666 return E_OUTOFMEMORY; 667 668 return CopyMediaType(*ppMediaType, This->props.pMediaType); 669 } 670 671 static HRESULT WINAPI StdMediaSample2_SetMediaType(IMediaSample2 * iface, AM_MEDIA_TYPE * pMediaType) 672 { 673 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 674 675 TRACE("(%p)->(%p)\n", iface, pMediaType); 676 677 if (This->props.pMediaType) 678 { 679 FreeMediaType(This->props.pMediaType); 680 This->props.pMediaType = NULL; 681 } 682 if (!pMediaType) 683 return S_FALSE; 684 if (!(This->props.pMediaType = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)))) 685 return E_OUTOFMEMORY; 686 687 return CopyMediaType(This->props.pMediaType, pMediaType); 688 } 689 690 static HRESULT WINAPI StdMediaSample2_IsDiscontinuity(IMediaSample2 * iface) 691 { 692 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 693 694 TRACE("(%p)->()\n", iface); 695 696 return (This->props.dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) ? S_OK : S_FALSE; 697 } 698 699 static HRESULT WINAPI StdMediaSample2_SetDiscontinuity(IMediaSample2 * iface, BOOL bIsDiscontinuity) 700 { 701 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 702 703 TRACE("(%p)->(%s)\n", iface, bIsDiscontinuity ? "TRUE" : "FALSE"); 704 705 if (bIsDiscontinuity) 706 This->props.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY; 707 else 708 This->props.dwSampleFlags &= ~AM_SAMPLE_DATADISCONTINUITY; 709 710 return S_OK; 711 } 712 713 static HRESULT WINAPI StdMediaSample2_GetMediaTime(IMediaSample2 * iface, LONGLONG * pStart, LONGLONG * pEnd) 714 { 715 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 716 717 TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd); 718 719 if (This->tMediaStart == INVALID_MEDIA_TIME) 720 return VFW_E_MEDIA_TIME_NOT_SET; 721 722 *pStart = This->tMediaStart; 723 *pEnd = This->tMediaEnd; 724 725 return S_OK; 726 } 727 728 static HRESULT WINAPI StdMediaSample2_SetMediaTime(IMediaSample2 * iface, LONGLONG * pStart, LONGLONG * pEnd) 729 { 730 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 731 732 TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd); 733 734 if (pStart) 735 This->tMediaStart = *pStart; 736 else 737 This->tMediaStart = INVALID_MEDIA_TIME; 738 739 if (pEnd) 740 This->tMediaEnd = *pEnd; 741 else 742 This->tMediaEnd = 0; 743 744 return S_OK; 745 } 746 747 static HRESULT WINAPI StdMediaSample2_GetProperties(IMediaSample2 * iface, DWORD cbProperties, BYTE * pbProperties) 748 { 749 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 750 751 TRACE("(%p)->(%d, %p)\n", iface, cbProperties, pbProperties); 752 753 memcpy(pbProperties, &This->props, min(cbProperties, sizeof(This->props))); 754 755 return S_OK; 756 } 757 758 static HRESULT WINAPI StdMediaSample2_SetProperties(IMediaSample2 * iface, DWORD cbProperties, const BYTE * pbProperties) 759 { 760 StdMediaSample2 *This = impl_from_IMediaSample2(iface); 761 762 TRACE("(%p)->(%d, %p)\n", iface, cbProperties, pbProperties); 763 764 /* NOTE: pbBuffer and cbBuffer are read-only */ 765 memcpy(&This->props, pbProperties, min(cbProperties, AM_SAMPLE2_PROP_SIZE_WRITABLE)); 766 767 return S_OK; 768 } 769 770 static const IMediaSample2Vtbl StdMediaSample2_VTable = 771 { 772 StdMediaSample2_QueryInterface, 773 StdMediaSample2_AddRef, 774 StdMediaSample2_Release, 775 StdMediaSample2_GetPointer, 776 StdMediaSample2_GetSize, 777 StdMediaSample2_GetTime, 778 StdMediaSample2_SetTime, 779 StdMediaSample2_IsSyncPoint, 780 StdMediaSample2_SetSyncPoint, 781 StdMediaSample2_IsPreroll, 782 StdMediaSample2_SetPreroll, 783 StdMediaSample2_GetActualDataLength, 784 StdMediaSample2_SetActualDataLength, 785 StdMediaSample2_GetMediaType, 786 StdMediaSample2_SetMediaType, 787 StdMediaSample2_IsDiscontinuity, 788 StdMediaSample2_SetDiscontinuity, 789 StdMediaSample2_GetMediaTime, 790 StdMediaSample2_SetMediaTime, 791 StdMediaSample2_GetProperties, 792 StdMediaSample2_SetProperties 793 }; 794 795 static inline StdMediaSample2 *unsafe_impl_from_IMediaSample(IMediaSample * iface) 796 { 797 IMediaSample2 *iface2 = (IMediaSample2 *)iface; 798 799 if (!iface) 800 return NULL; 801 assert(iface2->lpVtbl == &StdMediaSample2_VTable); 802 return impl_from_IMediaSample2(iface2); 803 } 804 805 typedef struct StdMemAllocator 806 { 807 BaseMemAllocator base; 808 CRITICAL_SECTION csState; 809 LPVOID pMemory; 810 } StdMemAllocator; 811 812 static inline StdMemAllocator *StdMemAllocator_from_IMemAllocator(IMemAllocator * iface) 813 { 814 return CONTAINING_RECORD(iface, StdMemAllocator, base.IMemAllocator_iface); 815 } 816 817 static HRESULT StdMemAllocator_Alloc(IMemAllocator * iface) 818 { 819 StdMemAllocator *This = StdMemAllocator_from_IMemAllocator(iface); 820 StdMediaSample2 * pSample = NULL; 821 SYSTEM_INFO si; 822 LONG i; 823 824 assert(list_empty(&This->base.free_list)); 825 826 /* check alignment */ 827 GetSystemInfo(&si); 828 829 /* we do not allow a courser alignment than the OS page size */ 830 if ((si.dwPageSize % This->base.props.cbAlign) != 0) 831 return VFW_E_BADALIGN; 832 833 /* FIXME: each sample has to have its buffer start on the right alignment. 834 * We don't do this at the moment */ 835 836 /* allocate memory */ 837 This->pMemory = VirtualAlloc(NULL, (This->base.props.cbBuffer + This->base.props.cbPrefix) * This->base.props.cBuffers, MEM_COMMIT, PAGE_READWRITE); 838 839 if (!This->pMemory) 840 return E_OUTOFMEMORY; 841 842 for (i = This->base.props.cBuffers - 1; i >= 0; i--) 843 { 844 /* pbBuffer does not start at the base address, it starts at base + cbPrefix */ 845 BYTE * pbBuffer = (BYTE *)This->pMemory + i * (This->base.props.cbBuffer + This->base.props.cbPrefix) + This->base.props.cbPrefix; 846 847 StdMediaSample2_Construct(pbBuffer, This->base.props.cbBuffer, iface, &pSample); 848 849 list_add_head(&This->base.free_list, &pSample->listentry); 850 } 851 852 return S_OK; 853 } 854 855 static HRESULT StdMemAllocator_Free(IMemAllocator * iface) 856 { 857 StdMemAllocator *This = StdMemAllocator_from_IMemAllocator(iface); 858 struct list * cursor; 859 860 if (!list_empty(&This->base.used_list)) 861 { 862 WARN("Freeing allocator with outstanding samples!\n"); 863 while ((cursor = list_head(&This->base.used_list)) != NULL) 864 { 865 StdMediaSample2 *pSample; 866 list_remove(cursor); 867 pSample = LIST_ENTRY(cursor, StdMediaSample2, listentry); 868 pSample->pParent = NULL; 869 } 870 } 871 872 while ((cursor = list_head(&This->base.free_list)) != NULL) 873 { 874 list_remove(cursor); 875 StdMediaSample2_Delete(LIST_ENTRY(cursor, StdMediaSample2, listentry)); 876 } 877 878 /* free memory */ 879 if (!VirtualFree(This->pMemory, 0, MEM_RELEASE)) 880 { 881 ERR("Couldn't free memory. Error: %u\n", GetLastError()); 882 return HRESULT_FROM_WIN32(GetLastError()); 883 } 884 885 return S_OK; 886 } 887 888 static void StdMemAllocator_Destroy(IMemAllocator *iface) 889 { 890 StdMemAllocator *This = StdMemAllocator_from_IMemAllocator(iface); 891 892 This->csState.DebugInfo->Spare[0] = 0; 893 DeleteCriticalSection(&This->csState); 894 895 CoTaskMemFree(This); 896 } 897 898 HRESULT StdMemAllocator_create(LPUNKNOWN lpUnkOuter, LPVOID * ppv) 899 { 900 StdMemAllocator * pMemAlloc; 901 HRESULT hr; 902 903 *ppv = NULL; 904 905 if (lpUnkOuter) 906 return CLASS_E_NOAGGREGATION; 907 908 if (!(pMemAlloc = CoTaskMemAlloc(sizeof(*pMemAlloc)))) 909 return E_OUTOFMEMORY; 910 911 InitializeCriticalSection(&pMemAlloc->csState); 912 pMemAlloc->csState.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": StdMemAllocator.csState"); 913 914 pMemAlloc->pMemory = NULL; 915 916 if (SUCCEEDED(hr = BaseMemAllocator_Init(StdMemAllocator_Alloc, StdMemAllocator_Free, NULL, NULL, NULL, StdMemAllocator_Destroy, &pMemAlloc->csState, &pMemAlloc->base))) 917 *ppv = pMemAlloc; 918 else 919 CoTaskMemFree(pMemAlloc); 920 921 return hr; 922 } 923