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