[PATCH 2/2] evr: Implement sample allocator.

Nikolay Sivov nsivov at codeweavers.com
Tue Oct 27 10:10:02 CDT 2020


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/evr/sample.c    | 284 +++++++++++++++++++++++++++++++++++++++++--
 dlls/evr/tests/evr.c |  23 ++--
 2 files changed, 283 insertions(+), 24 deletions(-)

diff --git a/dlls/evr/sample.c b/dlls/evr/sample.c
index f5189443307..b65cc45a662 100644
--- a/dlls/evr/sample.c
+++ b/dlls/evr/sample.c
@@ -26,6 +26,7 @@
 
 #include "wine/debug.h"
 #include "wine/heap.h"
+#include "wine/list.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(evr);
 
@@ -106,14 +107,23 @@ struct sample_allocator
 {
     IMFVideoSampleAllocator IMFVideoSampleAllocator_iface;
     IMFVideoSampleAllocatorCallback IMFVideoSampleAllocatorCallback_iface;
+    IMFAsyncCallback tracking_callback;
     LONG refcount;
 
     IMFVideoSampleAllocatorNotify *callback;
-    unsigned int free_samples;
     IDirect3DDeviceManager9 *device_manager;
+    unsigned int free_sample_count;
+    struct list free_samples;
+    struct list used_samples;
     CRITICAL_SECTION cs;
 };
 
+struct queued_sample
+{
+    struct list entry;
+    IMFSample *sample;
+};
+
 static struct sample_allocator *impl_from_IMFVideoSampleAllocator(IMFVideoSampleAllocator *iface)
 {
     return CONTAINING_RECORD(iface, struct sample_allocator, IMFVideoSampleAllocator_iface);
@@ -124,6 +134,11 @@ static struct sample_allocator *impl_from_IMFVideoSampleAllocatorCallback(IMFVid
     return CONTAINING_RECORD(iface, struct sample_allocator, IMFVideoSampleAllocatorCallback_iface);
 }
 
+static struct sample_allocator *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface)
+{
+    return CONTAINING_RECORD(iface, struct sample_allocator, tracking_callback);
+}
+
 static HRESULT WINAPI sample_allocator_QueryInterface(IMFVideoSampleAllocator *iface, REFIID riid, void **obj)
 {
     struct sample_allocator *allocator = impl_from_IMFVideoSampleAllocator(iface);
@@ -160,6 +175,25 @@ static ULONG WINAPI sample_allocator_AddRef(IMFVideoSampleAllocator *iface)
     return refcount;
 }
 
+static void sample_allocator_release_samples(struct sample_allocator *allocator)
+{
+    struct queued_sample *iter, *iter2;
+
+    LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &allocator->free_samples, struct queued_sample, entry)
+    {
+        list_remove(&iter->entry);
+        IMFSample_Release(iter->sample);
+        heap_free(iter);
+    }
+
+    LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &allocator->used_samples, struct queued_sample, entry)
+    {
+        list_remove(&iter->entry);
+        IMFSample_Release(iter->sample);
+        heap_free(iter);
+    }
+}
+
 static ULONG WINAPI sample_allocator_Release(IMFVideoSampleAllocator *iface)
 {
     struct sample_allocator *allocator = impl_from_IMFVideoSampleAllocator(iface);
@@ -173,6 +207,7 @@ static ULONG WINAPI sample_allocator_Release(IMFVideoSampleAllocator *iface)
             IMFVideoSampleAllocatorNotify_Release(allocator->callback);
         if (allocator->device_manager)
             IDirect3DDeviceManager9_Release(allocator->device_manager);
+        sample_allocator_release_samples(allocator);
         DeleteCriticalSection(&allocator->cs);
         heap_free(allocator);
     }
@@ -208,24 +243,176 @@ static HRESULT WINAPI sample_allocator_SetDirectXManager(IMFVideoSampleAllocator
 
 static HRESULT WINAPI sample_allocator_UninitializeSampleAllocator(IMFVideoSampleAllocator *iface)
 {
-    FIXME("%p.\n", iface);
+    struct sample_allocator *allocator = impl_from_IMFVideoSampleAllocator(iface);
 
-    return E_NOTIMPL;
+    TRACE("%p.\n", iface);
+
+    EnterCriticalSection(&allocator->cs);
+
+    sample_allocator_release_samples(allocator);
+    allocator->free_sample_count = 0;
+
+    LeaveCriticalSection(&allocator->cs);
+
+    return S_OK;
+}
+
+static HRESULT sample_allocator_create_samples(struct sample_allocator *allocator, unsigned int sample_count,
+        IMFMediaType *media_type)
+{
+    IDirectXVideoProcessorService *service = NULL;
+    unsigned int i, width, height;
+    IDirect3DSurface9 *surface;
+    HANDLE hdevice = NULL;
+    GUID major, subtype;
+    UINT64 frame_size;
+    IMFSample *sample;
+    D3DFORMAT format;
+    HRESULT hr;
+
+    if (FAILED(IMFMediaType_GetMajorType(media_type, &major)))
+        return MF_E_INVALIDMEDIATYPE;
+
+    if (!IsEqualGUID(&major, &MFMediaType_Video))
+        return MF_E_INVALIDMEDIATYPE;
+
+    if (FAILED(IMFMediaType_GetUINT64(media_type, &MF_MT_FRAME_SIZE, &frame_size)))
+        return MF_E_INVALIDMEDIATYPE;
+
+    if (FAILED(IMFMediaType_GetGUID(media_type, &MF_MT_SUBTYPE, &subtype)))
+        return MF_E_INVALIDMEDIATYPE;
+
+    format = subtype.Data1;
+    height = frame_size;
+    width = frame_size >> 32;
+
+    if (allocator->device_manager)
+    {
+        if (SUCCEEDED(hr = IDirect3DDeviceManager9_OpenDeviceHandle(allocator->device_manager, &hdevice)))
+        {
+            hr = IDirect3DDeviceManager9_GetVideoService(allocator->device_manager, hdevice,
+                    &IID_IDirectXVideoProcessorService, (void **)&service);
+        }
+
+        if (FAILED(hr))
+        {
+            WARN("Failed to get processor service, %#x.\n", hr);
+            return hr;
+        }
+    }
+
+    sample_allocator_release_samples(allocator);
+
+    for (i = 0; i < sample_count; ++i)
+    {
+        struct queued_sample *queued_sample = heap_alloc(sizeof(*queued_sample));
+
+        if (service)
+        {
+            if (SUCCEEDED(hr = IDirectXVideoProcessorService_CreateSurface(service, width, height, 0, format,
+                    D3DPOOL_DEFAULT, 0, DXVA2_VideoProcessorRenderTarget, &surface, NULL)))
+            {
+                hr = MFCreateVideoSampleFromSurface((IUnknown *)surface, &sample);
+                IDirect3DSurface9_Release(surface);
+            }
+        }
+        else
+        {
+            IMFMediaBuffer *buffer;
+
+            if (SUCCEEDED(hr = MFCreateVideoSampleFromSurface(NULL, &sample)))
+            {
+                if (SUCCEEDED(hr = MFCreate2DMediaBuffer(width, height, format, FALSE, &buffer)))
+                {
+                    hr = IMFSample_AddBuffer(sample, buffer);
+                    IMFMediaBuffer_Release(buffer);
+                }
+            }
+        }
+
+        if (FAILED(hr))
+        {
+            WARN("Unable to allocate %u samples.\n", sample_count);
+            sample_allocator_release_samples(allocator);
+            break;
+        }
+
+        queued_sample = heap_alloc(sizeof(*queued_sample));
+        queued_sample->sample = sample;
+        list_add_tail(&allocator->free_samples, &queued_sample->entry);
+        allocator->free_sample_count++;
+    }
+
+    if (service)
+        IDirectXVideoProcessorService_Release(service);
+
+    if (allocator->device_manager)
+        IDirect3DDeviceManager9_CloseDeviceHandle(allocator->device_manager, hdevice);
+
+    return hr;
 }
 
 static HRESULT WINAPI sample_allocator_InitializeSampleAllocator(IMFVideoSampleAllocator *iface,
         DWORD sample_count, IMFMediaType *media_type)
 {
-    FIXME("%p, %u, %p.\n", iface, sample_count, media_type);
+    struct sample_allocator *allocator = impl_from_IMFVideoSampleAllocator(iface);
+    HRESULT hr;
 
-    return E_NOTIMPL;
+    TRACE("%p, %u, %p.\n", iface, sample_count, media_type);
+
+    if (!sample_count)
+        sample_count = 1;
+
+    EnterCriticalSection(&allocator->cs);
+
+    hr = sample_allocator_create_samples(allocator, sample_count, media_type);
+
+    LeaveCriticalSection(&allocator->cs);
+
+    return hr;
 }
 
-static HRESULT WINAPI sample_allocator_AllocateSample(IMFVideoSampleAllocator *iface, IMFSample **sample)
+static HRESULT WINAPI sample_allocator_AllocateSample(IMFVideoSampleAllocator *iface, IMFSample **out)
 {
-    FIXME("%p, %p.\n", iface, sample);
+    struct sample_allocator *allocator = impl_from_IMFVideoSampleAllocator(iface);
+    IMFTrackedSample *tracked_sample;
+    IMFSample *sample;
+    HRESULT hr;
 
-    return E_NOTIMPL;
+    TRACE("%p, %p.\n", iface, out);
+
+    EnterCriticalSection(&allocator->cs);
+
+    if (list_empty(&allocator->free_samples) && list_empty(&allocator->used_samples))
+        hr = MF_E_NOT_INITIALIZED;
+    else if (list_empty(&allocator->free_samples))
+        hr = MF_E_SAMPLEALLOCATOR_EMPTY;
+    else
+    {
+        struct list *head = list_head(&allocator->free_samples);
+
+        sample = LIST_ENTRY(head, struct queued_sample, entry)->sample;
+
+        if (SUCCEEDED(hr = IMFSample_QueryInterface(sample, &IID_IMFTrackedSample, (void **)&tracked_sample)))
+        {
+            hr = IMFTrackedSample_SetAllocator(tracked_sample, &allocator->tracking_callback, NULL);
+            IMFTrackedSample_Release(tracked_sample);
+        }
+
+        if (SUCCEEDED(hr))
+        {
+            list_remove(head);
+            list_add_tail(&allocator->used_samples, head);
+            allocator->free_sample_count--;
+
+            *out = sample;
+            IMFSample_AddRef(*out);
+        }
+    }
+
+    LeaveCriticalSection(&allocator->cs);
+
+    return hr;
 }
 
 static const IMFVideoSampleAllocatorVtbl sample_allocator_vtbl =
@@ -285,7 +472,7 @@ static HRESULT WINAPI sample_allocator_callback_GetFreeSampleCount(IMFVideoSampl
 
     EnterCriticalSection(&allocator->cs);
     if (count)
-        *count = allocator->free_samples;
+        *count = allocator->free_sample_count;
     LeaveCriticalSection(&allocator->cs);
 
     return S_OK;
@@ -300,6 +487,77 @@ static const IMFVideoSampleAllocatorCallbackVtbl sample_allocator_callback_vtbl
     sample_allocator_callback_GetFreeSampleCount,
 };
 
+static HRESULT WINAPI sample_allocator_tracking_callback_QueryInterface(IMFAsyncCallback *iface,
+        REFIID riid, void **obj)
+{
+    if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
+            IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = iface;
+        IMFAsyncCallback_AddRef(iface);
+        return S_OK;
+    }
+
+    WARN("Unsupported interface %s.\n", debugstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI sample_allocator_tracking_callback_AddRef(IMFAsyncCallback *iface)
+{
+    struct sample_allocator *allocator = impl_from_IMFAsyncCallback(iface);
+    return IMFVideoSampleAllocator_AddRef(&allocator->IMFVideoSampleAllocator_iface);
+}
+
+static ULONG WINAPI sample_allocator_tracking_callback_Release(IMFAsyncCallback *iface)
+{
+    struct sample_allocator *allocator = impl_from_IMFAsyncCallback(iface);
+    return IMFVideoSampleAllocator_Release(&allocator->IMFVideoSampleAllocator_iface);
+}
+
+static HRESULT WINAPI sample_allocator_tracking_callback_GetParameters(IMFAsyncCallback *iface,
+        DWORD *flags, DWORD *queue)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI sample_allocator_tracking_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
+{
+    struct sample_allocator *allocator = impl_from_IMFAsyncCallback(iface);
+    struct queued_sample *iter;
+    IUnknown *sample = NULL;
+
+    EnterCriticalSection(&allocator->cs);
+
+    IMFAsyncResult_GetObject(result, (IUnknown **)&sample);
+
+    LIST_FOR_EACH_ENTRY(iter, &allocator->used_samples, struct queued_sample, entry)
+    {
+        if (sample == (IUnknown *)iter->sample)
+        {
+            list_remove(&iter->entry);
+            list_add_tail(&allocator->free_samples, &iter->entry);
+            allocator->free_sample_count++;
+            break;
+        }
+    }
+
+    IUnknown_Release(sample);
+
+    LeaveCriticalSection(&allocator->cs);
+
+    return S_OK;
+}
+
+static const IMFAsyncCallbackVtbl sample_allocator_tracking_callback_vtbl =
+{
+    sample_allocator_tracking_callback_QueryInterface,
+    sample_allocator_tracking_callback_AddRef,
+    sample_allocator_tracking_callback_Release,
+    sample_allocator_tracking_callback_GetParameters,
+    sample_allocator_tracking_callback_Invoke,
+};
+
 HRESULT WINAPI MFCreateVideoSampleAllocator(REFIID riid, void **obj)
 {
     struct sample_allocator *object;
@@ -312,7 +570,10 @@ HRESULT WINAPI MFCreateVideoSampleAllocator(REFIID riid, void **obj)
 
     object->IMFVideoSampleAllocator_iface.lpVtbl = &sample_allocator_vtbl;
     object->IMFVideoSampleAllocatorCallback_iface.lpVtbl = &sample_allocator_callback_vtbl;
+    object->tracking_callback.lpVtbl = &sample_allocator_tracking_callback_vtbl;
     object->refcount = 1;
+    list_init(&object->used_samples);
+    list_init(&object->free_samples);
     InitializeCriticalSection(&object->cs);
 
     hr = IMFVideoSampleAllocator_QueryInterface(&object->IMFVideoSampleAllocator_iface, riid, obj);
@@ -369,8 +630,7 @@ static ULONG WINAPI video_sample_Release(IMFSample *iface)
     HRESULT hr;
 
     IMFSample_LockStore(sample->sample);
-    refcount = InterlockedDecrement(&sample->refcount);
-    if (sample->tracked_result && sample->tracked_refcount == refcount)
+    if (sample->tracked_result && sample->tracked_refcount == (sample->refcount - 1))
     {
         /* Call could fail if queue system is not initialized, it's not critical. */
         if (FAILED(hr = MFInvokeCallback(sample->tracked_result)))
@@ -381,6 +641,8 @@ static ULONG WINAPI video_sample_Release(IMFSample *iface)
     }
     IMFSample_UnlockStore(sample->sample);
 
+    refcount = InterlockedDecrement(&sample->refcount);
+
     TRACE("%p, refcount %u.\n", iface, refcount);
 
     if (!refcount)
diff --git a/dlls/evr/tests/evr.c b/dlls/evr/tests/evr.c
index 8c82ae34f8d..fbcf798157e 100644
--- a/dlls/evr/tests/evr.c
+++ b/dlls/evr/tests/evr.c
@@ -1338,13 +1338,14 @@ static void test_MFCreateVideoSampleAllocator(void)
     ok(!count, "Unexpected count %d.\n", count);
 
     hr = IMFVideoSampleAllocator_UninitializeSampleAllocator(allocator);
-todo_wine
     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
 
     hr = IMFVideoSampleAllocator_AllocateSample(allocator, &sample);
-todo_wine
     ok(hr == MF_E_NOT_INITIALIZED, "Unexpected hr %#x.\n", hr);
 
+    hr = IMFVideoSampleAllocator_SetDirectXManager(allocator, NULL);
+    ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
+
     hr = MFCreateMediaType(&media_type);
     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
 
@@ -1356,42 +1357,34 @@ todo_wine
     video_type = create_video_type(&MFVideoFormat_RGB32);
 
     hr = IMFVideoSampleAllocator_InitializeSampleAllocator(allocator, 2, video_type);
-todo_wine
     ok(hr == MF_E_INVALIDMEDIATYPE, "Unexpected hr %#x.\n", hr);
 
     /* Frame size is required. */
     hr = IMFMediaType_SetUINT64(video_type, &MF_MT_FRAME_SIZE, (UINT64) 320 << 32 | 240);
     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
     hr = IMFVideoSampleAllocator_InitializeSampleAllocator(allocator, 0, video_type);
-todo_wine
     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
 
     hr = IMFVideoSampleAllocatorCallback_GetFreeSampleCount(allocator_cb, &count);
     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
-todo_wine
     ok(count == 1, "Unexpected count %d.\n", count);
 
     sample = NULL;
     hr = IMFVideoSampleAllocator_AllocateSample(allocator, &sample);
-todo_wine
     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
-    if (SUCCEEDED(hr))
-        ok(get_refcount(sample) == 3, "Unexpected refcount %u.\n", get_refcount(sample));
+    ok(get_refcount(sample) == 3, "Unexpected refcount %u.\n", get_refcount(sample));
 
     hr = IMFVideoSampleAllocator_AllocateSample(allocator, &sample2);
-todo_wine
     ok(hr == MF_E_SAMPLEALLOCATOR_EMPTY, "Unexpected hr %#x.\n", hr);
 
     /* Reinitialize with active sample. */
     hr = IMFVideoSampleAllocator_InitializeSampleAllocator(allocator, 4, video_type);
-todo_wine
     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
-    if (sample)
-        ok(get_refcount(sample) == 3, "Unexpected refcount %u.\n", get_refcount(sample));
+todo_wine
+    ok(get_refcount(sample) == 3, "Unexpected refcount %u.\n", get_refcount(sample));
 
     hr = IMFVideoSampleAllocatorCallback_GetFreeSampleCount(allocator_cb, &count);
     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
-todo_wine
     ok(count == 4, "Unexpected count %d.\n", count);
 
     if (sample)
@@ -1418,6 +1411,10 @@ todo_wine
             IMFGetService_Release(gs);
         }
 
+        hr = IMFMediaBuffer_QueryInterface(buffer, &IID_IMF2DBuffer, (void **)&unk);
+        ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
+        IUnknown_Release(unk);
+
         IMFMediaBuffer_Release(buffer);
 
         IMFSample_Release(sample);
-- 
2.28.0




More information about the wine-devel mailing list