Hi,
This is a revised version of the previous DSHOW-08 patch. LONG_LONG_MAX
wasn't portable so I used David Laight's suggestion of using inverting all
the bits to get the desired value. I still include limits.h for MS compiler
support (the compiler does not support the 'ull' suffix).
Changelog:
- Implement IMemAllocator and IMediaSample
Rob
? wine/dlls/quartz/list.h
? wine/dlls/quartz/memallocator.c
Index: wine/dlls/quartz/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/quartz/Makefile.in,v
retrieving revision 1.30
diff -u -r1.30 Makefile.in
--- wine/dlls/quartz/Makefile.in 23 Aug 2003 22:56:43 -0000 1.30
+++ wine/dlls/quartz/Makefile.in 9 Sep 2003 18:28:40 -0000
@@ -17,6 +17,7 @@
filtergraph.c \
filtermapper.c \
main.c \
+ memallocator.c \
pin.c \
regsvr.c
Index: wine/dlls/quartz/enummedia.c
===================================================================
RCS file: /home/wine/wine/dlls/quartz/enummedia.c,v
retrieving revision 1.1
diff -u -r1.1 enummedia.c
--- wine/dlls/quartz/enummedia.c 6 Aug 2003 22:04:45 -0000 1.1
+++ wine/dlls/quartz/enummedia.c 9 Sep 2003 18:28:41 -0000
@@ -24,11 +24,27 @@
WINE_DEFAULT_DEBUG_CHANNEL(quartz);
-void CopyMediaType(AM_MEDIA_TYPE * pDest, const AM_MEDIA_TYPE *pSrc)
+HRESULT CopyMediaType(AM_MEDIA_TYPE * pDest, const AM_MEDIA_TYPE *pSrc)
{
memcpy(pDest, pSrc, sizeof(AM_MEDIA_TYPE));
- pDest->pbFormat = CoTaskMemAlloc(pSrc->cbFormat);
+ if (!(pDest->pbFormat = CoTaskMemAlloc(pSrc->cbFormat)))
+ return E_OUTOFMEMORY;
memcpy(pDest->pbFormat, pSrc->pbFormat, pSrc->cbFormat);
+ return S_OK;
+}
+
+void DeleteMediaType(AM_MEDIA_TYPE * pMediaType)
+{
+ if (pMediaType->pbFormat)
+ {
+ CoTaskMemFree(pMediaType->pbFormat);
+ pMediaType->pbFormat = NULL;
+ }
+ if (pMediaType->pUnk)
+ {
+ IUnknown_Release(pMediaType->pUnk);
+ pMediaType->pUnk = NULL;
+ }
}
BOOL CompareMediaTypes(const AM_MEDIA_TYPE * pmt1, const AM_MEDIA_TYPE * pmt2, BOOL bWildcards)
Index: wine/dlls/quartz/main.c
===================================================================
RCS file: /home/wine/wine/dlls/quartz/main.c,v
retrieving revision 1.28
diff -u -r1.28 main.c
--- wine/dlls/quartz/main.c 23 Aug 2003 22:56:43 -0000 1.28
+++ wine/dlls/quartz/main.c 9 Sep 2003 18:28:41 -0000
@@ -64,6 +64,7 @@
{ &CLSID_FilterMapper, FilterMapper2_create },
{ &CLSID_FilterMapper2, FilterMapper2_create },
{ &CLSID_AsyncReader, AsyncReader_create },
+ { &CLSID_MemoryAllocator, StdMemAllocator_create },
};
static HRESULT WINAPI
Index: wine/dlls/quartz/quartz_private.h
===================================================================
RCS file: /home/wine/wine/dlls/quartz/quartz_private.h,v
retrieving revision 1.11
diff -u -r1.11 quartz_private.h
--- wine/dlls/quartz/quartz_private.h 5 Sep 2003 23:08:32 -0000 1.11
+++ wine/dlls/quartz/quartz_private.h 9 Sep 2003 18:28:41 -0000
@@ -35,6 +35,7 @@
HRESULT FILTERGRAPH_create(IUnknown *pUnkOuter, LPVOID *ppObj) ;
HRESULT FilterMapper2_create(IUnknown *pUnkOuter, LPVOID *ppObj);
HRESULT AsyncReader_create(IUnknown * pUnkOuter, LPVOID * ppv);
+HRESULT StdMemAllocator_create(IUnknown * pUnkOuter, LPVOID * ppv);
HRESULT EnumMonikerImpl_Create(IMoniker ** ppMoniker, ULONG nMonikerCount, IEnumMoniker ** ppEnum);
@@ -56,7 +57,8 @@
extern const char * qzdebugstr_guid(const GUID * id);
extern const char * qzdebugstr_State(FILTER_STATE state);
-void CopyMediaType(AM_MEDIA_TYPE * pDest, const AM_MEDIA_TYPE *pSrc);
+HRESULT CopyMediaType(AM_MEDIA_TYPE * pDest, const AM_MEDIA_TYPE *pSrc);
+void DeleteMediaType(AM_MEDIA_TYPE * pmt);
BOOL CompareMediaTypes(const AM_MEDIA_TYPE * pmt1, const AM_MEDIA_TYPE * pmt2, BOOL bWildcards);
void dump_AM_MEDIA_TYPE(const AM_MEDIA_TYPE * pmt);
--- /dev/null Mon Jun 24 01:53:01 2002
+++ wine/dlls/quartz/list.h Mon Sep 8 00:48:58 2003
@@ -0,0 +1,106 @@
+/*
+ * Linked lists support
+ *
+ * Copyright (C) 2002 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __WINE_SERVER_LIST_H
+#define __WINE_SERVER_LIST_H
+
+struct list
+{
+ struct list *next;
+ struct list *prev;
+};
+
+/* add element at the head of the list */
+inline static void list_add_head( struct list *list, struct list *elem )
+{
+ elem->next = list->next;
+ elem->prev = list;
+ list->next->prev = elem;
+ list->next = elem;
+}
+
+/* add element at the tail of the list */
+inline static void list_add_tail( struct list *list, struct list *elem )
+{
+ elem->next = list;
+ elem->prev = list->prev;
+ list->prev->next = elem;
+ list->prev = elem;
+}
+
+/* remove an element from its list */
+inline static void list_remove( struct list *elem )
+{
+ elem->next->prev = elem->prev;
+ elem->prev->next = elem->next;
+}
+
+/* get the next element */
+inline static struct list *list_next( struct list *list, struct list *elem )
+{
+ struct list *ret = elem->next;
+ if (elem->next == list) ret = NULL;
+ return ret;
+}
+
+/* get the previous element */
+inline static struct list *list_prev( struct list *list, struct list *elem )
+{
+ struct list *ret = elem->prev;
+ if (elem->prev == list) ret = NULL;
+ return ret;
+}
+
+/* get the first element */
+inline static struct list *list_head( struct list *list )
+{
+ return list_next( list, list );
+}
+
+/* get the last element */
+inline static struct list *list_tail( struct list *list )
+{
+ return list_prev( list, list );
+}
+
+/* check if a list is empty */
+inline static int list_empty( struct list *list )
+{
+ return list->next == list;
+}
+
+/* initialize a list */
+inline static void list_init( struct list *list )
+{
+ list->next = list->prev = list;
+}
+
+/* iterate through the list */
+#define LIST_FOR_EACH(cursor,list) \
+ for ((cursor) = (list)->next; (cursor) != (list); (cursor) = (cursor)->next)
+
+/* macros for statically initialized lists */
+#define LIST_INIT(list) { &(list), &(list) }
+
+/* get pointer to object containing list element */
+#define LIST_ENTRY(elem, type, field) \
+ ((type *)((char *)(elem) - (unsigned int)(&((type *)0)->field)))
+
+#endif /* __WINE_SERVER_LIST_H */
--- /dev/null Mon Jun 24 01:53:01 2002
+++ wine/dlls/quartz/memallocator.c Tue Sep 9 19:36:08 2003
@@ -0,0 +1,889 @@
+/*
+ * Memory Allocator and Media Sample Implementation
+ *
+ * Copyright 2003 Robert Shearman
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <limits.h>
+
+#include "quartz_private.h"
+#include "list.h"
+
+#include "wine/debug.h"
+
+#include "windef.h"
+#include "winbase.h"
+#include "vfwmsgs.h"
+#include <assert.h>
+
+WINE_DEFAULT_DEBUG_CHANNEL(quartz);
+
+void dump_AM_SAMPLE2_PROPERTIES(AM_SAMPLE2_PROPERTIES * pProps)
+{
+ if (!pProps)
+ {
+ TRACE("AM_SAMPLE2_PROPERTIES: (null)\n");
+ return;
+ }
+ TRACE("\tcbData: %ld\n", pProps->cbData);
+ TRACE("\tdwTypeSpecificFlags: 0x%8lx\n", pProps->dwTypeSpecificFlags);
+ TRACE("\tdwSampleFlags: 0x%8lx\n", pProps->dwSampleFlags);
+ TRACE("\tlActual: %ld\n", pProps->lActual);
+ TRACE("\ttStart: %lx%08lx%s\n", (LONG)(pProps->tStart >> 32), (LONG)pProps->tStart, pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID ? "" : " (not valid)");
+ TRACE("\ttStop: %lx%08lx%s\n", (LONG)(pProps->tStop >> 32), (LONG)pProps->tStop, pProps->dwSampleFlags & AM_SAMPLE_STOPVALID ? "" : " (not valid)");
+ TRACE("\tdwStreamId: 0x%lx\n", pProps->dwStreamId);
+ TRACE("\tpMediaType: %p\n", pProps->pMediaType);
+ TRACE("\tpbBuffer: %p\n", pProps->pbBuffer);
+ TRACE("\tcbBuffer: %ld\n", pProps->cbBuffer);
+}
+
+typedef struct BaseMemAllocator
+{
+ const IMemAllocatorVtbl * lpVtbl;
+
+ ULONG ref;
+ ALLOCATOR_PROPERTIES * pProps;
+ CRITICAL_SECTION csState;
+ HRESULT (* fnAlloc) (IMemAllocator *);
+ HRESULT (* fnFree)(IMemAllocator *);
+ HANDLE hSemWaiting;
+ BOOL bDecommitQueued;
+ BOOL bCommitted;
+ LONG lWaiting;
+ struct list * pFreeList;
+ struct list * pUsedList;
+} BaseMemAllocator;
+
+typedef struct StdMediaSample2
+{
+ const IMediaSample2Vtbl * lpvtbl;
+
+ ULONG ref;
+ AM_SAMPLE2_PROPERTIES props;
+ IMemAllocator * pParent;
+ struct list listentry;
+ LONGLONG tMediaStart;
+ LONGLONG tMediaEnd;
+} StdMediaSample2;
+
+static const struct IMemAllocatorVtbl BaseMemAllocator_VTable;
+static const struct IMediaSample2Vtbl StdMediaSample2_VTable;
+
+#define AM_SAMPLE2_PROP_SIZE_WRITABLE (unsigned int)(&((AM_SAMPLE2_PROPERTIES *)0)->pbBuffer)
+
+#ifdef _I64_MAX
+#define INVALID_MEDIA_TIME _I64_MAX
+#else
+#define INVALID_MEDIA_TIME (long long)(~0ull >> 1)
+#endif
+
+HRESULT BaseMemAllocator_Init(HRESULT (* fnAlloc)(IMemAllocator *), HRESULT (* fnFree)(IMemAllocator *), BaseMemAllocator * pMemAlloc)
+{
+ assert(fnAlloc && fnFree);
+
+ pMemAlloc->lpVtbl = &BaseMemAllocator_VTable;
+
+ pMemAlloc->ref = 0;
+ pMemAlloc->pProps = NULL;
+ pMemAlloc->pFreeList = NULL;
+ pMemAlloc->pUsedList = NULL;
+ pMemAlloc->fnAlloc = fnAlloc;
+ pMemAlloc->fnFree = fnFree;
+ pMemAlloc->bDecommitQueued = FALSE;
+ pMemAlloc->bCommitted = FALSE;
+ pMemAlloc->hSemWaiting = NULL;
+ pMemAlloc->lWaiting = 0;
+
+ InitializeCriticalSection(&pMemAlloc->csState);
+
+ return S_OK;
+}
+
+HRESULT BaseMemAllocator_Construct(HRESULT (* fnAlloc)(IMemAllocator *), HRESULT (* fnFree)(IMemAllocator *), LPVOID * ppv)
+{
+ HRESULT hr;
+ BaseMemAllocator * pMemAlloc = CoTaskMemAlloc(sizeof(BaseMemAllocator));
+
+ *ppv = NULL;
+
+ if (!pMemAlloc)
+ return E_OUTOFMEMORY;
+
+ if (SUCCEEDED(hr = BaseMemAllocator_Init(fnAlloc, fnFree, pMemAlloc)))
+ *ppv = (LPVOID)pMemAlloc;
+ else
+ CoTaskMemFree(pMemAlloc);
+
+ return hr;
+}
+
+static HRESULT WINAPI BaseMemAllocator_QueryInterface(IMemAllocator * iface, REFIID riid, LPVOID * ppv)
+{
+ ICOM_THIS(BaseMemAllocator, iface);
+ TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
+
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, &IID_IUnknown))
+ *ppv = (LPVOID)This;
+ else if (IsEqualIID(riid, &IID_IMemAllocator))
+ *ppv = (LPVOID)This;
+
+ if (*ppv)
+ {
+ IUnknown_AddRef((IUnknown *)(*ppv));
+ return S_OK;
+ }
+
+ FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
+
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI BaseMemAllocator_AddRef(IMemAllocator * iface)
+{
+ ICOM_THIS(BaseMemAllocator, iface);
+
+ TRACE("()\n");
+
+ return InterlockedIncrement(&This->ref);
+}
+
+static ULONG WINAPI BaseMemAllocator_Release(IMemAllocator * iface)
+{
+ ICOM_THIS(BaseMemAllocator, iface);
+
+ TRACE("()\n");
+
+ if (!InterlockedDecrement(&This->ref))
+ {
+ CloseHandle(This->hSemWaiting);
+ if (This->bCommitted)
+ This->fnFree(iface);
+ if (This->pProps)
+ HeapFree(GetProcessHeap(), 0, This->pProps);
+ CoTaskMemFree(This);
+ return 0;
+ }
+ return This->ref;
+}
+
+static HRESULT WINAPI BaseMemAllocator_SetProperties(IMemAllocator * iface, ALLOCATOR_PROPERTIES *pRequest, ALLOCATOR_PROPERTIES *pActual)
+{
+ ICOM_THIS(BaseMemAllocator, iface);
+ HRESULT hr;
+
+ TRACE("(%p, %p)\n", pRequest, pActual);
+
+ EnterCriticalSection(&This->csState);
+ {
+ if (This->pUsedList)
+ hr = VFW_E_BUFFERS_OUTSTANDING;
+ else if (This->bCommitted)
+ hr = VFW_E_ALREADY_COMMITTED;
+ else
+ {
+ if (!This->pProps)
+ This->pProps = HeapAlloc(GetProcessHeap(), 0, sizeof(*This->pProps));
+
+ if (!This->pProps)
+ hr = E_OUTOFMEMORY;
+ else
+ {
+ memcpy(This->pProps, pRequest, sizeof(*This->pProps));
+
+ memcpy(pActual, pRequest, sizeof(*pActual));
+
+ hr = S_OK;
+ }
+ }
+ }
+ LeaveCriticalSection(&This->csState);
+
+ return hr;
+}
+
+static HRESULT WINAPI BaseMemAllocator_GetProperties(IMemAllocator * iface, ALLOCATOR_PROPERTIES *pProps)
+{
+ ICOM_THIS(BaseMemAllocator, iface);
+ HRESULT hr = S_OK;
+
+ TRACE("(%p)\n", pProps);
+
+ EnterCriticalSection(&This->csState);
+ {
+ /* NOTE: this is different from the native version.
+ * It would silently succeed if the properties had
+ * not been set, but would fail further on down the
+ * line with some obscure error like having an
+ * invalid alignment. Whether or not our version
+ * will cause any problems remains to be seen */
+ if (!This->pProps)
+ hr = VFW_E_SIZENOTSET;
+ else
+ memcpy(pProps, This->pProps, sizeof(*pProps));
+ }
+ LeaveCriticalSection(&This->csState);
+
+ return hr;
+}
+
+static HRESULT WINAPI BaseMemAllocator_Commit(IMemAllocator * iface)
+{
+ ICOM_THIS(BaseMemAllocator, iface);
+ HRESULT hr;
+
+ TRACE("()\n");
+
+ EnterCriticalSection(&This->csState);
+ {
+ if (!This->pProps)
+ hr = VFW_E_SIZENOTSET;
+ else if (This->bCommitted)
+ hr = VFW_E_ALREADY_COMMITTED;
+ else if (This->bDecommitQueued)
+ {
+ This->bDecommitQueued = FALSE;
+ hr = S_OK;
+ }
+ else
+ {
+ if (!(This->hSemWaiting = CreateSemaphoreW(NULL, This->pProps->cBuffers, This->pProps->cBuffers, NULL)))
+ {
+ ERR("Couldn't create semaphore (error was %ld)\n", GetLastError());
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+ else
+ {
+ hr = This->fnAlloc(iface);
+ if (SUCCEEDED(hr))
+ This->bCommitted = TRUE;
+ else
+ ERR("fnAlloc failed with error 0x%lx\n", hr);
+ }
+ }
+ }
+ LeaveCriticalSection(&This->csState);
+
+ return hr;
+}
+
+static HRESULT WINAPI BaseMemAllocator_Decommit(IMemAllocator * iface)
+{
+ ICOM_THIS(BaseMemAllocator, iface);
+ HRESULT hr;
+
+ TRACE("()\n");
+
+ EnterCriticalSection(&This->csState);
+ {
+ if (!This->bCommitted)
+ hr = VFW_E_NOT_COMMITTED;
+ else
+ {
+ if (This->pUsedList)
+ {
+ This->bDecommitQueued = TRUE;
+ /* notify ALL waiting threads that they cannot be allocated a buffer any more */
+ ReleaseSemaphore(This->hSemWaiting, This->lWaiting, NULL);
+
+ hr = S_OK;
+ }
+ else
+ {
+ assert(This->lWaiting == 0);
+
+ This->bCommitted = FALSE;
+ CloseHandle(This->hSemWaiting);
+ This->hSemWaiting = NULL;
+
+ hr = This->fnFree(iface);
+ if (FAILED(hr))
+ ERR("fnFree failed with error 0x%lx\n", hr);
+ }
+ }
+ }
+ LeaveCriticalSection(&This->csState);
+
+ return hr;
+}
+
+static HRESULT WINAPI BaseMemAllocator_GetBuffer(IMemAllocator * iface, IMediaSample ** pSample, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime, DWORD dwFlags)
+{
+ ICOM_THIS(BaseMemAllocator, iface);
+ HRESULT hr = S_OK;
+
+ /* NOTE: The pStartTime and pEndTime parameters are not applied to the sample.
+ * The allocator might use these values to determine which buffer it retrieves */
+
+ TRACE("(%p, %p, %p, %lx)\n", pSample, pStartTime, pEndTime, dwFlags);
+
+ *pSample = NULL;
+
+ if (!This->bCommitted)
+ return VFW_E_NOT_COMMITTED;
+
+ This->lWaiting++;
+ if (WaitForSingleObject(This->hSemWaiting, (dwFlags & AM_GBF_NOWAIT) ? 0 : INFINITE) != WAIT_OBJECT_0)
+ {
+ This->lWaiting--;
+ return VFW_E_TIMEOUT;
+ }
+ This->lWaiting--;
+
+ EnterCriticalSection(&This->csState);
+ {
+ if (!This->bCommitted)
+ hr = VFW_E_NOT_COMMITTED;
+ else if (This->bDecommitQueued)
+ hr = VFW_E_TIMEOUT;
+ else
+ {
+ struct list * free = This->pFreeList;
+ This->pFreeList = list_head(free);
+ list_remove(free);
+ list_init(free);
+ if (This->pUsedList)
+ list_add_head(This->pUsedList, free);
+ else
+ This->pUsedList = free;
+
+ *pSample = (IMediaSample *)LIST_ENTRY(free, StdMediaSample2, listentry);
+
+ assert(((StdMediaSample2 *)*pSample)->ref == 0);
+
+ IMediaSample_AddRef(*pSample);
+ }
+ }
+ LeaveCriticalSection(&This->csState);
+
+ return hr;
+}
+
+static HRESULT WINAPI BaseMemAllocator_ReleaseBuffer(IMemAllocator * iface, IMediaSample * pSample)
+{
+ ICOM_THIS(BaseMemAllocator, iface);
+ StdMediaSample2 * pStdSample = (StdMediaSample2 *)pSample;
+ HRESULT hr = S_OK;
+ BOOL bLastSampleReleased = FALSE;
+
+ TRACE("(%p)\n", pSample);
+
+ /* FIXME: make sure that sample is currently on the used list */
+
+ /* FIXME: we should probably check the ref count on the sample before freeing
+ * it to make sure that it is not still in use */
+ EnterCriticalSection(&This->csState);
+ {
+ if (!This->bCommitted)
+ ERR("Releasing a buffer when the allocator is not committed?!?\n");
+
+ if ((This->pUsedList == &pStdSample->listentry) && (This->pUsedList->next == &pStdSample->listentry))
+ {
+ bLastSampleReleased = TRUE;
+ This->pUsedList = NULL;
+ }
+ else if ((This->pFreeList == &pStdSample->listentry) && (This->pFreeList->next == &pStdSample->listentry))
+ This->pFreeList = NULL;
+ else
+ list_remove(&pStdSample->listentry);
+
+ list_init(&pStdSample->listentry);
+ if (This->pFreeList)
+ list_add_head(This->pFreeList, &pStdSample->listentry);
+ else
+ This->pFreeList = &pStdSample->listentry;
+
+ if (bLastSampleReleased && This->bDecommitQueued && This->bCommitted)
+ {
+ HRESULT hrfree;
+
+ assert(This->lWaiting == 0);
+
+ This->bCommitted = FALSE;
+ This->bDecommitQueued = FALSE;
+
+ CloseHandle(This->hSemWaiting);
+ This->hSemWaiting = NULL;
+
+ if (FAILED(hrfree = This->fnFree(iface)))
+ ERR("fnFree failed with error 0x%lx\n", hrfree);
+ }
+ }
+ LeaveCriticalSection(&This->csState);
+
+ /* notify a waiting thread that there is now a free buffer */
+ if (!ReleaseSemaphore(This->hSemWaiting, 1, NULL))
+ {
+ ERR("ReleaseSemaphore failed with error %ld\n", GetLastError());
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ return hr;
+}
+
+static const IMemAllocatorVtbl BaseMemAllocator_VTable =
+{
+ ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+ BaseMemAllocator_QueryInterface,
+ BaseMemAllocator_AddRef,
+ BaseMemAllocator_Release,
+ BaseMemAllocator_SetProperties,
+ BaseMemAllocator_GetProperties,
+ BaseMemAllocator_Commit,
+ BaseMemAllocator_Decommit,
+ BaseMemAllocator_GetBuffer,
+ BaseMemAllocator_ReleaseBuffer
+};
+
+static HRESULT StdMediaSample2_Construct(BYTE * pbBuffer, LONG cbBuffer, IMemAllocator * pParent, StdMediaSample2 ** ppSample)
+{
+ assert(pbBuffer && pParent && (cbBuffer > 0));
+
+ if (!(*ppSample = CoTaskMemAlloc(sizeof(StdMediaSample2))))
+ return E_OUTOFMEMORY;
+
+ (*ppSample)->lpvtbl = &StdMediaSample2_VTable;
+ (*ppSample)->ref = 0;
+ ZeroMemory(&(*ppSample)->props, sizeof((*ppSample)->props));
+ list_init(&(*ppSample)->listentry);
+
+ /* NOTE: no need to AddRef as the parent is guaranteed to be around
+ * at least as long as us and we don't want to create circular
+ * dependencies on the ref count */
+ (*ppSample)->pParent = pParent;
+ (*ppSample)->props.cbData = sizeof(AM_SAMPLE2_PROPERTIES);
+ (*ppSample)->props.cbBuffer = (*ppSample)->props.lActual = cbBuffer;
+ (*ppSample)->props.pbBuffer = pbBuffer;
+ (*ppSample)->tMediaStart = INVALID_MEDIA_TIME;
+ (*ppSample)->tMediaEnd = 0;
+
+ return S_OK;
+}
+
+static void StdMediaSample2_Delete(StdMediaSample2 * This)
+{
+ /* NOTE: does not remove itself from the list it belongs to */
+ CoTaskMemFree(This);
+}
+
+static HRESULT WINAPI StdMediaSample2_QueryInterface(IMediaSample2 * iface, REFIID riid, LPVOID * ppv)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+ TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
+
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, &IID_IUnknown))
+ *ppv = (LPVOID)This;
+ else if (IsEqualIID(riid, &IID_IMediaSample))
+ *ppv = (LPVOID)This;
+ else if (IsEqualIID(riid, &IID_IMediaSample2))
+ *ppv = (LPVOID)This;
+
+ if (*ppv)
+ {
+ IUnknown_AddRef((IUnknown *)(*ppv));
+ return S_OK;
+ }
+
+ FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
+
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI StdMediaSample2_AddRef(IMediaSample2 * iface)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("()\n");
+
+ return InterlockedIncrement(&This->ref);
+}
+
+static ULONG WINAPI StdMediaSample2_Release(IMediaSample2 * iface)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("()\n");
+
+ if (!InterlockedDecrement(&This->ref))
+ {
+ IMemAllocator_ReleaseBuffer(This->pParent, (IMediaSample *)iface);
+ return 0;
+ }
+ return This->ref;
+}
+
+static HRESULT WINAPI StdMediaSample2_GetPointer(IMediaSample2 * iface, BYTE ** ppBuffer)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("(%p)\n", ppBuffer);
+
+ *ppBuffer = This->props.pbBuffer;
+
+ return S_OK;
+}
+
+static long WINAPI StdMediaSample2_GetSize(IMediaSample2 * iface)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("StdMediaSample2_GetSize()\n");
+
+ return This->props.cbBuffer;
+}
+
+static HRESULT WINAPI StdMediaSample2_GetTime(IMediaSample2 * iface, REFERENCE_TIME * pStart, REFERENCE_TIME * pEnd)
+{
+ HRESULT hr;
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("(%p, %p)\n", pStart, pEnd);
+
+ if (!(This->props.dwSampleFlags & AM_SAMPLE_TIMEVALID))
+ hr = VFW_E_SAMPLE_TIME_NOT_SET;
+ else if (!(This->props.dwSampleFlags & AM_SAMPLE_STOPVALID))
+ {
+ *pStart = This->props.tStart;
+ *pEnd = This->props.tStart + 1;
+
+ hr = VFW_S_NO_STOP_TIME;
+ }
+ else
+ {
+ *pStart = This->props.tStart;
+ *pEnd = This->props.tStop;
+
+ hr = S_OK;
+ }
+
+ return S_OK;
+}
+
+static HRESULT WINAPI StdMediaSample2_SetTime(IMediaSample2 * iface, REFERENCE_TIME * pStart, REFERENCE_TIME * pEnd)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("(%p, %p)\n", pStart, pEnd);
+
+ if (pStart)
+ {
+ This->props.tStart = *pStart;
+ This->props.dwSampleFlags |= AM_SAMPLE_TIMEVALID;
+ }
+ else
+ This->props.dwSampleFlags &= ~AM_SAMPLE_TIMEVALID;
+
+ if (pEnd)
+ {
+ This->props.tStop = *pEnd;
+ This->props.dwSampleFlags |= AM_SAMPLE_STOPVALID;
+ }
+ else
+ This->props.dwSampleFlags &= ~AM_SAMPLE_STOPVALID;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI StdMediaSample2_IsSyncPoint(IMediaSample2 * iface)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("()\n");
+
+ return (This->props.dwSampleFlags & AM_SAMPLE_SPLICEPOINT) ? S_OK : S_FALSE;
+}
+
+static HRESULT WINAPI StdMediaSample2_SetSyncPoint(IMediaSample2 * iface, BOOL bIsSyncPoint)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("(%s)\n", bIsSyncPoint ? "TRUE" : "FALSE");
+
+ This->props.dwSampleFlags = (This->props.dwSampleFlags & ~AM_SAMPLE_SPLICEPOINT) | bIsSyncPoint ? AM_SAMPLE_SPLICEPOINT : 0;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI StdMediaSample2_IsPreroll(IMediaSample2 * iface)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("()\n");
+
+ return (This->props.dwSampleFlags & AM_SAMPLE_PREROLL) ? S_OK : S_FALSE;
+}
+
+static HRESULT WINAPI StdMediaSample2_SetPreroll(IMediaSample2 * iface, BOOL bIsPreroll)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("(%s)\n", bIsPreroll ? "TRUE" : "FALSE");
+
+ This->props.dwSampleFlags = (This->props.dwSampleFlags & ~AM_SAMPLE_PREROLL) | bIsPreroll ? AM_SAMPLE_PREROLL : 0;
+
+ return S_OK;
+}
+
+static LONG WINAPI StdMediaSample2_GetActualDataLength(IMediaSample2 * iface)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("()\n");
+
+ return This->props.lActual;
+}
+
+static HRESULT WINAPI StdMediaSample2_SetActualDataLength(IMediaSample2 * iface, LONG len)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("(%ld)\n", len);
+
+ if ((len > This->props.cbBuffer) || (len < 0))
+ return VFW_E_BUFFER_OVERFLOW;
+ else
+ {
+ This->props.lActual = len;
+ return S_OK;
+ }
+}
+
+static HRESULT WINAPI StdMediaSample2_GetMediaType(IMediaSample2 * iface, AM_MEDIA_TYPE ** ppMediaType)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("(%p)\n", ppMediaType);
+
+ if (!This->props.pMediaType)
+ return S_FALSE;
+
+ if (!(*ppMediaType = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))))
+ return E_OUTOFMEMORY;
+
+ return CopyMediaType(*ppMediaType, This->props.pMediaType);
+}
+
+static HRESULT WINAPI StdMediaSample2_SetMediaType(IMediaSample2 * iface, AM_MEDIA_TYPE * pMediaType)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("(%p)\n", pMediaType);
+
+ if (This->props.pMediaType)
+ DeleteMediaType(This->props.pMediaType);
+ else if (!(This->props.pMediaType = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))))
+ return E_OUTOFMEMORY;
+
+ return CopyMediaType(This->props.pMediaType, pMediaType);
+}
+
+static HRESULT WINAPI StdMediaSample2_IsDiscontinuity(IMediaSample2 * iface)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("()\n");
+
+ return (This->props.dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) ? S_OK : S_FALSE;
+}
+
+static HRESULT WINAPI StdMediaSample2_SetDiscontinuity(IMediaSample2 * iface, BOOL bIsDiscontinuity)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("(%s)\n", bIsDiscontinuity ? "TRUE" : "FALSE");
+
+ This->props.dwSampleFlags = (This->props.dwSampleFlags & ~AM_SAMPLE_DATADISCONTINUITY) | bIsDiscontinuity ? AM_SAMPLE_DATADISCONTINUITY : 0;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI StdMediaSample2_GetMediaTime(IMediaSample2 * iface, LONGLONG * pStart, LONGLONG * pEnd)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("(%p, %p)\n", pStart, pEnd);
+
+ if (This->tMediaStart == INVALID_MEDIA_TIME)
+ return VFW_E_MEDIA_TIME_NOT_SET;
+
+ *pStart = This->tMediaStart;
+ *pEnd = This->tMediaEnd;
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI StdMediaSample2_SetMediaTime(IMediaSample2 * iface, LONGLONG * pStart, LONGLONG * pEnd)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("(%p, %p)\n", pStart, pEnd);
+
+ if (pStart)
+ This->tMediaStart = *pStart;
+ else
+ This->tMediaStart = INVALID_MEDIA_TIME;
+
+ if (pEnd)
+ This->tMediaEnd = *pEnd;
+ else
+ This->tMediaEnd = 0;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI StdMediaSample2_GetProperties(IMediaSample2 * iface, DWORD cbProperties, BYTE * pbProperties)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("(%ld, %p)\n", cbProperties, pbProperties);
+
+ memcpy(pbProperties, &This->props, min(cbProperties, sizeof(This->props)));
+
+ return S_OK;
+}
+
+static HRESULT WINAPI StdMediaSample2_SetProperties(IMediaSample2 * iface, DWORD cbProperties, const BYTE * pbProperties)
+{
+ ICOM_THIS(StdMediaSample2, iface);
+
+ TRACE("(%ld, %p)\n", cbProperties, pbProperties);
+
+ /* NOTE: pbBuffer and cbBuffer are read-only */
+ memcpy(&This->props, pbProperties, min(cbProperties, AM_SAMPLE2_PROP_SIZE_WRITABLE));
+
+ return S_OK;
+}
+
+static const IMediaSample2Vtbl StdMediaSample2_VTable =
+{
+ ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+ StdMediaSample2_QueryInterface,
+ StdMediaSample2_AddRef,
+ StdMediaSample2_Release,
+ StdMediaSample2_GetPointer,
+ StdMediaSample2_GetSize,
+ StdMediaSample2_GetTime,
+ StdMediaSample2_SetTime,
+ StdMediaSample2_IsSyncPoint,
+ StdMediaSample2_SetSyncPoint,
+ StdMediaSample2_IsPreroll,
+ StdMediaSample2_SetPreroll,
+ StdMediaSample2_GetActualDataLength,
+ StdMediaSample2_SetActualDataLength,
+ StdMediaSample2_GetMediaType,
+ StdMediaSample2_SetMediaType,
+ StdMediaSample2_IsDiscontinuity,
+ StdMediaSample2_SetDiscontinuity,
+ StdMediaSample2_GetMediaTime,
+ StdMediaSample2_SetMediaTime,
+ StdMediaSample2_GetProperties,
+ StdMediaSample2_SetProperties
+};
+
+typedef struct StdMemAllocator
+{
+ BaseMemAllocator base;
+ LPVOID pMemory;
+} StdMemAllocator;
+
+static HRESULT StdMemAllocator_Alloc(IMemAllocator * iface)
+{
+ ICOM_THIS(StdMemAllocator, iface);
+ StdMediaSample2 * pSample = NULL;
+ SYSTEM_INFO si;
+ long i;
+
+ assert(This->base.pFreeList == NULL);
+
+ /* check alignment */
+ GetSystemInfo(&si);
+
+ /* we do not allow a courser alignment than the OS page size */
+ if ((si.dwPageSize % This->base.pProps->cbAlign) != 0)
+ return VFW_E_BADALIGN;
+
+ /* FIXME: each sample has to have its buffer start on the right alignment.
+ * We don't do this at the moment */
+
+ /* allocate memory */
+ This->pMemory = VirtualAlloc(NULL, (This->base.pProps->cbBuffer + This->base.pProps->cbPrefix) * This->base.pProps->cBuffers, MEM_COMMIT, PAGE_READWRITE);
+
+ for (i = This->base.pProps->cBuffers - 1; i >= 0; i--)
+ {
+ /* pbBuffer does not start at the base address, it starts at base + cbPrefix */
+ BYTE * pbBuffer = (BYTE *)This->pMemory + i * (This->base.pProps->cbBuffer + This->base.pProps->cbPrefix) + This->base.pProps->cbPrefix;
+
+ StdMediaSample2_Construct(pbBuffer, This->base.pProps->cbBuffer, iface, &pSample);
+
+ if (This->base.pFreeList)
+ list_add_head(This->base.pFreeList, &pSample->listentry);
+
+ This->base.pFreeList = &pSample->listentry;
+ }
+
+ return S_OK;
+}
+
+static HRESULT StdMemAllocator_Free(IMemAllocator * iface)
+{
+ ICOM_THIS(StdMemAllocator, iface);
+ struct list * cursor;
+
+ assert(This->base.pUsedList == NULL);
+
+ while ((cursor = This->base.pFreeList) != NULL)
+ {
+ list_remove(cursor);
+ This->base.pFreeList = list_head(cursor);
+ StdMediaSample2_Delete(LIST_ENTRY(cursor, StdMediaSample2, listentry));
+ }
+
+ /* free memory */
+ if (!VirtualFree(This->pMemory, 0, MEM_RELEASE))
+ {
+ ERR("Couldn't free memory. Error: %ld\n", GetLastError());
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ return S_OK;
+}
+
+HRESULT StdMemAllocator_create(LPUNKNOWN lpUnkOuter, LPVOID * ppv)
+{
+ StdMemAllocator * pMemAlloc;
+ HRESULT hr;
+
+ *ppv = NULL;
+
+ if (lpUnkOuter)
+ return CLASS_E_NOAGGREGATION;
+
+ if (!(pMemAlloc = CoTaskMemAlloc(sizeof(*pMemAlloc))))
+ return E_OUTOFMEMORY;
+
+ pMemAlloc->pMemory = NULL;
+
+ if (SUCCEEDED(hr = BaseMemAllocator_Init(StdMemAllocator_Alloc, StdMemAllocator_Free, &pMemAlloc->base)))
+ *ppv = (LPVOID)pMemAlloc;
+ else
+ CoTaskMemFree(pMemAlloc);
+
+ return hr;
+}