The patch set is ready now.
-- v9: winegstreamer: Implement finalize for media sink. winestreamer: Implement wg_muxer_finalize. winegstreamer: Introduce media_sink_write_stream. winestreamer: Implement wg_muxer_{get,copy,free}_buffer. winegstreamer: Implement ProcessSample for media sink. winegstreamer: Implement wg_muxer_push_sample. winegstreamer: Implement wg_muxer_add_stream. winegstreamer: Create wg_muxer for media sink. winegstreamer: Implement seeking query for wg_muxer sink pad. winegstreamer: Introduce new wg_muxer struct.
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 3 + dlls/winegstreamer/main.c | 26 ++++++ dlls/winegstreamer/unix_private.h | 6 ++ dlls/winegstreamer/unixlib.c | 29 +++--- dlls/winegstreamer/unixlib.h | 33 +++++++ dlls/winegstreamer/wg_format.c | 39 +++++++++ dlls/winegstreamer/wg_muxer.c | 141 ++++++++++++++++++++++++++++++ dlls/winegstreamer/wg_parser.c | 25 +++++- 9 files changed, 291 insertions(+), 12 deletions(-) create mode 100644 dlls/winegstreamer/wg_muxer.c
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 78bdd6c0ef1..f963dcea7f0 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -22,6 +22,7 @@ C_SRCS = \ video_processor.c \ wg_allocator.c \ wg_format.c \ + wg_muxer.c \ wg_parser.c \ wg_sample.c \ wg_transform.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index ed867f741d9..0c814148447 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -110,6 +110,9 @@ bool wg_transform_get_status(wg_transform_t transform, bool *accepts_input); HRESULT wg_transform_drain(wg_transform_t transform); HRESULT wg_transform_flush(wg_transform_t transform);
+HRESULT wg_muxer_create(struct wg_container_format *format, wg_muxer_t *muxer); +void wg_muxer_destroy(wg_muxer_t muxer); + unsigned int wg_format_get_max_size(const struct wg_format *format);
HRESULT avi_splitter_create(IUnknown *outer, IUnknown **out); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index c78d73537f7..e1ddf1e52f7 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -456,6 +456,32 @@ HRESULT wg_transform_flush(wg_transform_t transform) return S_OK; }
+HRESULT wg_muxer_create(struct wg_container_format *format, wg_muxer_t *muxer) +{ + struct wg_muxer_create_params params = + { + .format = format, + }; + NTSTATUS status; + + TRACE("format %p, muxer %p.\n", format, muxer); + + if (SUCCEEDED(status = WINE_UNIX_CALL(unix_wg_muxer_create, ¶ms))) + { + *muxer = params.muxer; + TRACE("Created wg_muxer %#I64x.\n", params.muxer); + } + + return status; +} + +void wg_muxer_destroy(wg_muxer_t muxer) +{ + TRACE("muxer %#I64x.\n", muxer); + + WINE_UNIX_CALL(unix_wg_muxer_destroy, &muxer); +} + #define ALIGN(n, alignment) (((n) + (alignment) - 1) & ~((alignment) - 1))
unsigned int wg_format_get_stride(const struct wg_format *format) diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 8bef7b2b2bd..90a31f53f38 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -46,6 +46,7 @@ extern bool push_event(GstPad *pad, GstEvent *event) DECLSPEC_HIDDEN; extern void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) DECLSPEC_HIDDEN; extern bool wg_format_compare(const struct wg_format *a, const struct wg_format *b) DECLSPEC_HIDDEN; extern GstCaps *wg_format_to_caps(const struct wg_format *format) DECLSPEC_HIDDEN; +extern GstCaps *wg_container_format_to_caps(const struct wg_container_format *format) DECLSPEC_HIDDEN;
/* wg_transform.c */
@@ -58,6 +59,11 @@ extern NTSTATUS wg_transform_get_status(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_drain(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_flush(void *args) DECLSPEC_HIDDEN;
+/* wg_muxer.c */ + +extern NTSTATUS wg_muxer_create(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_muxer_destroy(void *args) DECLSPEC_HIDDEN; + /* wg_allocator.c */
static inline BYTE *wg_sample_data(struct wg_sample *sample) diff --git a/dlls/winegstreamer/unixlib.c b/dlls/winegstreamer/unixlib.c index 7e5ef34c4d7..eca2bb8aacc 100644 --- a/dlls/winegstreamer/unixlib.c +++ b/dlls/winegstreamer/unixlib.c @@ -87,15 +87,21 @@ GstElement *find_element(GstElementFactoryListType type, GstCaps *src_caps, GstC if (!(transforms = gst_element_factory_list_get_elements(type, GST_RANK_MARGINAL))) goto done;
- tmp = gst_element_factory_list_filter(transforms, src_caps, GST_PAD_SINK, FALSE); - gst_plugin_feature_list_free(transforms); - if (!(transforms = tmp)) - goto done; + if (src_caps) + { + tmp = gst_element_factory_list_filter(transforms, src_caps, GST_PAD_SINK, FALSE); + gst_plugin_feature_list_free(transforms); + if (!(transforms = tmp)) + goto done; + }
- tmp = gst_element_factory_list_filter(transforms, sink_caps, GST_PAD_SRC, FALSE); - gst_plugin_feature_list_free(transforms); - if (!(transforms = tmp)) - goto done; + if (sink_caps) + { + tmp = gst_element_factory_list_filter(transforms, sink_caps, GST_PAD_SRC, FALSE); + gst_plugin_feature_list_free(transforms); + if (!(transforms = tmp)) + goto done; + }
transforms = g_list_sort(transforms, gst_plugin_feature_rank_compare_func); for (tmp = transforms; tmp != NULL && element == NULL; tmp = tmp->next) @@ -139,16 +145,17 @@ bool append_element(GstElement *container, GstElement *element, GstElement **fir
if (!gst_bin_add(GST_BIN(container), element) || !gst_element_sync_state_with_parent(element) || - (*last && !gst_element_link(*last, element))) + (last && *last && !gst_element_link(*last, element))) { GST_ERROR("Failed to link %s element.", name); } else { GST_DEBUG("Linked %s element %p.", name, element); - if (!*first) + if (first && !*first) *first = element; - *last = element; + if (last) + *last = element; success = true; }
diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 15e0605fdde..6fa5fb99039 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -166,6 +166,29 @@ struct wg_format } u; };
+typedef UINT32 wg_container_type; +enum wg_container_type +{ + WG_CONTAINER_TYPE_UNKNOWN = 0, + WG_CONTAINER_TYPE_ID3, + WG_CONTAINER_TYPE_QUICKTIME, +}; + +typedef UINT32 wg_quicktime_variant; +enum wg_quicktime_variant +{ + WG_QUICKTIME_VARIANT_UNKNOWN = 0, + WG_QUICKTIME_VARIANT_APPLE, + WG_QUICKTIME_VARIANT_ISO, + WG_QUICKTIME_VARIANT_3GPP, +}; + +struct wg_container_format +{ + wg_container_type type; + wg_quicktime_variant quicktime_variant; +}; + enum wg_sample_flag { WG_SAMPLE_FLAG_INCOMPLETE = 1, @@ -209,6 +232,7 @@ enum wg_parser_type typedef UINT64 wg_parser_t; typedef UINT64 wg_parser_stream_t; typedef UINT64 wg_transform_t; +typedef UINT64 wg_muxer_t;
struct wg_parser_create_params { @@ -365,6 +389,12 @@ struct wg_transform_get_status_params UINT32 accepts_input; };
+struct wg_muxer_create_params +{ + wg_muxer_t muxer; + const struct wg_container_format *format; +}; + enum unix_funcs { unix_wg_init_gstreamer, @@ -404,6 +434,9 @@ enum unix_funcs unix_wg_transform_get_status, unix_wg_transform_drain, unix_wg_transform_flush, + + unix_wg_muxer_create, + unix_wg_muxer_destroy, };
#endif /* __WINE_WINEGSTREAMER_UNIXLIB_H */ diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index 2353839bbc4..029c6cf8b22 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -767,3 +767,42 @@ bool wg_format_compare(const struct wg_format *a, const struct wg_format *b) assert(0); return false; } + +GstCaps *wg_container_format_to_caps(const struct wg_container_format *format) +{ + GstCaps *caps = NULL; + + switch (format->type) + { + case WG_CONTAINER_TYPE_ID3: + caps = gst_caps_new_empty_simple("application/x-id3"); + break; + + case WG_CONTAINER_TYPE_QUICKTIME: + caps = gst_caps_new_empty_simple("video/quicktime"); + switch(format->quicktime_variant) + { + case WG_QUICKTIME_VARIANT_APPLE: + gst_caps_set_simple(caps, "variant", G_TYPE_STRING, "apple", NULL); + break; + case WG_QUICKTIME_VARIANT_ISO: + gst_caps_set_simple(caps, "variant", G_TYPE_STRING, "iso", NULL); + break; + case WG_QUICKTIME_VARIANT_3GPP: + gst_caps_set_simple(caps, "variant", G_TYPE_STRING, "3gpp", NULL); + break; + case WG_QUICKTIME_VARIANT_UNKNOWN: + default: + GST_WARNING("Quicktime variant %u not implemented.\n", format->type); + break; + } + break; + + case WG_CONTAINER_TYPE_UNKNOWN: + default: + GST_WARNING("Container type %u not implemented.\n", format->type); + break; + } + + return caps; +} diff --git a/dlls/winegstreamer/wg_muxer.c b/dlls/winegstreamer/wg_muxer.c new file mode 100644 index 00000000000..06045a3e6a3 --- /dev/null +++ b/dlls/winegstreamer/wg_muxer.c @@ -0,0 +1,141 @@ +/* + * GStreamer muxer backend + * + * Copyright 2023 Ziqing Hui 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 "ntstatus.h" +#define WIN32_NO_STATUS +#include "winternl.h" + +#include "unix_private.h" + +struct wg_muxer +{ + GstElement *container, *muxer; + + GstPad *my_sink; + GstAtomicQueue *output_queue; + GstMapInfo map_info; + GstBuffer *buffer; + + pthread_mutex_t mutex; + pthread_cond_t cond; +}; + +static struct wg_muxer *get_muxer(wg_muxer_t muxer) +{ + return (struct wg_muxer *)(ULONG_PTR)muxer; +} + +static void muxer_free_buffer(struct wg_muxer *muxer) +{ + gst_buffer_unmap(muxer->buffer, &muxer->map_info); + gst_buffer_unref(muxer->buffer); + muxer->buffer = NULL; +} + +static void muxer_destroy(struct wg_muxer *muxer) +{ + pthread_cond_destroy(&muxer->cond); + pthread_mutex_destroy(&muxer->mutex); + if (muxer->buffer) + muxer_free_buffer(muxer); + if (muxer->output_queue) + gst_atomic_queue_unref(muxer->output_queue); + if (muxer->my_sink) + gst_object_unref(muxer->my_sink); + if (muxer->container) + { + gst_element_set_state(muxer->container, GST_STATE_NULL); + gst_object_unref(muxer->container); + } + free(muxer); +} + +NTSTATUS wg_muxer_create(void *args) +{ + struct wg_muxer_create_params *params = args; + GstPadTemplate *template = NULL; + GstCaps *sink_caps = NULL; + NTSTATUS status = E_FAIL; + struct wg_muxer *muxer; + + /* Create wg_muxer object. */ + if (!(muxer = calloc(1, sizeof(*muxer)))) + return E_OUTOFMEMORY; + pthread_mutex_init(&muxer->mutex, NULL); + pthread_cond_init(&muxer->cond, NULL); + if (!(muxer->container = gst_bin_new("wg_muxer"))) + goto out; + if (!(muxer->output_queue = gst_atomic_queue_new(8))) + goto out; + + /* Create sink pad. */ + if (!(sink_caps = wg_container_format_to_caps(params->format))) + goto out; + if (!(template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, sink_caps))) + goto out; + muxer->my_sink = gst_pad_new_from_template(template, "wg_muxer_sink"); + if (!muxer->my_sink) + goto out; + gst_pad_set_element_private(muxer->my_sink, muxer); + + /* Create gstreamer muxer element. */ + if (!(muxer->muxer = find_element(GST_ELEMENT_FACTORY_TYPE_MUXER | GST_ELEMENT_FACTORY_TYPE_FORMATTER, + NULL, sink_caps))) + goto out; + if (!append_element(muxer->container, muxer->muxer, NULL, NULL)) + goto out; + + /* Link muxer to sink pad. */ + if (!link_element_to_sink(muxer->muxer, muxer->my_sink)) + goto out; + if (!gst_pad_set_active(muxer->my_sink, 1)) + goto out; + + /* Set to pause state. */ + gst_element_set_state(muxer->container, GST_STATE_PAUSED); + if (!gst_element_get_state(muxer->container, NULL, NULL, -1)) + goto out; + + gst_caps_unref(sink_caps); + + GST_INFO("Created winegstreamer muxer %p.", muxer); + params->muxer = (wg_transform_t)(ULONG_PTR)muxer; + + return S_OK; + +out: + if (sink_caps) + gst_caps_unref(sink_caps); + if (template) + gst_object_unref(template); + muxer_destroy(muxer); + return status; +} + +NTSTATUS wg_muxer_destroy(void *args) +{ + struct wg_muxer *muxer = get_muxer(*(wg_muxer_t *)args); + muxer_destroy(muxer); + return S_OK; +} diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 5556b52829c..016691d448d 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1945,6 +1945,9 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_transform_get_status), X(wg_transform_drain), X(wg_transform_flush), + + X(wg_muxer_create), + X(wg_muxer_destroy), };
#ifdef _WIN64 @@ -2052,7 +2055,6 @@ static NTSTATUS wow64_wg_parser_stream_copy_buffer(void *args) return wg_parser_stream_copy_buffer(¶ms); }
- static NTSTATUS wow64_wg_parser_stream_get_tag(void *args) { struct @@ -2152,6 +2154,24 @@ NTSTATUS wow64_wg_transform_read_data(void *args) return ret; }
+NTSTATUS wow64_wg_muxer_create(void *args) +{ + struct + { + wg_muxer_t muxer; + PTR32 format; + } *params32 = args; + struct wg_muxer_create_params params = + { + .format = ULongToPtr(params32->format), + }; + NTSTATUS ret; + + ret = wg_muxer_create(¶ms); + params32->muxer = params.muxer; + return ret; +} + const unixlib_entry_t __wine_unix_call_wow64_funcs[] = { #define X64(name) [unix_ ## name] = wow64_ ## name @@ -2192,6 +2212,9 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X(wg_transform_get_status), X(wg_transform_drain), X(wg_transform_flush), + + X64(wg_muxer_create), + X(wg_muxer_destroy), };
#endif /* _WIN64 */
From: Ziqing Hui zhui@codeweavers.com
This makes muxer element know that it can perform seeking. --- dlls/winegstreamer/wg_muxer.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/dlls/winegstreamer/wg_muxer.c b/dlls/winegstreamer/wg_muxer.c index 06045a3e6a3..67fa849cb10 100644 --- a/dlls/winegstreamer/wg_muxer.c +++ b/dlls/winegstreamer/wg_muxer.c @@ -71,6 +71,24 @@ static void muxer_destroy(struct wg_muxer *muxer) free(muxer); }
+static gboolean muxer_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct wg_muxer *muxer = gst_pad_get_element_private(pad); + + GST_DEBUG("pad %p, parent %p, query %p, muxer %p, type "%s".", + pad, parent, query, muxer, gst_query_type_get_name(query->type)); + + switch (query->type) + { + case GST_QUERY_SEEKING: + gst_query_set_seeking(query, GST_FORMAT_BYTES, true, 0, -1); + return true; + default: + GST_WARNING("Ignoring "%s" query.", gst_query_type_get_name(query->type)); + return gst_pad_query_default(pad, parent, query); + } +} + NTSTATUS wg_muxer_create(void *args) { struct wg_muxer_create_params *params = args; @@ -98,6 +116,7 @@ NTSTATUS wg_muxer_create(void *args) if (!muxer->my_sink) goto out; gst_pad_set_element_private(muxer->my_sink, muxer); + gst_pad_set_query_function(muxer->my_sink, muxer_sink_query_cb);
/* Create gstreamer muxer element. */ if (!(muxer->muxer = find_element(GST_ELEMENT_FACTORY_TYPE_MUXER | GST_ELEMENT_FACTORY_TYPE_FORMATTER,
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/gst_private.h | 3 +- dlls/winegstreamer/main.c | 7 ++-- dlls/winegstreamer/media_sink.c | 72 ++++++++++++++++++++++++++------ 3 files changed, 65 insertions(+), 17 deletions(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 0c814148447..243dab16c72 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -125,7 +125,8 @@ HRESULT wma_decoder_create(IUnknown *outer, IUnknown **out); HRESULT wmv_decoder_create(IUnknown *outer, IUnknown **out); HRESULT resampler_create(IUnknown *outer, IUnknown **out); HRESULT color_convert_create(IUnknown *outer, IUnknown **out); -HRESULT sink_class_factory_create(IUnknown *outer, IUnknown **out); +HRESULT mp3_sink_class_factory_create(IUnknown *outer, IUnknown **out); +HRESULT mpeg4_sink_class_factory_create(IUnknown *outer, IUnknown **out);
bool amt_from_wg_format(AM_MEDIA_TYPE *mt, const struct wg_format *format, bool wm); bool amt_to_wg_format(const AM_MEDIA_TYPE *mt, struct wg_format *format); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index e1ddf1e52f7..e4c93076eea 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -638,7 +638,8 @@ static struct class_factory wma_decoder_cf = {{&class_factory_vtbl}, wma_decoder static struct class_factory wmv_decoder_cf = {{&class_factory_vtbl}, wmv_decoder_create}; static struct class_factory resampler_cf = {{&class_factory_vtbl}, resampler_create}; static struct class_factory color_convert_cf = {{&class_factory_vtbl}, color_convert_create}; -static struct class_factory sink_class_factory_cf = {{&class_factory_vtbl}, sink_class_factory_create}; +static struct class_factory mp3_sink_class_factory_cf = {{&class_factory_vtbl}, mp3_sink_class_factory_create}; +static struct class_factory mpeg4_sink_class_factory_cf = {{&class_factory_vtbl}, mpeg4_sink_class_factory_create};
HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **out) { @@ -674,9 +675,9 @@ HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **out) else if (IsEqualGUID(clsid, &CLSID_CColorConvertDMO)) factory = &color_convert_cf; else if (IsEqualGUID(clsid, &CLSID_MFMP3SinkClassFactory)) - factory = &sink_class_factory_cf; + factory = &mp3_sink_class_factory_cf; else if (IsEqualGUID(clsid, &CLSID_MFMPEG4SinkClassFactory)) - factory = &sink_class_factory_cf; + factory = &mpeg4_sink_class_factory_cf; else { FIXME("%s not implemented, returning CLASS_E_CLASSNOTAVAILABLE.\n", debugstr_guid(clsid)); diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index fcea67eb8f1..392ffb17bf9 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -72,11 +72,14 @@ struct media_sink STATE_PAUSED, STATE_SHUTDOWN, } state; + struct wg_container_format format;
IMFByteStream *bytestream; IMFMediaEventQueue *event_queue;
struct list stream_sinks; + + wg_muxer_t muxer; };
static struct stream_sink *impl_from_IMFStreamSink(IMFStreamSink *iface) @@ -567,6 +570,7 @@ static ULONG WINAPI media_sink_Release(IMFFinalizableMediaSink *iface) IMFByteStream_Release(media_sink->bytestream); media_sink->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&media_sink->cs); + wg_muxer_destroy(media_sink->muxer); free(media_sink); }
@@ -1022,7 +1026,7 @@ static const IMFAsyncCallbackVtbl media_sink_callback_vtbl = media_sink_callback_Invoke, };
-static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink **out) +static HRESULT media_sink_create(IMFByteStream *bytestream, struct wg_container_format *format, struct media_sink **out) { struct media_sink *media_sink; HRESULT hr; @@ -1035,11 +1039,10 @@ static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink ** if (!(media_sink = calloc(1, sizeof(*media_sink)))) return E_OUTOFMEMORY;
+ if (FAILED(hr = wg_muxer_create(format, &media_sink->muxer))) + goto fail; if (FAILED(hr = MFCreateEventQueue(&media_sink->event_queue))) - { - free(media_sink); - return hr; - } + goto fail;
media_sink->IMFFinalizableMediaSink_iface.lpVtbl = &media_sink_vtbl; media_sink->IMFMediaEventGenerator_iface.lpVtbl = &media_sink_event_vtbl; @@ -1047,6 +1050,7 @@ static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink ** media_sink->async_callback.lpVtbl = &media_sink_callback_vtbl; media_sink->refcount = 1; media_sink->state = STATE_OPENED; + media_sink->format = *format; InitializeCriticalSection(&media_sink->cs); media_sink->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); IMFByteStream_AddRef((media_sink->bytestream = bytestream)); @@ -1056,6 +1060,14 @@ static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink ** TRACE("Created media sink %p.\n", media_sink);
return S_OK; + +fail: + if (media_sink->event_queue) + IMFMediaEventQueue_Release(media_sink->event_queue); + if (media_sink->muxer) + wg_muxer_destroy(media_sink->muxer); + free(media_sink); + return hr; }
static HRESULT WINAPI sink_class_factory_QueryInterface(IMFSinkClassFactory *iface, REFIID riid, void **out) @@ -1082,8 +1094,8 @@ static ULONG WINAPI sink_class_factory_Release(IMFSinkClassFactory *iface) return 1; }
-static HRESULT WINAPI sink_class_factory_CreateMediaSink(IMFSinkClassFactory *iface, IMFByteStream *bytestream, - IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **out) +static HRESULT WINAPI sink_class_factory_create_media_sink(IMFSinkClassFactory *iface, IMFByteStream *bytestream, + struct wg_container_format *format, IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **out) { IMFFinalizableMediaSink *media_sink_iface; struct media_sink *media_sink; @@ -1092,7 +1104,7 @@ static HRESULT WINAPI sink_class_factory_CreateMediaSink(IMFSinkClassFactory *if TRACE("iface %p, bytestream %p, video_type %p, audio_type %p, out %p.\n", iface, bytestream, video_type, audio_type, out);
- if (FAILED(hr = media_sink_create(bytestream, &media_sink))) + if (FAILED(hr = media_sink_create(bytestream, format, &media_sink))) return hr; media_sink_iface = &media_sink->IMFFinalizableMediaSink_iface;
@@ -1119,18 +1131,52 @@ static HRESULT WINAPI sink_class_factory_CreateMediaSink(IMFSinkClassFactory *if return S_OK; }
-static const IMFSinkClassFactoryVtbl sink_class_factory_vtbl = +static HRESULT WINAPI mp3_sink_class_factory_CreateMediaSink(IMFSinkClassFactory *iface, IMFByteStream *bytestream, + IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **out) +{ + struct wg_container_format format; + + format.type = WG_CONTAINER_TYPE_ID3; + return sink_class_factory_create_media_sink(iface, bytestream, &format, video_type, audio_type, out); +} + +static HRESULT WINAPI mpeg4_sink_class_factory_CreateMediaSink(IMFSinkClassFactory *iface, IMFByteStream *bytestream, + IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **out) +{ + struct wg_container_format format; + + format.type = WG_CONTAINER_TYPE_QUICKTIME; + format.quicktime_variant = WG_QUICKTIME_VARIANT_ISO; + return sink_class_factory_create_media_sink(iface, bytestream, &format, video_type, audio_type, out); +} + +static const IMFSinkClassFactoryVtbl mp3_sink_class_factory_vtbl = +{ + sink_class_factory_QueryInterface, + sink_class_factory_AddRef, + sink_class_factory_Release, + mp3_sink_class_factory_CreateMediaSink, +}; + +static const IMFSinkClassFactoryVtbl mpeg4_sink_class_factory_vtbl = { sink_class_factory_QueryInterface, sink_class_factory_AddRef, sink_class_factory_Release, - sink_class_factory_CreateMediaSink, + mpeg4_sink_class_factory_CreateMediaSink, };
-static IMFSinkClassFactory sink_class_factory = { &sink_class_factory_vtbl }; +static IMFSinkClassFactory mp3_sink_class_factory = { &mp3_sink_class_factory_vtbl }; +static IMFSinkClassFactory mpeg4_sink_class_factory = { &mpeg4_sink_class_factory_vtbl }; + +HRESULT mp3_sink_class_factory_create(IUnknown *outer, IUnknown **out) +{ + *out = (IUnknown *)&mp3_sink_class_factory; + return S_OK; +}
-HRESULT sink_class_factory_create(IUnknown *outer, IUnknown **out) +HRESULT mpeg4_sink_class_factory_create(IUnknown *outer, IUnknown **out) { - *out = (IUnknown *)&sink_class_factory; + *out = (IUnknown *)&mpeg4_sink_class_factory; return S_OK; }
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/Makefile.in | 2 +- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/main.c | 14 +++ dlls/winegstreamer/media_sink.c | 9 ++ dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 8 ++ dlls/winegstreamer/wg_muxer.c | 143 ++++++++++++++++++++++++++++++ dlls/winegstreamer/wg_parser.c | 19 ++++ 8 files changed, 196 insertions(+), 1 deletion(-)
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index f963dcea7f0..5b8e9fcab2c 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -3,7 +3,7 @@ UNIXLIB = winegstreamer.so IMPORTLIB = winegstreamer IMPORTS = strmbase ole32 oleaut32 msdmo DELAYIMPORTS = mfplat mf -UNIX_CFLAGS = $(GSTREAMER_CFLAGS) +UNIX_CFLAGS = $(GSTREAMER_CFLAGS) -Wno-deprecated-declarations UNIX_LIBS = $(GSTREAMER_LIBS) $(PTHREAD_LIBS)
C_SRCS = \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 243dab16c72..36f0030f6c4 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -112,6 +112,7 @@ HRESULT wg_transform_flush(wg_transform_t transform);
HRESULT wg_muxer_create(struct wg_container_format *format, wg_muxer_t *muxer); void wg_muxer_destroy(wg_muxer_t muxer); +HRESULT wg_muxer_add_stream(wg_muxer_t muxer, UINT32 stream_id, const struct wg_format *format);
unsigned int wg_format_get_max_size(const struct wg_format *format);
diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index e4c93076eea..4e8ae64db96 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -482,6 +482,20 @@ void wg_muxer_destroy(wg_muxer_t muxer) WINE_UNIX_CALL(unix_wg_muxer_destroy, &muxer); }
+HRESULT wg_muxer_add_stream(wg_muxer_t muxer, UINT32 stream_id, const struct wg_format *format) +{ + struct wg_muxer_add_stream_params params = + { + .muxer = muxer, + .stream_id = stream_id, + .format = format, + }; + + TRACE("muxer %#I64x, stream_id %u, format %p.\n", muxer, stream_id, format); + + return WINE_UNIX_CALL(unix_wg_muxer_add_stream, ¶ms); +} + #define ALIGN(n, alignment) (((n) + (alignment) - 1) & ~((alignment) - 1))
unsigned int wg_format_get_stride(const struct wg_format *format) diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index 392ffb17bf9..7b772a7dd13 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -589,6 +589,7 @@ static HRESULT WINAPI media_sink_AddStreamSink(IMFFinalizableMediaSink *iface, D { struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); struct stream_sink *object; + struct wg_format format; HRESULT hr;
TRACE("iface %p, stream_sink_id %#lx, media_type %p, stream_sink %p.\n", @@ -609,6 +610,14 @@ static HRESULT WINAPI media_sink_AddStreamSink(IMFFinalizableMediaSink *iface, D return hr; }
+ mf_media_type_to_wg_format(media_type, &format); + if (FAILED(hr = wg_muxer_add_stream(media_sink->muxer, stream_sink_id, &format))) + { + LeaveCriticalSection(&media_sink->cs); + IMFStreamSink_Release(&object->IMFStreamSink_iface); + return hr; + } + list_add_tail(&media_sink->stream_sinks, &object->entry);
LeaveCriticalSection(&media_sink->cs); diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 90a31f53f38..6078d73de93 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -63,6 +63,7 @@ extern NTSTATUS wg_transform_flush(void *args) DECLSPEC_HIDDEN;
extern NTSTATUS wg_muxer_create(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_muxer_destroy(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_muxer_add_stream(void *args) DECLSPEC_HIDDEN;
/* wg_allocator.c */
diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 6fa5fb99039..71a6112decb 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -395,6 +395,13 @@ struct wg_muxer_create_params const struct wg_container_format *format; };
+struct wg_muxer_add_stream_params +{ + wg_muxer_t muxer; + UINT32 stream_id; + const struct wg_format *format; +}; + enum unix_funcs { unix_wg_init_gstreamer, @@ -437,6 +444,7 @@ enum unix_funcs
unix_wg_muxer_create, unix_wg_muxer_destroy, + unix_wg_muxer_add_stream, };
#endif /* __WINE_WINEGSTREAMER_UNIXLIB_H */ diff --git a/dlls/winegstreamer/wg_muxer.c b/dlls/winegstreamer/wg_muxer.c index 67fa849cb10..280154efa3f 100644 --- a/dlls/winegstreamer/wg_muxer.c +++ b/dlls/winegstreamer/wg_muxer.c @@ -22,12 +22,16 @@ #pragma makedep unix #endif
+#include <stdio.h> + #include "ntstatus.h" #define WIN32_NO_STATUS #include "winternl.h"
#include "unix_private.h"
+#include "wine/list.h" + struct wg_muxer { GstElement *container, *muxer; @@ -39,6 +43,20 @@ struct wg_muxer
pthread_mutex_t mutex; pthread_cond_t cond; + + struct list streams; +}; + +struct wg_muxer_stream +{ + struct wg_muxer *muxer; + struct wg_format format; + uint32_t id; + + GstPad *my_src; + GstSegment segment; + + struct list entry; };
static struct wg_muxer *get_muxer(wg_muxer_t muxer) @@ -55,6 +73,15 @@ static void muxer_free_buffer(struct wg_muxer *muxer)
static void muxer_destroy(struct wg_muxer *muxer) { + struct wg_muxer_stream *stream, *next; + + LIST_FOR_EACH_ENTRY_SAFE(stream, next, &muxer->streams, struct wg_muxer_stream, entry) + { + list_remove(&stream->entry); + if (stream->my_src) + gst_object_unref(stream->my_src); + free(stream); + } pthread_cond_destroy(&muxer->cond); pthread_mutex_destroy(&muxer->mutex); if (muxer->buffer) @@ -71,6 +98,23 @@ static void muxer_destroy(struct wg_muxer *muxer) free(muxer); }
+static gboolean muxer_src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct wg_muxer_stream *stream = gst_pad_get_element_private(pad); + + GST_DEBUG("pad %p, parent %p, query %p, stream %u, type "%s".", + pad, parent, query, stream->id, gst_query_type_get_name(query->type)); + + switch (query->type) + { + case GST_QUERY_LATENCY: + gst_query_set_latency(query, true, 0, 0); + return true; + default: + return gst_pad_query_default(pad, parent, query); + } +} + static gboolean muxer_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) { struct wg_muxer *muxer = gst_pad_get_element_private(pad); @@ -100,6 +144,7 @@ NTSTATUS wg_muxer_create(void *args) /* Create wg_muxer object. */ if (!(muxer = calloc(1, sizeof(*muxer)))) return E_OUTOFMEMORY; + list_init(&muxer->streams); pthread_mutex_init(&muxer->mutex, NULL); pthread_cond_init(&muxer->cond, NULL); if (!(muxer->container = gst_bin_new("wg_muxer"))) @@ -158,3 +203,101 @@ NTSTATUS wg_muxer_destroy(void *args) muxer_destroy(muxer); return S_OK; } + +NTSTATUS wg_muxer_add_stream(void *args) +{ + struct wg_muxer_add_stream_params *params = args; + struct wg_muxer *muxer = get_muxer(params->muxer); + GstPadTemplate *template = NULL; + const char *muxer_sink_pad_name; + struct wg_muxer_stream *stream; + GstPad *muxer_sink = NULL; + NTSTATUS status = E_FAIL; + GstCaps *src_caps = NULL; + char src_pad_name[32]; + int ret; + + /* Create stream object. */ + if (!(stream = calloc(1, sizeof(*stream)))) + return E_OUTOFMEMORY; + stream->muxer = muxer; + stream->format = *params->format; + stream->id = params->stream_id; + + /* Create src pad. */ + if (!(src_caps = wg_format_to_caps(params->format))) + goto out; + if (params->format->major_type == WG_MAJOR_TYPE_VIDEO_H264) + gst_caps_set_simple(src_caps, "stream-format", G_TYPE_STRING, "avc", NULL); + if (!(template = gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_ALWAYS, src_caps))) + goto out; + sprintf(src_pad_name, "wg_muxer_src_%u", stream->id); + stream->my_src = gst_pad_new_from_template(template, src_pad_name); + gst_object_unref(template); + if (!stream->my_src) + goto out; + gst_pad_set_element_private(stream->my_src, stream); + gst_pad_set_query_function(stream->my_src, muxer_src_query_cb); + + /* Request muxer sink pad. */ + switch (stream->format.major_type) + { + case WG_MAJOR_TYPE_VIDEO: + case WG_MAJOR_TYPE_VIDEO_CINEPAK: + case WG_MAJOR_TYPE_VIDEO_H264: + case WG_MAJOR_TYPE_VIDEO_WMV: + case WG_MAJOR_TYPE_VIDEO_INDEO: + muxer_sink_pad_name = "video_%u"; + break; + case WG_MAJOR_TYPE_AUDIO: + case WG_MAJOR_TYPE_AUDIO_MPEG1: + case WG_MAJOR_TYPE_AUDIO_MPEG4: + case WG_MAJOR_TYPE_AUDIO_WMA: + muxer_sink_pad_name = "audio_%u"; + break; + default: + goto out; + } + if (!(muxer_sink = gst_element_get_request_pad(muxer->muxer, muxer_sink_pad_name))) + goto out; + + /* Link src pad to muxer sink pad. */ + if ((ret = gst_pad_link(stream->my_src, muxer_sink)) < 0) + { + GST_ERROR("Failed to link %s to muxer, ret %d.\n", src_pad_name, ret); + goto out; + } + if (!gst_pad_set_active(stream->my_src, 1)) + goto out; + + /* Push events to prepare for streaming. */ + if (!push_event(stream->my_src, gst_event_new_stream_start(src_pad_name))) + goto out; + if (!push_event(stream->my_src, gst_event_new_caps(src_caps))) + goto out; + gst_segment_init(&stream->segment, GST_FORMAT_TIME); + if (!push_event(stream->my_src, gst_event_new_segment(&stream->segment))) + goto out; + + list_add_tail(&muxer->streams, &stream->entry); + + gst_object_unref(muxer_sink); + gst_caps_unref(src_caps); + GST_INFO("Created winegstreamer muxer stream %p.", stream); + + return S_OK; + +out: + if (muxer_sink) + { + gst_element_release_request_pad(muxer->muxer, muxer_sink); + gst_object_unref(muxer_sink); + } + if (stream->my_src) + gst_object_unref(stream->my_src); + if (src_caps) + gst_caps_unref(src_caps); + free(stream); + + return status; +} diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 016691d448d..1880b052680 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1948,6 +1948,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
X(wg_muxer_create), X(wg_muxer_destroy), + X(wg_muxer_add_stream), };
#ifdef _WIN64 @@ -2172,6 +2173,23 @@ NTSTATUS wow64_wg_muxer_create(void *args) return ret; }
+NTSTATUS wow64_wg_muxer_add_stream(void *args) +{ + struct + { + wg_muxer_t muxer; + DWORD stream_id; + PTR32 format; + } *params32 = args; + struct wg_muxer_add_stream_params params = + { + .muxer = params32->muxer, + .stream_id = params32->stream_id, + .format = ULongToPtr(params32->format), + }; + return wg_muxer_add_stream(¶ms); +} + const unixlib_entry_t __wine_unix_call_wow64_funcs[] = { #define X64(name) [unix_ ## name] = wow64_ ## name @@ -2215,6 +2233,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
X64(wg_muxer_create), X(wg_muxer_destroy), + X64(wg_muxer_add_stream), };
#endif /* _WIN64 */
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/main.c | 13 +++ dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 8 ++ dlls/winegstreamer/wg_muxer.c | 129 ++++++++++++++++++++++++++++++ dlls/winegstreamer/wg_parser.c | 17 ++++ 6 files changed, 169 insertions(+)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 36f0030f6c4..5c69a9ca316 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -113,6 +113,7 @@ HRESULT wg_transform_flush(wg_transform_t transform); HRESULT wg_muxer_create(struct wg_container_format *format, wg_muxer_t *muxer); void wg_muxer_destroy(wg_muxer_t muxer); HRESULT wg_muxer_add_stream(wg_muxer_t muxer, UINT32 stream_id, const struct wg_format *format); +HRESULT wg_muxer_push_sample(wg_muxer_t muxer, struct wg_sample *sample);
unsigned int wg_format_get_max_size(const struct wg_format *format);
diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 4e8ae64db96..6d2dd5b6026 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -496,6 +496,19 @@ HRESULT wg_muxer_add_stream(wg_muxer_t muxer, UINT32 stream_id, const struct wg_ return WINE_UNIX_CALL(unix_wg_muxer_add_stream, ¶ms); }
+HRESULT wg_muxer_push_sample(wg_muxer_t muxer, struct wg_sample *sample) +{ + struct wg_muxer_push_sample_params params = + { + .muxer = muxer, + .sample = sample, + }; + + TRACE("muxer %#I64x, sample %p.\n", muxer, sample); + + return WINE_UNIX_CALL(unix_wg_muxer_push_sample, ¶ms); +} + #define ALIGN(n, alignment) (((n) + (alignment) - 1) & ~((alignment) - 1))
unsigned int wg_format_get_stride(const struct wg_format *format) diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 6078d73de93..eeafeffb78a 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -64,6 +64,7 @@ extern NTSTATUS wg_transform_flush(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_muxer_create(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_muxer_destroy(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_muxer_add_stream(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_muxer_push_sample(void *args) DECLSPEC_HIDDEN;
/* wg_allocator.c */
diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 71a6112decb..8292c72ba45 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -208,6 +208,7 @@ struct wg_sample UINT32 max_size; UINT32 size; UINT64 data; /* pointer to user memory */ + UINT32 stream_id; };
struct wg_parser_buffer @@ -402,6 +403,12 @@ struct wg_muxer_add_stream_params const struct wg_format *format; };
+struct wg_muxer_push_sample_params +{ + wg_muxer_t muxer; + struct wg_sample *sample; +}; + enum unix_funcs { unix_wg_init_gstreamer, @@ -445,6 +452,7 @@ enum unix_funcs unix_wg_muxer_create, unix_wg_muxer_destroy, unix_wg_muxer_add_stream, + unix_wg_muxer_push_sample, };
#endif /* __WINE_WINEGSTREAMER_UNIXLIB_H */ diff --git a/dlls/winegstreamer/wg_muxer.c b/dlls/winegstreamer/wg_muxer.c index 280154efa3f..9e02eef4d96 100644 --- a/dlls/winegstreamer/wg_muxer.c +++ b/dlls/winegstreamer/wg_muxer.c @@ -27,6 +27,7 @@ #include "ntstatus.h" #define WIN32_NO_STATUS #include "winternl.h" +#include "mferror.h"
#include "unix_private.h"
@@ -43,6 +44,7 @@ struct wg_muxer
pthread_mutex_t mutex; pthread_cond_t cond; + guint64 offset;
struct list streams; }; @@ -64,6 +66,13 @@ static struct wg_muxer *get_muxer(wg_muxer_t muxer) return (struct wg_muxer *)(ULONG_PTR)muxer; }
+static void sample_free_notify(void *arg) +{ + struct wg_sample *sample = arg; + GST_DEBUG("Releasing wg_sample %p", sample); + InterlockedDecrement(&sample->refcount); +} + static void muxer_free_buffer(struct wg_muxer *muxer) { gst_buffer_unmap(muxer->buffer, &muxer->map_info); @@ -98,6 +107,19 @@ static void muxer_destroy(struct wg_muxer *muxer) free(muxer); }
+static struct wg_muxer_stream *muxer_get_stream_by_id(struct wg_muxer *muxer, DWORD id) +{ + struct wg_muxer_stream *stream; + + LIST_FOR_EACH_ENTRY(stream, &muxer->streams, struct wg_muxer_stream, entry) + { + if (stream->id == id) + return stream; + } + + return NULL; +} + static gboolean muxer_src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) { struct wg_muxer_stream *stream = gst_pad_get_element_private(pad); @@ -133,6 +155,66 @@ static gboolean muxer_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery *qu } }
+static gboolean muxer_sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) +{ + struct wg_muxer *muxer = gst_pad_get_element_private(pad); + const GstSegment *segment; + + GST_DEBUG("pad %p, parent %p, event %p, muxer %p, type "%s".", + pad, parent, event, muxer, GST_EVENT_TYPE_NAME(event)); + + switch (event->type) + { + case GST_EVENT_SEGMENT: + pthread_mutex_lock(&muxer->mutex); + + gst_event_parse_segment(event, &segment); + if (segment->format != GST_FORMAT_BYTES) + { + pthread_mutex_unlock(&muxer->mutex); + GST_FIXME("Unhandled segment format "%s".", gst_format_get_name(segment->format)); + break; + } + muxer->offset = segment->start; + + pthread_mutex_unlock(&muxer->mutex); + break; + + default: + GST_WARNING("Ignoring "%s" event.", GST_EVENT_TYPE_NAME(event)); + break; + } + + gst_event_unref(event); + return TRUE; +} + +static GstFlowReturn muxer_sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *buffer) +{ + struct wg_muxer *muxer = gst_pad_get_element_private(pad); + + GST_DEBUG("pad %p, parent %p, buffer %p, muxer %p.", pad, parent, buffer, muxer); + + pthread_mutex_lock(&muxer->mutex); + + GST_BUFFER_OFFSET(buffer) = GST_BUFFER_OFFSET_NONE; + if (muxer->offset != GST_BUFFER_OFFSET_NONE) + { + GST_BUFFER_OFFSET(buffer) = muxer->offset; + muxer->offset = GST_BUFFER_OFFSET_NONE; + } + gst_atomic_queue_push(muxer->output_queue, buffer); + + GST_LOG("Pushed buffer %p to output queue %p, buffer size %" G_GSIZE_FORMAT ", " + "offset %" G_GUINT64_FORMAT ", %u buffers in queue now.", + buffer, muxer->output_queue, gst_buffer_get_size(buffer), GST_BUFFER_OFFSET(buffer), + gst_atomic_queue_length(muxer->output_queue)); + + pthread_mutex_unlock(&muxer->mutex); + + return GST_FLOW_OK; +} + NTSTATUS wg_muxer_create(void *args) { struct wg_muxer_create_params *params = args; @@ -145,6 +227,7 @@ NTSTATUS wg_muxer_create(void *args) if (!(muxer = calloc(1, sizeof(*muxer)))) return E_OUTOFMEMORY; list_init(&muxer->streams); + muxer->offset = GST_BUFFER_OFFSET_NONE; pthread_mutex_init(&muxer->mutex, NULL); pthread_cond_init(&muxer->cond, NULL); if (!(muxer->container = gst_bin_new("wg_muxer"))) @@ -162,6 +245,8 @@ NTSTATUS wg_muxer_create(void *args) goto out; gst_pad_set_element_private(muxer->my_sink, muxer); gst_pad_set_query_function(muxer->my_sink, muxer_sink_query_cb); + gst_pad_set_event_function(muxer->my_sink, muxer_sink_event_cb); + gst_pad_set_chain_function(muxer->my_sink, muxer_sink_chain_cb);
/* Create gstreamer muxer element. */ if (!(muxer->muxer = find_element(GST_ELEMENT_FACTORY_TYPE_MUXER | GST_ELEMENT_FACTORY_TYPE_FORMATTER, @@ -301,3 +386,47 @@ out:
return status; } + +NTSTATUS wg_muxer_push_sample(void *args) +{ + struct wg_muxer_push_sample_params *params = args; + struct wg_muxer *muxer = get_muxer(params->muxer); + struct wg_sample *sample = params->sample; + struct wg_muxer_stream *stream; + GstFlowReturn ret; + GstBuffer *buffer; + + if (!(stream = muxer_get_stream_by_id(muxer, sample->stream_id))) + return MF_E_INVALIDSTREAMNUMBER; + + /* Create sample data buffer by wrapping wg_sample. */ + if (!(buffer = gst_buffer_new_wrapped_full(GST_MEMORY_FLAG_READONLY, wg_sample_data(sample), sample->max_size, + 0, sample->size, sample, sample_free_notify))) + { + GST_ERROR("Failed to allocate input buffer."); + return E_OUTOFMEMORY; + } + + InterlockedIncrement(&sample->refcount); + GST_INFO("Wrapped %u/%u bytes from sample %p to buffer %p.", sample->size, sample->max_size, sample, buffer); + + /* Set sample properties. */ + if (sample->flags & WG_SAMPLE_FLAG_HAS_PTS) + GST_BUFFER_PTS(buffer) = sample->pts * 100; + if (sample->flags & WG_SAMPLE_FLAG_HAS_DURATION) + GST_BUFFER_DURATION(buffer) = sample->duration * 100; + if (!(sample->flags & WG_SAMPLE_FLAG_SYNC_POINT)) + GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT); + if (sample->flags & WG_SAMPLE_FLAG_DISCONTINUITY) + GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_FLAG_DISCONT); + + /* Push sample data buffer to stream src pad. */ + if ((ret = gst_pad_push(stream->my_src, buffer)) < 0) + { + GST_ERROR("Failed to push buffer %p to pad %s, reason %s.", + buffer, gst_pad_get_name(stream->my_src), gst_flow_get_name(ret)); + return E_FAIL; + } + + return S_OK; +} diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 1880b052680..30b30b405b2 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1949,6 +1949,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_muxer_create), X(wg_muxer_destroy), X(wg_muxer_add_stream), + X(wg_muxer_push_sample), };
#ifdef _WIN64 @@ -2190,6 +2191,21 @@ NTSTATUS wow64_wg_muxer_add_stream(void *args) return wg_muxer_add_stream(¶ms); }
+NTSTATUS wow64_wg_muxer_push_sample(void *args) +{ + struct + { + wg_muxer_t muxer; + PTR32 sample; + } *params32 = args; + struct wg_muxer_push_sample_params params = + { + .muxer = params32->muxer, + .sample = ULongToPtr(params32->sample), + }; + return wg_muxer_push_sample(¶ms); +} + const unixlib_entry_t __wine_unix_call_wow64_funcs[] = { #define X64(name) [unix_ ## name] = wow64_ ## name @@ -2234,6 +2250,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X64(wg_muxer_create), X(wg_muxer_destroy), X64(wg_muxer_add_stream), + X64(wg_muxer_push_sample), };
#endif /* _WIN64 */
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/media_sink.c | 85 ++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index 7b772a7dd13..73679123bf3 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -31,6 +31,7 @@ enum async_op ASYNC_START, ASYNC_STOP, ASYNC_PAUSE, + ASYNC_PROCESS, };
struct async_command @@ -39,6 +40,15 @@ struct async_command LONG refcount;
enum async_op op; + + union + { + struct + { + IMFSample *sample; + UINT32 stream_id; + } process; + } u; };
struct stream_sink @@ -143,7 +153,11 @@ static ULONG WINAPI async_command_Release(IUnknown *iface) ULONG refcount = InterlockedDecrement(&command->refcount);
if (!refcount) + { + if (command->op == ASYNC_PROCESS && command->u.process.sample) + IMFSample_Release(command->u.process.sample); free(command); + }
return refcount; } @@ -302,9 +316,40 @@ static HRESULT WINAPI stream_sink_GetMediaTypeHandler(IMFStreamSink *iface, IMFM
static HRESULT WINAPI stream_sink_ProcessSample(IMFStreamSink *iface, IMFSample *sample) { - FIXME("iface %p, sample %p stub!\n", iface, sample); + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(stream_sink->media_sink); + struct async_command *command; + HRESULT hr;
- return E_NOTIMPL; + TRACE("iface %p, sample %p.\n", iface, sample); + + EnterCriticalSection(&media_sink->cs); + + if (media_sink->state == STATE_SHUTDOWN) + { + LeaveCriticalSection(&media_sink->cs); + return MF_E_SHUTDOWN; + } + + if (media_sink->state != STATE_STARTED && media_sink->state != STATE_PAUSED) + { + LeaveCriticalSection(&media_sink->cs); + return MF_E_INVALIDREQUEST; + } + + if (FAILED(hr = (async_command_create(ASYNC_PROCESS, &command)))) + { + LeaveCriticalSection(&media_sink->cs); + return hr; + } + IMFSample_AddRef((command->u.process.sample = sample)); + command->u.process.stream_id = stream_sink->id; + + hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &media_sink->async_callback, &command->IUnknown_iface); + + LeaveCriticalSection(&media_sink->cs); + + return hr; }
static HRESULT WINAPI stream_sink_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type, @@ -516,6 +561,38 @@ static HRESULT media_sink_pause(struct media_sink *media_sink) return media_sink_queue_stream_event(media_sink, MEStreamSinkPaused); }
+static HRESULT media_sink_process(struct media_sink *media_sink, IMFSample *sample, UINT32 stream_id) +{ + wg_muxer_t muxer = media_sink->muxer; + struct wg_sample *wg_sample; + LONGLONG time, duration; + UINT32 value; + HRESULT hr; + + TRACE("media_sink %p, sample %p, stream_id %u.\n", media_sink, sample, stream_id); + + if (FAILED(hr = wg_sample_create_mf(sample, &wg_sample))) + return hr; + wg_sample->stream_id = stream_id; + + if (SUCCEEDED(IMFSample_GetSampleTime(sample, &time))) + { + wg_sample->flags |= WG_SAMPLE_FLAG_HAS_PTS; + wg_sample->pts = time; + } + if (SUCCEEDED(IMFSample_GetSampleDuration(sample, &duration))) + { + wg_sample->flags |= WG_SAMPLE_FLAG_HAS_DURATION; + wg_sample->duration = duration; + } + if (SUCCEEDED(IMFSample_GetUINT32(sample, &MFSampleExtension_CleanPoint, &value)) && value) + wg_sample->flags |= WG_SAMPLE_FLAG_SYNC_POINT; + if (SUCCEEDED(IMFSample_GetUINT32(sample, &MFSampleExtension_Discontinuity, &value)) && value) + wg_sample->flags |= WG_SAMPLE_FLAG_DISCONTINUITY; + + return wg_muxer_push_sample(muxer, wg_sample); +} + static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, REFIID riid, void **obj) { struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); @@ -1016,6 +1093,10 @@ static HRESULT WINAPI media_sink_callback_Invoke(IMFAsyncCallback *iface, IMFAsy case ASYNC_PAUSE: hr = media_sink_pause(media_sink); break; + case ASYNC_PROCESS: + if (FAILED(hr = media_sink_process(media_sink, command->u.process.sample, command->u.process.stream_id))) + WARN("Failed to process sample, hr %#lx.\n", hr); + break; default: WARN("Unsupported op %u.\n", command->op); break;
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/gst_private.h | 3 ++ dlls/winegstreamer/main.c | 42 +++++++++++++++++++++++++ dlls/winegstreamer/unix_private.h | 3 ++ dlls/winegstreamer/unixlib.h | 17 ++++++++++ dlls/winegstreamer/wg_muxer.c | 52 +++++++++++++++++++++++++++++++ dlls/winegstreamer/wg_parser.c | 23 ++++++++++++++ 6 files changed, 140 insertions(+)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 5c69a9ca316..614675ce99d 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -114,6 +114,9 @@ HRESULT wg_muxer_create(struct wg_container_format *format, wg_muxer_t *muxer); void wg_muxer_destroy(wg_muxer_t muxer); HRESULT wg_muxer_add_stream(wg_muxer_t muxer, UINT32 stream_id, const struct wg_format *format); HRESULT wg_muxer_push_sample(wg_muxer_t muxer, struct wg_sample *sample); +HRESULT wg_muxer_get_buffer(wg_muxer_t muxer, uint32_t *size, uint64_t *offset); +HRESULT wg_muxer_copy_buffer(wg_muxer_t muxer, void *buffer, UINT32 size); +void wg_muxer_free_buffer(wg_muxer_t muxer);
unsigned int wg_format_get_max_size(const struct wg_format *format);
diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 6d2dd5b6026..afc237bcb61 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -509,6 +509,48 @@ HRESULT wg_muxer_push_sample(wg_muxer_t muxer, struct wg_sample *sample) return WINE_UNIX_CALL(unix_wg_muxer_push_sample, ¶ms); }
+HRESULT wg_muxer_get_buffer(wg_muxer_t muxer, uint32_t *size, uint64_t *offset) +{ + struct wg_muxer_get_buffer_params params = + { + .muxer = muxer, + .size = 0, + .offset = UINT64_MAX, + }; + NTSTATUS status; + + TRACE("muxer %#I64x, size %p.\n", muxer, size); + + if (SUCCEEDED(status = WINE_UNIX_CALL(unix_wg_muxer_get_buffer, ¶ms))) + { + *size = params.size; + *offset = params.offset; + } + + return status; +} + +HRESULT wg_muxer_copy_buffer(wg_muxer_t muxer, void *buffer, uint32_t size) +{ + struct wg_muxer_copy_buffer_params params = + { + .muxer = muxer, + .buffer = buffer, + .size = size, + }; + + TRACE("muxer %#I64x, buffer %p, size %u.\n", muxer, buffer, size); + + return WINE_UNIX_CALL(unix_wg_muxer_copy_buffer, ¶ms); +} + +void wg_muxer_free_buffer(wg_muxer_t muxer) +{ + TRACE("muxer %#I64x.\n", muxer); + + WINE_UNIX_CALL(unix_wg_muxer_free_buffer, &muxer); +} + #define ALIGN(n, alignment) (((n) + (alignment) - 1) & ~((alignment) - 1))
unsigned int wg_format_get_stride(const struct wg_format *format) diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index eeafeffb78a..0aad76322b3 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -65,6 +65,9 @@ extern NTSTATUS wg_muxer_create(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_muxer_destroy(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_muxer_add_stream(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_muxer_push_sample(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_muxer_get_buffer(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_muxer_copy_buffer(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_muxer_free_buffer(void *args) DECLSPEC_HIDDEN;
/* wg_allocator.c */
diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 8292c72ba45..f9d0ba5a0d6 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -409,6 +409,20 @@ struct wg_muxer_push_sample_params struct wg_sample *sample; };
+struct wg_muxer_get_buffer_params +{ + wg_muxer_t muxer; + UINT32 size; + UINT64 offset; +}; + +struct wg_muxer_copy_buffer_params +{ + wg_muxer_t muxer; + void *buffer; + UINT32 size; +}; + enum unix_funcs { unix_wg_init_gstreamer, @@ -453,6 +467,9 @@ enum unix_funcs unix_wg_muxer_destroy, unix_wg_muxer_add_stream, unix_wg_muxer_push_sample, + unix_wg_muxer_get_buffer, + unix_wg_muxer_copy_buffer, + unix_wg_muxer_free_buffer, };
#endif /* __WINE_WINEGSTREAMER_UNIXLIB_H */ diff --git a/dlls/winegstreamer/wg_muxer.c b/dlls/winegstreamer/wg_muxer.c index 9e02eef4d96..cea3790776c 100644 --- a/dlls/winegstreamer/wg_muxer.c +++ b/dlls/winegstreamer/wg_muxer.c @@ -430,3 +430,55 @@ NTSTATUS wg_muxer_push_sample(void *args)
return S_OK; } + +NTSTATUS wg_muxer_get_buffer(void *args) +{ + struct wg_muxer_get_buffer_params *params = args; + struct wg_muxer *muxer = get_muxer(params->muxer); + + if (!muxer->buffer) + { + if (!(muxer->buffer = gst_atomic_queue_pop(muxer->output_queue))) + return MF_E_SINK_NO_SAMPLES_PROCESSED; + + if (!gst_buffer_map(muxer->buffer, &muxer->map_info, GST_MAP_READ)) + { + GST_ERROR("Failed to map buffer %p, dropped.", muxer->buffer); + gst_buffer_unref(muxer->buffer); + muxer->buffer = NULL; + return E_FAIL; + } + } + + params->size = muxer->map_info.size; + if (GST_BUFFER_OFFSET_IS_VALID(muxer->buffer)) + params->offset = GST_BUFFER_OFFSET(muxer->buffer); + + return S_OK; +} + +NTSTATUS wg_muxer_copy_buffer(void *args) +{ + struct wg_muxer_copy_buffer_params *params = args; + struct wg_muxer *muxer = get_muxer(params->muxer); + + if (!muxer->buffer) + return MF_E_SINK_NO_SAMPLES_PROCESSED; + + if (params->size < muxer->map_info.size) + return MF_E_BUFFERTOOSMALL; + + memcpy(params->buffer, muxer->map_info.data, muxer->map_info.size); + + return S_OK; +} + +NTSTATUS wg_muxer_free_buffer(void *args) +{ + struct wg_muxer *muxer = get_muxer(*(wg_muxer_t *)args); + + if (muxer->buffer) + muxer_free_buffer(muxer); + + return S_OK; +} diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 30b30b405b2..ce082161efa 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1950,6 +1950,9 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_muxer_destroy), X(wg_muxer_add_stream), X(wg_muxer_push_sample), + X(wg_muxer_get_buffer), + X(wg_muxer_copy_buffer), + X(wg_muxer_free_buffer), };
#ifdef _WIN64 @@ -2206,6 +2209,23 @@ NTSTATUS wow64_wg_muxer_push_sample(void *args) return wg_muxer_push_sample(¶ms); }
+NTSTATUS wow64_wg_muxer_copy_buffer(void *args) +{ + struct + { + wg_muxer_t muxer; + PTR32 buffer; + UINT32 size; + } *params32 = args; + struct wg_muxer_copy_buffer_params params = + { + .muxer = params32->muxer, + .buffer = ULongToPtr(params32->buffer), + .size = params32->size, + }; + return wg_muxer_copy_buffer(¶ms); +} + const unixlib_entry_t __wine_unix_call_wow64_funcs[] = { #define X64(name) [unix_ ## name] = wow64_ ## name @@ -2251,6 +2271,9 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X(wg_muxer_destroy), X64(wg_muxer_add_stream), X64(wg_muxer_push_sample), + X(wg_muxer_get_buffer), + X64(wg_muxer_copy_buffer), + X(wg_muxer_free_buffer), };
#endif /* _WIN64 */
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/media_sink.c | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index 73679123bf3..eab81ff0914 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -542,6 +542,40 @@ static HRESULT media_sink_queue_stream_event(struct media_sink *media_sink, Medi return S_OK; }
+static HRESULT media_sink_write_stream(struct media_sink *media_sink) +{ + BYTE *data = NULL; + ULONG written; + QWORD offset; + UINT32 size; + HRESULT hr; + + while (SUCCEEDED(hr = wg_muxer_get_buffer(media_sink->muxer, &size, &offset))) + { + if (!(data = calloc(1, size))) + return E_OUTOFMEMORY; + + if (FAILED(hr = wg_muxer_copy_buffer(media_sink->muxer, data, size))) + { + free(data); + WARN("Failed to copy buffer, hr %#lx.", hr); + return hr; + } + + if ((offset != UINT64_MAX && FAILED(hr = IMFByteStream_SetCurrentPosition(media_sink->bytestream, offset))) + || FAILED(hr = IMFByteStream_Write(media_sink->bytestream, data, size, &written))) + { + free(data); + return hr; + } + + free(data); + wg_muxer_free_buffer(media_sink->muxer); + } + + return S_OK; +} + static HRESULT media_sink_start(struct media_sink *media_sink) { media_sink->state = STATE_STARTED; @@ -571,6 +605,9 @@ static HRESULT media_sink_process(struct media_sink *media_sink, IMFSample *samp
TRACE("media_sink %p, sample %p, stream_id %u.\n", media_sink, sample, stream_id);
+ if (FAILED(hr = media_sink_write_stream(media_sink))) + WARN("Failed to write output samples to stream, hr %#lx.\n", hr); + if (FAILED(hr = wg_sample_create_mf(sample, &wg_sample))) return hr; wg_sample->stream_id = stream_id;
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/main.c | 7 +++++++ dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_muxer.c | 30 ++++++++++++++++++++++++++++++ dlls/winegstreamer/wg_parser.c | 2 ++ 6 files changed, 42 insertions(+)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 614675ce99d..66deb3702ec 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -117,6 +117,7 @@ HRESULT wg_muxer_push_sample(wg_muxer_t muxer, struct wg_sample *sample); HRESULT wg_muxer_get_buffer(wg_muxer_t muxer, uint32_t *size, uint64_t *offset); HRESULT wg_muxer_copy_buffer(wg_muxer_t muxer, void *buffer, UINT32 size); void wg_muxer_free_buffer(wg_muxer_t muxer); +HRESULT wg_muxer_finalize(wg_muxer_t muxer);
unsigned int wg_format_get_max_size(const struct wg_format *format);
diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index afc237bcb61..d5b208d3cf3 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -551,6 +551,13 @@ void wg_muxer_free_buffer(wg_muxer_t muxer) WINE_UNIX_CALL(unix_wg_muxer_free_buffer, &muxer); }
+HRESULT wg_muxer_finalize(wg_muxer_t muxer) +{ + TRACE("muxer %#I64x.\n", muxer); + + return WINE_UNIX_CALL(unix_wg_muxer_finalize, &muxer); +} + #define ALIGN(n, alignment) (((n) + (alignment) - 1) & ~((alignment) - 1))
unsigned int wg_format_get_stride(const struct wg_format *format) diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 0aad76322b3..e011655a58f 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -68,6 +68,7 @@ extern NTSTATUS wg_muxer_push_sample(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_muxer_get_buffer(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_muxer_copy_buffer(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_muxer_free_buffer(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_muxer_finalize(void *args) DECLSPEC_HIDDEN;
/* wg_allocator.c */
diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index f9d0ba5a0d6..60e25fc1689 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -470,6 +470,7 @@ enum unix_funcs unix_wg_muxer_get_buffer, unix_wg_muxer_copy_buffer, unix_wg_muxer_free_buffer, + unix_wg_muxer_finalize, };
#endif /* __WINE_WINEGSTREAMER_UNIXLIB_H */ diff --git a/dlls/winegstreamer/wg_muxer.c b/dlls/winegstreamer/wg_muxer.c index cea3790776c..2de31c86ea8 100644 --- a/dlls/winegstreamer/wg_muxer.c +++ b/dlls/winegstreamer/wg_muxer.c @@ -44,6 +44,7 @@ struct wg_muxer
pthread_mutex_t mutex; pthread_cond_t cond; + bool eos; guint64 offset;
struct list streams; @@ -165,6 +166,13 @@ static gboolean muxer_sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *ev
switch (event->type) { + case GST_EVENT_EOS: + pthread_mutex_lock(&muxer->mutex); + muxer->eos = true; + pthread_mutex_unlock(&muxer->mutex); + pthread_cond_signal(&muxer->cond); + break; + case GST_EVENT_SEGMENT: pthread_mutex_lock(&muxer->mutex);
@@ -482,3 +490,25 @@ NTSTATUS wg_muxer_free_buffer(void *args)
return S_OK; } + +NTSTATUS wg_muxer_finalize(void *args) +{ + struct wg_muxer *muxer = get_muxer(*(wg_muxer_t *)args); + struct wg_muxer_stream *stream; + + /* Notify each stream of EOS. */ + LIST_FOR_EACH_ENTRY(stream, &muxer->streams, struct wg_muxer_stream, entry) + { + if (!push_event(stream->my_src, gst_event_new_segment_done(GST_FORMAT_BYTES, -1)) + || !push_event(stream->my_src, gst_event_new_eos())) + return E_FAIL; + } + + /* Wait for muxer EOS. */ + pthread_mutex_lock(&muxer->mutex); + while (!muxer->eos) + pthread_cond_wait(&muxer->cond, &muxer->mutex); + pthread_mutex_unlock(&muxer->mutex); + + return S_OK; +} diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index ce082161efa..7424851ef0c 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1953,6 +1953,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_muxer_get_buffer), X(wg_muxer_copy_buffer), X(wg_muxer_free_buffer), + X(wg_muxer_finalize), };
#ifdef _WIN64 @@ -2274,6 +2275,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X(wg_muxer_get_buffer), X64(wg_muxer_copy_buffer), X(wg_muxer_free_buffer), + X(wg_muxer_finalize), };
#endif /* _WIN64 */
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/media_sink.c | 68 +++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index eab81ff0914..075ef0e73b5 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -32,6 +32,7 @@ enum async_op ASYNC_STOP, ASYNC_PAUSE, ASYNC_PROCESS, + ASYNC_FINALIZE, };
struct async_command @@ -48,6 +49,10 @@ struct async_command IMFSample *sample; UINT32 stream_id; } process; + struct + { + IMFAsyncResult *result; + } finalize; } u; };
@@ -80,6 +85,7 @@ struct media_sink STATE_STARTED, STATE_STOPPED, STATE_PAUSED, + STATE_FINALIZED, STATE_SHUTDOWN, } state; struct wg_container_format format; @@ -630,6 +636,23 @@ static HRESULT media_sink_process(struct media_sink *media_sink, IMFSample *samp return wg_muxer_push_sample(muxer, wg_sample); }
+static HRESULT media_sink_finalize(struct media_sink *media_sink, IMFAsyncResult *result) +{ + HRESULT hr; + + media_sink->state = STATE_FINALIZED; + + hr = wg_muxer_finalize(media_sink->muxer); + + if (SUCCEEDED(hr)) + hr = media_sink_write_stream(media_sink); + + IMFAsyncResult_SetStatus(result, hr); + MFInvokeCallback(result); + + return hr; +} + static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, REFIID riid, void **obj) { struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); @@ -869,16 +892,49 @@ static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface)
static HRESULT WINAPI media_sink_BeginFinalize(IMFFinalizableMediaSink *iface, IMFAsyncCallback *callback, IUnknown *state) { - FIXME("iface %p, callback %p, state %p stub!\n", iface, callback, state); + struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + struct async_command *command; + IMFAsyncResult *result; + HRESULT hr;
- return E_NOTIMPL; + TRACE("iface %p, callback %p, state %p.\n", iface, callback, state); + + EnterCriticalSection(&media_sink->cs); + + if (media_sink->state == STATE_SHUTDOWN) + { + LeaveCriticalSection(&media_sink->cs); + return MF_E_SHUTDOWN; + } + + if (FAILED(hr = async_command_create(ASYNC_FINALIZE, &command))) + { + LeaveCriticalSection(&media_sink->cs); + return hr; + } + if (callback) + { + if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &result))) + { + LeaveCriticalSection(&media_sink->cs); + free(command); + return hr; + } + IMFAsyncResult_AddRef((command->u.finalize.result = result)); + } + + hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &media_sink->async_callback, &command->IUnknown_iface); + + LeaveCriticalSection(&media_sink->cs); + + return hr; }
static HRESULT WINAPI media_sink_EndFinalize(IMFFinalizableMediaSink *iface, IMFAsyncResult *result) { - FIXME("iface %p, result %p stub!\n", iface, result); + TRACE("iface %p, result %p.\n", iface, result);
- return E_NOTIMPL; + return result ? IMFAsyncResult_GetStatus(result) : E_INVALIDARG; }
static const IMFFinalizableMediaSinkVtbl media_sink_vtbl = @@ -1134,6 +1190,10 @@ static HRESULT WINAPI media_sink_callback_Invoke(IMFAsyncCallback *iface, IMFAsy if (FAILED(hr = media_sink_process(media_sink, command->u.process.sample, command->u.process.stream_id))) WARN("Failed to process sample, hr %#lx.\n", hr); break; + case ASYNC_FINALIZE: + if (FAILED(hr = media_sink_finalize(media_sink, command->u.finalize.result))) + WARN("Failed to finalize, hr %#lx.\n", hr); + break; default: WARN("Unsupported op %u.\n", command->op); break;
This merge request was closed by Ziqing Hui.