xref: /reactos/dll/directx/wine/quartz/memallocator.c (revision d5b576b2)
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