[PATCH] mf: Implement file scheme handler.

Nikolay Sivov nsivov at codeweavers.com
Fri May 3 06:53:00 CDT 2019


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/mf/Makefile.in        |   2 +-
 dlls/mf/main.c             | 381 ++++++++++++++++++++++++++++++++++++-
 dlls/mfplat/tests/mfplat.c |  27 +--
 3 files changed, 385 insertions(+), 25 deletions(-)

diff --git a/dlls/mf/Makefile.in b/dlls/mf/Makefile.in
index 3499a4a61a..221ef42ec9 100644
--- a/dlls/mf/Makefile.in
+++ b/dlls/mf/Makefile.in
@@ -1,6 +1,6 @@
 MODULE    = mf.dll
 IMPORTLIB = mf
-IMPORTS   = mfplat
+IMPORTS   = mfplat mfuuid
 
 C_SRCS = \
 	main.c \
diff --git a/dlls/mf/main.c b/dlls/mf/main.c
index 8500cd9cef..77081e2a57 100644
--- a/dlls/mf/main.c
+++ b/dlls/mf/main.c
@@ -30,8 +30,15 @@
 #include "initguid.h"
 #include "mf.h"
 
+#undef INITGUID
+#include <guiddef.h>
+#include "mfapi.h"
+#include "mferror.h"
+
 #include "wine/debug.h"
 #include "wine/heap.h"
+#include "wine/unicode.h"
+#include "wine/list.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
 
@@ -106,10 +113,22 @@ static const IClassFactoryVtbl class_factory_vtbl =
     class_factory_LockServer,
 };
 
+struct file_scheme_handler_result
+{
+    struct list entry;
+    IMFAsyncResult *result;
+    MF_OBJECT_TYPE obj_type;
+    IUnknown *object;
+};
+
 struct file_scheme_handler
 {
     IMFSchemeHandler IMFSchemeHandler_iface;
+    IMFAsyncCallback IMFAsyncCallback_iface;
     LONG refcount;
+    IMFSourceResolver *resolver;
+    struct list results;
+    CRITICAL_SECTION cs;
 };
 
 static struct file_scheme_handler *impl_from_IMFSchemeHandler(IMFSchemeHandler *iface)
@@ -117,6 +136,11 @@ static struct file_scheme_handler *impl_from_IMFSchemeHandler(IMFSchemeHandler *
     return CONTAINING_RECORD(iface, struct file_scheme_handler, IMFSchemeHandler_iface);
 }
 
+static struct file_scheme_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface)
+{
+    return CONTAINING_RECORD(iface, struct file_scheme_handler, IMFAsyncCallback_iface);
+}
+
 static HRESULT WINAPI file_scheme_handler_QueryInterface(IMFSchemeHandler *iface, REFIID riid, void **obj)
 {
     TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
@@ -148,36 +172,237 @@ static ULONG WINAPI file_scheme_handler_Release(IMFSchemeHandler *iface)
 {
     struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface);
     ULONG refcount = InterlockedDecrement(&handler->refcount);
+    struct file_scheme_handler_result *result, *next;
 
     TRACE("%p, refcount %u.\n", iface, refcount);
 
     if (!refcount)
+    {
+        LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct file_scheme_handler_result, entry)
+        {
+            list_remove(&result->entry);
+            IMFAsyncResult_Release(result->result);
+            if (result->object)
+                IUnknown_Release(result->object);
+            heap_free(result);
+        }
+        DeleteCriticalSection(&handler->cs);
+        if (handler->resolver)
+            IMFSourceResolver_Release(handler->resolver);
         heap_free(handler);
+    }
+
+    return refcount;
+}
+
+struct create_object_context
+{
+    IUnknown IUnknown_iface;
+    LONG refcount;
+
+    IPropertyStore *props;
+    WCHAR *url;
+    DWORD flags;
+};
+
+static struct create_object_context *impl_from_IUnknown(IUnknown *iface)
+{
+    return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface);
+}
+
+static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj)
+{
+    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
+
+    if (IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = iface;
+        IUnknown_AddRef(iface);
+        return S_OK;
+    }
+
+    WARN("Unsupported %s.\n", debugstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI create_object_context_AddRef(IUnknown *iface)
+{
+    struct create_object_context *context = impl_from_IUnknown(iface);
+    ULONG refcount = InterlockedIncrement(&context->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    return refcount;
+}
+
+static ULONG WINAPI create_object_context_Release(IUnknown *iface)
+{
+    struct create_object_context *context = impl_from_IUnknown(iface);
+    ULONG refcount = InterlockedDecrement(&context->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    if (!refcount)
+    {
+        if (context->props)
+            IPropertyStore_Release(context->props);
+        heap_free(context->url);
+        heap_free(context);
+    }
 
     return refcount;
 }
 
+static const IUnknownVtbl create_object_context_vtbl =
+{
+    create_object_context_QueryInterface,
+    create_object_context_AddRef,
+    create_object_context_Release,
+};
+
+static WCHAR *heap_strdupW(const WCHAR *str)
+{
+    WCHAR *ret = NULL;
+
+    if (str)
+    {
+        unsigned int size;
+
+        size = (strlenW(str) + 1) * sizeof(WCHAR);
+        ret = heap_alloc(size);
+        if (ret)
+            memcpy(ret, str, size);
+    }
+
+    return ret;
+}
+
 static HRESULT WINAPI file_scheme_handler_BeginCreateObject(IMFSchemeHandler *iface, const WCHAR *url, DWORD flags,
         IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state)
 {
-    FIXME("%p, %s, %#x, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state);
+    struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface);
+    struct create_object_context *context;
+    IMFAsyncResult *caller, *item;
+    HRESULT hr;
 
-    return E_NOTIMPL;
+    TRACE("%p, %s, %#x, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state);
+
+    if (cancel_cookie)
+        *cancel_cookie = NULL;
+
+    if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller)))
+        return hr;
+
+    context = heap_alloc(sizeof(*context));
+    if (!context)
+    {
+        IMFAsyncResult_Release(caller);
+        return E_OUTOFMEMORY;
+    }
+
+    context->IUnknown_iface.lpVtbl = &create_object_context_vtbl;
+    context->refcount = 1;
+    context->props = props;
+    if (context->props)
+        IPropertyStore_AddRef(context->props);
+    context->flags = flags;
+    context->url = heap_strdupW(url);
+    if (!context->url)
+    {
+        IMFAsyncResult_Release(caller);
+        IUnknown_Release(&context->IUnknown_iface);
+        return E_OUTOFMEMORY;
+    }
+
+    hr = MFCreateAsyncResult(&context->IUnknown_iface, &handler->IMFAsyncCallback_iface, (IUnknown *)caller, &item);
+    IUnknown_Release(&context->IUnknown_iface);
+    IMFAsyncResult_Release(caller);
+    if (SUCCEEDED(hr))
+    {
+        if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item)))
+        {
+            if (cancel_cookie)
+                IMFAsyncResult_GetState(item, cancel_cookie);
+        }
+
+        IMFAsyncResult_Release(item);
+    }
+
+    return hr;
 }
 
 static HRESULT WINAPI file_scheme_handler_EndCreateObject(IMFSchemeHandler *iface, IMFAsyncResult *result,
         MF_OBJECT_TYPE *obj_type, IUnknown **object)
 {
-    FIXME("%p, %p, %p, %p.\n", iface, result, obj_type, object);
+    struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface);
+    struct file_scheme_handler_result *found = NULL, *cur;
+    HRESULT hr;
 
-    return E_NOTIMPL;
+    TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object);
+
+    EnterCriticalSection(&handler->cs);
+
+    LIST_FOR_EACH_ENTRY(cur, &handler->results, struct file_scheme_handler_result, entry)
+    {
+        if (result == cur->result)
+        {
+            list_remove(&cur->entry);
+            found = cur;
+            break;
+        }
+    }
+
+    LeaveCriticalSection(&handler->cs);
+
+    if (found)
+    {
+        *obj_type = found->obj_type;
+        *object = found->object;
+        hr = IMFAsyncResult_GetStatus(found->result);
+        IMFAsyncResult_Release(found->result);
+        heap_free(found);
+    }
+    else
+    {
+        *obj_type = MF_OBJECT_INVALID;
+        *object = NULL;
+        hr = MF_E_UNEXPECTED;
+    }
+
+    return hr;
 }
 
 static HRESULT WINAPI file_scheme_handler_CancelObjectCreation(IMFSchemeHandler *iface, IUnknown *cancel_cookie)
 {
-    FIXME("%p, %p.\n", iface, cancel_cookie);
+    struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface);
+    struct file_scheme_handler_result *found = NULL, *cur;
 
-    return E_NOTIMPL;
+    TRACE("%p, %p.\n", iface, cancel_cookie);
+
+    EnterCriticalSection(&handler->cs);
+
+    LIST_FOR_EACH_ENTRY(cur, &handler->results, struct file_scheme_handler_result, entry)
+    {
+        if (cancel_cookie == (IUnknown *)cur->result)
+        {
+            list_remove(&cur->entry);
+            found = cur;
+            break;
+        }
+    }
+
+    LeaveCriticalSection(&handler->cs);
+
+    if (found)
+    {
+        IMFAsyncResult_Release(found->result);
+        if (found->object)
+            IUnknown_Release(found->object);
+        heap_free(found);
+    }
+
+    return found ? S_OK : MF_E_UNEXPECTED;
 }
 
 static const IMFSchemeHandlerVtbl file_scheme_handler_vtbl =
@@ -190,6 +415,145 @@ static const IMFSchemeHandlerVtbl file_scheme_handler_vtbl =
     file_scheme_handler_CancelObjectCreation,
 };
 
+static HRESULT WINAPI file_scheme_handler_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 %s.\n", debugstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI file_scheme_handler_callback_AddRef(IMFAsyncCallback *iface)
+{
+    struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface);
+    return IMFSchemeHandler_AddRef(&handler->IMFSchemeHandler_iface);
+}
+
+static ULONG WINAPI file_scheme_handler_callback_Release(IMFAsyncCallback *iface)
+{
+    struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface);
+    return IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface);
+}
+
+static HRESULT WINAPI file_scheme_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT file_scheme_handler_get_resolver(struct file_scheme_handler *handler, IMFSourceResolver **resolver)
+{
+    HRESULT hr;
+
+    if (!handler->resolver)
+    {
+        IMFSourceResolver *resolver;
+
+        if (FAILED(hr = MFCreateSourceResolver(&resolver)))
+            return hr;
+
+        if (InterlockedCompareExchangePointer((void **)&handler->resolver, resolver, NULL))
+            IMFSourceResolver_Release(resolver);
+    }
+
+    *resolver = handler->resolver;
+    IMFSourceResolver_AddRef(*resolver);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI file_scheme_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
+{
+    static const WCHAR schemeW[] = {'f','i','l','e',':','/','/'};
+    struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface);
+    struct file_scheme_handler_result *handler_result;
+    MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID;
+    IUnknown *object = NULL, *context_object;
+    struct create_object_context *context;
+    IMFSourceResolver *resolver;
+    IMFAsyncResult *caller;
+    IMFByteStream *stream;
+    const WCHAR *url;
+    HRESULT hr;
+
+    caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result);
+
+    if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object)))
+    {
+        WARN("Expected context set for callee result.\n");
+        return hr;
+    }
+
+    context = impl_from_IUnknown(context_object);
+
+    /* Strip from scheme, MFCreateFile() won't be expecting it. */
+    url = context->url;
+    if (!strncmpiW(context->url, schemeW, ARRAY_SIZE(schemeW)))
+        url += ARRAY_SIZE(schemeW);
+
+    hr = MFCreateFile(context->flags & MF_RESOLUTION_WRITE ? MF_ACCESSMODE_READWRITE : MF_ACCESSMODE_READ,
+            MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, url, &stream);
+    if (SUCCEEDED(hr))
+    {
+        if (context->flags & MF_RESOLUTION_MEDIASOURCE)
+        {
+            if (SUCCEEDED(hr = file_scheme_handler_get_resolver(handler, &resolver)))
+            {
+                hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, context->url, context->flags,
+                        context->props, &obj_type, &object);
+                IMFSourceResolver_Release(resolver);
+                IMFByteStream_Release(stream);
+            }
+        }
+        else
+        {
+            object = (IUnknown *)stream;
+            obj_type = MF_OBJECT_BYTESTREAM;
+        }
+    }
+
+    handler_result = heap_alloc(sizeof(*handler_result));
+    if (handler_result)
+    {
+        handler_result->result = caller;
+        IMFAsyncResult_AddRef(handler_result->result);
+        handler_result->obj_type = obj_type;
+        handler_result->object = object;
+
+        EnterCriticalSection(&handler->cs);
+        list_add_tail(&handler->results, &handler_result->entry);
+        LeaveCriticalSection(&handler->cs);
+    }
+    else
+    {
+        if (object)
+            IUnknown_Release(object);
+        hr = E_OUTOFMEMORY;
+    }
+
+    IUnknown_Release(&context->IUnknown_iface);
+
+    IMFAsyncResult_SetStatus(caller, hr);
+    MFInvokeCallback(caller);
+
+    return S_OK;
+}
+
+static const IMFAsyncCallbackVtbl file_scheme_handler_callback_vtbl =
+{
+    file_scheme_handler_callback_QueryInterface,
+    file_scheme_handler_callback_AddRef,
+    file_scheme_handler_callback_Release,
+    file_scheme_handler_callback_GetParameters,
+    file_scheme_handler_callback_Invoke,
+};
+
 static HRESULT file_scheme_handler_construct(REFIID riid, void **obj)
 {
     struct file_scheme_handler *handler;
@@ -197,12 +561,15 @@ static HRESULT file_scheme_handler_construct(REFIID riid, void **obj)
 
     TRACE("%s, %p.\n", debugstr_guid(riid), obj);
 
-    handler = heap_alloc(sizeof(*handler));
+    handler = heap_alloc_zero(sizeof(*handler));
     if (!handler)
         return E_OUTOFMEMORY;
 
     handler->IMFSchemeHandler_iface.lpVtbl = &file_scheme_handler_vtbl;
+    handler->IMFAsyncCallback_iface.lpVtbl = &file_scheme_handler_callback_vtbl;
     handler->refcount = 1;
+    list_init(&handler->results);
+    InitializeCriticalSection(&handler->cs);
 
     hr = IMFSchemeHandler_QueryInterface(&handler->IMFSchemeHandler_iface, riid, obj);
     IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface);
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c
index 0a378b55b4..6eab13e917 100644
--- a/dlls/mfplat/tests/mfplat.c
+++ b/dlls/mfplat/tests/mfplat.c
@@ -265,14 +265,18 @@ static HRESULT WINAPI test_create_from_url_callback_Invoke(IMFAsyncCallback *ifa
 
     resolver = (IMFSourceResolver *)IMFAsyncResult_GetStateNoAddRef(result);
 
+    object = NULL;
     hr = IMFSourceResolver_EndCreateObjectFromURL(resolver, result, &obj_type, &object);
+todo_wine
     ok(hr == S_OK, "Failed to create an object, hr %#x.\n", hr);
 
     hr = IMFAsyncResult_GetObject(result, &object2);
     ok(hr == S_OK, "Failed to get result object, hr %#x.\n", hr);
+todo_wine
     ok(object2 == object, "Unexpected object.\n");
 
-    IUnknown_Release(object);
+    if (object)
+        IUnknown_Release(object);
     IUnknown_Release(object2);
 
     SetEvent(callback->event);
@@ -437,19 +441,14 @@ todo_wine
 
     hr = IMFSourceResolver_CreateObjectFromURL(resolver, filename, MF_RESOLUTION_BYTESTREAM, NULL, &obj_type,
             (IUnknown **)&stream);
-todo_wine
     ok(hr == S_OK, "Failed to resolve url, hr %#x.\n", hr);
-    if (SUCCEEDED(hr))
-        IMFByteStream_Release(stream);
+    IMFByteStream_Release(stream);
 
     hr = IMFSourceResolver_BeginCreateObjectFromURL(resolver, filename, MF_RESOLUTION_BYTESTREAM, NULL,
             &cancel_cookie, &callback.IMFAsyncCallback_iface, (IUnknown *)resolver);
-todo_wine {
     ok(hr == S_OK, "Create request failed, hr %#x.\n", hr);
     ok(cancel_cookie != NULL, "Unexpected cancel object.\n");
-}
-    if (cancel_cookie)
-       IUnknown_Release(cancel_cookie);
+    IUnknown_Release(cancel_cookie);
 
     if (SUCCEEDED(hr))
         WaitForSingleObject(callback.event, INFINITE);
@@ -460,10 +459,8 @@ todo_wine {
 
     hr = IMFSourceResolver_CreateObjectFromURL(resolver, pathW, MF_RESOLUTION_BYTESTREAM, NULL, &obj_type,
             (IUnknown **)&stream);
-todo_wine
     ok(hr == S_OK, "Failed to resolve url, hr %#x.\n", hr);
-    if (SUCCEEDED(hr))
-        IMFByteStream_Release(stream);
+    IMFByteStream_Release(stream);
 
     IMFSourceResolver_Release(resolver);
 
@@ -480,15 +477,11 @@ todo_wine
     cancel_cookie = NULL;
     hr = IMFSchemeHandler_BeginCreateObject(scheme_handler, pathW, MF_RESOLUTION_MEDIASOURCE, NULL, &cancel_cookie,
             &callback2.IMFAsyncCallback_iface, (IUnknown *)scheme_handler);
-todo_wine {
     ok(hr == S_OK, "Create request failed, hr %#x.\n", hr);
     ok(!!cancel_cookie, "Unexpected cancel object.\n");
-}
-    if (cancel_cookie)
-        IUnknown_Release(cancel_cookie);
+    IUnknown_Release(cancel_cookie);
 
-    if (SUCCEEDED(hr))
-        WaitForSingleObject(callback2.event, INFINITE);
+    WaitForSingleObject(callback2.event, INFINITE);
 
     IMFSchemeHandler_Release(scheme_handler);
 
-- 
2.20.1




More information about the wine-devel mailing list