Let The Good Life able to play its intro video, the game creates a source reader from a `http://localhost:6000/<random-hash>` URL. This should also probably work with other games playing streams over http(s).
This is a very basic implementation, using urlmon, and it will download the entire stream to a local temporary file before playback. An more optimized implementation would probably use WinHttp and range requests to partially download the requested stream segments, but this can be implemented in a future change.
-- v2: mfreadwrite: Allow URL source resolution to not match extension or mime type. mf/scheme_handler: Implement http(s):// scheme handler using urlmon. mf/scheme_handler: Split file scheme handler to scheme_handler.c. mfplat: Support MF_BYTESTREAM_EFFECTIVE_URL attribute. mf/tests: Add some network scheme resolver tests. include: Add MF_BYTESTREAM_EFFECTIVE_URL GUID declaration.
From: Rémi Bernon rbernon@codeweavers.com
--- include/mfobjects.idl | 1 + 1 file changed, 1 insertion(+)
diff --git a/include/mfobjects.idl b/include/mfobjects.idl index 640cae88af6..33b4bbad008 100644 --- a/include/mfobjects.idl +++ b/include/mfobjects.idl @@ -702,6 +702,7 @@ cpp_quote("EXTERN_GUID(MF_BYTESTREAM_DURATION, 0xfc35828a,0x3cb6,0x460 cpp_quote("EXTERN_GUID(MF_BYTESTREAM_LAST_MODIFIED_TIME, 0xfc35828b,0x3cb6,0x460c,0xa4,0x24,0xb6,0x68,0x12,0x60,0x37,0x5a);") cpp_quote("EXTERN_GUID(MF_BYTESTREAM_IFO_FILE_URI, 0xfc35828c,0x3cb6,0x460c,0xa4,0x24,0xb6,0x68,0x12,0x60,0x37,0x5a);") cpp_quote("EXTERN_GUID(MF_BYTESTREAM_DLNA_PROFILE_ID, 0xfc35828d,0x3cb6,0x460c,0xa4,0x24,0xb6,0x68,0x12,0x60,0x37,0x5a);") +cpp_quote("EXTERN_GUID(MF_BYTESTREAM_EFFECTIVE_URL, 0x9afa0209,0x89d1,0x42af,0x84,0x56,0x1d,0xe6,0xb5,0x62,0xd6,0x91);")
typedef enum MF_FILE_ACCESSMODE { MF_ACCESSMODE_READ = 1,
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/tests/mf.c | 131 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 743946f7fba..41e943757e2 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -33,6 +33,7 @@ #include "mmdeviceapi.h" #include "uuids.h" #include "wmcodecdsp.h" +#include "nserror.h"
#include "mf_test.h"
@@ -6432,6 +6433,135 @@ static void test_MFGetSupportedSchemes(void) PropVariantClear(&value); }
+static void test_scheme_resolvers(void) +{ + static const DWORD expect_caps = MFBYTESTREAM_IS_READABLE | MFBYTESTREAM_IS_SEEKABLE; + static const WCHAR *urls[] = + { + L"http://test.winehq.org/tests/test.mp3", + L"https://test.winehq.org/tests/test.mp3", + L"httpd://test.winehq.org/tests/test.mp3", + L"httpsd://test.winehq.org/tests/test.mp3", + L"mms://test.winehq.org/tests/test.mp3", + }; + static const WCHAR *expect_urls[] = + { + /* Win10 */ L"http://test.winehq.org:80/tests/test.mp3", /* Win8 */ L"http://test.winehq.org/tests/test.mp3", + /* Win10 */ L"https://test.winehq.org:443/tests/test.mp3", /* Win8 */ L"https://test.winehq.org/tests/test.mp3", + /* Win10 */ L"http://test.winehq.org:80/tests/test.mp3", /* Win8 */ L"http://test.winehq.org/tests/test.mp3", + /* Win10 */ L"https://test.winehq.org:443/tests/test.mp3", /* Win8 */ L"https://test.winehq.org/tests/test.mp3", + /* Win10 */ L"http://test.winehq.org:80/tests/test.mp3", /* Win8 */ L"http://test.winehq.org/tests/test.mp3", + }; + + IMFSourceResolver *resolver; + IMFByteStream *byte_stream; + IMFAttributes *attributes; + PROPVARIANT propvar; + MF_OBJECT_TYPE type; + IUnknown *object; + UINT64 length; + DWORD i, caps; + HRESULT hr; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "got hr %#lx\n", hr); + + hr = MFCreateSourceResolver(&resolver); + ok(hr == S_OK, "got hr %#lx\n", hr); + + for (i = 0; i < ARRAY_SIZE(urls); i++) + { + hr = IMFSourceResolver_CreateObjectFromURL(resolver, urls[i], MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + if (hr != S_OK) + continue; + + hr = IUnknown_QueryInterface(object, &IID_IMFAttributes, (void **)&attributes); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFAttributes_GetItem(attributes, &MF_BYTESTREAM_ORIGIN_NAME, NULL); + ok(hr == MF_E_ATTRIBUTENOTFOUND, "got hr %#lx\n", hr); + + PropVariantInit(&propvar); + hr = IMFAttributes_GetItem(attributes, &MF_BYTESTREAM_EFFECTIVE_URL, &propvar); + ok(hr == S_OK || broken(hr == MF_E_ATTRIBUTENOTFOUND) /* Win7 */, "got hr %#lx\n", hr); + todo_wine + ok(broken(hr == MF_E_ATTRIBUTENOTFOUND) /* Win7 */ + || !wcscmp(expect_urls[2 * i + 0], propvar.pwszVal) + || !wcscmp(expect_urls[2 * i + 1], propvar.pwszVal), + "got url %s\n", debugstr_w(propvar.pwszVal)); + hr = PropVariantClear(&propvar); + ok(hr == S_OK, "got hr %#lx\n", hr); + + hr = IMFAttributes_GetItem(attributes, &MF_BYTESTREAM_CONTENT_TYPE, NULL); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFAttributes_GetItem(attributes, &MF_BYTESTREAM_LAST_MODIFIED_TIME, NULL); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + IMFAttributes_Release(attributes); + + hr = IUnknown_QueryInterface(object, &IID_IMFByteStream, (void **)&byte_stream); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMFByteStream_GetCapabilities(byte_stream, &caps); + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(caps == (expect_caps | MFBYTESTREAM_IS_PARTIALLY_DOWNLOADED) + || caps == (expect_caps | MFBYTESTREAM_DOES_NOT_USE_NETWORK), + "got caps %#lx\n", caps); + hr = IMFByteStream_GetLength(byte_stream, &length); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(length == 0x110d, "got length %#I64x\n", length); + IMFByteStream_Release(byte_stream); + + IUnknown_Release(object); + } + + hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"httpt://test.winehq.org/tests/test.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); + todo_wine + ok(hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE, "got hr %#lx\n", hr); + hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"httpu://test.winehq.org/tests/test.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); + todo_wine + ok(hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE, "got hr %#lx\n", hr); + + hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"http://test.winehq.bla/tests/test.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); + todo_wine + ok(hr == NS_E_SERVER_NOT_FOUND, "got hr %#lx\n", hr); + hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"https://test.winehq.bla/tests/test.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); + todo_wine + ok(hr == WININET_E_NAME_NOT_RESOLVED, "got hr %#lx\n", hr); + hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"httpd://test.winehq.bla/tests/test.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); + todo_wine + ok(hr == WININET_E_NAME_NOT_RESOLVED, "got hr %#lx\n", hr); + hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"httpsd://test.winehq.bla/tests/test.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); + todo_wine + ok(hr == WININET_E_NAME_NOT_RESOLVED, "got hr %#lx\n", hr); + hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"mms://test.winehq.bla/tests/test.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); + todo_wine + ok(hr == WININET_E_NAME_NOT_RESOLVED, "got hr %#lx\n", hr); + + hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"http://test.winehq.org/tests/invalid.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); + todo_wine + ok(hr == NS_E_FILE_NOT_FOUND, "got hr %#lx\n", hr); + hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"https://test.winehq.org/tests/invalid.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); + todo_wine + ok(hr == NS_E_FILE_NOT_FOUND, "got hr %#lx\n", hr); + hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"httpd://test.winehq.org/tests/invalid.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); + todo_wine + ok(hr == NS_E_FILE_NOT_FOUND, "got hr %#lx\n", hr); + hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"httpsd://test.winehq.org/tests/invalid.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); + todo_wine + ok(hr == NS_E_FILE_NOT_FOUND, "got hr %#lx\n", hr); + hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"mms://test.winehq.org/tests/invalid.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); + todo_wine + ok(hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE, "got hr %#lx\n", hr); + + IMFSourceResolver_Release(resolver); + + hr = MFShutdown(); + ok(hr == S_OK, "got hr %#lx\n", hr); +} + static void test_MFGetTopoNodeCurrentType(void) { static const struct attribute_desc media_type_desc[] = @@ -7082,6 +7212,7 @@ START_TEST(mf) test_MFCreateSimpleTypeHandler(); test_MFGetSupportedMimeTypes(); test_MFGetSupportedSchemes(); + test_scheme_resolvers(); test_MFGetTopoNodeCurrentType(); test_MFRequireProtectedEnvironment(); test_mpeg4_media_sink();
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mfplat/main.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index 8aaf9f93cdc..a751edd27a5 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -6321,6 +6321,11 @@ static HRESULT resolver_get_bytestream_handler(IMFByteStream *stream, const WCHA IMFAttributes_GetAllocatedString(attributes, &MF_BYTESTREAM_ORIGIN_NAME, &urlW, &length); url = urlW; } + if (!url) + { + IMFAttributes_GetAllocatedString(attributes, &MF_BYTESTREAM_EFFECTIVE_URL, &urlW, &length); + url = urlW; + } IMFAttributes_Release(attributes); }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/Makefile.in | 1 + dlls/mf/main.c | 448 ------------------------------------ dlls/mf/mf_private.h | 2 + dlls/mf/scheme_handler.c | 481 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 484 insertions(+), 448 deletions(-) create mode 100644 dlls/mf/scheme_handler.c
diff --git a/dlls/mf/Makefile.in b/dlls/mf/Makefile.in index d5478cf4853..870ca7cbcba 100644 --- a/dlls/mf/Makefile.in +++ b/dlls/mf/Makefile.in @@ -12,6 +12,7 @@ C_SRCS = \ main.c \ quality.c \ samplegrabber.c \ + scheme_handler.c \ sar.c \ session.c \ topology.c \ diff --git a/dlls/mf/main.c b/dlls/mf/main.c index 12c9f11c6b0..b25a47d78c0 100644 --- a/dlls/mf/main.c +++ b/dlls/mf/main.c @@ -548,454 +548,6 @@ 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) -{ - 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); - - if (IsEqualIID(riid, &IID_IMFSchemeHandler) || - IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IMFSchemeHandler_AddRef(iface); - return S_OK; - } - - WARN("Unsupported %s.\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI file_scheme_handler_AddRef(IMFSchemeHandler *iface) -{ - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - ULONG refcount = InterlockedIncrement(&handler->refcount); - - TRACE("%p, refcount %lu.\n", handler, refcount); - - return refcount; -} - -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 %lu.\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); - free(result); - } - DeleteCriticalSection(&handler->cs); - if (handler->resolver) - IMFSourceResolver_Release(handler->resolver); - 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 %lu.\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 %lu.\n", iface, refcount); - - if (!refcount) - { - if (context->props) - IPropertyStore_Release(context->props); - free(context->url); - free(context); - } - - return refcount; -} - -static const IUnknownVtbl create_object_context_vtbl = -{ - create_object_context_QueryInterface, - create_object_context_AddRef, - create_object_context_Release, -}; - -static HRESULT WINAPI file_scheme_handler_BeginCreateObject(IMFSchemeHandler *iface, const WCHAR *url, DWORD flags, - IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state) -{ - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - struct create_object_context *context; - IMFAsyncResult *caller, *item; - HRESULT hr; - - TRACE("%p, %s, %#lx, %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; - - if (!(context = malloc(sizeof(*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 = wcsdup(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); - if (SUCCEEDED(hr)) - { - if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) - { - if (cancel_cookie) - { - *cancel_cookie = (IUnknown *)caller; - IUnknown_AddRef(*cancel_cookie); - } - } - - IMFAsyncResult_Release(item); - } - IMFAsyncResult_Release(caller); - - return hr; -} - -static HRESULT WINAPI file_scheme_handler_EndCreateObject(IMFSchemeHandler *iface, IMFAsyncResult *result, - MF_OBJECT_TYPE *obj_type, IUnknown **object) -{ - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - struct file_scheme_handler_result *found = NULL, *cur; - HRESULT hr; - - 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); - 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) -{ - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - struct file_scheme_handler_result *found = NULL, *cur; - - 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); - free(found); - } - - return found ? S_OK : MF_E_UNEXPECTED; -} - -static const IMFSchemeHandlerVtbl file_scheme_handler_vtbl = -{ - file_scheme_handler_QueryInterface, - file_scheme_handler_AddRef, - file_scheme_handler_Release, - file_scheme_handler_BeginCreateObject, - file_scheme_handler_EndCreateObject, - 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 (!wcsnicmp(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 = malloc(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; - HRESULT hr; - - TRACE("%s, %p.\n", debugstr_guid(riid), obj); - - if (!(handler = calloc(1, sizeof(*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); - - return hr; -} - static struct class_factory file_scheme_handler_factory = { { &class_factory_vtbl }, file_scheme_handler_construct };
static const struct class_object diff --git a/dlls/mf/mf_private.h b/dlls/mf/mf_private.h index d191dadafb1..90ad8eaf8b7 100644 --- a/dlls/mf/mf_private.h +++ b/dlls/mf/mf_private.h @@ -113,6 +113,8 @@ static inline const char *debugstr_propvar(const PROPVARIANT *v) } }
+extern HRESULT file_scheme_handler_construct(REFIID riid, void **obj); + extern BOOL mf_is_sample_copier_transform(IMFTransform *transform); extern BOOL mf_is_sar_sink(IMFMediaSink *sink); extern HRESULT topology_node_get_object(IMFTopologyNode *node, REFIID riid, void **obj); diff --git a/dlls/mf/scheme_handler.c b/dlls/mf/scheme_handler.c new file mode 100644 index 00000000000..9f23ffcc76c --- /dev/null +++ b/dlls/mf/scheme_handler.c @@ -0,0 +1,481 @@ +/* + * Copyright 2022 Nikolay Sivov for CodeWeavers + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stddef.h> +#include <stdarg.h> + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" + +#include "evr.h" +#include "mfidl.h" +#include "mf_private.h" + +#include "wine/debug.h" +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct scheme_handler_result +{ + struct list entry; + IMFAsyncResult *result; + MF_OBJECT_TYPE obj_type; + IUnknown *object; +}; + +struct scheme_handler +{ + IMFSchemeHandler IMFSchemeHandler_iface; + IMFAsyncCallback IMFAsyncCallback_iface; + LONG refcount; + IMFSourceResolver *resolver; + struct list results; + CRITICAL_SECTION cs; +}; + +static struct scheme_handler *impl_from_IMFSchemeHandler(IMFSchemeHandler *iface) +{ + return CONTAINING_RECORD(iface, struct scheme_handler, IMFSchemeHandler_iface); +} + +static struct scheme_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct scheme_handler, IMFAsyncCallback_iface); +} + +static HRESULT WINAPI scheme_handler_QueryInterface(IMFSchemeHandler *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFSchemeHandler) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFSchemeHandler_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI scheme_handler_AddRef(IMFSchemeHandler *iface) +{ + struct scheme_handler *handler = impl_from_IMFSchemeHandler(iface); + ULONG refcount = InterlockedIncrement(&handler->refcount); + + TRACE("%p, refcount %lu.\n", handler, refcount); + + return refcount; +} + +static ULONG WINAPI scheme_handler_Release(IMFSchemeHandler *iface) +{ + struct scheme_handler *handler = impl_from_IMFSchemeHandler(iface); + ULONG refcount = InterlockedDecrement(&handler->refcount); + struct scheme_handler_result *result, *next; + + TRACE("%p, refcount %lu.\n", iface, refcount); + + if (!refcount) + { + LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct scheme_handler_result, entry) + { + list_remove(&result->entry); + IMFAsyncResult_Release(result->result); + if (result->object) + IUnknown_Release(result->object); + free(result); + } + DeleteCriticalSection(&handler->cs); + if (handler->resolver) + IMFSourceResolver_Release(handler->resolver); + 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 %lu.\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 %lu.\n", iface, refcount); + + if (!refcount) + { + if (context->props) + IPropertyStore_Release(context->props); + free(context->url); + free(context); + } + + return refcount; +} + +static const IUnknownVtbl create_object_context_vtbl = +{ + create_object_context_QueryInterface, + create_object_context_AddRef, + create_object_context_Release, +}; + +static HRESULT WINAPI scheme_handler_BeginCreateObject(IMFSchemeHandler *iface, const WCHAR *url, DWORD flags, + IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state) +{ + struct scheme_handler *handler = impl_from_IMFSchemeHandler(iface); + struct create_object_context *context; + IMFAsyncResult *caller, *item; + HRESULT hr; + + TRACE("%p, %s, %#lx, %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; + + if (!(context = malloc(sizeof(*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 = wcsdup(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); + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) + { + if (cancel_cookie) + { + *cancel_cookie = (IUnknown *)caller; + IUnknown_AddRef(*cancel_cookie); + } + } + + IMFAsyncResult_Release(item); + } + IMFAsyncResult_Release(caller); + + return hr; +} + +static HRESULT WINAPI scheme_handler_EndCreateObject(IMFSchemeHandler *iface, IMFAsyncResult *result, + MF_OBJECT_TYPE *obj_type, IUnknown **object) +{ + struct scheme_handler *handler = impl_from_IMFSchemeHandler(iface); + struct scheme_handler_result *found = NULL, *cur; + HRESULT hr; + + TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object); + + EnterCriticalSection(&handler->cs); + + LIST_FOR_EACH_ENTRY(cur, &handler->results, struct 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); + free(found); + } + else + { + *obj_type = MF_OBJECT_INVALID; + *object = NULL; + hr = MF_E_UNEXPECTED; + } + + return hr; +} + +static HRESULT WINAPI scheme_handler_CancelObjectCreation(IMFSchemeHandler *iface, IUnknown *cancel_cookie) +{ + struct scheme_handler *handler = impl_from_IMFSchemeHandler(iface); + struct scheme_handler_result *found = NULL, *cur; + + TRACE("%p, %p.\n", iface, cancel_cookie); + + EnterCriticalSection(&handler->cs); + + LIST_FOR_EACH_ENTRY(cur, &handler->results, struct 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); + free(found); + } + + return found ? S_OK : MF_E_UNEXPECTED; +} + +static const IMFSchemeHandlerVtbl scheme_handler_vtbl = +{ + scheme_handler_QueryInterface, + scheme_handler_AddRef, + scheme_handler_Release, + scheme_handler_BeginCreateObject, + scheme_handler_EndCreateObject, + scheme_handler_CancelObjectCreation, +}; + +static HRESULT WINAPI 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 scheme_handler_callback_AddRef(IMFAsyncCallback *iface) +{ + struct scheme_handler *handler = impl_from_IMFAsyncCallback(iface); + return IMFSchemeHandler_AddRef(&handler->IMFSchemeHandler_iface); +} + +static ULONG WINAPI scheme_handler_callback_Release(IMFAsyncCallback *iface) +{ + struct scheme_handler *handler = impl_from_IMFAsyncCallback(iface); + return IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface); +} + +static HRESULT WINAPI scheme_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT scheme_handler_get_resolver(struct 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 scheme_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + static const WCHAR schemeW[] = {'f','i','l','e',':','/','/'}; + struct scheme_handler *handler = impl_from_IMFAsyncCallback(iface); + struct 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 (!wcsnicmp(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 = 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 = malloc(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 scheme_handler_callback_vtbl = +{ + scheme_handler_callback_QueryInterface, + scheme_handler_callback_AddRef, + scheme_handler_callback_Release, + scheme_handler_callback_GetParameters, + scheme_handler_callback_Invoke, +}; + +HRESULT file_scheme_handler_construct(REFIID riid, void **obj) +{ + struct scheme_handler *handler; + HRESULT hr; + + TRACE("%s, %p.\n", debugstr_guid(riid), obj); + + if (!(handler = calloc(1, sizeof(*handler)))) + return E_OUTOFMEMORY; + + handler->IMFSchemeHandler_iface.lpVtbl = &scheme_handler_vtbl; + handler->IMFAsyncCallback_iface.lpVtbl = &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); + + return hr; +}
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/Makefile.in | 2 +- dlls/mf/main.c | 2 + dlls/mf/mf.idl | 6 +++ dlls/mf/mf.rgs | 8 ++++ dlls/mf/mf_private.h | 1 + dlls/mf/scheme_handler.c | 82 +++++++++++++++++++++++++++++++++++----- dlls/mf/tests/mf.c | 3 +- 7 files changed, 91 insertions(+), 13 deletions(-)
diff --git a/dlls/mf/Makefile.in b/dlls/mf/Makefile.in index 870ca7cbcba..b400f19b3a5 100644 --- a/dlls/mf/Makefile.in +++ b/dlls/mf/Makefile.in @@ -1,6 +1,6 @@ MODULE = mf.dll IMPORTLIB = mf -IMPORTS = advapi32 mfplat ole32 uuid mfuuid strmiids +IMPORTS = advapi32 mfplat ole32 uuid mfuuid strmiids urlmon DELAYIMPORTS = evr user32
EXTRADLLFLAGS = -Wb,--prefer-native diff --git a/dlls/mf/main.c b/dlls/mf/main.c index b25a47d78c0..92558a86b30 100644 --- a/dlls/mf/main.c +++ b/dlls/mf/main.c @@ -549,6 +549,7 @@ static const IClassFactoryVtbl class_factory_vtbl = };
static struct class_factory file_scheme_handler_factory = { { &class_factory_vtbl }, file_scheme_handler_construct }; +static struct class_factory urlmon_scheme_handler_factory = { { &class_factory_vtbl }, urlmon_scheme_handler_construct };
static const struct class_object { @@ -558,6 +559,7 @@ static const struct class_object class_objects[] = { { &CLSID_FileSchemePlugin, &file_scheme_handler_factory.IClassFactory_iface }, + { &CLSID_UrlmonSchemePlugin, &urlmon_scheme_handler_factory.IClassFactory_iface }, };
/******************************************************************************* diff --git a/dlls/mf/mf.idl b/dlls/mf/mf.idl index 289a521b4f2..4f5ef36c965 100644 --- a/dlls/mf/mf.idl +++ b/dlls/mf/mf.idl @@ -24,3 +24,9 @@ uuid(477ec299-1421-4bdd-971f-7ccb933f21ad) ] coclass FileSchemePlugin { } + +[ + threading(both), + uuid(9ec4b4f9-3029-45ad-947b-344de2a249e2) +] +coclass UrlmonSchemePlugin {} diff --git a/dlls/mf/mf.rgs b/dlls/mf/mf.rgs index f127df76321..f06576baccb 100644 --- a/dlls/mf/mf.rgs +++ b/dlls/mf/mf.rgs @@ -12,6 +12,14 @@ HKLM { val '{477ec299-1421-4bdd-971f-7ccb933f21ad}' = s 'File Scheme Handler' } + 'http:' + { + val '{9ec4b4f9-3029-45ad-947b-344de2a249e2}' = s 'Urlmon Scheme Handler' + } + 'https:' + { + val '{9ec4b4f9-3029-45ad-947b-344de2a249e2}' = s 'Urlmon Scheme Handler' + } } } } diff --git a/dlls/mf/mf_private.h b/dlls/mf/mf_private.h index 90ad8eaf8b7..bbfadaee5d8 100644 --- a/dlls/mf/mf_private.h +++ b/dlls/mf/mf_private.h @@ -114,6 +114,7 @@ static inline const char *debugstr_propvar(const PROPVARIANT *v) }
extern HRESULT file_scheme_handler_construct(REFIID riid, void **obj); +extern HRESULT urlmon_scheme_handler_construct(REFIID riid, void **obj);
extern BOOL mf_is_sample_copier_transform(IMFTransform *transform); extern BOOL mf_is_sar_sink(IMFMediaSink *sink); diff --git a/dlls/mf/scheme_handler.c b/dlls/mf/scheme_handler.c index 9f23ffcc76c..6a04902e053 100644 --- a/dlls/mf/scheme_handler.c +++ b/dlls/mf/scheme_handler.c @@ -32,6 +32,8 @@
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+typedef HRESULT (*resolve_cb)(const WCHAR *url, DWORD flags, IMFByteStream **out); + struct scheme_handler_result { struct list entry; @@ -48,6 +50,7 @@ struct scheme_handler IMFSourceResolver *resolver; struct list results; CRITICAL_SECTION cs; + resolve_cb resolve; };
static struct scheme_handler *impl_from_IMFSchemeHandler(IMFSchemeHandler *iface) @@ -374,7 +377,6 @@ static HRESULT scheme_handler_get_resolver(struct scheme_handler *handler, IMFSo
static HRESULT WINAPI scheme_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { - static const WCHAR schemeW[] = {'f','i','l','e',':','/','/'}; struct scheme_handler *handler = impl_from_IMFAsyncCallback(iface); struct scheme_handler_result *handler_result; MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID; @@ -383,7 +385,6 @@ static HRESULT WINAPI scheme_handler_callback_Invoke(IMFAsyncCallback *iface, IM IMFSourceResolver *resolver; IMFAsyncResult *caller; IMFByteStream *stream; - const WCHAR *url; HRESULT hr;
caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); @@ -396,14 +397,7 @@ static HRESULT WINAPI scheme_handler_callback_Invoke(IMFAsyncCallback *iface, IM
context = impl_from_IUnknown(context_object);
- /* Strip from scheme, MFCreateFile() won't be expecting it. */ - url = context->url; - if (!wcsnicmp(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 (SUCCEEDED(hr = handler->resolve(context->url, context->flags, &stream))) { if (context->flags & MF_RESOLUTION_MEDIASOURCE) { @@ -458,6 +452,14 @@ static const IMFAsyncCallbackVtbl scheme_handler_callback_vtbl = scheme_handler_callback_Invoke, };
+static HRESULT file_scheme_handler_resolve(const WCHAR *url, DWORD flags, IMFByteStream **out) +{ + if (!wcsnicmp(url, L"file://", 7)) + url += 7; + return MFCreateFile(flags & MF_RESOLUTION_WRITE ? MF_ACCESSMODE_READWRITE : MF_ACCESSMODE_READ, + MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, url, out); +} + HRESULT file_scheme_handler_construct(REFIID riid, void **obj) { struct scheme_handler *handler; @@ -473,6 +475,66 @@ HRESULT file_scheme_handler_construct(REFIID riid, void **obj) handler->refcount = 1; list_init(&handler->results); InitializeCriticalSection(&handler->cs); + handler->resolve = file_scheme_handler_resolve; + + hr = IMFSchemeHandler_QueryInterface(&handler->IMFSchemeHandler_iface, riid, obj); + IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface); + + return hr; +} + +static HRESULT urlmon_scheme_handler_resolve(const WCHAR *url, DWORD flags, IMFByteStream **out) +{ + IStream *stream; + HRESULT hr; + + if (flags & MF_RESOLUTION_WRITE) + return E_INVALIDARG; + + if (FAILED(hr = URLOpenBlockingStreamW(NULL, url, &stream, 0, NULL))) + { + WARN("Failed to open url %s, hr %#lx\n", debugstr_w(url), hr); + return hr; + } + + if (FAILED(hr = MFCreateMFByteStreamOnStream(stream, out))) + WARN("Failed to create IMFByteStream from IStream, hr %#lx\n", hr); + else + { + IMFAttributes *attributes; + + if (FAILED(IMFByteStream_QueryInterface(*out, &IID_IMFAttributes, (void **)&attributes))) + WARN("Failed to get IMFAttributes interface\n"); + else + { + if (FAILED(IMFAttributes_DeleteItem(attributes, &MF_BYTESTREAM_ORIGIN_NAME))) + WARN("Failed to delete MF_BYTESTREAM_ORIGIN_NAME attribute\n"); + if (FAILED(IMFAttributes_SetString(attributes, &MF_BYTESTREAM_EFFECTIVE_URL, url))) + WARN("Failed to set MF_BYTESTREAM_EFFECTIVE_URL attribute\n"); + IMFAttributes_Release(attributes); + } + } + + IStream_Release(stream); + return hr; +} + +HRESULT urlmon_scheme_handler_construct(REFIID riid, void **obj) +{ + struct scheme_handler *handler; + HRESULT hr; + + TRACE("%s, %p.\n", debugstr_guid(riid), obj); + + if (!(handler = calloc(1, sizeof(*handler)))) + return E_OUTOFMEMORY; + + handler->IMFSchemeHandler_iface.lpVtbl = &scheme_handler_vtbl; + handler->IMFAsyncCallback_iface.lpVtbl = &scheme_handler_callback_vtbl; + handler->refcount = 1; + list_init(&handler->results); + InitializeCriticalSection(&handler->cs); + handler->resolve = urlmon_scheme_handler_resolve;
hr = IMFSchemeHandler_QueryInterface(&handler->IMFSchemeHandler_iface, riid, obj); IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface); diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 41e943757e2..6ce1f5d2b60 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -6472,7 +6472,7 @@ static void test_scheme_resolvers(void) for (i = 0; i < ARRAY_SIZE(urls); i++) { hr = IMFSourceResolver_CreateObjectFromURL(resolver, urls[i], MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); - todo_wine + todo_wine_if(i >= 2) ok(hr == S_OK, "got hr %#lx\n", hr); if (hr != S_OK) continue; @@ -6485,7 +6485,6 @@ static void test_scheme_resolvers(void) PropVariantInit(&propvar); hr = IMFAttributes_GetItem(attributes, &MF_BYTESTREAM_EFFECTIVE_URL, &propvar); ok(hr == S_OK || broken(hr == MF_E_ATTRIBUTENOTFOUND) /* Win7 */, "got hr %#lx\n", hr); - todo_wine ok(broken(hr == MF_E_ATTRIBUTENOTFOUND) /* Win7 */ || !wcscmp(expect_urls[2 * i + 0], propvar.pwszVal) || !wcscmp(expect_urls[2 * i + 1], propvar.pwszVal),
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mfreadwrite/reader.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c index e21f4e10fa9..4a60e18d455 100644 --- a/dlls/mfreadwrite/reader.c +++ b/dlls/mfreadwrite/reader.c @@ -2468,15 +2468,15 @@ static HRESULT create_source_reader_from_url(const WCHAR *url, IMFAttributes *at IMFAttributes_GetUnknown(attributes, &MF_SOURCE_READER_MEDIASOURCE_CONFIG, &IID_IPropertyStore, (void **)&props);
- hr = IMFSourceResolver_CreateObjectFromURL(resolver, url, MF_RESOLUTION_MEDIASOURCE, props, &obj_type, - &object); + hr = IMFSourceResolver_CreateObjectFromURL(resolver, url, MF_RESOLUTION_MEDIASOURCE + | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE, props, &obj_type, &object); if (SUCCEEDED(hr)) { switch (obj_type) { case MF_OBJECT_BYTESTREAM: - hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, (IMFByteStream *)object, NULL, - MF_RESOLUTION_MEDIASOURCE, props, &obj_type, (IUnknown **)&source); + hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, (IMFByteStream *)object, NULL, MF_RESOLUTION_MEDIASOURCE + | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE, props, &obj_type, (IUnknown **)&source); break; case MF_OBJECT_MEDIASOURCE: source = (IMFMediaSource *)object;
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=135379
Your paranoid android.
=== debian11b (64 bit WoW report) ===
win32u: win32u.c:1056: Test failed: win32u:win32u:09b0 done (0) in 0s 1337B
v2: Remove a succeeding todo_wine.
Nikolay Sivov (@nsivov) commented about dlls/mfplat/main.c:
IMFAttributes_GetAllocatedString(attributes, &MF_BYTESTREAM_ORIGIN_NAME, &urlW, &length); url = urlW; }
if (!url)
{
IMFAttributes_GetAllocatedString(attributes, &MF_BYTESTREAM_EFFECTIVE_URL, &urlW, &length);
url = urlW;
} IMFAttributes_Release(attributes);
I don't see this being used by e.g. CreateObjectFromByteStream(). How did you check that? I tried by removing MF_BYTESTREAM_ORIGIN_NAME and setting this MF_BYTESTREAM_EFFECTIVE_URL. Resolution fails for me.
Nikolay Sivov (@nsivov) commented about dlls/mfreadwrite/reader.c:
- hr = IMFSourceResolver_CreateObjectFromURL(resolver, url, MF_RESOLUTION_MEDIASOURCE, props, &obj_type,
&object);
- hr = IMFSourceResolver_CreateObjectFromURL(resolver, url, MF_RESOLUTION_MEDIASOURCE
if (SUCCEEDED(hr)) { switch (obj_type) { case MF_OBJECT_BYTESTREAM:| MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE, props, &obj_type, &object);
hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, (IMFByteStream *)object, NULL,
MF_RESOLUTION_MEDIASOURCE, props, &obj_type, (IUnknown **)&source);
hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, (IMFByteStream *)object, NULL, MF_RESOLUTION_MEDIASOURCE
| MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE, props, &obj_type, (IUnknown **)&source); break; case MF_OBJECT_MEDIASOURCE: source = (IMFMediaSource *)object;
Could you add a test for this? I know we are using this flag for stream case, and I think it's correct. When creating from url, using test.wav with e.g. mp3 content type should be enough I guess.
On Sun Jul 30 23:22:12 2023 +0000, Nikolay Sivov wrote:
I don't see this being used by e.g. CreateObjectFromByteStream(). How did you check that? I tried by removing MF_BYTESTREAM_ORIGIN_NAME and setting this MF_BYTESTREAM_EFFECTIVE_URL. Resolution fails for me.
I didn't test it, it was only to have the same behavior as `MF_BYTESTREAM_ORIGIN_NAME`. I'll remove it instead.
On Sun Jul 30 23:22:34 2023 +0000, Nikolay Sivov wrote:
Could you add a test for this? I know we are using this flag for stream case, and I think it's correct. When creating from url, using test.wav with e.g. mp3 content type should be enough I guess.
This doesn't seem to work with a file:// url, I've opened https://gitlab.winehq.org/winehq/tools/-/merge_requests/64 to have a mp3 file with an incorrect extension to test it with.
On Mon Jul 31 08:01:42 2023 +0000, Rémi Bernon wrote:
This doesn't seem to work with a file:// url, I've opened https://gitlab.winehq.org/winehq/tools/-/merge_requests/64 to have a mp3 file with an incorrect extension to test it with.
Actually I'm not even sure this is very useful. The scheme resolver also sets the `MF_BYTESTREAM_CONTENT_TYPE` attributes on Windows, which will be enough to not require `MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE`.
On Wine we will miss the content type until we have a better implementation of http scheme handlers with access to the response headers.