[PATCH v2 3/6] winegstreamer: Defer blocking on GStreamer initialization until getting a stream.

Derek Lesho dlesho at codeweavers.com
Fri Sep 17 14:58:55 CDT 2021


This commit has several purposes.

- It will allow wg_parser_create and wg_parser_connect to be merged, as the functionality of both functions now can be run before the read thread starts.
- It also allows us to simplify a future push-mode client where there is no separate read thread, and we want to send buffers and check for a response from the same thread which initialized the object.

Signed-off-by: Derek Lesho <dlesho at codeweavers.com>
---
 dlls/winegstreamer/media_source.c  |   6 +-
 dlls/winegstreamer/quartz_parser.c |  20 +--
 dlls/winegstreamer/wg_parser.c     | 196 +++++++++++++++--------------
 3 files changed, 123 insertions(+), 99 deletions(-)

diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index 6c2bf92e2a2..21e5f6c5d5b 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -1346,7 +1346,11 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_
      * leak occurs with native. */
     unix_funcs->wg_parser_set_unlimited_buffering(parser);
 
-    stream_count = unix_funcs->wg_parser_get_stream_count(parser);
+    if (!(stream_count = unix_funcs->wg_parser_get_stream_count(parser)))
+    {
+        hr = E_FAIL;
+        goto fail;
+    }
 
     if (!(object->streams = calloc(stream_count, sizeof(*object->streams))))
     {
diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c
index bf69a881d57..20000f63eb7 100644
--- a/dlls/winegstreamer/quartz_parser.c
+++ b/dlls/winegstreamer/quartz_parser.c
@@ -1039,7 +1039,7 @@ static BOOL decodebin_parser_filter_init_gst(struct parser *filter)
             return FALSE;
     }
 
-    return TRUE;
+    return !!stream_count;
 }
 
 static HRESULT decodebin_parser_source_query_accept(struct parser_source *pin, const AM_MEDIA_TYPE *mt)
@@ -1603,11 +1603,12 @@ static const struct strmbase_sink_ops wave_parser_sink_ops =
 static BOOL wave_parser_filter_init_gst(struct parser *filter)
 {
     struct wg_parser *parser = filter->wg_parser;
+    struct wg_parser_stream *stream =  unix_funcs->wg_parser_get_stream(parser, 0);
 
-    if (!create_pin(filter, unix_funcs->wg_parser_get_stream(parser, 0), L"output"))
+    if (!stream)
         return FALSE;
 
-    return TRUE;
+    return !!create_pin(filter, stream, L"output");
 }
 
 static HRESULT wave_parser_source_query_accept(struct parser_source *pin, const AM_MEDIA_TYPE *mt)
@@ -1678,18 +1679,21 @@ static const struct strmbase_sink_ops avi_splitter_sink_ops =
 static BOOL avi_splitter_filter_init_gst(struct parser *filter)
 {
     struct wg_parser *parser = filter->wg_parser;
+    struct wg_parser_stream *stream;
     uint32_t i, stream_count;
     WCHAR source_name[20];
 
     stream_count = unix_funcs->wg_parser_get_stream_count(parser);
     for (i = 0; i < stream_count; ++i)
     {
+        if (!(stream = unix_funcs->wg_parser_get_stream(parser, i)))
+            return FALSE;
         swprintf(source_name, ARRAY_SIZE(source_name), L"Stream %02u", i);
-        if (!create_pin(filter, unix_funcs->wg_parser_get_stream(parser, i), source_name))
+        if (!create_pin(filter, stream, source_name))
             return FALSE;
     }
 
-    return TRUE;
+    return !!stream_count;
 }
 
 static HRESULT avi_splitter_source_query_accept(struct parser_source *pin, const AM_MEDIA_TYPE *mt)
@@ -1765,11 +1769,13 @@ static const struct strmbase_sink_ops mpeg_splitter_sink_ops =
 static BOOL mpeg_splitter_filter_init_gst(struct parser *filter)
 {
     struct wg_parser *parser = filter->wg_parser;
+    struct wg_parser_stream *stream = unix_funcs->wg_parser_get_stream(parser, 0);
 
-    if (!create_pin(filter, unix_funcs->wg_parser_get_stream(parser, 0), L"Audio"))
+    if (!stream)
         return FALSE;
 
-    return TRUE;
+    return !!create_pin(filter, stream, L"Audio");
+        return FALSE;
 }
 
 static HRESULT mpeg_splitter_source_query_accept(struct parser_source *pin, const AM_MEDIA_TYPE *mt)
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c
index 08823d52101..98b7491998b 100644
--- a/dlls/winegstreamer/wg_parser.c
+++ b/dlls/winegstreamer/wg_parser.c
@@ -478,13 +478,113 @@ static bool wg_format_compare(const struct wg_format *a, const struct wg_format
     return false;
 }
 
+static bool wg_parser_initialize(struct wg_parser *parser)
+{
+    unsigned int i;
+    int ret;
+
+    ret = gst_element_get_state(parser->container, NULL, NULL, -1);
+    if (ret == GST_STATE_CHANGE_FAILURE)
+    {
+        GST_ERROR("Failed to play stream.\n");
+        return 0;
+    }
+
+    pthread_mutex_lock(&parser->mutex);
+
+    while (!parser->no_more_pads && !parser->error)
+        pthread_cond_wait(&parser->state_cond, &parser->mutex);
+    if (parser->error)
+    {
+        pthread_mutex_unlock(&parser->mutex);
+        return 0;
+    }
+
+    for (i = 0; i < parser->stream_count; ++i)
+    {
+        struct wg_parser_stream *stream = parser->streams[i];
+        gint64 duration;
+
+        while (!stream->has_caps && !parser->error)
+            pthread_cond_wait(&parser->state_cond, &parser->mutex);
+
+        /* GStreamer doesn't actually provide any guarantees about when duration
+         * is available, even for seekable streams. It's basically built for
+         * applications that don't care, e.g. movie players that can display
+         * a duration once it's available, and update it visually if a better
+         * estimate is found. This doesn't really match well with DirectShow or
+         * Media Foundation, which both expect duration to be available
+         * immediately on connecting, so we have to use some complex heuristics
+         * to try to actually get a usable duration.
+         *
+         * Some elements (avidemux, wavparse, qtdemux) record duration almost
+         * immediately, before fixing caps. Such elements don't send
+         * duration-changed messages. Therefore always try querying duration
+         * after caps have been found.
+         *
+         * Some elements (mpegaudioparse) send duration-changed. In the case of
+         * a mp3 stream without seek tables it will not be sent immediately, but
+         * only after enough frames have been parsed to form an estimate. They
+         * may send it multiple times with increasingly accurate estimates, but
+         * unfortunately we have no way of knowing whether another estimate will
+         * be sent, so we always take the first one. We assume that if the
+         * duration is not immediately available then the element will always
+         * send duration-changed.
+         */
+
+        for (;;)
+        {
+            if (parser->error)
+            {
+                pthread_mutex_unlock(&parser->mutex);
+                return 0;
+            }
+            if (gst_pad_query_duration(stream->their_src, GST_FORMAT_TIME, &duration))
+            {
+                stream->duration = duration / 100;
+                break;
+            }
+
+            if (stream->eos)
+            {
+                stream->duration = 0;
+                GST_WARNING("Failed to query duration.\n");
+                break;
+            }
+
+            /* Elements based on GstBaseParse send duration-changed before
+             * actually updating the duration in GStreamer versions prior
+             * to 1.17.1. See <gstreamer.git:d28e0b4147fe7073b2>. So after
+             * receiving duration-changed we have to continue polling until
+             * the query succeeds. */
+            if (parser->has_duration)
+            {
+                pthread_mutex_unlock(&parser->mutex);
+                g_usleep(10000);
+                pthread_mutex_lock(&parser->mutex);
+            }
+            else
+            {
+                pthread_cond_wait(&parser->state_cond, &parser->mutex);
+            }
+        }
+    }
+
+    pthread_mutex_unlock(&parser->mutex);
+    return 1;
+}
+
 static uint32_t CDECL wg_parser_get_stream_count(struct wg_parser *parser)
 {
+    if (!wg_parser_initialize(parser))
+        return 0;
     return parser->stream_count;
 }
 
 static struct wg_parser_stream * CDECL wg_parser_get_stream(struct wg_parser *parser, uint32_t index)
 {
+    if (!wg_parser_initialize(parser))
+        return NULL;
     return parser->streams[index];
 }
 
@@ -492,6 +592,9 @@ static void CDECL wg_parser_begin_flush(struct wg_parser *parser)
 {
     unsigned int i;
 
+    if (!wg_parser_initialize(parser))
+        return;
+
     pthread_mutex_lock(&parser->mutex);
     parser->flushing = true;
     pthread_mutex_unlock(&parser->mutex);
@@ -1342,9 +1445,6 @@ static GstBusSyncReply bus_handler_cb(GstBus *bus, GstMessage *msg, gpointer use
 
 static HRESULT CDECL wg_parser_connect(struct wg_parser *parser, uint64_t file_size)
 {
-    unsigned int i;
-    int ret;
-
     if (!parser->bus)
     {
         parser->bus = gst_bus_new();
@@ -1370,94 +1470,6 @@ static HRESULT CDECL wg_parser_connect(struct wg_parser *parser, uint64_t file_s
         return E_FAIL;
 
     gst_element_set_state(parser->container, GST_STATE_PAUSED);
-    ret = gst_element_get_state(parser->container, NULL, NULL, -1);
-    if (ret == GST_STATE_CHANGE_FAILURE)
-    {
-        GST_ERROR("Failed to play stream.\n");
-        return E_FAIL;
-    }
-
-    pthread_mutex_lock(&parser->mutex);
-
-    while (!parser->no_more_pads && !parser->error)
-        pthread_cond_wait(&parser->state_cond, &parser->mutex);
-    if (parser->error)
-    {
-        pthread_mutex_unlock(&parser->mutex);
-        return E_FAIL;
-    }
-
-    for (i = 0; i < parser->stream_count; ++i)
-    {
-        struct wg_parser_stream *stream = parser->streams[i];
-        gint64 duration;
-
-        while (!stream->has_caps && !parser->error)
-            pthread_cond_wait(&parser->state_cond, &parser->mutex);
-
-        /* GStreamer doesn't actually provide any guarantees about when duration
-         * is available, even for seekable streams. It's basically built for
-         * applications that don't care, e.g. movie players that can display
-         * a duration once it's available, and update it visually if a better
-         * estimate is found. This doesn't really match well with DirectShow or
-         * Media Foundation, which both expect duration to be available
-         * immediately on connecting, so we have to use some complex heuristics
-         * to try to actually get a usable duration.
-         *
-         * Some elements (avidemux, wavparse, qtdemux) record duration almost
-         * immediately, before fixing caps. Such elements don't send
-         * duration-changed messages. Therefore always try querying duration
-         * after caps have been found.
-         *
-         * Some elements (mpegaudioparse) send duration-changed. In the case of
-         * a mp3 stream without seek tables it will not be sent immediately, but
-         * only after enough frames have been parsed to form an estimate. They
-         * may send it multiple times with increasingly accurate estimates, but
-         * unfortunately we have no way of knowing whether another estimate will
-         * be sent, so we always take the first one. We assume that if the
-         * duration is not immediately available then the element will always
-         * send duration-changed.
-         */
-
-        for (;;)
-        {
-            if (parser->error)
-            {
-                pthread_mutex_unlock(&parser->mutex);
-                return E_FAIL;
-            }
-            if (gst_pad_query_duration(stream->their_src, GST_FORMAT_TIME, &duration))
-            {
-                stream->duration = duration / 100;
-                break;
-            }
-
-            if (stream->eos)
-            {
-                stream->duration = 0;
-                GST_WARNING("Failed to query duration.\n");
-                break;
-            }
-
-            /* Elements based on GstBaseParse send duration-changed before
-             * actually updating the duration in GStreamer versions prior
-             * to 1.17.1. See <gstreamer.git:d28e0b4147fe7073b2>. So after
-             * receiving duration-changed we have to continue polling until
-             * the query succeeds. */
-            if (parser->has_duration)
-            {
-                pthread_mutex_unlock(&parser->mutex);
-                g_usleep(10000);
-                pthread_mutex_lock(&parser->mutex);
-            }
-            else
-            {
-                pthread_cond_wait(&parser->state_cond, &parser->mutex);
-            }
-        }
-    }
-
-    pthread_mutex_unlock(&parser->mutex);
 
     return S_OK;
 }
@@ -1639,6 +1651,8 @@ static void CDECL wg_parser_destroy(struct wg_parser *parser)
 {
     unsigned int i;
 
+    wg_parser_initialize(parser);
+
     pthread_mutex_lock(&parser->mutex);
     parser->close_reader = true;
     pthread_cond_signal(&parser->read_cond);
-- 
2.33.0




More information about the wine-devel mailing list