From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/wg_parser.c | 209 ++++++++++++++++++++++++++++----- 1 file changed, 178 insertions(+), 31 deletions(-)
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index a8da149e7be..21561674086 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -104,15 +104,15 @@ struct wg_parser_stream uint32_t number;
GstPad *their_src, *post_sink, *post_src, *my_sink; - GstElement *flip; + GstElement *flip, *decodebin; GstSegment segment; - struct wg_format preferred_format, current_format; + struct wg_format preferred_format, current_format, stream_format;
pthread_cond_t event_cond, event_empty_cond; GstBuffer *buffer; GstMapInfo map_info;
- bool flushing, eos, enabled, has_caps, has_tags, has_buffer; + bool flushing, eos, enabled, has_caps, has_tags, has_buffer, no_more_pads;
uint64_t duration; gchar *tags[WG_PARSER_TAG_COUNT]; @@ -455,6 +455,33 @@ static NTSTATUS wg_parser_stream_notify_qos(void *args) return S_OK; }
+static bool parser_no_more_pads(struct wg_parser *parser) +{ + unsigned int i; + + for (i = 0; i < parser->stream_count; ++i) + { + if (!parser->streams[i]->no_more_pads) + return false; + } + + return parser->no_more_pads; +} + +static gboolean autoplug_continue_cb(GstElement * decodebin, GstPad *pad, GstCaps * caps, gpointer user) +{ + struct wg_format format; + + wg_format_from_caps(&format, caps); + + if (format.major_type != WG_MAJOR_TYPE_UNKNOWN + && format.major_type != WG_MAJOR_TYPE_VIDEO + && format.major_type != WG_MAJOR_TYPE_AUDIO) + return false; + + return true; +} + static GstAutoplugSelectResult autoplug_select_cb(GstElement *bin, GstPad *pad, GstCaps *caps, GstElementFactory *fact, gpointer user) { @@ -761,6 +788,7 @@ static struct wg_parser_stream *create_stream(struct wg_parser *parser)
stream->parser = parser; stream->number = parser->stream_count; + stream->no_more_pads = true; stream->current_format.major_type = WG_MAJOR_TYPE_UNKNOWN; pthread_cond_init(&stream->event_cond, NULL); pthread_cond_init(&stream->event_empty_cond, NULL); @@ -803,24 +831,15 @@ static void free_stream(struct wg_parser_stream *stream) free(stream); }
-static void pad_added_cb(GstElement *element, GstPad *pad, gpointer user) +static bool stream_create_post_processing_elements(struct wg_parser_stream *stream) { - struct wg_parser *parser = user; - struct wg_parser_stream *stream; + struct wg_parser *parser = stream->parser; const char *name; GstCaps *caps; - int ret; - - GST_LOG("parser %p, element %p, pad %p.", parser, element, pad); - - if (gst_pad_is_linked(pad)) - return;
- caps = gst_pad_query_caps(pad, NULL); + caps = gst_pad_query_caps(stream->their_src, NULL); name = gst_structure_get_name(gst_caps_get_structure(caps, 0)); - - if (!(stream = create_stream(parser))) - goto out; + gst_caps_unref(caps);
if (!strcmp(name, "video/x-raw")) { @@ -829,22 +848,22 @@ static void pad_added_cb(GstElement *element, GstPad *pad, gpointer user) /* DirectShow can express interlaced video, but downstream filters can't * necessarily consume it. In particular, the video renderer can't. */ if (!(deinterlace = create_element("deinterlace", "good"))) - goto out; + return false;
/* decodebin considers many YUV formats to be "raw", but some quartz * filters can't handle those. Also, videoflip can't handle all "raw" * formats either. Add a videoconvert to swap color spaces. */ if (!(vconv = create_element("videoconvert", "base"))) - goto out; + return false;
/* GStreamer outputs RGB video top-down, but DirectShow expects bottom-up. */ if (!(flip = create_element("videoflip", "good"))) - goto out; + return false;
/* videoflip does not support 15 and 16-bit RGB so add a second videoconvert * to do the final conversion. */ if (!(vconv2 = create_element("videoconvert", "base"))) - goto out; + return false;
/* The bin takes ownership of these elements. */ gst_bin_add(GST_BIN(parser->container), deinterlace); @@ -873,7 +892,7 @@ static void pad_added_cb(GstElement *element, GstPad *pad, gpointer user) * 64-bit formats either. Add an audioconvert to allow changing bit * depth and channel count. */ if (!(convert = create_element("audioconvert", "base"))) - goto out; + return false;
gst_bin_add(GST_BIN(parser->container), convert); gst_element_sync_state_with_parent(convert); @@ -882,15 +901,22 @@ static void pad_added_cb(GstElement *element, GstPad *pad, gpointer user) stream->post_src = gst_element_get_static_pad(convert, "src"); }
+ return true; +} + +static bool stream_link_elements(struct wg_parser_stream *stream) +{ + int ret; + if (stream->post_sink) { - if ((ret = gst_pad_link(pad, stream->post_sink)) < 0) + if ((ret = gst_pad_link(stream->their_src, stream->post_sink)) < 0) { GST_ERROR("Failed to link decodebin source pad to post-processing elements, error %s.", gst_pad_link_get_name(ret)); gst_object_unref(stream->post_sink); stream->post_sink = NULL; - goto out; + return false; }
if ((ret = gst_pad_link(stream->post_src, stream->my_sink)) < 0) @@ -901,25 +927,125 @@ static void pad_added_cb(GstElement *element, GstPad *pad, gpointer user) stream->post_src = NULL; gst_object_unref(stream->post_sink); stream->post_sink = NULL; - goto out; + return false; } } - else if ((ret = gst_pad_link(pad, stream->my_sink)) < 0) + else if ((ret = gst_pad_link(stream->their_src, stream->my_sink)) < 0) { GST_ERROR("Failed to link decodebin source pad to our sink pad, error %s.", gst_pad_link_get_name(ret)); - goto out; + return false; }
- gst_pad_set_active(stream->my_sink, 1); + return true; +} + +static void stream_decodebin_no_more_pads_cb(GstElement *element, gpointer user) +{ + struct wg_parser_stream *stream = user; + struct wg_parser *parser = stream->parser; + + GST_DEBUG("stream %p, parser %p, element %p.", stream, parser, element); + + pthread_mutex_lock(&parser->mutex); + stream->no_more_pads = true; + pthread_mutex_unlock(&parser->mutex); + pthread_cond_signal(&parser->init_cond); +} + +static void stream_decodebin_pad_added_cb(GstElement *element, GstPad *pad, gpointer user) +{ + struct wg_parser_stream *stream = user; + struct wg_parser *parser = stream->parser; + + GST_LOG("stream %p, parser %p, element %p, pad %p.", stream, parser, element, pad); + + if (gst_pad_is_linked(pad)) + return; + gst_object_ref(stream->their_src = pad); -out: + if (!stream_create_post_processing_elements(stream)) + return; + if (!stream_link_elements(stream)) + return; + gst_pad_set_active(stream->my_sink, 1); +} + +static bool stream_decodebin_create(struct wg_parser_stream *stream) +{ + struct wg_parser *parser = stream->parser; + + GST_LOG("stream %p, parser %p.", stream, parser); + + if (!(stream->decodebin = create_element("decodebin", "base"))) + return false; + gst_bin_add(GST_BIN(parser->container), stream->decodebin); + + g_signal_connect(stream->decodebin, "pad-added", G_CALLBACK(stream_decodebin_pad_added_cb), stream); + g_signal_connect(stream->decodebin, "autoplug-select", G_CALLBACK(autoplug_select_cb), stream); + g_signal_connect(stream->decodebin, "no-more-pads", G_CALLBACK(stream_decodebin_no_more_pads_cb), stream); + + pthread_mutex_lock(&parser->mutex); + stream->no_more_pads = false; + pthread_mutex_unlock(&parser->mutex); + gst_element_sync_state_with_parent(stream->decodebin); + + GST_LOG("Created stream decodebin %p for %u.", stream->decodebin, stream->number); + + return true; +} + +static void pad_added_cb(GstElement *element, GstPad *pad, gpointer user) +{ + struct wg_parser_stream *stream; + struct wg_parser *parser = user; + GstPad *stream_decodebin_sink; + GstCaps *caps; + int ret; + + GST_LOG("parser %p, element %p, pad %p.", parser, element, pad); + + if (gst_pad_is_linked(pad)) + return; + + if (!(stream = create_stream(parser))) + return; + + caps = gst_pad_query_caps(pad, NULL); + wg_format_from_caps(&stream->stream_format, caps); gst_caps_unref(caps); + + /* For compressed stream, create an extra decodebin to decode it. */ + if (stream->stream_format.major_type != WG_MAJOR_TYPE_UNKNOWN + && stream->stream_format.major_type != WG_MAJOR_TYPE_VIDEO + && stream->stream_format.major_type != WG_MAJOR_TYPE_AUDIO) + { + if (!stream_decodebin_create(stream)) + { + GST_ERROR("Unable to create decodebin for stream %u.", stream->number); + return; + } + + stream_decodebin_sink = gst_element_get_static_pad(stream->decodebin, "sink"); + if ((ret = gst_pad_link(pad, stream_decodebin_sink)) < 0) + GST_ERROR("Failed to link pads, error %d.", ret); + gst_object_unref(stream_decodebin_sink); + + return; + } + + gst_object_ref(stream->their_src = pad); + if (!stream_create_post_processing_elements(stream)) + return; + if (!stream_link_elements(stream)) + return; + gst_pad_set_active(stream->my_sink, 1); }
static void pad_removed_cb(GstElement *element, GstPad *pad, gpointer user) { struct wg_parser *parser = user; + bool done = false; unsigned int i; char *name;
@@ -928,8 +1054,16 @@ static void pad_removed_cb(GstElement *element, GstPad *pad, gpointer user) for (i = 0; i < parser->stream_count; ++i) { struct wg_parser_stream *stream = parser->streams[i]; + GstPad *stream_decodebin_sink_peer = NULL; + GstPad *stream_decodebin_sink = NULL; + + if (stream->decodebin) + { + stream_decodebin_sink = gst_element_get_static_pad(stream->decodebin, "sink"); + stream_decodebin_sink_peer = gst_pad_get_peer(stream_decodebin_sink); + }
- if (stream->their_src == pad) + if (stream->their_src == pad || stream_decodebin_sink_peer == pad) { if (stream->post_sink) gst_pad_unlink(stream->their_src, stream->post_sink); @@ -937,8 +1071,20 @@ static void pad_removed_cb(GstElement *element, GstPad *pad, gpointer user) gst_pad_unlink(stream->their_src, stream->my_sink); gst_object_unref(stream->their_src); stream->their_src = NULL; - return; + + if (stream_decodebin_sink_peer == pad) + gst_pad_unlink(pad, stream_decodebin_sink); + + done = true; } + + if (stream_decodebin_sink_peer) + gst_object_unref(stream_decodebin_sink_peer); + if (stream_decodebin_sink) + gst_object_unref(stream_decodebin_sink); + + if (done) + return; }
name = gst_pad_get_name(pad); @@ -1377,7 +1523,7 @@ static NTSTATUS wg_parser_connect(void *args)
pthread_mutex_lock(&parser->mutex);
- while (!parser->no_more_pads && !parser->error) + while (!parser_no_more_pads(parser) && !parser->error) pthread_cond_wait(&parser->init_cond, &parser->mutex); if (parser->error) { @@ -1564,6 +1710,7 @@ static BOOL decodebin_parser_init_gst(struct wg_parser *parser)
g_signal_connect(element, "pad-added", G_CALLBACK(pad_added_cb), parser); g_signal_connect(element, "pad-removed", G_CALLBACK(pad_removed_cb), parser); + g_signal_connect(element, "autoplug-continue", G_CALLBACK(autoplug_continue_cb), parser); g_signal_connect(element, "autoplug-select", G_CALLBACK(autoplug_select_cb), parser); g_signal_connect(element, "no-more-pads", G_CALLBACK(no_more_pads_cb), parser);