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