From 8c69a77032ff8d5f0a79c387646f40716d9393ad Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Fri, 3 May 2019 14:53:00 +0300 Subject: [PATCH] mf: Implement file scheme handler. Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- 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 3499a4a61aa..221ef42ec92 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 8500cd9cef8..77081e2a576 100644 --- a/dlls/mf/main.c +++ b/dlls/mf/main.c @@ -30,8 +30,15 @@ #include "initguid.h" #include "mf.h" +#undef INITGUID +#include +#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 0a378b55b42..6eab13e9179 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);