First part of a series introducing a separate unix interface for the MF media source. The goal is to use a synchronous demuxing-only interface, similar to native, which will be simpler and faster and better fitted to most MF media source use cases.
From: Rémi Bernon rbernon@codeweavers.com
--- MAINTAINERS | 1 + dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 3 + dlls/winegstreamer/main.c | 25 ++++++++ dlls/winegstreamer/media_source.c | 15 ++++- dlls/winegstreamer/unix_private.h | 5 ++ dlls/winegstreamer/unixlib.h | 9 +++ dlls/winegstreamer/wg_parser.c | 6 ++ dlls/winegstreamer/wg_source.c | 94 +++++++++++++++++++++++++++++++ 9 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 dlls/winegstreamer/wg_source.c
diff --git a/MAINTAINERS b/MAINTAINERS index 29fa77c0404..64c696ddc09 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -217,6 +217,7 @@ F: dlls/winegstreamer/mfplat.c F: dlls/winegstreamer/resampler.c F: dlls/winegstreamer/video_decoder.c F: dlls/winegstreamer/video_processor.c +F: dlls/winegstreamer/wg_source.c F: dlls/winegstreamer/wg_sample.c F: dlls/winegstreamer/wg_transform.c F: dlls/winegstreamer/wma_decoder.c diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 63ca3f61fdf..40ff4515281 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -27,6 +27,7 @@ SOURCES = \ wg_muxer.c \ wg_parser.c \ wg_sample.c \ + wg_source.c \ wg_transform.c \ winegstreamer_classes.idl \ wm_reader.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 0f7d945ba37..6380b8fb7ff 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -104,6 +104,9 @@ char *wg_parser_stream_get_tag(wg_parser_stream_t stream, enum wg_parser_tag tag void wg_parser_stream_seek(wg_parser_stream_t stream, double rate, uint64_t start_pos, uint64_t stop_pos, DWORD start_flags, DWORD stop_flags);
+HRESULT wg_source_create(wg_source_t *out); +void wg_source_destroy(wg_source_t source); + HRESULT wg_transform_create_mf(IMFMediaType *input_type, IMFMediaType *output_type, const struct wg_transform_attrs *attrs, wg_transform_t *transform); HRESULT wg_transform_create_quartz(const AM_MEDIA_TYPE *input_format, const AM_MEDIA_TYPE *output_format, diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 2d144a6f71f..92a44be0c96 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -435,6 +435,31 @@ void wg_parser_stream_seek(wg_parser_stream_t stream, double rate, WINE_UNIX_CALL(unix_wg_parser_stream_seek, ¶ms); }
+HRESULT wg_source_create(wg_source_t *out) +{ + struct wg_source_create_params params = {0}; + NTSTATUS status; + + TRACE("out %p\n", out); + + if ((status = WINE_UNIX_CALL(unix_wg_source_create, ¶ms))) + WARN("wg_source_create returned status %#lx\n", status); + else + { + TRACE("Returning source %#I64x.\n", params.source); + *out = params.source; + } + + return HRESULT_FROM_NT(status); +} + +void wg_source_destroy(wg_source_t source) +{ + TRACE("source %#I64x.\n", source); + + WINE_UNIX_CALL(unix_wg_source_destroy, &source); +} + HRESULT wg_transform_create_mf(IMFMediaType *input_type, IMFMediaType *output_type, const struct wg_transform_attrs *attrs, wg_transform_t *transform) { diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 030d0c1b9a2..0de82d76db9 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -36,6 +36,8 @@ struct object_context IMFByteStream *stream; UINT64 file_size; WCHAR *url; + + wg_source_t wg_source; };
static struct object_context *impl_from_IUnknown(IUnknown *iface) @@ -78,6 +80,8 @@ static ULONG WINAPI object_context_Release(IUnknown *iface)
if (!refcount) { + if (context->wg_source) + wg_source_destroy(context->wg_source); IMFAsyncResult_Release(context->result); IMFByteStream_Release(context->stream); free(context->url); @@ -182,6 +186,7 @@ struct media_source
CRITICAL_SECTION cs;
+ wg_source_t wg_source; UINT64 file_size; wg_parser_t wg_parser; UINT64 duration; @@ -1364,6 +1369,7 @@ static ULONG WINAPI media_source_Release(IMFMediaSource *iface) IMFMediaSource_Shutdown(iface); IMFMediaEventQueue_Release(source->event_queue); IMFByteStream_Release(source->byte_stream); + wg_source_destroy(source->wg_source); wg_parser_destroy(source->wg_parser); source->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&source->cs); @@ -1642,6 +1648,9 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc InitializeCriticalSectionEx(&object->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO); object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs");
+ object->wg_source = context->wg_source; + context->wg_source = 0; + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) goto fail;
@@ -1721,6 +1730,8 @@ fail: WaitForSingleObject(object->read_thread, INFINITE); CloseHandle(object->read_thread); } + if (object->wg_source) + wg_source_destroy(object->wg_source); if (object->wg_parser) wg_parser_destroy(object->wg_parser); if (object->async_commands_queue) @@ -2003,7 +2014,9 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM if (!state || !(context = impl_from_IUnknown(state))) return E_INVALIDARG;
- if (FAILED(hr = media_source_create(context, (IMFMediaSource **)&object))) + if (FAILED(hr = wg_source_create(&context->wg_source))) + WARN("Failed to create wg_source, hr %#lx\n", hr); + else if (FAILED(hr = media_source_create(context, (IMFMediaSource **)&object))) WARN("Failed to create media source, hr %#lx\n", hr); else { diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index b2cc036c914..52412a283ab 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -68,6 +68,11 @@ extern NTSTATUS wg_transform_drain(void *args); extern NTSTATUS wg_transform_flush(void *args); extern NTSTATUS wg_transform_notify_qos(void *args);
+/* wg_source.c */ + +extern NTSTATUS wg_source_create(void *args); +extern NTSTATUS wg_source_destroy(void *args); + /* wg_media_type.c */
static inline BOOL is_mf_video_area_empty(const MFVideoArea *area) diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 19270bd731b..0a98aadd1ab 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -205,6 +205,7 @@ C_ASSERT(sizeof(struct wg_parser_buffer) == 32);
typedef UINT64 wg_parser_t; typedef UINT64 wg_parser_stream_t; +typedef UINT64 wg_source_t; typedef UINT64 wg_transform_t; typedef UINT64 wg_muxer_t;
@@ -329,6 +330,11 @@ struct wg_parser_stream_seek_params DWORD start_flags, stop_flags; };
+struct wg_source_create_params +{ + wg_source_t source; +}; + struct wg_transform_attrs { UINT32 output_plane_align; @@ -444,6 +450,9 @@ enum unix_funcs unix_wg_parser_stream_get_tag, unix_wg_parser_stream_seek,
+ unix_wg_source_create, + unix_wg_source_destroy, + unix_wg_transform_create, unix_wg_transform_destroy, unix_wg_transform_get_output_type, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 710cfe6a0a5..51753b1f429 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1887,6 +1887,9 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_parser_stream_get_tag), X(wg_parser_stream_seek),
+ X(wg_source_create), + X(wg_source_destroy), + X(wg_transform_create), X(wg_transform_destroy), X(wg_transform_get_output_type), @@ -2284,6 +2287,9 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X64(wg_parser_stream_get_tag), X(wg_parser_stream_seek),
+ X(wg_source_create), + X(wg_source_destroy), + X64(wg_transform_create), X(wg_transform_destroy), X64(wg_transform_get_output_type), diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c new file mode 100644 index 00000000000..52a54196ea9 --- /dev/null +++ b/dlls/winegstreamer/wg_source.c @@ -0,0 +1,94 @@ +/* + * Copyright 2023 Rémi Bernon 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 + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> + +#include <gst/gst.h> +#include <gst/video/video.h> +#include <gst/audio/audio.h> +#include <gst/base/base.h> +#include <gst/tag/tag.h> + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "winternl.h" +#include "mferror.h" + +#include "unix_private.h" + +struct wg_source +{ + GstElement *container; +}; + +static struct wg_source *get_source(wg_source_t source) +{ + return (struct wg_source *)(ULONG_PTR)source; +} + +NTSTATUS wg_source_create(void *args) +{ + struct wg_source_create_params *params = args; + struct wg_source *source; + + if (!(source = calloc(1, sizeof(*source)))) + return STATUS_UNSUCCESSFUL; + + if (!(source->container = gst_bin_new("wg_source"))) + goto error; + + gst_element_set_state(source->container, GST_STATE_PAUSED); + if (!gst_element_get_state(source->container, NULL, NULL, -1)) + goto error; + + params->source = (wg_source_t)(ULONG_PTR)source; + GST_INFO("Created winegstreamer source %p.", source); + return STATUS_SUCCESS; + +error: + if (source->container) + { + gst_element_set_state(source->container, GST_STATE_NULL); + gst_object_unref(source->container); + } + free(source); + + GST_ERROR("Failed to create winegstreamer source."); + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS wg_source_destroy(void *args) +{ + struct wg_source *source = get_source(*(wg_source_t *)args); + + GST_TRACE("source %p", source); + + gst_element_set_state(source->container, GST_STATE_NULL); + gst_object_unref(source->container); + free(source); + + return STATUS_SUCCESS; +}
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winegstreamer/media_source.c | 38 +++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 12 deletions(-)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 0de82d76db9..61a93ffa4ba 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -38,6 +38,8 @@ struct object_context WCHAR *url;
wg_source_t wg_source; + UINT32 buffer_size; + BYTE *buffer; };
static struct object_context *impl_from_IUnknown(IUnknown *iface) @@ -84,6 +86,7 @@ static ULONG WINAPI object_context_Release(IUnknown *iface) wg_source_destroy(context->wg_source); IMFAsyncResult_Release(context->result); IMFByteStream_Release(context->stream); + free(context->buffer); free(context->url); free(context); } @@ -99,14 +102,17 @@ static const IUnknownVtbl object_context_vtbl = };
static HRESULT object_context_create(DWORD flags, IMFByteStream *stream, const WCHAR *url, - QWORD file_size, IMFAsyncResult *result, IUnknown **out) + QWORD file_size, IMFAsyncResult *result, struct object_context **out) { + UINT buffer_size = file_size >= 128 * 1024 ? 32 * 1024 : min(file_size, 4 * 1024); WCHAR *tmp_url = url ? wcsdup(url) : NULL; struct object_context *context;
- if (!(context = calloc(1, sizeof(*context)))) + if (!(context = calloc(1, sizeof(*context))) + || !(context->buffer = malloc(buffer_size))) { free(tmp_url); + free(context); return E_OUTOFMEMORY; }
@@ -118,8 +124,9 @@ static HRESULT object_context_create(DWORD flags, IMFByteStream *stream, const W context->url = tmp_url; context->result = result; IMFAsyncResult_AddRef(context->result); + context->buffer_size = buffer_size;
- *out = &context->IUnknown_iface; + *out = context; return S_OK; }
@@ -1865,8 +1872,8 @@ static HRESULT WINAPI stream_handler_BeginCreateObject(IMFByteStreamHandler *ifa { struct stream_handler *handler = impl_from_IMFByteStreamHandler(iface); IMFAsyncResult *result; - IUnknown *context; - QWORD file_size; + struct object_context *context; + QWORD file_size, position; HRESULT hr; DWORD caps;
@@ -1896,13 +1903,16 @@ static HRESULT WINAPI stream_handler_BeginCreateObject(IMFByteStreamHandler *ifa if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &result))) return hr; if (FAILED(hr = object_context_create(flags, stream, url, file_size, result, &context))) - { - IMFAsyncResult_Release(result); - return hr; - } + goto done;
- hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_IO, &handler->IMFAsyncCallback_iface, context); - IUnknown_Release(context); + if (FAILED(hr = IMFByteStream_GetCurrentPosition(stream, &position))) + WARN("Failed to get byte stream position, hr %#lx\n", hr); + else if (position != 0 && FAILED(hr = IMFByteStream_SetCurrentPosition(stream, 0))) + WARN("Failed to set byte stream position, hr %#lx\n", hr); + else if (FAILED(hr = IMFByteStream_BeginRead(context->stream, context->buffer, context->buffer_size, + &handler->IMFAsyncCallback_iface, &context->IUnknown_iface))) + WARN("Failed to queue byte stream async read, hr %#lx\n", hr); + IUnknown_Release(&context->IUnknown_iface);
if (SUCCEEDED(hr) && cancel_cookie) { @@ -1910,6 +1920,7 @@ static HRESULT WINAPI stream_handler_BeginCreateObject(IMFByteStreamHandler *ifa IUnknown_AddRef(*cancel_cookie); }
+done: IMFAsyncResult_Release(result);
return hr; @@ -2009,12 +2020,15 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM IUnknown *object, *state = IMFAsyncResult_GetStateNoAddRef(result); struct object_context *context; struct result_entry *entry; + DWORD size = 0; HRESULT hr;
if (!state || !(context = impl_from_IUnknown(state))) return E_INVALIDARG;
- if (FAILED(hr = wg_source_create(&context->wg_source))) + if (FAILED(hr = IMFByteStream_EndRead(context->stream, result, &size))) + WARN("Failed to complete stream read, hr %#lx\n", hr); + else if (FAILED(hr = wg_source_create(&context->wg_source))) WARN("Failed to create wg_source, hr %#lx\n", hr); else if (FAILED(hr = media_source_create(context, (IMFMediaSource **)&object))) WARN("Failed to create media source, hr %#lx\n", hr);
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winegstreamer/gst_private.h | 2 +- dlls/winegstreamer/main.c | 17 +++++++++++++--- dlls/winegstreamer/media_source.c | 2 +- dlls/winegstreamer/unixlib.h | 3 +++ dlls/winegstreamer/wg_parser.c | 24 +++++++++++++++++++++- dlls/winegstreamer/wg_source.c | 33 +++++++++++++++++++++++++++++++ 6 files changed, 75 insertions(+), 6 deletions(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 6380b8fb7ff..11dfbf8e0b5 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -104,7 +104,7 @@ char *wg_parser_stream_get_tag(wg_parser_stream_t stream, enum wg_parser_tag tag void wg_parser_stream_seek(wg_parser_stream_t stream, double rate, uint64_t start_pos, uint64_t stop_pos, DWORD start_flags, DWORD stop_flags);
-HRESULT wg_source_create(wg_source_t *out); +HRESULT wg_source_create(const WCHAR *url, const void *data, uint32_t size, wg_source_t *out); void wg_source_destroy(wg_source_t source);
HRESULT wg_transform_create_mf(IMFMediaType *input_type, IMFMediaType *output_type, diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 92a44be0c96..04bff47bd5c 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -435,12 +435,22 @@ void wg_parser_stream_seek(wg_parser_stream_t stream, double rate, WINE_UNIX_CALL(unix_wg_parser_stream_seek, ¶ms); }
-HRESULT wg_source_create(wg_source_t *out) +HRESULT wg_source_create(const WCHAR *url, const void *data, uint32_t size, wg_source_t *out) { - struct wg_source_create_params params = {0}; + struct wg_source_create_params params = + { + .data = data, .size = size, + }; + char *tmp = NULL; NTSTATUS status; + UINT len; + + TRACE("url %s, data %p, size %#x\n", debugstr_w(url), data, size);
- TRACE("out %p\n", out); + if (url && (len = WideCharToMultiByte(CP_ACP, 0, url, -1, NULL, 0, NULL, NULL))) + tmp = malloc(len); + if ((params.url = tmp)) + WideCharToMultiByte(CP_ACP, 0, url, -1, tmp, len, NULL, NULL);
if ((status = WINE_UNIX_CALL(unix_wg_source_create, ¶ms))) WARN("wg_source_create returned status %#lx\n", status); @@ -450,6 +460,7 @@ HRESULT wg_source_create(wg_source_t *out) *out = params.source; }
+ free(tmp); return HRESULT_FROM_NT(status); }
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 61a93ffa4ba..ffc7d652934 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -2028,7 +2028,7 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM
if (FAILED(hr = IMFByteStream_EndRead(context->stream, result, &size))) WARN("Failed to complete stream read, hr %#lx\n", hr); - else if (FAILED(hr = wg_source_create(&context->wg_source))) + else if (FAILED(hr = wg_source_create(context->url, context->buffer, size, &context->wg_source))) WARN("Failed to create wg_source, hr %#lx\n", hr); else if (FAILED(hr = media_source_create(context, (IMFMediaSource **)&object))) WARN("Failed to create media source, hr %#lx\n", hr); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 0a98aadd1ab..9eab68e14da 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -332,6 +332,9 @@ struct wg_parser_stream_seek_params
struct wg_source_create_params { + const char *url; + const void *data; + UINT32 size; wg_source_t source; };
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 51753b1f429..60dca79ef16 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -2063,6 +2063,28 @@ static NTSTATUS wow64_wg_parser_stream_get_tag(void *args) return wg_parser_stream_get_tag(¶ms); }
+NTSTATUS wow64_wg_source_create(void *args) +{ + struct + { + PTR32 url; + PTR32 data; + UINT32 size; + wg_source_t source; + } *params32 = args; + struct wg_source_create_params params = + { + .url = ULongToPtr(params32->url), + .data = ULongToPtr(params32->data), + .size = params32->size, + }; + NTSTATUS ret; + + ret = wg_source_create(¶ms); + params32->source = params.source; + return ret; +} + NTSTATUS wow64_wg_transform_create(void *args) { struct @@ -2287,7 +2309,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X64(wg_parser_stream_get_tag), X(wg_parser_stream_seek),
- X(wg_source_create), + X64(wg_source_create), X(wg_source_destroy),
X64(wg_transform_create), diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 52a54196ea9..071b800b6d1 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -49,13 +49,42 @@ static struct wg_source *get_source(wg_source_t source) return (struct wg_source *)(ULONG_PTR)source; }
+static GstCaps *detect_caps_from_data(const char *url, const void *data, guint size) +{ + const char *extension = url ? strrchr(url, '.') : NULL; + GstTypeFindProbability probability; + GstCaps *caps; + + GST_LOG("url %s, data %p, size %#x", url, data, size); + + if (!(caps = gst_type_find_helper_for_data_with_extension(NULL, data, size, + extension ? extension + 1 : NULL, &probability))) + { + GST_ERROR("Failed to detect caps for url %s", url); + return NULL; + } + + if (probability > GST_TYPE_FIND_POSSIBLE) + GST_INFO("Got probability %u for caps %" GST_PTR_FORMAT, probability, caps); + else + GST_FIXME("Got probability %u for caps %" GST_PTR_FORMAT, probability, caps); + + return caps; +} + NTSTATUS wg_source_create(void *args) { struct wg_source_create_params *params = args; struct wg_source *source; + GstCaps *src_caps;
+ if (!(src_caps = detect_caps_from_data(params->url, params->data, params->size))) + return STATUS_UNSUCCESSFUL; if (!(source = calloc(1, sizeof(*source)))) + { + gst_caps_unref(src_caps); return STATUS_UNSUCCESSFUL; + }
if (!(source->container = gst_bin_new("wg_source"))) goto error; @@ -64,6 +93,8 @@ NTSTATUS wg_source_create(void *args) if (!gst_element_get_state(source->container, NULL, NULL, -1)) goto error;
+ gst_caps_unref(src_caps); + params->source = (wg_source_t)(ULONG_PTR)source; GST_INFO("Created winegstreamer source %p.", source); return STATUS_SUCCESS; @@ -76,6 +107,8 @@ error: } free(source);
+ gst_caps_unref(src_caps); + GST_ERROR("Failed to create winegstreamer source."); return STATUS_UNSUCCESSFUL; }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winegstreamer/media_source.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index ffc7d652934..7688631f7bf 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -101,11 +101,31 @@ static const IUnknownVtbl object_context_vtbl = object_context_Release, };
+static WCHAR *byte_stream_get_url(IMFByteStream *stream, const WCHAR *url) +{ + IMFAttributes *attributes; + WCHAR buffer[MAX_PATH]; + UINT32 size; + HRESULT hr; + + if (SUCCEEDED(hr = IMFByteStream_QueryInterface(stream, &IID_IMFAttributes, (void **)&attributes))) + { + if (FAILED(hr = IMFAttributes_GetString(attributes, &MF_BYTESTREAM_ORIGIN_NAME, + buffer, ARRAY_SIZE(buffer), &size))) + WARN("Failed to get MF_BYTESTREAM_ORIGIN_NAME got size %#x, hr %#lx\n", size, hr); + else + url = buffer; + IMFAttributes_Release(attributes); + } + + return url ? wcsdup(url) : NULL; +} + static HRESULT object_context_create(DWORD flags, IMFByteStream *stream, const WCHAR *url, QWORD file_size, IMFAsyncResult *result, struct object_context **out) { UINT buffer_size = file_size >= 128 * 1024 ? 32 * 1024 : min(file_size, 4 * 1024); - WCHAR *tmp_url = url ? wcsdup(url) : NULL; + WCHAR *tmp_url = byte_stream_get_url(stream, url); struct object_context *context;
if (!(context = calloc(1, sizeof(*context)))
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winegstreamer/wg_source.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 071b800b6d1..8bc67f2c6cb 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -41,6 +41,7 @@
struct wg_source { + GstPad *src_pad; GstElement *container; };
@@ -72,6 +73,24 @@ static GstCaps *detect_caps_from_data(const char *url, const void *data, guint s return caps; }
+static GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) +{ + GstCaps *pad_caps = caps ? gst_caps_ref(caps) : gst_caps_new_any(); + const char *name = direction == GST_PAD_SRC ? "src" : "sink"; + GstPadTemplate *template; + GstPad *pad; + + if (!pad_caps) + return NULL; + if ((template = gst_pad_template_new(name, direction, GST_PAD_ALWAYS, pad_caps))) + { + pad = gst_pad_new_from_template(template, "src"); + g_object_unref(template); + } + gst_caps_unref(pad_caps); + return pad; +} + NTSTATUS wg_source_create(void *args) { struct wg_source_create_params *params = args; @@ -88,6 +107,9 @@ NTSTATUS wg_source_create(void *args)
if (!(source->container = gst_bin_new("wg_source"))) goto error; + if (!(source->src_pad = create_pad_with_caps(GST_PAD_SRC, src_caps))) + goto error; + gst_pad_set_element_private(source->src_pad, source);
gst_element_set_state(source->container, GST_STATE_PAUSED); if (!gst_element_get_state(source->container, NULL, NULL, -1)) @@ -105,6 +127,8 @@ error: gst_element_set_state(source->container, GST_STATE_NULL); gst_object_unref(source->container); } + if (source->src_pad) + gst_object_unref(source->src_pad); free(source);
gst_caps_unref(src_caps); @@ -121,6 +145,7 @@ NTSTATUS wg_source_destroy(void *args)
gst_element_set_state(source->container, GST_STATE_NULL); gst_object_unref(source->container); + gst_object_unref(source->src_pad); free(source);
return STATUS_SUCCESS;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winegstreamer/wg_source.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 8bc67f2c6cb..3a902c6bce2 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -94,6 +94,7 @@ static GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) NTSTATUS wg_source_create(void *args) { struct wg_source_create_params *params = args; + GstElement *first = NULL, *last = NULL, *element; struct wg_source *source; GstCaps *src_caps;
@@ -111,6 +112,15 @@ NTSTATUS wg_source_create(void *args) goto error; gst_pad_set_element_private(source->src_pad, source);
+ if (!(element = find_element(GST_ELEMENT_FACTORY_TYPE_DECODABLE, src_caps, GST_CAPS_ANY)) + || !append_element(source->container, element, &first, &last)) + goto error; + + if (!link_src_to_element(source->src_pad, first)) + goto error; + if (!gst_pad_set_active(source->src_pad, true)) + goto error; + gst_element_set_state(source->container, GST_STATE_PAUSED); if (!gst_element_get_state(source->container, NULL, NULL, -1)) goto error;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/main.c | 15 ++++++++++ dlls/winegstreamer/media_source.c | 3 ++ dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 9 ++++++ dlls/winegstreamer/wg_parser.c | 22 ++++++++++++++ dlls/winegstreamer/wg_source.c | 48 +++++++++++++++++++++++++++++++ 7 files changed, 99 insertions(+)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 11dfbf8e0b5..527fdf47b07 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -106,6 +106,7 @@ void wg_parser_stream_seek(wg_parser_stream_t stream, double rate,
HRESULT wg_source_create(const WCHAR *url, const void *data, uint32_t size, wg_source_t *out); void wg_source_destroy(wg_source_t source); +HRESULT wg_source_push_data(wg_source_t source, UINT64 offset, const void *data, uint32_t size);
HRESULT wg_transform_create_mf(IMFMediaType *input_type, IMFMediaType *output_type, const struct wg_transform_attrs *attrs, wg_transform_t *transform); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 04bff47bd5c..7caf1fb0782 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -471,6 +471,21 @@ void wg_source_destroy(wg_source_t source) WINE_UNIX_CALL(unix_wg_source_destroy, &source); }
+HRESULT wg_source_push_data(wg_source_t source, UINT64 offset, const void *data, uint32_t size) +{ + struct wg_source_push_data_params params = + { + .source = source, + .offset = offset, + .data = data, + .size = size, + }; + + TRACE("source %#I64x, offset %#I64x, data %p, size %#x\n", source, offset, data, size); + + return HRESULT_FROM_NT(WINE_UNIX_CALL(unix_wg_source_push_data, ¶ms)); +} + HRESULT wg_transform_create_mf(IMFMediaType *input_type, IMFMediaType *output_type, const struct wg_transform_attrs *attrs, wg_transform_t *transform) { diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 7688631f7bf..0cb83437b61 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -38,6 +38,7 @@ struct object_context WCHAR *url;
wg_source_t wg_source; + UINT64 read_offset; UINT32 buffer_size; BYTE *buffer; }; @@ -2050,6 +2051,8 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM WARN("Failed to complete stream read, hr %#lx\n", hr); else if (FAILED(hr = wg_source_create(context->url, context->buffer, size, &context->wg_source))) WARN("Failed to create wg_source, hr %#lx\n", hr); + else if (FAILED(hr = wg_source_push_data(context->wg_source, context->read_offset, context->buffer, size))) + WARN("Failed to push wg_source data, hr %#lx\n", hr); else if (FAILED(hr = media_source_create(context, (IMFMediaSource **)&object))) WARN("Failed to create media source, hr %#lx\n", hr); else diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 52412a283ab..7ae74c13a8c 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -72,6 +72,7 @@ extern NTSTATUS wg_transform_notify_qos(void *args);
extern NTSTATUS wg_source_create(void *args); extern NTSTATUS wg_source_destroy(void *args); +extern NTSTATUS wg_source_push_data(void *args);
/* wg_media_type.c */
diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 9eab68e14da..a70ecf9cd25 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -338,6 +338,14 @@ struct wg_source_create_params wg_source_t source; };
+struct wg_source_push_data_params +{ + wg_source_t source; + UINT64 offset; + UINT64 size; + const void *data; +}; + struct wg_transform_attrs { UINT32 output_plane_align; @@ -455,6 +463,7 @@ enum unix_funcs
unix_wg_source_create, unix_wg_source_destroy, + unix_wg_source_push_data,
unix_wg_transform_create, unix_wg_transform_destroy, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 60dca79ef16..d2292087544 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1889,6 +1889,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
X(wg_source_create), X(wg_source_destroy), + X(wg_source_push_data),
X(wg_transform_create), X(wg_transform_destroy), @@ -2085,6 +2086,26 @@ NTSTATUS wow64_wg_source_create(void *args) return ret; }
+NTSTATUS wow64_wg_source_push_data(void *args) +{ + struct + { + wg_source_t source; + UINT64 offset; + UINT64 size; + PTR32 data; + } *params32 = args; + struct wg_source_push_data_params params = + { + .source = params32->source, + .offset = params32->offset, + .size = params32->size, + .data = ULongToPtr(params32->data), + }; + + return wg_source_push_data(¶ms); +} + NTSTATUS wow64_wg_transform_create(void *args) { struct @@ -2311,6 +2332,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
X64(wg_source_create), X(wg_source_destroy), + X64(wg_source_push_data),
X64(wg_transform_create), X(wg_transform_destroy), diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 3a902c6bce2..c43cd4a34e2 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -43,6 +43,7 @@ struct wg_source { GstPad *src_pad; GstElement *container; + GstSegment segment; };
static struct wg_source *get_source(wg_source_t source) @@ -73,6 +74,21 @@ static GstCaps *detect_caps_from_data(const char *url, const void *data, guint s return caps; }
+static GstEvent *create_stream_start_event(const char *stream_id) +{ + GstEvent *event; + + if ((event = gst_event_new_stream_start(stream_id))) + { + GstStream *stream = gst_stream_new(stream_id, NULL, GST_STREAM_TYPE_UNKNOWN, 0); + gst_event_set_stream(event, stream); + gst_event_set_group_id(event, 1); + gst_object_unref(stream); + } + + return event; +} + static GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) { GstCaps *pad_caps = caps ? gst_caps_ref(caps) : gst_caps_new_any(); @@ -105,6 +121,7 @@ NTSTATUS wg_source_create(void *args) gst_caps_unref(src_caps); return STATUS_UNSUCCESSFUL; } + gst_segment_init(&source->segment, GST_FORMAT_BYTES);
if (!(source->container = gst_bin_new("wg_source"))) goto error; @@ -125,6 +142,11 @@ NTSTATUS wg_source_create(void *args) if (!gst_element_get_state(source->container, NULL, NULL, -1)) goto error;
+ if (!push_event(source->src_pad, create_stream_start_event("wg_source"))) + goto error; + if (!push_event(source->src_pad, gst_event_new_segment(&source->segment))) + goto error; + gst_caps_unref(src_caps);
params->source = (wg_source_t)(ULONG_PTR)source; @@ -160,3 +182,29 @@ NTSTATUS wg_source_destroy(void *args)
return STATUS_SUCCESS; } + +NTSTATUS wg_source_push_data(void *args) +{ + struct wg_source_push_data_params *params = args; + struct wg_source *source = get_source(params->source); + + GST_TRACE("source %p, offset %#" G_GINT64_MODIFIER "x, size %#" G_GINT64_MODIFIER "x, data %p", + source, params->offset, params->size, params->data); + + if (!params->size) + { + push_event(source->src_pad, gst_event_new_eos()); + return STATUS_SUCCESS; + } + + if (params->offset > source->segment.start) + source->segment.start = params->offset; + else if (params->offset < source->segment.start) + { + source->segment.start = params->offset; + push_event(source->src_pad, gst_event_new_segment(&source->segment)); + } + + source->segment.start += params->size; + return STATUS_SUCCESS; +}
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winegstreamer/wg_source.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)
diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index c43cd4a34e2..a977701d3c1 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -41,6 +41,7 @@
struct wg_source { + gchar *url; GstPad *src_pad; GstElement *container; GstSegment segment; @@ -107,6 +108,34 @@ static GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) return pad; }
+static gboolean src_query_uri(struct wg_source *source, GstQuery *query) +{ + gchar *uri; + + GST_LOG("source %p, query %" GST_PTR_FORMAT, source, query); + + gst_query_parse_uri(query, &uri); + gst_query_set_uri(query, source->url); + + return true; +} + +static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct wg_source *source = gst_pad_get_element_private(pad); + + switch (GST_QUERY_TYPE(query)) + { + case GST_QUERY_URI: + if (!source->url) + return false; + return src_query_uri(source, query); + default: + GST_TRACE("source %p, ignoring %" GST_PTR_FORMAT, source, query); + return gst_pad_query_default(pad, parent, query); + } +} + NTSTATUS wg_source_create(void *args) { struct wg_source_create_params *params = args; @@ -121,6 +150,7 @@ NTSTATUS wg_source_create(void *args) gst_caps_unref(src_caps); return STATUS_UNSUCCESSFUL; } + source->url = params->url ? strdup(params->url) : NULL; gst_segment_init(&source->segment, GST_FORMAT_BYTES);
if (!(source->container = gst_bin_new("wg_source"))) @@ -128,6 +158,7 @@ NTSTATUS wg_source_create(void *args) if (!(source->src_pad = create_pad_with_caps(GST_PAD_SRC, src_caps))) goto error; gst_pad_set_element_private(source->src_pad, source); + gst_pad_set_query_function(source->src_pad, src_query_cb);
if (!(element = find_element(GST_ELEMENT_FACTORY_TYPE_DECODABLE, src_caps, GST_CAPS_ANY)) || !append_element(source->container, element, &first, &last)) @@ -161,6 +192,7 @@ error: } if (source->src_pad) gst_object_unref(source->src_pad); + free(source->url); free(source);
gst_caps_unref(src_caps); @@ -178,6 +210,7 @@ NTSTATUS wg_source_destroy(void *args) gst_element_set_state(source->container, GST_STATE_NULL); gst_object_unref(source->container); gst_object_unref(source->src_pad); + free(source->url); free(source);
return STATUS_SUCCESS;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winegstreamer/gst_private.h | 3 ++- dlls/winegstreamer/main.c | 7 +++++-- dlls/winegstreamer/media_source.c | 3 ++- dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_source.c | 17 +++++++++++++++++ 5 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 527fdf47b07..cf46768122f 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -104,7 +104,8 @@ char *wg_parser_stream_get_tag(wg_parser_stream_t stream, enum wg_parser_tag tag void wg_parser_stream_seek(wg_parser_stream_t stream, double rate, uint64_t start_pos, uint64_t stop_pos, DWORD start_flags, DWORD stop_flags);
-HRESULT wg_source_create(const WCHAR *url, const void *data, uint32_t size, wg_source_t *out); +HRESULT wg_source_create(const WCHAR *url, uint64_t file_size, + const void *data, uint32_t size, wg_source_t *out); void wg_source_destroy(wg_source_t source); HRESULT wg_source_push_data(wg_source_t source, UINT64 offset, const void *data, uint32_t size);
diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 7caf1fb0782..038ddf3e045 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -435,17 +435,20 @@ void wg_parser_stream_seek(wg_parser_stream_t stream, double rate, WINE_UNIX_CALL(unix_wg_parser_stream_seek, ¶ms); }
-HRESULT wg_source_create(const WCHAR *url, const void *data, uint32_t size, wg_source_t *out) +HRESULT wg_source_create(const WCHAR *url, uint64_t file_size, + const void *data, uint32_t size, wg_source_t *out) { struct wg_source_create_params params = { + .file_size = file_size, .data = data, .size = size, }; char *tmp = NULL; NTSTATUS status; UINT len;
- TRACE("url %s, data %p, size %#x\n", debugstr_w(url), data, size); + TRACE("url %s, file_size %#I64x, data %p, size %#x\n", debugstr_w(url), + file_size, data, size);
if (url && (len = WideCharToMultiByte(CP_ACP, 0, url, -1, NULL, 0, NULL, NULL))) tmp = malloc(len); diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 0cb83437b61..3393ff33dd5 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -2049,7 +2049,8 @@ static HRESULT WINAPI stream_handler_callback_Invoke(IMFAsyncCallback *iface, IM
if (FAILED(hr = IMFByteStream_EndRead(context->stream, result, &size))) WARN("Failed to complete stream read, hr %#lx\n", hr); - else if (FAILED(hr = wg_source_create(context->url, context->buffer, size, &context->wg_source))) + else if (FAILED(hr = wg_source_create(context->url, context->file_size, + context->buffer, size, &context->wg_source))) WARN("Failed to create wg_source, hr %#lx\n", hr); else if (FAILED(hr = wg_source_push_data(context->wg_source, context->read_offset, context->buffer, size))) WARN("Failed to push wg_source data, hr %#lx\n", hr); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index a70ecf9cd25..f7ed2fafd54 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -333,6 +333,7 @@ struct wg_parser_stream_seek_params struct wg_source_create_params { const char *url; + UINT64 file_size; const void *data; UINT32 size; wg_source_t source; diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index a977701d3c1..c9a0f127eac 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -108,6 +108,20 @@ static GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) return pad; }
+static gboolean src_query_duration(struct wg_source *source, GstQuery *query) +{ + GstFormat format; + + GST_LOG("source %p, query %" GST_PTR_FORMAT, source, query); + + gst_query_parse_duration(query, &format, NULL); + if (format != GST_FORMAT_BYTES) + return false; + + gst_query_set_duration(query, format, source->segment.stop); + return true; +} + static gboolean src_query_uri(struct wg_source *source, GstQuery *query) { gchar *uri; @@ -126,6 +140,8 @@ static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query)
switch (GST_QUERY_TYPE(query)) { + case GST_QUERY_DURATION: + return src_query_duration(source, query); case GST_QUERY_URI: if (!source->url) return false; @@ -152,6 +168,7 @@ NTSTATUS wg_source_create(void *args) } source->url = params->url ? strdup(params->url) : NULL; gst_segment_init(&source->segment, GST_FORMAT_BYTES); + source->segment.stop = params->file_size;
if (!(source->container = gst_bin_new("wg_source"))) goto error;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winegstreamer/wg_source.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index c9a0f127eac..40ad9f9ac25 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -122,6 +122,16 @@ static gboolean src_query_duration(struct wg_source *source, GstQuery *query) return true; }
+static gboolean src_query_scheduling(struct wg_source *source, GstQuery *query) +{ + GST_LOG("source %p, query %" GST_PTR_FORMAT, source, query); + + gst_query_set_scheduling(query, GST_SCHEDULING_FLAG_SEEKABLE, 1, -1, 0); + gst_query_add_scheduling_mode(query, GST_PAD_MODE_PUSH); + + return true; +} + static gboolean src_query_uri(struct wg_source *source, GstQuery *query) { gchar *uri; @@ -142,6 +152,8 @@ static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) { case GST_QUERY_DURATION: return src_query_duration(source, query); + case GST_QUERY_SCHEDULING: + return src_query_scheduling(source, query); case GST_QUERY_URI: if (!source->url) return false;