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