Skip to content

Instantly share code, notes, and snippets.

@yshui
Last active April 22, 2022 21:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yshui/6625f4537dd9a7d0b28a59a9a3a79967 to your computer and use it in GitHub Desktop.
Save yshui/6625f4537dd9a7d0b28a59a9a3a79967 to your computer and use it in GitHub Desktop.
Wine VRChat video player support patch

This patches apply on top of wine-7.0, with staging patches applied.

Easiest way to build this is to use something like wine-tkg, and apply this as user patches.

From 996f10c9857318f53a309924a18b03283a246d6a Mon Sep 17 00:00:00 2001
From: Alistair Leslie-Hughes <leslie_alistair@hotmail.com>
Date: Wed, 23 Feb 2022 12:23:34 +0300
Subject: [PATCH 1/2] mfplat: Correctly calculate url scheme length.
When a url is passed in, for example "http://..."
We need to include the : in the scheme string not exclude it.
Signed-off-by: Alistair Leslie-Hughes <leslie_alistair@hotmail.com>
Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
---
dlls/mfplat/main.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c
index 5d5fbbe1bdd..0cc90f40699 100644
--- a/dlls/mfplat/main.c
+++ b/dlls/mfplat/main.c
@@ -6172,10 +6172,11 @@ static HRESULT resolver_get_scheme_handler(const WCHAR *url, DWORD flags, IMFSch
if (ptr == url || *ptr != ':')
{
url = fileschemeW;
- ptr = fileschemeW + ARRAY_SIZE(fileschemeW) - 1;
+ len = ARRAY_SIZE(fileschemeW) - 1;
}
+ else
+ len = ptr - url + 1;
- len = ptr - url;
scheme = malloc((len + 1) * sizeof(WCHAR));
if (!scheme)
return E_OUTOFMEMORY;
--
2.35.1
From f2dcdde5c19f0ac41ab2d7af41384c15fa4b885a Mon Sep 17 00:00:00 2001
From: Yuxuan Shui <yshuiv7@gmail.com>
Date: Tue, 5 Apr 2022 01:28:09 +0100
Subject: [PATCH 2/2] mfplat: add generic scheme handler
Add a generic scheme handler supported by GStreamer's uridecodebin. This should support all
protocols supported by GStreamer's plugins.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
---
dlls/mf/Makefile.in | 2 +-
dlls/mf/main.c | 175 +++++++++++++++++++-------
dlls/mf/mf.idl | 7 ++
dlls/mf/mf.rgs | 12 ++
dlls/mfplat/tests/mfplat.c | 6 +
dlls/winegstreamer/Makefile.in | 1 +
dlls/winegstreamer/gst_private.h | 3 +-
dlls/winegstreamer/media_source.c | 119 +++++++++++++-----
dlls/winegstreamer/quartz_parser.c | 2 +-
dlls/winegstreamer/wg_parser.c | 136 +++++++++++++++++---
dlls/winegstreamer/winegstreamer.spec | 1 +
libs/mfuuid/mfuuid.c | 1 +
12 files changed, 370 insertions(+), 95 deletions(-)
diff --git a/dlls/mf/Makefile.in b/dlls/mf/Makefile.in
index 77d85ad041b..f06537c8087 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 user32 winegstreamer
EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/mf/main.c b/dlls/mf/main.c
index acbb8377e52..358b35e9550 100644
--- a/dlls/mf/main.c
+++ b/dlls/mf/main.c
@@ -34,6 +34,9 @@
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
extern const GUID CLSID_FileSchemePlugin;
+extern const GUID CLSID_GStreamerSchemePlugin;
+
+HRESULT WINAPI winegstreamer_create_media_source_from_uri(const WCHAR *, IUnknown **);
struct activate_object
{
@@ -547,7 +550,7 @@ static const IClassFactoryVtbl class_factory_vtbl =
class_factory_LockServer,
};
-struct file_scheme_handler_result
+struct scheme_handler_result
{
struct list entry;
IMFAsyncResult *result;
@@ -555,7 +558,7 @@ struct file_scheme_handler_result
IUnknown *object;
};
-struct file_scheme_handler
+struct scheme_handler
{
IMFSchemeHandler IMFSchemeHandler_iface;
IMFAsyncCallback IMFAsyncCallback_iface;
@@ -565,17 +568,17 @@ struct file_scheme_handler
CRITICAL_SECTION cs;
};
-static struct file_scheme_handler *impl_from_IMFSchemeHandler(IMFSchemeHandler *iface)
+static struct scheme_handler *impl_from_IMFSchemeHandler(IMFSchemeHandler *iface)
{
- return CONTAINING_RECORD(iface, struct file_scheme_handler, IMFSchemeHandler_iface);
+ return CONTAINING_RECORD(iface, struct scheme_handler, IMFSchemeHandler_iface);
}
-static struct file_scheme_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface)
+static struct scheme_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface)
{
- return CONTAINING_RECORD(iface, struct file_scheme_handler, IMFAsyncCallback_iface);
+ return CONTAINING_RECORD(iface, struct scheme_handler, IMFAsyncCallback_iface);
}
-static HRESULT WINAPI file_scheme_handler_QueryInterface(IMFSchemeHandler *iface, REFIID riid, void **obj)
+static HRESULT WINAPI scheme_handler_QueryIntace(IMFSchemeHandler *iface, REFIID riid, void **obj)
{
TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
@@ -592,9 +595,9 @@ static HRESULT WINAPI file_scheme_handler_QueryInterface(IMFSchemeHandler *iface
return E_NOINTERFACE;
}
-static ULONG WINAPI file_scheme_handler_AddRef(IMFSchemeHandler *iface)
+static ULONG WINAPI scheme_handler_AddRef(IMFSchemeHandler *iface)
{
- struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface);
+ struct scheme_handler *handler = impl_from_IMFSchemeHandler(iface);
ULONG refcount = InterlockedIncrement(&handler->refcount);
TRACE("%p, refcount %u.\n", handler, refcount);
@@ -602,17 +605,17 @@ static ULONG WINAPI file_scheme_handler_AddRef(IMFSchemeHandler *iface)
return refcount;
}
-static ULONG WINAPI file_scheme_handler_Release(IMFSchemeHandler *iface)
+static ULONG WINAPI scheme_handler_Release(IMFSchemeHandler *iface)
{
- struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface);
+ struct scheme_handler *handler = impl_from_IMFSchemeHandler(iface);
ULONG refcount = InterlockedDecrement(&handler->refcount);
- struct file_scheme_handler_result *result, *next;
+ struct scheme_handler_result *result, *next;
TRACE("%p, refcount %u.\n", iface, refcount);
if (!refcount)
{
- LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct file_scheme_handler_result, entry)
+ LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct scheme_handler_result, entry)
{
list_remove(&result->entry);
IMFAsyncResult_Release(result->result);
@@ -695,10 +698,10 @@ static const IUnknownVtbl create_object_context_vtbl =
create_object_context_Release,
};
-static HRESULT WINAPI file_scheme_handler_BeginCreateObject(IMFSchemeHandler *iface, const WCHAR *url, DWORD flags,
+static HRESULT WINAPI 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 scheme_handler *handler = impl_from_IMFSchemeHandler(iface);
struct create_object_context *context;
IMFAsyncResult *caller, *item;
HRESULT hr;
@@ -751,18 +754,18 @@ static HRESULT WINAPI file_scheme_handler_BeginCreateObject(IMFSchemeHandler *if
return hr;
}
-static HRESULT WINAPI file_scheme_handler_EndCreateObject(IMFSchemeHandler *iface, IMFAsyncResult *result,
+static HRESULT WINAPI 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;
+ 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 file_scheme_handler_result, entry)
+ LIST_FOR_EACH_ENTRY(cur, &handler->results, struct scheme_handler_result, entry)
{
if (result == cur->result)
{
@@ -792,16 +795,16 @@ static HRESULT WINAPI file_scheme_handler_EndCreateObject(IMFSchemeHandler *ifac
return hr;
}
-static HRESULT WINAPI file_scheme_handler_CancelObjectCreation(IMFSchemeHandler *iface, IUnknown *cancel_cookie)
+static HRESULT WINAPI 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;
+ 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 file_scheme_handler_result, entry)
+ LIST_FOR_EACH_ENTRY(cur, &handler->results, struct scheme_handler_result, entry)
{
if (cancel_cookie == (IUnknown *)cur->result)
{
@@ -824,17 +827,17 @@ static HRESULT WINAPI file_scheme_handler_CancelObjectCreation(IMFSchemeHandler
return found ? S_OK : MF_E_UNEXPECTED;
}
-static const IMFSchemeHandlerVtbl file_scheme_handler_vtbl =
+static const IMFSchemeHandlerVtbl 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,
+ scheme_handler_QueryIntace,
+ scheme_handler_AddRef,
+ scheme_handler_Release,
+ scheme_handler_BeginCreateObject,
+ scheme_handler_EndCreateObject,
+ scheme_handler_CancelObjectCreation,
};
-static HRESULT WINAPI file_scheme_handler_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
+static HRESULT WINAPI scheme_handler_callback_QueryIntace(IMFAsyncCallback *iface, REFIID riid, void **obj)
{
if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
IsEqualIID(riid, &IID_IUnknown))
@@ -849,24 +852,24 @@ static HRESULT WINAPI file_scheme_handler_callback_QueryInterface(IMFAsyncCallba
return E_NOINTERFACE;
}
-static ULONG WINAPI file_scheme_handler_callback_AddRef(IMFAsyncCallback *iface)
+static ULONG WINAPI scheme_handler_callback_AddRef(IMFAsyncCallback *iface)
{
- struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface);
+ struct scheme_handler *handler = impl_from_IMFAsyncCallback(iface);
return IMFSchemeHandler_AddRef(&handler->IMFSchemeHandler_iface);
}
-static ULONG WINAPI file_scheme_handler_callback_Release(IMFAsyncCallback *iface)
+static ULONG WINAPI scheme_handler_callback_Release(IMFAsyncCallback *iface)
{
- struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface);
+ struct 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)
+static HRESULT WINAPI 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)
+static HRESULT file_scheme_handler_get_resolver(struct scheme_handler *handler, IMFSourceResolver **resolver)
{
HRESULT hr;
@@ -890,8 +893,8 @@ static HRESULT file_scheme_handler_get_resolver(struct file_scheme_handler *hand
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;
+ 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;
@@ -964,18 +967,75 @@ static HRESULT WINAPI file_scheme_handler_callback_Invoke(IMFAsyncCallback *ifac
return S_OK;
}
+static HRESULT WINAPI gstreamer_scheme_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
+{
+ IMFAsyncResult *caller;
+ struct scheme_handler *handler = impl_from_IMFAsyncCallback(iface);
+ struct scheme_handler_result *handler_result;
+ IUnknown *object = NULL, *context_object;
+ struct create_object_context *context;
+ 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);
+
+ hr = winegstreamer_create_media_source_from_uri(context->url, &object);
+
+ handler_result = malloc(sizeof(*handler_result));
+ if (handler_result)
+ {
+ handler_result->result = caller;
+ IMFAsyncResult_AddRef(handler_result->result);
+
+ // We only know how to create media source
+ handler_result->obj_type = MF_OBJECT_MEDIASOURCE;
+ 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;
+ }
+
+ 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,
+ scheme_handler_callback_QueryIntace,
+ scheme_handler_callback_AddRef,
+ scheme_handler_callback_Release,
+ scheme_handler_callback_GetParameters,
file_scheme_handler_callback_Invoke,
};
+static const IMFAsyncCallbackVtbl gstreamer_scheme_handler_callback_vtbl =
+{
+ scheme_handler_callback_QueryIntace,
+ scheme_handler_callback_AddRef,
+ scheme_handler_callback_Release,
+ scheme_handler_callback_GetParameters,
+ gstreamer_scheme_handler_callback_Invoke,
+};
+
static HRESULT file_scheme_handler_construct(REFIID riid, void **obj)
{
- struct file_scheme_handler *handler;
+ struct scheme_handler *handler;
HRESULT hr;
TRACE("%s, %p.\n", debugstr_guid(riid), obj);
@@ -983,7 +1043,7 @@ static HRESULT file_scheme_handler_construct(REFIID riid, void **obj)
if (!(handler = calloc(1, sizeof(*handler))))
return E_OUTOFMEMORY;
- handler->IMFSchemeHandler_iface.lpVtbl = &file_scheme_handler_vtbl;
+ handler->IMFSchemeHandler_iface.lpVtbl = &scheme_handler_vtbl;
handler->IMFAsyncCallback_iface.lpVtbl = &file_scheme_handler_callback_vtbl;
handler->refcount = 1;
list_init(&handler->results);
@@ -997,6 +1057,30 @@ static HRESULT file_scheme_handler_construct(REFIID riid, void **obj)
static struct class_factory file_scheme_handler_factory = { { &class_factory_vtbl }, file_scheme_handler_construct };
+static HRESULT gstreamer_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 = &gstreamer_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 gstreamer_scheme_handler_factory = { { &class_factory_vtbl }, gstreamer_scheme_handler_construct };
+
static const struct class_object
{
const GUID *clsid;
@@ -1005,6 +1089,7 @@ static const struct class_object
class_objects[] =
{
{ &CLSID_FileSchemePlugin, &file_scheme_handler_factory.IClassFactory_iface },
+ { &CLSID_GStreamerSchemePlugin, &gstreamer_scheme_handler_factory.IClassFactory_iface },
};
/*******************************************************************************
diff --git a/dlls/mf/mf.idl b/dlls/mf/mf.idl
index 289a521b4f2..3c0d2513e9d 100644
--- a/dlls/mf/mf.idl
+++ b/dlls/mf/mf.idl
@@ -24,3 +24,10 @@
uuid(477ec299-1421-4bdd-971f-7ccb933f21ad)
]
coclass FileSchemePlugin { }
+
+[
+ helpstring("GStreamer scheme handler"),
+ threading(both),
+ uuid(587eeb6a-7336-4ebd-a4f2-91c948de622c)
+]
+coclass GStreamerSchemePlugin { }
diff --git a/dlls/mf/mf.rgs b/dlls/mf/mf.rgs
index f127df76321..53d26cf7a79 100644
--- a/dlls/mf/mf.rgs
+++ b/dlls/mf/mf.rgs
@@ -12,6 +12,18 @@ HKLM
{
val '{477ec299-1421-4bdd-971f-7ccb933f21ad}' = s 'File Scheme Handler'
}
+ 'http:'
+ {
+ val '{587eeb6a-7336-4ebd-a4f2-91c948de622c}' = s 'GStreamer Scheme Handler'
+ }
+ 'https:'
+ {
+ val '{587eeb6a-7336-4ebd-a4f2-91c948de622c}' = s 'GStreamer Scheme Handler'
+ }
+ 'rtsp:'
+ {
+ val '{587eeb6a-7336-4ebd-a4f2-91c948de622c}' = s 'GStreamer Scheme Handler'
+ }
}
}
}
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c
index 21eab6d1172..243bf670815 100644
--- a/dlls/mfplat/tests/mfplat.c
+++ b/dlls/mfplat/tests/mfplat.c
@@ -520,7 +520,11 @@ static HRESULT WINAPI test_create_from_url_callback_Invoke(IMFAsyncCallback *ifa
ok(object2 == object, "Unexpected object.\n");
if (object)
+ {
+ if (obj_type == MF_OBJECT_MEDIASOURCE)
+ IMFMediaSource_Shutdown(object);
IUnknown_Release(object);
+ }
IUnknown_Release(object2);
SetEvent(callback->event);
@@ -557,6 +561,8 @@ static HRESULT WINAPI test_create_from_file_handler_callback_Invoke(IMFAsyncCall
hr = IMFAsyncResult_GetObject(result, &object2);
ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr);
+ if (obj_type == MF_OBJECT_MEDIASOURCE)
+ IMFMediaSource_Shutdown((IMFMediaSource *)object);
IUnknown_Release(object);
}
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in
index 9234188619a..07ae6e3b6c3 100644
--- a/dlls/winegstreamer/Makefile.in
+++ b/dlls/winegstreamer/Makefile.in
@@ -1,5 +1,6 @@
MODULE = winegstreamer.dll
IMPORTS = strmbase strmiids uuid ole32 mfuuid
+IMPORTLIB = winegstreamer
DELAYIMPORTS = mfplat
EXTRAINCL = $(GSTREAMER_CFLAGS)
EXTRALIBS = $(GSTREAMER_LIBS) $(PTHREAD_LIBS)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index bf712663d9c..ccba61538b2 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -193,13 +193,14 @@ enum wg_read_result
struct unix_funcs
{
struct wg_parser *(CDECL *wg_decodebin_parser_create)(void);
+ struct wg_parser *(CDECL *wg_uridecodebin_parser_create)(void);
struct wg_parser *(CDECL *wg_avi_parser_create)(void);
struct wg_parser *(CDECL *wg_mpeg_audio_parser_create)(void);
struct wg_parser *(CDECL *wg_wave_parser_create)(void);
struct wg_parser *(CDECL *wg_raw_media_converter_create)(void);
void (CDECL *wg_parser_destroy)(struct wg_parser *parser);
- HRESULT (CDECL *wg_parser_connect)(struct wg_parser *parser, uint64_t file_size);
+ HRESULT (CDECL *wg_parser_connect)(struct wg_parser *parser, uint64_t file_size, const char *uri);
HRESULT (CDECL *wg_parser_connect_unseekable)(struct wg_parser *parser,
const struct wg_format *in_format, uint32_t stream_count, const struct wg_format *out_formats, const struct wg_rect *apertures);
void (CDECL *wg_parser_disconnect)(struct wg_parser *parser);
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index 7925a5ed856..ea4acf91e0b 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -1364,63 +1364,42 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl =
media_source_Shutdown,
};
-static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_source **out_media_source)
+static HRESULT media_source_init_from_parser(struct wg_parser *parser, uint64_t file_size, const WCHAR *uri, struct media_source *object)
{
BOOL video_selected = FALSE, audio_selected = FALSE;
IMFStreamDescriptor **descriptors = NULL;
- struct media_source *object;
UINT64 total_pres_time = 0;
- struct wg_parser *parser;
- DWORD bytestream_caps;
- uint64_t file_size;
unsigned int i;
HRESULT hr;
+ char *uri_unix = NULL;
- if (FAILED(hr = IMFByteStream_GetCapabilities(bytestream, &bytestream_caps)))
- return hr;
-
- if (!(bytestream_caps & MFBYTESTREAM_IS_SEEKABLE))
+ if (uri)
{
- FIXME("Non-seekable bytestreams not supported.\n");
- return MF_E_BYTESTREAM_NOT_SEEKABLE;
- }
+ uri_unix = malloc(wcslen(uri) * 3 + 1);
+ if (!uri_unix) {
+ hr = E_OUTOFMEMORY;
+ goto fail;
+ }
- if (FAILED(hr = IMFByteStream_GetLength(bytestream, &file_size)))
- {
- FIXME("Failed to get byte stream length, hr %#x.\n", hr);
- return hr;
+ WideCharToMultiByte(CP_UNIXCP, 0, uri, -1, uri_unix, wcslen(uri) * 3 + 1, NULL, NULL);
}
- if (!(object = calloc(1, sizeof(*object))))
- return E_OUTOFMEMORY;
-
object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl;
object->IMFGetService_iface.lpVtbl = &media_source_get_service_vtbl;
object->IMFRateSupport_iface.lpVtbl = &media_source_rate_support_vtbl;
object->IMFRateControl_iface.lpVtbl = &media_source_rate_control_vtbl;
object->async_commands_callback.lpVtbl = &source_async_commands_callback_vtbl;
object->ref = 1;
- object->byte_stream = bytestream;
- IMFByteStream_AddRef(bytestream);
if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
goto fail;
if (FAILED(hr = MFAllocateWorkQueue(&object->async_commands_queue)))
goto fail;
-
- if (!(parser = unix_funcs->wg_decodebin_parser_create()))
- {
- hr = E_OUTOFMEMORY;
- goto fail;
- }
object->wg_parser = parser;
-
- object->read_thread = CreateThread(NULL, 0, read_thread, object, 0, NULL);
-
object->state = SOURCE_OPENING;
- if (FAILED(hr = unix_funcs->wg_parser_connect(parser, file_size)))
+ if (FAILED(hr = unix_funcs->wg_parser_connect(parser, file_size, uri_unix)))
goto fail;
/* In Media Foundation, sources may read from any media source stream
@@ -1517,13 +1496,87 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_
object->state = SOURCE_STOPPED;
- *out_media_source = object;
return S_OK;
fail:
- WARN("Failed to construct MFMediaSource, hr %#x.\n", hr);
+ WARN("Failed to setup MFMediaSource, hr %#x.\n", hr);
+ free(uri_unix);
free(descriptors);
+ return hr;
+}
+
+static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_source **out_media_source)
+{
+ struct wg_parser *parser;
+ struct media_source *object;
+ DWORD bytestream_caps;
+ uint64_t file_size;
+ HRESULT hr;
+
+ if (!(object = calloc(1, sizeof(*object))))
+ return E_OUTOFMEMORY;
+
+ if (FAILED(hr = IMFByteStream_GetCapabilities(bytestream, &bytestream_caps)))
+ return hr;
+
+ if (!(bytestream_caps & MFBYTESTREAM_IS_SEEKABLE))
+ {
+ FIXME("Non-seekable bytestreams not supported.\n");
+ return MF_E_BYTESTREAM_NOT_SEEKABLE;
+ }
+
+ if (FAILED(hr = IMFByteStream_GetLength(bytestream, &file_size)))
+ {
+ FIXME("Failed to get byte stream length, hr %#lx.\n", hr);
+ return hr;
+ }
+
+ if (!(parser = unix_funcs->wg_decodebin_parser_create()))
+ {
+ hr = E_OUTOFMEMORY;
+ goto fail;
+ }
+
+ object->byte_stream = bytestream;
+ IMFByteStream_AddRef(bytestream);
+
+ object->read_thread = CreateThread(NULL, 0, read_thread, object, 0, NULL);
+
+ if (FAILED(hr = media_source_init_from_parser(parser, file_size, NULL, object)))
+ goto fail;
+
+ *out_media_source = object;
+
+ return S_OK;
+ fail:
+ WARN("Failed to construct MFMediaSource, hr %#x.\n", hr);
+ IMFMediaSource_Release(&object->IMFMediaSource_iface);
+ return hr;
+}
+
+HRESULT WINAPI winegstreamer_create_media_source_from_uri(const WCHAR *uri, IUnknown **out_media_source)
+{
+ struct media_source *object;
+ struct wg_parser *parser;
+ HRESULT hr;
+
+ if (!(object = calloc(1, sizeof(*object))))
+ return E_OUTOFMEMORY;
+
+ if (!(parser = unix_funcs->wg_uridecodebin_parser_create()))
+ {
+ hr = E_OUTOFMEMORY;
+ goto fail;
+ }
+
+ if (FAILED(hr = media_source_init_from_parser(parser, 0, uri, object)))
+ goto fail;
+
+ *out_media_source = (IUnknown *)&object->IMFMediaSource_iface;
+ return S_OK;
+ fail:
+ WARN("Failed to construct MFMediaSource, hr %#x.\n", hr);
IMFMediaSource_Release(&object->IMFMediaSource_iface);
return hr;
}
diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c
index 85ba612a077..fc2cc0bf58a 100644
--- a/dlls/winegstreamer/quartz_parser.c
+++ b/dlls/winegstreamer/quartz_parser.c
@@ -960,7 +960,7 @@ static HRESULT parser_sink_connect(struct strmbase_sink *iface, IPin *peer, cons
filter->sink_connected = true;
filter->read_thread = CreateThread(NULL, 0, read_thread, filter, 0, NULL);
- if (FAILED(hr = unix_funcs->wg_parser_connect(filter->wg_parser, file_size)))
+ if (FAILED(hr = unix_funcs->wg_parser_connect(filter->wg_parser, file_size, NULL)))
goto err;
if (!filter->init_gst(filter))
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c
index 255c7bc09c8..129fef306d6 100644
--- a/dlls/winegstreamer/wg_parser.c
+++ b/dlls/winegstreamer/wg_parser.c
@@ -29,6 +29,7 @@
#define WIN32_NO_STATUS
#include "gst_private.h"
#include "winternl.h"
+#include "wine/unixlib.h"
#include <gst/gst.h>
#include <gst/video/video.h>
@@ -52,6 +53,7 @@ GST_DEBUG_CATEGORY_STATIC(wine);
struct wg_parser
{
BOOL (*init_gst)(struct wg_parser *parser);
+ void (*set_unlimited_buffering)(struct wg_parser *parser);
struct wg_parser_stream **streams;
unsigned int stream_count, expected_stream_count;
@@ -62,6 +64,7 @@ struct wg_parser
guint64 file_size, start_offset, next_offset, stop_offset;
guint64 next_pull_offset;
+ gchar *uri;
pthread_t push_thread;
@@ -715,11 +718,22 @@ static void CDECL wg_parser_complete_read_request(struct wg_parser *parser, enum
}
static void CDECL wg_parser_set_unlimited_buffering(struct wg_parser *parser)
+{
+ if (parser->set_unlimited_buffering)
+ parser->set_unlimited_buffering(parser);
+}
+
+static void wg_parser_decodebin_set_unlimited_buffering(struct wg_parser *parser)
{
g_object_set(parser->decodebin, "max-size-buffers", G_MAXUINT, NULL);
g_object_set(parser->decodebin, "max-size-time", G_MAXUINT64, NULL);
g_object_set(parser->decodebin, "max-size-bytes", G_MAXUINT, NULL);
}
+static void wg_parser_uridecodebin_set_unlimited_buffering(struct wg_parser *parser)
+{
+ g_object_set(parser->decodebin, "buffer-duration", G_MAXINT64, NULL);
+ g_object_set(parser->decodebin, "buffer-size", G_MAXINT, NULL);
+}
static void CDECL wg_parser_stream_get_preferred_format(struct wg_parser_stream *stream, struct wg_format *format)
{
@@ -1970,11 +1984,22 @@ static gchar *query_language(GstPad *pad)
return ret;
}
-static HRESULT wg_parser_connect_inner(struct wg_parser *parser)
+static void wg_parser_create_my_src(struct wg_parser *parser)
{
GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE("wine_src",
GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY);
+ parser->my_src = gst_pad_new_from_static_template(&src_template, "wine-src");
+ gst_pad_set_getrange_function(parser->my_src, src_getrange_cb);
+ gst_pad_set_query_function(parser->my_src, src_query_cb);
+ gst_pad_set_activatemode_function(parser->my_src, src_activate_mode_cb);
+ gst_pad_set_event_function(parser->my_src, src_event_cb);
+ gst_pad_set_element_private(parser->my_src, parser);
+}
+
+static HRESULT wg_parser_connect_inner(struct wg_parser *parser)
+{
+
parser->sink_connected = true;
if (!parser->bus)
@@ -1986,26 +2011,21 @@ static HRESULT wg_parser_connect_inner(struct wg_parser *parser)
parser->container = gst_bin_new(NULL);
gst_element_set_bus(parser->container, parser->bus);
- parser->my_src = gst_pad_new_from_static_template(&src_template, "wine-src");
- gst_pad_set_getrange_function(parser->my_src, src_getrange_cb);
- gst_pad_set_query_function(parser->my_src, src_query_cb);
- gst_pad_set_activatemode_function(parser->my_src, src_activate_mode_cb);
- gst_pad_set_event_function(parser->my_src, src_event_cb);
- gst_pad_set_element_private(parser->my_src, parser);
-
parser->start_offset = parser->next_offset = parser->stop_offset = 0;
parser->next_pull_offset = 0;
return S_OK;
}
-static HRESULT CDECL wg_parser_connect(struct wg_parser *parser, uint64_t file_size)
+static HRESULT CDECL wg_parser_connect(struct wg_parser *parser, uint64_t file_size, const char *uri)
{
unsigned int i;
HRESULT hr;
parser->seekable = true;
parser->file_size = file_size;
+ if (uri)
+ parser->uri = strdup(uri);
if ((hr = wg_parser_connect_inner(parser)))
return hr;
@@ -2170,11 +2190,18 @@ static void CDECL wg_parser_disconnect(struct wg_parser *parser)
pthread_mutex_unlock(&parser->mutex);
gst_element_set_state(parser->container, GST_STATE_NULL);
- if (!parser->pull_mode)
- gst_pad_set_active(parser->my_src, 0);
- gst_pad_unlink(parser->my_src, parser->their_sink);
- gst_object_unref(parser->my_src);
- gst_object_unref(parser->their_sink);
+ if (parser->my_src)
+ {
+ if (!parser->pull_mode)
+ gst_pad_set_active(parser->my_src, 0);
+ gst_object_unref(parser->my_src);
+ if (parser->their_sink)
+ gst_pad_unlink(parser->my_src, parser->their_sink);
+ }
+
+ if (parser->their_sink)
+ gst_object_unref(parser->their_sink);
+
parser->my_src = parser->their_sink = NULL;
pthread_mutex_lock(&parser->mutex);
@@ -2219,6 +2246,8 @@ static BOOL decodebin_parser_init_gst(struct wg_parser *parser)
parser->no_more_pads = parser->error = false;
pthread_mutex_unlock(&parser->mutex);
+ wg_parser_create_my_src(parser);
+
if ((ret = gst_pad_link(parser->my_src, parser->their_sink)) < 0)
{
GST_ERROR("Failed to link pads, error %d.\n", ret);
@@ -2248,6 +2277,48 @@ static BOOL decodebin_parser_init_gst(struct wg_parser *parser)
return TRUE;
}
+static BOOL uridecodebin_parser_init_gst(struct wg_parser *parser)
+{
+ GstElement *element;
+ int ret;
+
+ if (!(element = create_element("uridecodebin", "base")))
+ return FALSE;
+
+ gst_bin_add(GST_BIN(parser->container), element);
+ parser->decodebin = element;
+
+ g_object_set(parser->decodebin, "uri", parser->uri, NULL);
+
+ parser->no_more_pads = false;
+
+ g_signal_connect(element, "pad-added", G_CALLBACK(pad_added_cb), parser);
+ g_signal_connect(element, "pad-removed", G_CALLBACK(pad_removed_cb), parser);
+ g_signal_connect(element, "autoplug-select", G_CALLBACK(autoplug_select_cb), parser);
+ g_signal_connect(element, "no-more-pads", G_CALLBACK(no_more_pads_cb), parser);
+
+ gst_element_set_state(parser->container, GST_STATE_PAUSED);
+
+ ret = gst_element_get_state(parser->container, NULL, NULL, -1);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ {
+ GST_ERROR("Failed to play stream.\n");
+ return FALSE;
+ }
+
+ pthread_mutex_lock(&parser->mutex);
+ while (!parser->no_more_pads && !parser->error)
+ pthread_cond_wait(&parser->init_cond, &parser->mutex);
+ if (parser->error)
+ {
+ pthread_mutex_unlock(&parser->mutex);
+ return FALSE;
+ }
+ pthread_mutex_unlock(&parser->mutex);
+
+ return TRUE;
+}
+
static BOOL avi_parser_init_gst(struct wg_parser *parser)
{
GstElement *element;
@@ -2268,6 +2339,8 @@ static BOOL avi_parser_init_gst(struct wg_parser *parser)
parser->no_more_pads = parser->error = false;
pthread_mutex_unlock(&parser->mutex);
+ wg_parser_create_my_src(parser);
+
if ((ret = gst_pad_link(parser->my_src, parser->their_sink)) < 0)
{
GST_ERROR("Failed to link pads, error %d.\n", ret);
@@ -2308,6 +2381,8 @@ static BOOL mpeg_audio_parser_init_gst(struct wg_parser *parser)
gst_bin_add(GST_BIN(parser->container), element);
+ wg_parser_create_my_src(parser);
+
parser->their_sink = gst_element_get_static_pad(element, "sink");
if ((ret = gst_pad_link(parser->my_src, parser->their_sink)) < 0)
{
@@ -2350,6 +2425,8 @@ static BOOL wave_parser_init_gst(struct wg_parser *parser)
gst_bin_add(GST_BIN(parser->container), element);
+ wg_parser_create_my_src(parser);
+
parser->their_sink = gst_element_get_static_pad(element, "sink");
if ((ret = gst_pad_link(parser->my_src, parser->their_sink)) < 0)
{
@@ -2435,6 +2512,8 @@ static BOOL raw_media_converter_init_gst(struct wg_parser *parser)
their_src = gst_element_get_static_pad(resampler, "src");
}
+ wg_parser_create_my_src(parser);
+
if ((ret = gst_pad_link(parser->my_src, parser->their_sink)) < 0)
{
GST_ERROR("Failed to link sink pads, error %d.\n", ret);
@@ -2487,7 +2566,22 @@ static struct wg_parser * CDECL wg_decodebin_parser_create(void)
struct wg_parser *parser;
if ((parser = wg_parser_create()))
+ {
parser->init_gst = decodebin_parser_init_gst;
+ parser->set_unlimited_buffering = wg_parser_decodebin_set_unlimited_buffering;
+ }
+ return parser;
+}
+
+static struct wg_parser * CDECL wg_uridecodebin_parser_create(void)
+{
+ struct wg_parser *parser;
+
+ if ((parser = wg_parser_create()))
+ {
+ parser->init_gst = uridecodebin_parser_init_gst;
+ parser->set_unlimited_buffering = wg_parser_uridecodebin_set_unlimited_buffering;
+ }
return parser;
}
@@ -2496,7 +2590,10 @@ static struct wg_parser * CDECL wg_avi_parser_create(void)
struct wg_parser *parser;
if ((parser = wg_parser_create()))
+ {
parser->init_gst = avi_parser_init_gst;
+ parser->set_unlimited_buffering = wg_parser_decodebin_set_unlimited_buffering;
+ }
return parser;
}
@@ -2505,7 +2602,10 @@ static struct wg_parser * CDECL wg_mpeg_audio_parser_create(void)
struct wg_parser *parser;
if ((parser = wg_parser_create()))
+ {
parser->init_gst = mpeg_audio_parser_init_gst;
+ parser->set_unlimited_buffering = wg_parser_decodebin_set_unlimited_buffering;
+ }
return parser;
}
@@ -2514,7 +2614,10 @@ static struct wg_parser * CDECL wg_wave_parser_create(void)
struct wg_parser *parser;
if ((parser = wg_parser_create()))
+ {
parser->init_gst = wave_parser_init_gst;
+ parser->set_unlimited_buffering = wg_parser_decodebin_set_unlimited_buffering;
+ }
return parser;
}
@@ -2523,7 +2626,10 @@ static struct wg_parser * CDECL wg_raw_media_converter_create(void)
struct wg_parser *parser;
if ((parser = wg_parser_create()))
+ {
parser->init_gst = raw_media_converter_init_gst;
+ parser->set_unlimited_buffering = wg_parser_decodebin_set_unlimited_buffering;
+ }
return parser;
}
@@ -2540,12 +2646,14 @@ static void CDECL wg_parser_destroy(struct wg_parser *parser)
pthread_cond_destroy(&parser->read_cond);
pthread_cond_destroy(&parser->read_done_cond);
+ free(parser->uri);
free(parser);
}
static const struct unix_funcs funcs =
{
wg_decodebin_parser_create,
+ wg_uridecodebin_parser_create,
wg_avi_parser_create,
wg_mpeg_audio_parser_create,
wg_wave_parser_create,
diff --git a/dlls/winegstreamer/winegstreamer.spec b/dlls/winegstreamer/winegstreamer.spec
index b16365d0c9f..8e19857032a 100644
--- a/dlls/winegstreamer/winegstreamer.spec
+++ b/dlls/winegstreamer/winegstreamer.spec
@@ -2,3 +2,4 @@
@ stdcall -private DllGetClassObject(ptr ptr ptr)
@ stdcall -private DllRegisterServer()
@ stdcall -private DllUnregisterServer()
+@ stdcall winegstreamer_create_media_source_from_uri(ptr ptr)
diff --git a/libs/mfuuid/mfuuid.c b/libs/mfuuid/mfuuid.c
index 537eaef6427..76c19089007 100644
--- a/libs/mfuuid/mfuuid.c
+++ b/libs/mfuuid/mfuuid.c
@@ -35,3 +35,4 @@
DEFINE_GUID(MF_SCRUBBING_SERVICE, 0xdd0ac3d8,0x40e3,0x4128,0xac,0x48,0xc0,0xad,0xd0,0x67,0xb7,0x14);
DEFINE_GUID(CLSID_FileSchemePlugin, 0x477ec299,0x1421,0x4bdd,0x97,0x1f,0x7c,0xcb,0x93,0x3f,0x21,0xad);
+DEFINE_GUID(CLSID_GStreamerSchemePlugin, 0x587eeb6a,0x7336,0x4ebd,0xa4,0xf2,0x91,0xc9,0x48,0xde,0x62,0x2c);
--
2.35.1
@Arr4y
Copy link

Arr4y commented Apr 22, 2022

I'm having trouble setting this up using wine-tkg. I am patching under proton-tkg but still am placing the files under the wine-tkg directory. I placed them in wine-tkg-userpatches but it didn't use them at all, so I instead renamed their extension to ".mypatch" and it seemed to work. In the end I get an error:
==> ERROR: Patch application has failed. The error was logged to /home/shadd/.wine-tkg/wine-tkg-git/wine-tkg-git/prepare.log for your convenience.
-> Removed BIG_UGLY_FROGMINER - Ribbit
-> Removed Proton-tkg token - Valve Ribbit
-> exit cleanup done

And looking at the end of the log file:
Applying your own patch 0001-mfplat-Correctly-calculate-url-scheme-length.mypatch
patching file dlls/mfplat/main.c
Reversed (or previously applied) patch detected! Skipping patch.
1 out of 1 hunk ignored -- saving rejects to file dlls/mfplat/main.c.rej

I also received a warning at the beginning that looks like it would affect these patches:
==> WARNING: ! Staging has disabled mfplat support on this revision, so video playback will not work in games using mfplat unless a hotfix is available !

I'm not sure if I'm applying this patch correctly. I'm also unsure where to post this other than here. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment