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