From 7cee5e5580decd8071dc0dfeb7158d4f7ef74bcd Mon Sep 17 00:00:00 2001 From: Ziqing Hui Date: Mon, 25 Sep 2023 11:52:29 +0800 Subject: [PATCH] winegstreamer: Implement wg_muxer_start. --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/main.c | 15 ++++ dlls/winegstreamer/media_sink.c | 9 ++- dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_muxer.c | 160 +++++++++++++++++++++++++++++++------- dlls/winegstreamer/wg_parser.c | 2 + 7 files changed, 159 insertions(+), 30 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index cc408177e7c..22d92cfeef7 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(const char *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_start(wg_muxer_t muxer); unsigned int wg_format_get_bytes_for_uncompressed(wg_video_format format, unsigned int width, unsigned int height); 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 1893ca8d3a9..58ad8c6f3d7 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -508,6 +508,21 @@ HRESULT wg_muxer_add_stream(wg_muxer_t muxer, UINT32 stream_id, const struct wg_ return S_OK; } +HRESULT wg_muxer_start(wg_muxer_t muxer) +{ + NTSTATUS status; + + TRACE("muxer %#I64x.\n", muxer); + + if ((status = WINE_UNIX_CALL(unix_wg_muxer_start, &muxer))) + { + WARN("Failed to start muxer, status %#lx.\n", status); + return HRESULT_FROM_NT(status); + } + + return S_OK; +} + #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 344134d1633..489ede1d26a 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -498,7 +498,13 @@ static HRESULT media_sink_queue_stream_event(struct media_sink *media_sink, Medi static HRESULT media_sink_start(struct media_sink *media_sink) { + HRESULT hr; + + if (FAILED(hr = wg_muxer_start(media_sink->muxer))) + return hr; + media_sink->state = STATE_STARTED; + return media_sink_queue_stream_event(media_sink, MEStreamSinkStarted); } @@ -1007,7 +1013,8 @@ static HRESULT WINAPI media_sink_callback_Invoke(IMFAsyncCallback *iface, IMFAsy switch (command->op) { case ASYNC_START: - hr = media_sink_start(media_sink); + if (FAILED(hr = media_sink_start(media_sink))) + WARN("Failed to start media sink.\n"); break; case ASYNC_STOP: hr = media_sink_stop(media_sink); diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index cf4a293c863..ad1817d8c2d 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -68,6 +68,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_start(void *args) DECLSPEC_HIDDEN; /* wg_allocator.c */ diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 33457c52681..70bbe89208f 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -428,6 +428,7 @@ enum unix_funcs unix_wg_muxer_create, unix_wg_muxer_destroy, unix_wg_muxer_add_stream, + unix_wg_muxer_start, unix_wg_funcs_count, }; diff --git a/dlls/winegstreamer/wg_muxer.c b/dlls/winegstreamer/wg_muxer.c index 64a19a2ff44..aa453ad9837 100644 --- a/dlls/winegstreamer/wg_muxer.c +++ b/dlls/winegstreamer/wg_muxer.c @@ -18,6 +18,27 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +/* + * wg_muxer will autoplug gstreamer muxer and parser elements. + * It creates a pipeline like this: + * + * ------------------- ------- + * [my_src 1] ==> |parser 1 (optional)| ==> | | + * ------------------- | | + * | | + * ------------------- | | + * [my_src 2] ==> |parser 2 (optional)| ==> | | + * ------------------- | | + * | muxer | ==> [my_sink] + * | | + * [ ...... ] | | + * | | + * | | + * ------------------- | | + * [my_src n] ==> |parser n (optional)| ==> | | + * ------------------- ------- + */ + #if 0 #pragma makedep unix #endif @@ -36,6 +57,8 @@ struct wg_muxer { GstElement *container, *muxer; GstPad *my_sink; + GstCaps *my_sink_caps; + struct list streams; }; @@ -48,6 +71,7 @@ struct wg_muxer_stream GstPad *my_src; GstCaps *my_src_caps, *parser_src_caps; GstElement *parser; + GstSegment segment; struct list entry; }; @@ -57,6 +81,53 @@ static struct wg_muxer *get_muxer(wg_muxer_t muxer) return (struct wg_muxer *)(ULONG_PTR)muxer; } +static bool muxer_try_muxer_factory(struct wg_muxer *muxer, GstElementFactory *muxer_factory) +{ + struct wg_muxer_stream *stream; + + GST_INFO("Trying %"GST_PTR_FORMAT".", muxer_factory); + + LIST_FOR_EACH_ENTRY(stream, &muxer->streams, struct wg_muxer_stream, entry) + { + GstCaps *caps = stream->parser ? stream->parser_src_caps : stream->my_src_caps; + + if (!gst_element_factory_can_sink_any_caps(muxer_factory, caps)) + { + GST_INFO("%"GST_PTR_FORMAT" cannot sink stream %u %p, caps %"GST_PTR_FORMAT, + muxer_factory, stream->id, stream, caps); + return false; + } + } + + return true; +} + +static GstElement *muxer_find_muxer(struct wg_muxer *muxer) +{ + /* Some muxers are formatter, eg. id3mux. */ + GstElementFactoryListType muxer_type = GST_ELEMENT_FACTORY_TYPE_MUXER | GST_ELEMENT_FACTORY_TYPE_FORMATTER; + GstElement *element = NULL; + GList *muxers, *tmp; + + GST_DEBUG("muxer %p.", muxer); + + muxers = find_element_factories(muxer_type, GST_RANK_NONE, NULL, muxer->my_sink_caps); + + for (tmp = muxers; tmp && !element; tmp = tmp->next) + { + GstElementFactory *factory = GST_ELEMENT_FACTORY(tmp->data); + if (muxer_try_muxer_factory(muxer, factory)) + element = factory_create_element(factory); + } + + gst_plugin_feature_list_free(muxers); + + if (!element) + GST_WARNING("Failed to find any compatible muxer element."); + + return element; +} + static gboolean muxer_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) { struct wg_muxer *muxer = gst_pad_get_element_private(pad); @@ -87,10 +158,8 @@ static void stream_free(struct wg_muxer_stream *stream) NTSTATUS wg_muxer_create(void *args) { struct wg_muxer_create_params *params = args; - GstElement *first = NULL, *last = NULL; NTSTATUS status = STATUS_UNSUCCESSFUL; GstPadTemplate *template = NULL; - GstCaps *sink_caps = NULL; struct wg_muxer *muxer; /* Create wg_muxer object. */ @@ -101,12 +170,12 @@ NTSTATUS wg_muxer_create(void *args) goto out; /* Create sink pad. */ - if (!(sink_caps = gst_caps_from_string(params->format))) + if (!(muxer->my_sink_caps = gst_caps_from_string(params->format))) { GST_ERROR("Failed to get caps from format string: \"%s\".", params->format); goto out; } - if (!(template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, sink_caps))) + if (!(template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, muxer->my_sink_caps))) goto out; muxer->my_sink = gst_pad_new_from_template(template, "wg_muxer_sink"); if (!muxer->my_sink) @@ -114,26 +183,7 @@ NTSTATUS wg_muxer_create(void *args) 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, - NULL, sink_caps))) - goto out; - if (!append_element(muxer->container, muxer->muxer, &first, &last)) - 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_object_unref(template); - gst_caps_unref(sink_caps); GST_INFO("Created winegstreamer muxer %p.", muxer); params->muxer = (wg_transform_t)(ULONG_PTR)muxer; @@ -145,13 +195,10 @@ out: gst_object_unref(muxer->my_sink); if (template) gst_object_unref(template); - if (sink_caps) - gst_caps_unref(sink_caps); + if (muxer->my_sink_caps) + gst_caps_unref(muxer->my_sink_caps); if (muxer->container) - { - gst_element_set_state(muxer->container, GST_STATE_NULL); gst_object_unref(muxer->container); - } free(muxer); return status; @@ -168,6 +215,7 @@ NTSTATUS wg_muxer_destroy(void *args) stream_free(stream); } gst_object_unref(muxer->my_sink); + gst_caps_unref(muxer->my_sink_caps); gst_element_set_state(muxer->container, GST_STATE_NULL); gst_object_unref(muxer->container); free(muxer); @@ -184,7 +232,7 @@ NTSTATUS wg_muxer_add_stream(void *args) struct wg_muxer_stream *stream; char src_pad_name[64]; - GST_DEBUG("muxer %p, stream_id %u, format %p.", muxer, params->stream_id, params->format); + GST_DEBUG("muxer %p, stream %u, format %p.", muxer, params->stream_id, params->format); /* Create stream object. */ if (!(stream = calloc(1, sizeof(*stream)))) @@ -241,3 +289,57 @@ out: return status; } + +NTSTATUS wg_muxer_start(void *args) +{ + struct wg_muxer *muxer = get_muxer(*(wg_muxer_t *)args); + NTSTATUS status = STATUS_UNSUCCESSFUL; + struct wg_muxer_stream *stream; + + GST_DEBUG("muxer %p.", muxer); + + /* Create muxer element. */ + if (!(muxer->muxer = muxer_find_muxer(muxer)) + || !gst_bin_add(GST_BIN(muxer->container), muxer->muxer)) + return status; + + /* Link muxer element to my_sink */ + if (!link_element_to_sink(muxer->muxer, muxer->my_sink) + || !gst_pad_set_active(muxer->my_sink, 1)) + return status; + + /* Link each stream to muxer element. */ + LIST_FOR_EACH_ENTRY(stream, &muxer->streams, struct wg_muxer_stream, entry) + { + bool link_ok = stream->parser ? + gst_element_link(stream->parser, muxer->muxer) : + link_src_to_element(stream->my_src, muxer->muxer); + + if (!link_ok) + return status; + } + + /* Set to pause state. */ + if (gst_element_set_state(muxer->container, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE + || gst_element_get_state(muxer->container, NULL, NULL, -1) == GST_STATE_CHANGE_FAILURE) + return status; + + /* Active stream my_src pad and push events to prepare for streaming. */ + LIST_FOR_EACH_ENTRY(stream, &muxer->streams, struct wg_muxer_stream, entry) + { + char buffer[64]; + + sprintf(buffer, "wg_muxer_stream_src_%u", stream->id); + gst_segment_init(&stream->segment, GST_FORMAT_BYTES); + if (!gst_pad_set_active(stream->my_src, 1)) + return status; + if (!push_event(stream->my_src, gst_event_new_stream_start(buffer)) + || !push_event(stream->my_src, gst_event_new_caps(stream->my_src_caps)) + || !push_event(stream->my_src, gst_event_new_segment(&stream->segment))) + return status; + } + + GST_DEBUG("Started muxer %p.", muxer); + + return STATUS_SUCCESS; +} diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 2015b1f46c5..065b4216dd4 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1922,6 +1922,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_start), }; C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == unix_wg_funcs_count); @@ -2209,6 +2210,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X64(wg_muxer_create), X(wg_muxer_destroy), X64(wg_muxer_add_stream), + X(wg_muxer_start), }; C_ASSERT(ARRAYSIZE(__wine_unix_call_wow64_funcs) == unix_wg_funcs_count); -- 2.11.4.GIT