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