riched20/tests: Don't cast NULL.
[wine.git] / dlls / quartz / memallocator.c
blob531fbe874cf8d01f3ac9218b58746393c2571bea
1 /*
2 * Memory Allocator and Media Sample Implementation
4 * Copyright 2003 Robert Shearman
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.
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.
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
21 #include <assert.h>
22 #include <limits.h>
23 #include <stdarg.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "vfwmsgs.h"
29 #include "quartz_private.h"
30 #include "wine/debug.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
34 typedef struct StdMediaSample2
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 BOOL media_time_valid;
44 } StdMediaSample2;
46 typedef struct BaseMemAllocator
48 IMemAllocator IMemAllocator_iface;
50 LONG ref;
51 ALLOCATOR_PROPERTIES props;
52 HRESULT (* fnAlloc) (IMemAllocator *);
53 HRESULT (* fnFree)(IMemAllocator *);
54 HRESULT (* fnVerify)(IMemAllocator *, ALLOCATOR_PROPERTIES *);
55 HRESULT (* fnBufferPrepare)(IMemAllocator *, StdMediaSample2 *, DWORD flags);
56 HRESULT (* fnBufferReleased)(IMemAllocator *, StdMediaSample2 *);
57 void (* fnDestroyed)(IMemAllocator *);
58 HANDLE hSemWaiting;
59 BOOL bDecommitQueued;
60 BOOL bCommitted;
61 LONG lWaiting;
62 struct list free_list;
63 struct list used_list;
64 CRITICAL_SECTION *pCritSect;
65 } BaseMemAllocator;
67 static inline BaseMemAllocator *impl_from_IMemAllocator(IMemAllocator *iface)
69 return CONTAINING_RECORD(iface, BaseMemAllocator, IMemAllocator_iface);
72 static const IMemAllocatorVtbl BaseMemAllocator_VTable;
73 static const IMediaSample2Vtbl StdMediaSample2_VTable;
74 static inline StdMediaSample2 *unsafe_impl_from_IMediaSample(IMediaSample * iface);
76 #define AM_SAMPLE2_PROP_SIZE_WRITABLE FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, pbBuffer)
78 static HRESULT BaseMemAllocator_Init(HRESULT (* fnAlloc)(IMemAllocator *),
79 HRESULT (* fnFree)(IMemAllocator *),
80 HRESULT (* fnVerify)(IMemAllocator *, ALLOCATOR_PROPERTIES *),
81 HRESULT (* fnBufferPrepare)(IMemAllocator *, StdMediaSample2 *, DWORD),
82 HRESULT (* fnBufferReleased)(IMemAllocator *, StdMediaSample2 *),
83 void (* fnDestroyed)(IMemAllocator *),
84 CRITICAL_SECTION *pCritSect,
85 BaseMemAllocator * pMemAlloc)
87 assert(fnAlloc && fnFree && fnDestroyed);
89 pMemAlloc->IMemAllocator_iface.lpVtbl = &BaseMemAllocator_VTable;
91 pMemAlloc->ref = 1;
92 ZeroMemory(&pMemAlloc->props, sizeof(pMemAlloc->props));
93 list_init(&pMemAlloc->free_list);
94 list_init(&pMemAlloc->used_list);
95 pMemAlloc->fnAlloc = fnAlloc;
96 pMemAlloc->fnFree = fnFree;
97 pMemAlloc->fnVerify = fnVerify;
98 pMemAlloc->fnBufferPrepare = fnBufferPrepare;
99 pMemAlloc->fnBufferReleased = fnBufferReleased;
100 pMemAlloc->fnDestroyed = fnDestroyed;
101 pMemAlloc->bDecommitQueued = FALSE;
102 pMemAlloc->bCommitted = FALSE;
103 pMemAlloc->hSemWaiting = NULL;
104 pMemAlloc->lWaiting = 0;
105 pMemAlloc->pCritSect = pCritSect;
107 return S_OK;
110 static HRESULT WINAPI BaseMemAllocator_QueryInterface(IMemAllocator * iface, REFIID riid, LPVOID * ppv)
112 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
113 TRACE("(%p)->(%s, %p)\n", This, qzdebugstr_guid(riid), ppv);
115 *ppv = NULL;
117 if (IsEqualIID(riid, &IID_IUnknown))
118 *ppv = &This->IMemAllocator_iface;
119 else if (IsEqualIID(riid, &IID_IMemAllocator))
120 *ppv = &This->IMemAllocator_iface;
122 if (*ppv)
124 IUnknown_AddRef((IUnknown *)(*ppv));
125 return S_OK;
128 FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
130 return E_NOINTERFACE;
133 static ULONG WINAPI BaseMemAllocator_AddRef(IMemAllocator * iface)
135 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
136 ULONG ref = InterlockedIncrement(&This->ref);
138 TRACE("(%p)->() AddRef from %d\n", iface, ref - 1);
140 return ref;
143 static ULONG WINAPI BaseMemAllocator_Release(IMemAllocator * iface)
145 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
146 ULONG ref = InterlockedDecrement(&This->ref);
148 TRACE("(%p)->() Release from %d\n", iface, ref + 1);
150 if (!ref)
152 CloseHandle(This->hSemWaiting);
153 if (This->bCommitted)
154 This->fnFree(iface);
156 This->fnDestroyed(iface);
157 return 0;
159 return ref;
162 static HRESULT WINAPI BaseMemAllocator_SetProperties(IMemAllocator * iface, ALLOCATOR_PROPERTIES *pRequest, ALLOCATOR_PROPERTIES *pActual)
164 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
165 HRESULT hr;
167 TRACE("(%p)->(%p, %p)\n", This, pRequest, pActual);
169 TRACE("Requested %d buffers, size %d, alignment %d, prefix %d.\n",
170 pRequest->cBuffers, pRequest->cbBuffer, pRequest->cbAlign, pRequest->cbPrefix);
172 EnterCriticalSection(This->pCritSect);
174 if (!list_empty(&This->used_list))
175 hr = VFW_E_BUFFERS_OUTSTANDING;
176 else if (This->bCommitted)
177 hr = VFW_E_ALREADY_COMMITTED;
178 else if (pRequest->cbAlign == 0)
179 hr = VFW_E_BADALIGN;
180 else
182 if (This->fnVerify)
183 hr = This->fnVerify(iface, pRequest);
184 else
185 hr = S_OK;
187 if (SUCCEEDED(hr))
188 This->props = *pRequest;
190 *pActual = This->props;
193 LeaveCriticalSection(This->pCritSect);
195 return hr;
198 static HRESULT WINAPI BaseMemAllocator_GetProperties(IMemAllocator * iface, ALLOCATOR_PROPERTIES *pProps)
200 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
202 TRACE("(%p)->(%p)\n", This, pProps);
204 EnterCriticalSection(This->pCritSect);
206 memcpy(pProps, &This->props, sizeof(*pProps));
208 LeaveCriticalSection(This->pCritSect);
210 return S_OK;
213 static HRESULT WINAPI BaseMemAllocator_Commit(IMemAllocator * iface)
215 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
216 HRESULT hr;
218 TRACE("(%p)->()\n", This);
220 EnterCriticalSection(This->pCritSect);
222 if (!This->props.cbAlign)
223 hr = VFW_E_BADALIGN;
224 else if (!This->props.cbBuffer)
225 hr = VFW_E_SIZENOTSET;
226 else if (!This->props.cBuffers)
227 hr = VFW_E_BUFFER_NOTSET;
228 else if (This->bDecommitQueued && This->bCommitted)
230 This->bDecommitQueued = FALSE;
231 hr = S_OK;
233 else if (This->bCommitted)
234 hr = S_OK;
235 else
237 if (!(This->hSemWaiting = CreateSemaphoreW(NULL, This->props.cBuffers, This->props.cBuffers, NULL)))
239 ERR("Couldn't create semaphore (error was %u)\n", GetLastError());
240 hr = HRESULT_FROM_WIN32(GetLastError());
242 else
244 hr = This->fnAlloc(iface);
245 if (SUCCEEDED(hr))
246 This->bCommitted = TRUE;
247 else
248 ERR("fnAlloc failed with error 0x%x\n", hr);
252 LeaveCriticalSection(This->pCritSect);
254 return hr;
257 static HRESULT WINAPI BaseMemAllocator_Decommit(IMemAllocator * iface)
259 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
260 HRESULT hr;
262 TRACE("(%p)->()\n", This);
264 EnterCriticalSection(This->pCritSect);
266 if (!This->bCommitted)
267 hr = S_OK;
268 else
270 if (!list_empty(&This->used_list))
272 This->bDecommitQueued = TRUE;
273 /* notify ALL waiting threads that they cannot be allocated a buffer any more */
274 ReleaseSemaphore(This->hSemWaiting, This->lWaiting, NULL);
276 hr = S_OK;
278 else
280 if (This->lWaiting != 0)
281 ERR("Waiting: %d\n", This->lWaiting);
283 This->bCommitted = FALSE;
284 CloseHandle(This->hSemWaiting);
285 This->hSemWaiting = NULL;
287 hr = This->fnFree(iface);
288 if (FAILED(hr))
289 ERR("fnFree failed with error 0x%x\n", hr);
293 LeaveCriticalSection(This->pCritSect);
295 return hr;
298 static HRESULT WINAPI BaseMemAllocator_GetBuffer(IMemAllocator * iface, IMediaSample ** pSample, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime, DWORD dwFlags)
300 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
301 HRESULT hr = S_OK;
303 /* NOTE: The pStartTime and pEndTime parameters are not applied to the sample.
304 * The allocator might use these values to determine which buffer it retrieves */
306 TRACE("(%p)->(%p, %p, %p, %x)\n", This, pSample, pStartTime, pEndTime, dwFlags);
308 *pSample = NULL;
310 EnterCriticalSection(This->pCritSect);
311 if (!This->bCommitted || This->bDecommitQueued)
313 WARN("Not committed\n");
314 hr = VFW_E_NOT_COMMITTED;
316 else
317 ++This->lWaiting;
318 LeaveCriticalSection(This->pCritSect);
319 if (FAILED(hr))
320 return hr;
322 if (WaitForSingleObject(This->hSemWaiting, (dwFlags & AM_GBF_NOWAIT) ? 0 : INFINITE) != WAIT_OBJECT_0)
324 EnterCriticalSection(This->pCritSect);
325 --This->lWaiting;
326 LeaveCriticalSection(This->pCritSect);
327 WARN("Timed out\n");
328 return VFW_E_TIMEOUT;
331 EnterCriticalSection(This->pCritSect);
333 --This->lWaiting;
334 if (!This->bCommitted)
335 hr = VFW_E_NOT_COMMITTED;
336 else if (This->bDecommitQueued)
337 hr = VFW_E_TIMEOUT;
338 else
340 StdMediaSample2 *ms;
341 struct list * free = list_head(&This->free_list);
342 list_remove(free);
343 list_add_head(&This->used_list, free);
345 ms = LIST_ENTRY(free, StdMediaSample2, listentry);
346 assert(ms->ref == 0);
347 *pSample = (IMediaSample *)&ms->IMediaSample2_iface;
348 IMediaSample_AddRef(*pSample);
351 LeaveCriticalSection(This->pCritSect);
353 if (hr != S_OK)
354 WARN("%08x\n", hr);
355 return hr;
358 static HRESULT WINAPI BaseMemAllocator_ReleaseBuffer(IMemAllocator * iface, IMediaSample * pSample)
360 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
361 StdMediaSample2 * pStdSample = unsafe_impl_from_IMediaSample(pSample);
362 HRESULT hr = S_OK;
364 TRACE("(%p)->(%p)\n", This, pSample);
366 /* FIXME: make sure that sample is currently on the used list */
368 /* FIXME: we should probably check the ref count on the sample before freeing
369 * it to make sure that it is not still in use */
370 EnterCriticalSection(This->pCritSect);
372 if (!This->bCommitted)
373 ERR("Releasing a buffer when the allocator is not committed?!?\n");
375 /* remove from used_list */
376 list_remove(&pStdSample->listentry);
378 list_add_head(&This->free_list, &pStdSample->listentry);
380 if (list_empty(&This->used_list) && This->bDecommitQueued && This->bCommitted)
382 HRESULT hrfree;
384 if (This->lWaiting != 0)
385 ERR("Waiting: %d\n", This->lWaiting);
387 This->bCommitted = FALSE;
388 This->bDecommitQueued = FALSE;
390 CloseHandle(This->hSemWaiting);
391 This->hSemWaiting = NULL;
393 if (FAILED(hrfree = This->fnFree(iface)))
394 ERR("fnFree failed with error 0x%x\n", hrfree);
397 LeaveCriticalSection(This->pCritSect);
399 /* notify a waiting thread that there is now a free buffer */
400 if (This->hSemWaiting && !ReleaseSemaphore(This->hSemWaiting, 1, NULL))
402 ERR("ReleaseSemaphore failed with error %u\n", GetLastError());
403 hr = HRESULT_FROM_WIN32(GetLastError());
406 return hr;
409 static const IMemAllocatorVtbl BaseMemAllocator_VTable =
411 BaseMemAllocator_QueryInterface,
412 BaseMemAllocator_AddRef,
413 BaseMemAllocator_Release,
414 BaseMemAllocator_SetProperties,
415 BaseMemAllocator_GetProperties,
416 BaseMemAllocator_Commit,
417 BaseMemAllocator_Decommit,
418 BaseMemAllocator_GetBuffer,
419 BaseMemAllocator_ReleaseBuffer
422 static HRESULT StdMediaSample2_Construct(BYTE * pbBuffer, LONG cbBuffer, IMemAllocator * pParent, StdMediaSample2 ** ppSample)
424 assert(pbBuffer && pParent && (cbBuffer > 0));
426 if (!(*ppSample = CoTaskMemAlloc(sizeof(StdMediaSample2))))
427 return E_OUTOFMEMORY;
429 (*ppSample)->IMediaSample2_iface.lpVtbl = &StdMediaSample2_VTable;
430 (*ppSample)->ref = 0;
431 ZeroMemory(&(*ppSample)->props, sizeof((*ppSample)->props));
433 /* NOTE: no need to AddRef as the parent is guaranteed to be around
434 * at least as long as us and we don't want to create circular
435 * dependencies on the ref count */
436 (*ppSample)->pParent = pParent;
437 (*ppSample)->props.cbData = sizeof(AM_SAMPLE2_PROPERTIES);
438 (*ppSample)->props.cbBuffer = (*ppSample)->props.lActual = cbBuffer;
439 (*ppSample)->props.pbBuffer = pbBuffer;
440 (*ppSample)->media_time_valid = FALSE;
442 return S_OK;
445 static void StdMediaSample2_Delete(StdMediaSample2 * This)
447 if (This->props.pMediaType)
448 DeleteMediaType(This->props.pMediaType);
450 /* NOTE: does not remove itself from the list it belongs to */
451 CoTaskMemFree(This);
454 static inline StdMediaSample2 *impl_from_IMediaSample2(IMediaSample2 * iface)
456 return CONTAINING_RECORD(iface, StdMediaSample2, IMediaSample2_iface);
459 static HRESULT WINAPI StdMediaSample2_QueryInterface(IMediaSample2 * iface, REFIID riid, void ** ppv)
461 TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
463 *ppv = NULL;
465 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IMediaSample) ||
466 IsEqualIID(riid, &IID_IMediaSample2))
468 *ppv = iface;
469 IMediaSample2_AddRef(iface);
470 return S_OK;
473 FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
474 return E_NOINTERFACE;
477 static ULONG WINAPI StdMediaSample2_AddRef(IMediaSample2 * iface)
479 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
480 ULONG ref = InterlockedIncrement(&This->ref);
482 TRACE("(%p)->(): new ref = %d\n", This, ref);
484 return ref;
487 static ULONG WINAPI StdMediaSample2_Release(IMediaSample2 * iface)
489 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
490 ULONG ref = InterlockedDecrement(&This->ref);
492 TRACE("(%p)->(): new ref = %d\n", This, ref);
494 if (!ref)
496 if (This->props.pMediaType)
497 DeleteMediaType(This->props.pMediaType);
498 This->props.pMediaType = NULL;
499 This->props.dwSampleFlags = 0;
500 This->media_time_valid = FALSE;
502 if (This->pParent)
503 IMemAllocator_ReleaseBuffer(This->pParent, (IMediaSample *)iface);
504 else
505 StdMediaSample2_Delete(This);
507 return ref;
510 static HRESULT WINAPI StdMediaSample2_GetPointer(IMediaSample2 * iface, BYTE ** ppBuffer)
512 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
514 TRACE("(%p)->(%p)\n", iface, ppBuffer);
516 *ppBuffer = This->props.pbBuffer;
518 if (!*ppBuffer)
520 ERR("Requested an unlocked surface and trying to lock regardless\n");
521 return E_FAIL;
524 return S_OK;
527 static LONG WINAPI StdMediaSample2_GetSize(IMediaSample2 * iface)
529 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
531 TRACE("StdMediaSample2_GetSize()\n");
533 return This->props.cbBuffer;
536 static HRESULT WINAPI StdMediaSample2_GetTime(IMediaSample2 * iface, REFERENCE_TIME * pStart, REFERENCE_TIME * pEnd)
538 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
539 HRESULT hr;
541 TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd);
543 if (!(This->props.dwSampleFlags & AM_SAMPLE_TIMEVALID))
544 hr = VFW_E_SAMPLE_TIME_NOT_SET;
545 else if (!(This->props.dwSampleFlags & AM_SAMPLE_STOPVALID))
547 *pStart = This->props.tStart;
548 *pEnd = This->props.tStart + 1;
550 hr = VFW_S_NO_STOP_TIME;
552 else
554 *pStart = This->props.tStart;
555 *pEnd = This->props.tStop;
557 hr = S_OK;
560 return hr;
563 static HRESULT WINAPI StdMediaSample2_SetTime(IMediaSample2 *iface, REFERENCE_TIME *start, REFERENCE_TIME *end)
565 StdMediaSample2 *sample = impl_from_IMediaSample2(iface);
567 TRACE("sample %p, start %s, end %s.\n", sample, start ? debugstr_time(*start) : "(null)",
568 end ? debugstr_time(*end) : "(null)");
570 if (start)
572 sample->props.tStart = *start;
573 sample->props.dwSampleFlags |= AM_SAMPLE_TIMEVALID;
575 if (end)
577 sample->props.tStop = *end;
578 sample->props.dwSampleFlags |= AM_SAMPLE_STOPVALID;
580 else
581 sample->props.dwSampleFlags &= ~AM_SAMPLE_STOPVALID;
583 else
584 sample->props.dwSampleFlags &= ~(AM_SAMPLE_TIMEVALID | AM_SAMPLE_STOPVALID);
586 return S_OK;
589 static HRESULT WINAPI StdMediaSample2_IsSyncPoint(IMediaSample2 * iface)
591 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
593 TRACE("(%p)->()\n", iface);
595 return (This->props.dwSampleFlags & AM_SAMPLE_SPLICEPOINT) ? S_OK : S_FALSE;
598 static HRESULT WINAPI StdMediaSample2_SetSyncPoint(IMediaSample2 * iface, BOOL bIsSyncPoint)
600 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
602 TRACE("(%p)->(%s)\n", iface, bIsSyncPoint ? "TRUE" : "FALSE");
604 if (bIsSyncPoint)
605 This->props.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT;
606 else
607 This->props.dwSampleFlags &= ~AM_SAMPLE_SPLICEPOINT;
609 return S_OK;
612 static HRESULT WINAPI StdMediaSample2_IsPreroll(IMediaSample2 * iface)
614 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
616 TRACE("(%p)->()\n", iface);
618 return (This->props.dwSampleFlags & AM_SAMPLE_PREROLL) ? S_OK : S_FALSE;
621 static HRESULT WINAPI StdMediaSample2_SetPreroll(IMediaSample2 * iface, BOOL bIsPreroll)
623 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
625 TRACE("(%p)->(%s)\n", iface, bIsPreroll ? "TRUE" : "FALSE");
627 if (bIsPreroll)
628 This->props.dwSampleFlags |= AM_SAMPLE_PREROLL;
629 else
630 This->props.dwSampleFlags &= ~AM_SAMPLE_PREROLL;
632 return S_OK;
635 static LONG WINAPI StdMediaSample2_GetActualDataLength(IMediaSample2 * iface)
637 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
639 TRACE("(%p)->()\n", iface);
641 return This->props.lActual;
644 static HRESULT WINAPI StdMediaSample2_SetActualDataLength(IMediaSample2 * iface, LONG len)
646 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
648 TRACE("(%p)->(%d)\n", iface, len);
650 if ((len > This->props.cbBuffer) || (len < 0))
652 WARN("Tried to set length to %d, while max is %d\n", len, This->props.cbBuffer);
653 return VFW_E_BUFFER_OVERFLOW;
655 else
657 This->props.lActual = len;
658 return S_OK;
662 static HRESULT WINAPI StdMediaSample2_GetMediaType(IMediaSample2 * iface, AM_MEDIA_TYPE ** ppMediaType)
664 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
666 TRACE("(%p)->(%p)\n", iface, ppMediaType);
668 if (!This->props.pMediaType) {
669 /* Make sure we return a NULL pointer (required by native Quartz dll) */
670 if (ppMediaType)
671 *ppMediaType = NULL;
672 return S_FALSE;
675 if (!(*ppMediaType = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))))
676 return E_OUTOFMEMORY;
678 return CopyMediaType(*ppMediaType, This->props.pMediaType);
681 static HRESULT WINAPI StdMediaSample2_SetMediaType(IMediaSample2 * iface, AM_MEDIA_TYPE * pMediaType)
683 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
685 TRACE("(%p)->(%p)\n", iface, pMediaType);
687 if (This->props.pMediaType)
689 DeleteMediaType(This->props.pMediaType);
690 This->props.pMediaType = NULL;
693 if (!pMediaType)
695 This->props.dwSampleFlags &= ~AM_SAMPLE_TYPECHANGED;
696 return S_OK;
699 This->props.dwSampleFlags |= AM_SAMPLE_TYPECHANGED;
701 if (!(This->props.pMediaType = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))))
702 return E_OUTOFMEMORY;
704 return CopyMediaType(This->props.pMediaType, pMediaType);
707 static HRESULT WINAPI StdMediaSample2_IsDiscontinuity(IMediaSample2 * iface)
709 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
711 TRACE("(%p)->()\n", iface);
713 return (This->props.dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) ? S_OK : S_FALSE;
716 static HRESULT WINAPI StdMediaSample2_SetDiscontinuity(IMediaSample2 * iface, BOOL bIsDiscontinuity)
718 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
720 TRACE("(%p)->(%s)\n", iface, bIsDiscontinuity ? "TRUE" : "FALSE");
722 if (bIsDiscontinuity)
723 This->props.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;
724 else
725 This->props.dwSampleFlags &= ~AM_SAMPLE_DATADISCONTINUITY;
727 return S_OK;
730 static HRESULT WINAPI StdMediaSample2_GetMediaTime(IMediaSample2 * iface, LONGLONG * pStart, LONGLONG * pEnd)
732 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
734 TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd);
736 if (!This->media_time_valid)
737 return VFW_E_MEDIA_TIME_NOT_SET;
739 *pStart = This->tMediaStart;
740 *pEnd = This->tMediaEnd;
742 return S_OK;
745 static HRESULT WINAPI StdMediaSample2_SetMediaTime(IMediaSample2 *iface, LONGLONG *start, LONGLONG *end)
747 StdMediaSample2 *sample = impl_from_IMediaSample2(iface);
749 TRACE("sample %p, start %s, end %s.\n", sample, start ? debugstr_time(*start) : "(null)",
750 end ? debugstr_time(*end) : "(null)");
752 if (start)
754 if (!end) return E_POINTER;
755 sample->tMediaStart = *start;
756 sample->tMediaEnd = *end;
757 sample->media_time_valid = TRUE;
759 else
760 sample->media_time_valid = FALSE;
762 return S_OK;
765 static HRESULT WINAPI StdMediaSample2_GetProperties(IMediaSample2 * iface, DWORD cbProperties, BYTE * pbProperties)
767 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
769 TRACE("(%p)->(%d, %p)\n", iface, cbProperties, pbProperties);
771 memcpy(pbProperties, &This->props, min(cbProperties, sizeof(This->props)));
773 return S_OK;
776 static HRESULT WINAPI StdMediaSample2_SetProperties(IMediaSample2 * iface, DWORD cbProperties, const BYTE * pbProperties)
778 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
780 TRACE("(%p)->(%d, %p)\n", iface, cbProperties, pbProperties);
782 /* NOTE: pbBuffer and cbBuffer are read-only */
783 memcpy(&This->props, pbProperties, min(cbProperties, AM_SAMPLE2_PROP_SIZE_WRITABLE));
785 return S_OK;
788 static const IMediaSample2Vtbl StdMediaSample2_VTable =
790 StdMediaSample2_QueryInterface,
791 StdMediaSample2_AddRef,
792 StdMediaSample2_Release,
793 StdMediaSample2_GetPointer,
794 StdMediaSample2_GetSize,
795 StdMediaSample2_GetTime,
796 StdMediaSample2_SetTime,
797 StdMediaSample2_IsSyncPoint,
798 StdMediaSample2_SetSyncPoint,
799 StdMediaSample2_IsPreroll,
800 StdMediaSample2_SetPreroll,
801 StdMediaSample2_GetActualDataLength,
802 StdMediaSample2_SetActualDataLength,
803 StdMediaSample2_GetMediaType,
804 StdMediaSample2_SetMediaType,
805 StdMediaSample2_IsDiscontinuity,
806 StdMediaSample2_SetDiscontinuity,
807 StdMediaSample2_GetMediaTime,
808 StdMediaSample2_SetMediaTime,
809 StdMediaSample2_GetProperties,
810 StdMediaSample2_SetProperties
813 static inline StdMediaSample2 *unsafe_impl_from_IMediaSample(IMediaSample * iface)
815 IMediaSample2 *iface2 = (IMediaSample2 *)iface;
817 if (!iface)
818 return NULL;
819 assert(iface2->lpVtbl == &StdMediaSample2_VTable);
820 return impl_from_IMediaSample2(iface2);
823 typedef struct StdMemAllocator
825 BaseMemAllocator base;
826 CRITICAL_SECTION csState;
827 LPVOID pMemory;
828 } StdMemAllocator;
830 static inline StdMemAllocator *StdMemAllocator_from_IMemAllocator(IMemAllocator * iface)
832 return CONTAINING_RECORD(iface, StdMemAllocator, base.IMemAllocator_iface);
835 static HRESULT StdMemAllocator_Alloc(IMemAllocator * iface)
837 StdMemAllocator *This = StdMemAllocator_from_IMemAllocator(iface);
838 StdMediaSample2 * pSample = NULL;
839 SYSTEM_INFO si;
840 LONG i;
842 assert(list_empty(&This->base.free_list));
844 /* check alignment */
845 GetSystemInfo(&si);
847 /* we do not allow a courser alignment than the OS page size */
848 if ((si.dwPageSize % This->base.props.cbAlign) != 0)
849 return VFW_E_BADALIGN;
851 /* FIXME: each sample has to have its buffer start on the right alignment.
852 * We don't do this at the moment */
854 /* allocate memory */
855 This->pMemory = VirtualAlloc(NULL, (This->base.props.cbBuffer + This->base.props.cbPrefix) * This->base.props.cBuffers, MEM_COMMIT, PAGE_READWRITE);
857 if (!This->pMemory)
858 return E_OUTOFMEMORY;
860 for (i = This->base.props.cBuffers - 1; i >= 0; i--)
862 /* pbBuffer does not start at the base address, it starts at base + cbPrefix */
863 BYTE * pbBuffer = (BYTE *)This->pMemory + i * (This->base.props.cbBuffer + This->base.props.cbPrefix) + This->base.props.cbPrefix;
865 StdMediaSample2_Construct(pbBuffer, This->base.props.cbBuffer, iface, &pSample);
867 list_add_head(&This->base.free_list, &pSample->listentry);
870 return S_OK;
873 static HRESULT StdMemAllocator_Free(IMemAllocator * iface)
875 StdMemAllocator *This = StdMemAllocator_from_IMemAllocator(iface);
876 struct list * cursor;
878 if (!list_empty(&This->base.used_list))
880 WARN("Freeing allocator with outstanding samples!\n");
881 while ((cursor = list_head(&This->base.used_list)) != NULL)
883 StdMediaSample2 *pSample;
884 list_remove(cursor);
885 pSample = LIST_ENTRY(cursor, StdMediaSample2, listentry);
886 pSample->pParent = NULL;
890 while ((cursor = list_head(&This->base.free_list)) != NULL)
892 list_remove(cursor);
893 StdMediaSample2_Delete(LIST_ENTRY(cursor, StdMediaSample2, listentry));
896 /* free memory */
897 if (!VirtualFree(This->pMemory, 0, MEM_RELEASE))
899 ERR("Couldn't free memory. Error: %u\n", GetLastError());
900 return HRESULT_FROM_WIN32(GetLastError());
903 return S_OK;
906 static void StdMemAllocator_Destroy(IMemAllocator *iface)
908 StdMemAllocator *This = StdMemAllocator_from_IMemAllocator(iface);
910 This->csState.DebugInfo->Spare[0] = 0;
911 DeleteCriticalSection(&This->csState);
913 CoTaskMemFree(This);
916 HRESULT mem_allocator_create(IUnknown *lpUnkOuter, IUnknown **out)
918 StdMemAllocator * pMemAlloc;
919 HRESULT hr;
921 if (lpUnkOuter)
922 return CLASS_E_NOAGGREGATION;
924 if (!(pMemAlloc = CoTaskMemAlloc(sizeof(*pMemAlloc))))
925 return E_OUTOFMEMORY;
927 InitializeCriticalSection(&pMemAlloc->csState);
928 pMemAlloc->csState.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": StdMemAllocator.csState");
930 pMemAlloc->pMemory = NULL;
932 if (SUCCEEDED(hr = BaseMemAllocator_Init(StdMemAllocator_Alloc, StdMemAllocator_Free, NULL, NULL, NULL, StdMemAllocator_Destroy, &pMemAlloc->csState, &pMemAlloc->base)))
933 *out = (IUnknown *)&pMemAlloc->base.IMemAllocator_iface;
934 else
935 CoTaskMemFree(pMemAlloc);
937 return hr;