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.
-- v5: 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 | 130 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 743946f7fba..13dea7e0838 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,134 @@ 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_domain[] = + { + L"http://test.winehq.org", + L"https://test.winehq.org", + L"http://test.winehq.org", + L"https://test.winehq.org", + L"http://test.winehq.org", + }; + + 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(!wcsncmp(expect_domain[i], propvar.pwszVal, wcslen(expect_domain[i])) + || broken(hr == MF_E_ATTRIBUTENOTFOUND) /* Win7 */, + "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 +7211,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 | 74 ++++++++++++++++++++++++++++++++++------ dlls/mf/tests/mf.c | 3 +- 7 files changed, 83 insertions(+), 13 deletions(-)
diff --git a/dlls/mf/Makefile.in b/dlls/mf/Makefile.in index 870ca7cbcba..1877fa6665d 100644 --- a/dlls/mf/Makefile.in +++ b/dlls/mf/Makefile.in @@ -1,7 +1,7 @@ MODULE = mf.dll IMPORTLIB = mf IMPORTS = advapi32 mfplat ole32 uuid mfuuid strmiids -DELAYIMPORTS = evr user32 +DELAYIMPORTS = evr urlmon 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..7e92748aeb9 100644 --- a/dlls/mf/scheme_handler.c +++ b/dlls/mf/scheme_handler.c @@ -32,6 +32,8 @@
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+typedef HRESULT (*create_stream_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; + create_stream_cb create_stream; };
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->create_stream(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_stream_create(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,58 @@ HRESULT file_scheme_handler_construct(REFIID riid, void **obj) handler->refcount = 1; list_init(&handler->results); InitializeCriticalSection(&handler->cs); + handler->create_stream = file_stream_create; + + hr = IMFSchemeHandler_QueryInterface(&handler->IMFSchemeHandler_iface, riid, obj); + IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface); + + return hr; +} + +static HRESULT urlmon_stream_create(const WCHAR *url, DWORD flags, IMFByteStream **out) +{ + IMFAttributes *attributes; + 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; + } + + hr = MFCreateMFByteStreamOnStream(stream, out); + IStream_Release(stream); + if (FAILED(hr)) + return hr; + + IMFByteStream_QueryInterface(*out, &IID_IMFAttributes, (void **)&attributes); + IMFAttributes_DeleteItem(attributes, &MF_BYTESTREAM_ORIGIN_NAME); + IMFAttributes_SetString(attributes, &MF_BYTESTREAM_EFFECTIVE_URL, url); + IMFAttributes_SetString(attributes, &MF_BYTESTREAM_CONTENT_TYPE, L"application/octet-stream"); + IMFAttributes_Release(attributes); + 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->create_stream = urlmon_stream_create;
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 13dea7e0838..a52466fd0c5 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(!wcsncmp(expect_domain[i], propvar.pwszVal, wcslen(expect_domain[i])) || broken(hr == MF_E_ATTRIBUTENOTFOUND) /* Win7 */, "got url %s\n", debugstr_w(propvar.pwszVal));
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=136567
Your paranoid android.
=== debian11 (32 bit report) ===
mf: mf.c:6496: Test succeeded inside todo block: got hr 0 mf.c:6496: Test succeeded inside todo block: got hr 0
=== debian11 (32 bit ar:MA report) ===
mf: mf.c:6496: Test succeeded inside todo block: got hr 0 mf.c:6496: Test succeeded inside todo block: got hr 0
=== debian11 (32 bit de report) ===
mf: mf.c:6496: Test succeeded inside todo block: got hr 0 mf.c:6496: Test succeeded inside todo block: got hr 0
=== debian11 (32 bit fr report) ===
mf: mf.c:6496: Test succeeded inside todo block: got hr 0 mf.c:6496: Test succeeded inside todo block: got hr 0
=== debian11 (32 bit he:IL report) ===
mf: mf.c:6496: Test succeeded inside todo block: got hr 0 mf.c:6496: Test succeeded inside todo block: got hr 0
=== debian11 (32 bit hi:IN report) ===
mf: mf.c:6496: Test succeeded inside todo block: got hr 0 mf.c:6496: Test succeeded inside todo block: got hr 0
=== debian11 (32 bit ja:JP report) ===
mf: mf.c:6496: Test succeeded inside todo block: got hr 0 mf.c:6496: Test succeeded inside todo block: got hr 0
=== debian11 (32 bit zh:CN report) ===
mf: mf.c:6496: Test succeeded inside todo block: got hr 0 mf.c:6496: Test succeeded inside todo block: got hr 0
=== debian11b (32 bit WoW report) ===
mf: mf.c:6496: Test succeeded inside todo block: got hr 0 mf.c:6496: Test succeeded inside todo block: got hr 0
=== debian11b (64 bit WoW report) ===
mf: mf.c:6496: Test succeeded inside todo block: got hr 0 mf.c:6496: Test succeeded inside todo block: got hr 0
On Tue Aug 22 09:18:43 2023 +0000, Nikolay Sivov wrote:
Is it important to match those exactly? Maybe test for "http://test.winehq.org" part only?
As far as I know nothing depends on these.
On Tue Aug 22 09:18:44 2023 +0000, Nikolay Sivov wrote:
How useful is this? Maybe checking for expect_caps mask only?
We would miss if a new flag is eventually reported. Nothing that I know depends on the caps, but I also don't see any reason to have a relaxed test when the expected result is simple. It also shows that the byte stream is allowed to be either fully or partially downloaded.
On Tue Aug 22 09:18:46 2023 +0000, Nikolay Sivov wrote:
Just release ulrmon stream right away, and return if mf stream creation failed. IMFAttributes are probably always available if you're going to use MFCreateMFByteStreamOnStream(). I don't see any value in these warnings.
I did that, but fwiw I personally really don't like having unchecked errors, even though they are expected to always succeed.