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.
-- v3: mf/scheme_handler: Implement http(s):// scheme handler using urlmon. mf/scheme_handler: Split file scheme handler to scheme_handler.c. 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/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 | 84 +++++++++++++++++++++++++++++++++++----- dlls/mf/tests/mf.c | 4 +- 7 files changed, 93 insertions(+), 14 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..b2b9abbe5ac 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,68 @@ 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"); + if (FAILED(IMFAttributes_SetString(attributes, &MF_BYTESTREAM_CONTENT_TYPE, L"application/octet-stream"))) + WARN("Failed to set MF_BYTESTREAM_CONTENT_TYPE 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..1c5ac065b84 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), @@ -6494,7 +6493,6 @@ static void test_scheme_resolvers(void) 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