[DSHOW-08] IMemAllocator and IMediaSample implementation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

Yes, I am still working on DirectShow and here is a little progress report. I 
have the AVI Splitter and Video Renderer filters functioning, but I am 
working on cleaning them up and making sure they work correctly in all cases. 
I am also working on implementing helper classes like these two in this patch.

This patch implements the IMemAllocator and IMediaSample interfaces which are 
exposed through CLSID_MemoryAllocator. The implementation allocates memory 
for samples using VirtualAlloc, which is the same as the native version 
according to my DirectX SDK documentation and testing which shows that the 
alignment is set to the system page size. I have copied the header 
wine/server/list.h in order to implement the freeing and issuing of media 
samples with the minimum amount of effort. I hope this is ok, but people 
should let me know if they have a better solution than duplicating the code 
like this.

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	8 Sep 2003 23:56:57 -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	8 Sep 2003 23:56:58 -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	8 Sep 2003 23:56:58 -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	8 Sep 2003 23:56:58 -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 01:01:34 2003
@@ -0,0 +1,892 @@
+/*
+ * 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
+ */
+
+/* needed so that LONG_LONG_MAX is defined on GCC headers */
+#define _GNU_SOURCE
+#include <limits.h>
+#undef _GNU_SOURCE
+
+#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_MAX
+#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;
+}

[Index of Archives]     [Gimp for Windows]     [Red Hat]     [Samba]     [Yosemite Camping]     [Graphics Cards]     [Wine Home]

  Powered by Linux