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