Hi, This patch implements Alexandre's suggestions and uses the list functions properly now, and indeed, the code is much cleaner. Rob Changelog: - Implement IMemAllocator and IMediaSample
? 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 23:25:27 -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 23:25:27 -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 23:25:27 -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 23:25:28 -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 Wed Sep 10 00:19:20 2003 @@ -0,0 +1,854 @@ +/* + * 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 free_list; + struct list used_list; +} 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 + +static HRESULT BaseMemAllocator_Init(HRESULT (* fnAlloc)(IMemAllocator *), HRESULT (* fnFree)(IMemAllocator *), BaseMemAllocator * pMemAlloc) +{ + assert(fnAlloc && fnFree); + + pMemAlloc->lpVtbl = &BaseMemAllocator_VTable; + + pMemAlloc->ref = 0; + pMemAlloc->pProps = NULL; + list_init(&pMemAlloc->free_list); + list_init(&pMemAlloc->used_list); + pMemAlloc->fnAlloc = fnAlloc; + pMemAlloc->fnFree = fnFree; + pMemAlloc->bDecommitQueued = FALSE; + pMemAlloc->bCommitted = FALSE; + pMemAlloc->hSemWaiting = NULL; + pMemAlloc->lWaiting = 0; + + InitializeCriticalSection(&pMemAlloc->csState); + + return S_OK; +} + +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 (!list_empty(&This->used_list)) + 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 (!list_empty(&This->used_list)) + { + 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 = list_head(&This->free_list); + list_remove(free); + list_init(free); + list_add_head(&This->used_list, 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; + + 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"); + + /* remove from used_list */ + list_remove(&pStdSample->listentry); + + /* re-init the sample's list */ + list_init(&pStdSample->listentry); + + list_add_head(&This->free_list, &pStdSample->listentry); + + if (list_empty(&This->used_list) && 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(list_empty(&This->base.free_list)); + + /* 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); + + list_add_head(&This->base.free_list, &pSample->listentry); + } + + return S_OK; +} + +static HRESULT StdMemAllocator_Free(IMemAllocator * iface) +{ + ICOM_THIS(StdMemAllocator, iface); + struct list * cursor; + + assert(list_empty(&This->base.used_list)); + + while ((cursor = list_head(&This->base.free_list)) != NULL) + { + list_remove(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; +}