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