Prevent crash when no URL is specified.
[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 = 1;
101     pMemAlloc->pProps = NULL;
102     list_init(&pMemAlloc->free_list);
103     list_init(&pMemAlloc->used_list);
104     pMemAlloc->fnAlloc = fnAlloc;
105     pMemAlloc->fnFree = fnFree;
106     pMemAlloc->bDecommitQueued = FALSE;
107     pMemAlloc->bCommitted = FALSE;
108     pMemAlloc->hSemWaiting = NULL;
109     pMemAlloc->lWaiting = 0;
110
111     InitializeCriticalSection(&pMemAlloc->csState);
112
113     return S_OK;
114 }
115
116 static HRESULT WINAPI BaseMemAllocator_QueryInterface(IMemAllocator * iface, REFIID riid, LPVOID * ppv)
117 {
118     BaseMemAllocator *This = (BaseMemAllocator *)iface;
119     TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
120
121     *ppv = NULL;
122
123     if (IsEqualIID(riid, &IID_IUnknown))
124         *ppv = (LPVOID)This;
125     else if (IsEqualIID(riid, &IID_IMemAllocator))
126         *ppv = (LPVOID)This;
127
128     if (*ppv)
129     {
130         IUnknown_AddRef((IUnknown *)(*ppv));
131         return S_OK;
132     }
133
134     FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
135
136     return E_NOINTERFACE;
137 }
138
139 static ULONG WINAPI BaseMemAllocator_AddRef(IMemAllocator * iface)
140 {
141     BaseMemAllocator *This = (BaseMemAllocator *)iface;
142     ULONG ref = InterlockedIncrement(&This->ref);
143
144     TRACE("(%p)->() AddRef from %ld\n", iface, ref - 1);
145
146     return ref;
147 }
148
149 static ULONG WINAPI BaseMemAllocator_Release(IMemAllocator * iface)
150 {
151     BaseMemAllocator *This = (BaseMemAllocator *)iface;
152     ULONG ref = InterlockedDecrement(&This->ref);
153
154     TRACE("(%p)->() Release from %ld\n", iface, ref + 1);
155
156     if (!ref)
157     {
158         CloseHandle(This->hSemWaiting);
159         if (This->bCommitted)
160             This->fnFree(iface);
161         HeapFree(GetProcessHeap(), 0, This->pProps);
162         CoTaskMemFree(This);
163         return 0;
164     }
165     return ref;
166 }
167
168 static HRESULT WINAPI BaseMemAllocator_SetProperties(IMemAllocator * iface, ALLOCATOR_PROPERTIES *pRequest, ALLOCATOR_PROPERTIES *pActual)
169 {
170     BaseMemAllocator *This = (BaseMemAllocator *)iface;
171     HRESULT hr;
172
173     TRACE("(%p, %p)\n", pRequest, pActual);
174
175     EnterCriticalSection(&This->csState);
176     {
177         if (!list_empty(&This->used_list))
178             hr = VFW_E_BUFFERS_OUTSTANDING;
179         else if (This->bCommitted)
180             hr = VFW_E_ALREADY_COMMITTED;
181         else
182         {
183             if (!This->pProps)
184                 This->pProps = HeapAlloc(GetProcessHeap(), 0, sizeof(*This->pProps));
185
186             if (!This->pProps)
187                 hr = E_OUTOFMEMORY;
188             else
189             {
190                 memcpy(This->pProps, pRequest, sizeof(*This->pProps));
191                     
192                 memcpy(pActual, pRequest, sizeof(*pActual));
193
194                 hr = S_OK;
195             }
196         }
197     }
198     LeaveCriticalSection(&This->csState);
199
200     return hr;
201 }
202
203 static HRESULT WINAPI BaseMemAllocator_GetProperties(IMemAllocator * iface, ALLOCATOR_PROPERTIES *pProps)
204 {
205     BaseMemAllocator *This = (BaseMemAllocator *)iface;
206     HRESULT hr = S_OK;
207
208     TRACE("(%p)\n", pProps);
209
210     EnterCriticalSection(&This->csState);
211     {
212         /* NOTE: this is different from the native version.
213          * It would silently succeed if the properties had
214          * not been set, but would fail further on down the
215          * line with some obscure error like having an
216          * invalid alignment. Whether or not our version
217          * will cause any problems remains to be seen */
218         if (!This->pProps)
219             hr = VFW_E_SIZENOTSET;
220         else
221             memcpy(pProps, This->pProps, sizeof(*pProps));
222     }
223     LeaveCriticalSection(&This->csState);
224
225     return hr;
226 }
227
228 static HRESULT WINAPI BaseMemAllocator_Commit(IMemAllocator * iface)
229 {
230     BaseMemAllocator *This = (BaseMemAllocator *)iface;
231     HRESULT hr;
232
233     TRACE("()\n");
234
235     EnterCriticalSection(&This->csState);
236     {
237         if (!This->pProps)
238             hr = VFW_E_SIZENOTSET;
239         else if (This->bCommitted)
240             hr = VFW_E_ALREADY_COMMITTED;
241         else if (This->bDecommitQueued)
242         {
243             This->bDecommitQueued = FALSE;
244             hr = S_OK;
245         }
246         else
247         {
248             if (!(This->hSemWaiting = CreateSemaphoreW(NULL, This->pProps->cBuffers, This->pProps->cBuffers, NULL)))
249             {
250                 ERR("Couldn't create semaphore (error was %ld)\n", GetLastError());
251                 hr = HRESULT_FROM_WIN32(GetLastError());
252             }
253             else
254             {
255                 hr = This->fnAlloc(iface);
256                 if (SUCCEEDED(hr))
257                     This->bCommitted = TRUE;
258                 else
259                     ERR("fnAlloc failed with error 0x%lx\n", hr);
260             }
261         }
262     }
263     LeaveCriticalSection(&This->csState);
264
265     return hr;
266 }
267
268 static HRESULT WINAPI BaseMemAllocator_Decommit(IMemAllocator * iface)
269 {
270     BaseMemAllocator *This = (BaseMemAllocator *)iface;
271     HRESULT hr;
272
273     TRACE("()\n");
274
275     EnterCriticalSection(&This->csState);
276     {
277         if (!This->bCommitted)
278             hr = VFW_E_NOT_COMMITTED;
279         else
280         {
281             if (!list_empty(&This->used_list))
282             {
283                 This->bDecommitQueued = TRUE;
284                 /* notify ALL waiting threads that they cannot be allocated a buffer any more */
285                 ReleaseSemaphore(This->hSemWaiting, This->lWaiting, NULL);
286                 
287                 hr = S_OK;
288             }
289             else
290             {
291                 assert(This->lWaiting == 0);
292
293                 This->bCommitted = FALSE;
294                 CloseHandle(This->hSemWaiting);
295                 This->hSemWaiting = NULL;
296
297                 hr = This->fnFree(iface);
298                 if (FAILED(hr))
299                     ERR("fnFree failed with error 0x%lx\n", hr);
300             }
301         }
302     }
303     LeaveCriticalSection(&This->csState);
304
305     return hr;
306 }
307
308 static HRESULT WINAPI BaseMemAllocator_GetBuffer(IMemAllocator * iface, IMediaSample ** pSample, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime, DWORD dwFlags)
309 {
310     BaseMemAllocator *This = (BaseMemAllocator *)iface;
311     HRESULT hr = S_OK;
312
313     /* NOTE: The pStartTime and pEndTime parameters are not applied to the sample. 
314      * The allocator might use these values to determine which buffer it retrieves */
315     
316     TRACE("(%p, %p, %p, %lx)\n", pSample, pStartTime, pEndTime, dwFlags);
317
318     *pSample = NULL;
319
320     if (!This->bCommitted)
321         return VFW_E_NOT_COMMITTED;
322
323     This->lWaiting++;
324     if (WaitForSingleObject(This->hSemWaiting, (dwFlags & AM_GBF_NOWAIT) ? 0 : INFINITE) != WAIT_OBJECT_0)
325     {
326         This->lWaiting--;
327         return VFW_E_TIMEOUT;
328     }
329     This->lWaiting--;
330
331     EnterCriticalSection(&This->csState);
332     {
333         if (!This->bCommitted)
334             hr = VFW_E_NOT_COMMITTED;
335         else if (This->bDecommitQueued)
336             hr = VFW_E_TIMEOUT;
337         else
338         {
339             struct list * free = list_head(&This->free_list);
340             list_remove(free);
341             list_add_head(&This->used_list, free);
342
343             *pSample = (IMediaSample *)LIST_ENTRY(free, StdMediaSample2, listentry);
344
345             assert(((StdMediaSample2 *)*pSample)->ref == 0);
346
347             IMediaSample_AddRef(*pSample);
348         }
349     }
350     LeaveCriticalSection(&This->csState);
351
352     return hr;
353 }
354
355 static HRESULT WINAPI BaseMemAllocator_ReleaseBuffer(IMemAllocator * iface, IMediaSample * pSample)
356 {
357     BaseMemAllocator *This = (BaseMemAllocator *)iface;
358     StdMediaSample2 * pStdSample = (StdMediaSample2 *)pSample;
359     HRESULT hr = S_OK;
360     
361     TRACE("(%p)\n", pSample);
362
363     /* FIXME: make sure that sample is currently on the used list */
364
365     /* FIXME: we should probably check the ref count on the sample before freeing
366      * it to make sure that it is not still in use */
367     EnterCriticalSection(&This->csState);
368     {
369         if (!This->bCommitted)
370             ERR("Releasing a buffer when the allocator is not committed?!?\n");
371
372                 /* remove from used_list */
373         list_remove(&pStdSample->listentry);
374
375         list_add_head(&This->free_list, &pStdSample->listentry);
376
377         if (list_empty(&This->used_list) && This->bDecommitQueued && This->bCommitted)
378         {
379             HRESULT hrfree;
380
381             assert(This->lWaiting == 0);
382
383             This->bCommitted = FALSE;
384             This->bDecommitQueued = FALSE;
385
386             CloseHandle(This->hSemWaiting);
387             This->hSemWaiting = NULL;
388             
389             if (FAILED(hrfree = This->fnFree(iface)))
390                 ERR("fnFree failed with error 0x%lx\n", hrfree);
391         }
392     }
393     LeaveCriticalSection(&This->csState);
394
395     /* notify a waiting thread that there is now a free buffer */
396     if (!ReleaseSemaphore(This->hSemWaiting, 1, NULL))
397     {
398         ERR("ReleaseSemaphore failed with error %ld\n", GetLastError());
399         hr = HRESULT_FROM_WIN32(GetLastError());
400     }
401
402     return hr;
403 }
404
405 static const IMemAllocatorVtbl BaseMemAllocator_VTable = 
406 {
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     StdMediaSample2 *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     StdMediaSample2 *This = (StdMediaSample2 *)iface;
476     ULONG ref = InterlockedIncrement(&This->ref);
477
478     TRACE("(%p)->() AddRef from %ld\n", iface, ref - 1);
479
480     return ref;
481 }
482
483 static ULONG WINAPI StdMediaSample2_Release(IMediaSample2 * iface)
484 {
485     StdMediaSample2 *This = (StdMediaSample2 *)iface;
486     ULONG ref = InterlockedDecrement(&This->ref);
487
488     TRACE("(%p)->() Release from %ld\n", iface, ref + 1);
489
490     if (!ref)
491     {
492         IMemAllocator_ReleaseBuffer(This->pParent, (IMediaSample *)iface);
493         return 0;
494     }
495     return ref;
496 }
497
498 static HRESULT WINAPI StdMediaSample2_GetPointer(IMediaSample2 * iface, BYTE ** ppBuffer)
499 {
500     StdMediaSample2 *This = (StdMediaSample2 *)iface;
501
502     TRACE("(%p)\n", ppBuffer);
503
504     *ppBuffer = This->props.pbBuffer;
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)\n", 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 S_OK;
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)\n", 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("()\n");
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("(%s)\n", bIsSyncPoint ? "TRUE" : "FALSE");
584
585     This->props.dwSampleFlags = (This->props.dwSampleFlags & ~AM_SAMPLE_SPLICEPOINT) | bIsSyncPoint ? AM_SAMPLE_SPLICEPOINT : 0;
586
587     return S_OK;
588 }
589
590 static HRESULT WINAPI StdMediaSample2_IsPreroll(IMediaSample2 * iface)
591 {
592     StdMediaSample2 *This = (StdMediaSample2 *)iface;
593
594     TRACE("()\n");
595
596     return (This->props.dwSampleFlags & AM_SAMPLE_PREROLL) ? S_OK : S_FALSE;
597 }
598
599 static HRESULT WINAPI StdMediaSample2_SetPreroll(IMediaSample2 * iface, BOOL bIsPreroll)
600 {
601     StdMediaSample2 *This = (StdMediaSample2 *)iface;
602
603     TRACE("(%s)\n", bIsPreroll ? "TRUE" : "FALSE");
604
605     This->props.dwSampleFlags = (This->props.dwSampleFlags & ~AM_SAMPLE_PREROLL) | bIsPreroll ? AM_SAMPLE_PREROLL : 0;
606
607     return S_OK;
608 }
609
610 static LONG WINAPI StdMediaSample2_GetActualDataLength(IMediaSample2 * iface)
611 {
612     StdMediaSample2 *This = (StdMediaSample2 *)iface;
613
614     TRACE("()\n");
615
616     return This->props.lActual;
617 }
618
619 static HRESULT WINAPI StdMediaSample2_SetActualDataLength(IMediaSample2 * iface, LONG len)
620 {
621     StdMediaSample2 *This = (StdMediaSample2 *)iface;
622
623     TRACE("(%ld)\n", len);
624
625     if ((len > This->props.cbBuffer) || (len < 0))
626         return VFW_E_BUFFER_OVERFLOW;
627     else
628     {
629         This->props.lActual = len;
630         return S_OK;
631     }
632 }
633
634 static HRESULT WINAPI StdMediaSample2_GetMediaType(IMediaSample2 * iface, AM_MEDIA_TYPE ** ppMediaType)
635 {
636     StdMediaSample2 *This = (StdMediaSample2 *)iface;
637
638     TRACE("(%p)\n", ppMediaType);
639
640     if (!This->props.pMediaType) {
641         /* Make sure we return a NULL pointer (required by native Quartz dll) */
642         if (ppMediaType)
643             *ppMediaType = NULL;
644         return S_FALSE;
645     }
646
647     if (!(*ppMediaType = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))))
648         return E_OUTOFMEMORY;
649
650     return CopyMediaType(*ppMediaType, This->props.pMediaType);
651 }
652
653 static HRESULT WINAPI StdMediaSample2_SetMediaType(IMediaSample2 * iface, AM_MEDIA_TYPE * pMediaType)
654 {
655     StdMediaSample2 *This = (StdMediaSample2 *)iface;
656
657     TRACE("(%p)\n", pMediaType);
658
659     if (This->props.pMediaType)
660         DeleteMediaType(This->props.pMediaType);
661     else if (!(This->props.pMediaType = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))))
662         return E_OUTOFMEMORY;
663
664     return CopyMediaType(This->props.pMediaType, pMediaType);
665 }
666
667 static HRESULT WINAPI StdMediaSample2_IsDiscontinuity(IMediaSample2 * iface)
668 {
669     StdMediaSample2 *This = (StdMediaSample2 *)iface;
670
671     TRACE("()\n");
672
673     return (This->props.dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) ? S_OK : S_FALSE;
674 }
675
676 static HRESULT WINAPI StdMediaSample2_SetDiscontinuity(IMediaSample2 * iface, BOOL bIsDiscontinuity)
677 {
678     StdMediaSample2 *This = (StdMediaSample2 *)iface;
679
680     TRACE("(%s)\n", bIsDiscontinuity ? "TRUE" : "FALSE");
681
682     This->props.dwSampleFlags = (This->props.dwSampleFlags & ~AM_SAMPLE_DATADISCONTINUITY) | bIsDiscontinuity ? AM_SAMPLE_DATADISCONTINUITY : 0;
683
684     return S_OK;
685 }
686
687 static HRESULT WINAPI StdMediaSample2_GetMediaTime(IMediaSample2 * iface, LONGLONG * pStart, LONGLONG * pEnd)
688 {
689     StdMediaSample2 *This = (StdMediaSample2 *)iface;
690
691     TRACE("(%p, %p)\n", pStart, pEnd);
692
693     if (This->tMediaStart == INVALID_MEDIA_TIME)
694         return VFW_E_MEDIA_TIME_NOT_SET;
695
696     *pStart = This->tMediaStart;
697     *pEnd = This->tMediaEnd;
698
699     return E_NOTIMPL;
700 }
701
702 static HRESULT WINAPI StdMediaSample2_SetMediaTime(IMediaSample2 * iface, LONGLONG * pStart, LONGLONG * pEnd)
703 {
704     StdMediaSample2 *This = (StdMediaSample2 *)iface;
705
706     TRACE("(%p, %p)\n", pStart, pEnd);
707
708     if (pStart)
709         This->tMediaStart = *pStart;
710     else
711         This->tMediaStart = INVALID_MEDIA_TIME;
712
713     if (pEnd)
714         This->tMediaEnd = *pEnd;
715     else
716         This->tMediaEnd = 0;
717
718     return S_OK;
719 }
720
721 static HRESULT WINAPI StdMediaSample2_GetProperties(IMediaSample2 * iface, DWORD cbProperties, BYTE * pbProperties)
722 {
723     StdMediaSample2 *This = (StdMediaSample2 *)iface;
724
725     TRACE("(%ld, %p)\n", cbProperties, pbProperties);
726
727     memcpy(pbProperties, &This->props, min(cbProperties, sizeof(This->props)));
728
729     return S_OK;
730 }
731
732 static HRESULT WINAPI StdMediaSample2_SetProperties(IMediaSample2 * iface, DWORD cbProperties, const BYTE * pbProperties)
733 {
734     StdMediaSample2 *This = (StdMediaSample2 *)iface;
735
736     TRACE("(%ld, %p)\n", cbProperties, pbProperties);
737
738     /* NOTE: pbBuffer and cbBuffer are read-only */
739     memcpy(&This->props, pbProperties, min(cbProperties, AM_SAMPLE2_PROP_SIZE_WRITABLE));
740
741     return S_OK;
742 }
743
744 static const IMediaSample2Vtbl StdMediaSample2_VTable = 
745 {
746     StdMediaSample2_QueryInterface,
747     StdMediaSample2_AddRef,
748     StdMediaSample2_Release,
749     StdMediaSample2_GetPointer,
750     StdMediaSample2_GetSize,
751     StdMediaSample2_GetTime,
752     StdMediaSample2_SetTime,
753     StdMediaSample2_IsSyncPoint,
754     StdMediaSample2_SetSyncPoint,
755     StdMediaSample2_IsPreroll,
756     StdMediaSample2_SetPreroll,
757     StdMediaSample2_GetActualDataLength,
758     StdMediaSample2_SetActualDataLength,
759     StdMediaSample2_GetMediaType,
760     StdMediaSample2_SetMediaType,
761     StdMediaSample2_IsDiscontinuity,
762     StdMediaSample2_SetDiscontinuity,
763     StdMediaSample2_GetMediaTime,
764     StdMediaSample2_SetMediaTime,
765     StdMediaSample2_GetProperties,
766     StdMediaSample2_SetProperties
767 };
768
769 typedef struct StdMemAllocator
770 {
771     BaseMemAllocator base;
772     LPVOID pMemory;
773 } StdMemAllocator;
774
775 static HRESULT StdMemAllocator_Alloc(IMemAllocator * iface)
776 {
777     StdMemAllocator *This = (StdMemAllocator *)iface;
778     StdMediaSample2 * pSample = NULL;
779     SYSTEM_INFO si;
780     long i;
781
782     assert(list_empty(&This->base.free_list));
783
784     /* check alignment */
785     GetSystemInfo(&si);
786
787     /* we do not allow a courser alignment than the OS page size */
788     if ((si.dwPageSize % This->base.pProps->cbAlign) != 0)
789         return VFW_E_BADALIGN;
790
791     /* FIXME: each sample has to have its buffer start on the right alignment.
792      * We don't do this at the moment */
793
794     /* allocate memory */
795     This->pMemory = VirtualAlloc(NULL, (This->base.pProps->cbBuffer + This->base.pProps->cbPrefix) * This->base.pProps->cBuffers, MEM_COMMIT, PAGE_READWRITE);
796
797     for (i = This->base.pProps->cBuffers - 1; i >= 0; i--)
798     {
799         /* pbBuffer does not start at the base address, it starts at base + cbPrefix */
800         BYTE * pbBuffer = (BYTE *)This->pMemory + i * (This->base.pProps->cbBuffer + This->base.pProps->cbPrefix) + This->base.pProps->cbPrefix;
801         
802         StdMediaSample2_Construct(pbBuffer, This->base.pProps->cbBuffer, iface, &pSample);
803
804         list_add_head(&This->base.free_list, &pSample->listentry);
805     }
806
807     return S_OK;
808 }
809
810 static HRESULT StdMemAllocator_Free(IMemAllocator * iface)
811 {
812     StdMemAllocator *This = (StdMemAllocator *)iface;
813     struct list * cursor;
814
815     assert(list_empty(&This->base.used_list));
816
817     while ((cursor = list_head(&This->base.free_list)) != NULL)
818     {
819         list_remove(cursor);
820         StdMediaSample2_Delete(LIST_ENTRY(cursor, StdMediaSample2, listentry));
821     }
822     
823     /* free memory */
824     if (!VirtualFree(This->pMemory, 0, MEM_RELEASE))
825     {
826         ERR("Couldn't free memory. Error: %ld\n", GetLastError());
827         return HRESULT_FROM_WIN32(GetLastError());
828     }
829
830     return S_OK;
831 }
832
833 HRESULT StdMemAllocator_create(LPUNKNOWN lpUnkOuter, LPVOID * ppv)
834 {
835     StdMemAllocator * pMemAlloc;
836     HRESULT hr;
837
838     *ppv = NULL;
839     
840     if (lpUnkOuter)
841         return CLASS_E_NOAGGREGATION;
842
843     if (!(pMemAlloc = CoTaskMemAlloc(sizeof(*pMemAlloc))))
844         return E_OUTOFMEMORY;
845
846     pMemAlloc->pMemory = NULL;
847
848     if (SUCCEEDED(hr = BaseMemAllocator_Init(StdMemAllocator_Alloc, StdMemAllocator_Free, &pMemAlloc->base)))
849         *ppv = (LPVOID)pMemAlloc;
850     else
851         CoTaskMemFree(pMemAlloc);
852
853     return hr;
854 }