[PATCH] winegstreamer: Deliver samples in PTS order instead of alternating.

Zebediah Figura zfigura at codeweavers.com
Wed Mar 9 18:56:15 CST 2022


For the async reader, and when requesting stream 0.

Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
---
 dlls/winegstreamer/gst_private.h    |  4 +-
 dlls/winegstreamer/wm_asyncreader.c | 97 +++++++++++++----------------
 dlls/winegstreamer/wm_reader.c      | 97 ++++++++++++++++++++++++-----
 dlls/winegstreamer/wm_syncreader.c  | 57 +++--------------
 4 files changed, 137 insertions(+), 118 deletions(-)

diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 2cbba09f9a7..a3f8c0ab5eb 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -180,8 +180,8 @@ HRESULT wm_reader_get_output_props(struct wm_reader *reader, DWORD output,
         IWMOutputMediaProps **props);
 struct wm_stream *wm_reader_get_stream_by_stream_number(struct wm_reader *reader,
         WORD stream_number);
-HRESULT wm_reader_get_stream_sample(struct wm_stream *stream,
-        INSSBuffer **sample, QWORD *pts, QWORD *duration, DWORD *flags);
+HRESULT wm_reader_get_stream_sample(struct wm_reader *reader, WORD stream_number,
+        INSSBuffer **ret_sample, QWORD *pts, QWORD *duration, DWORD *flags, WORD *ret_stream_number);
 HRESULT wm_reader_get_stream_selection(struct wm_reader *reader,
         WORD stream_number, WMT_STREAM_SELECTION *selection);
 void wm_reader_init(struct wm_reader *reader, const struct wm_reader_ops *ops);
diff --git a/dlls/winegstreamer/wm_asyncreader.c b/dlls/winegstreamer/wm_asyncreader.c
index 9dfe2380284..947b3307a3a 100644
--- a/dlls/winegstreamer/wm_asyncreader.c
+++ b/dlls/winegstreamer/wm_asyncreader.c
@@ -72,11 +72,11 @@ static void open_stream(struct async_reader *reader, IWMReaderCallback *callback
 static DWORD WINAPI stream_thread(void *arg)
 {
     struct async_reader *reader = arg;
-    WORD i, stream_count = reader->reader.stream_count;
     IWMReaderCallback *callback = reader->callback;
     REFERENCE_TIME start_time;
     static const DWORD zero;
     QWORD pts, duration;
+    WORD stream_number;
     INSSBuffer *sample;
     DWORD flags;
     HRESULT hr;
@@ -87,71 +87,56 @@ static DWORD WINAPI stream_thread(void *arg)
 
     while (reader->running)
     {
-        bool all_eos = true;
+        hr = wm_reader_get_stream_sample(&reader->reader, 0, &sample, &pts, &duration, &flags, &stream_number);
 
-        for (i = 0; i < stream_count; ++i)
+        if (hr == S_OK)
         {
-            struct wm_stream *stream = &reader->reader.streams[i];
+            struct wm_stream *stream = wm_reader_get_stream_by_stream_number(&reader->reader, stream_number);
 
-            if (stream->selection == WMT_OFF)
-                continue;
-
-            hr = wm_reader_get_stream_sample(stream, &sample, &pts, &duration, &flags);
-            if (hr == S_OK)
+            if (reader->user_clock)
             {
-                if (reader->user_clock)
-                {
-                    QWORD user_time = reader->user_time;
+                QWORD user_time = reader->user_time;
+
+                if (pts > user_time && reader->reader.callback_advanced)
+                    IWMReaderCallbackAdvanced_OnTime(reader->reader.callback_advanced, user_time, reader->context);
+                while (pts > reader->user_time && reader->running)
+                    SleepConditionVariableCS(&reader->stream_cv, &reader->stream_cs, INFINITE);
+                if (!reader->running)
+                {
+                    INSSBuffer_Release(sample);
+                    goto out;
+                }
+            }
+            else
+            {
+                for (;;)
+                {
+                    REFERENCE_TIME current_time = get_current_time(reader);
+
+                    if (pts <= current_time - start_time)
+                        break;
+
+                    SleepConditionVariableCS(&reader->stream_cv, &reader->stream_cs,
+                            (pts - (current_time - start_time)) / 10000);
 
-                    if (pts > user_time && reader->reader.callback_advanced)
-                        IWMReaderCallbackAdvanced_OnTime(reader->reader.callback_advanced, user_time, reader->context);
-                    while (pts > reader->user_time && reader->running)
-                        SleepConditionVariableCS(&reader->stream_cv, &reader->stream_cs, INFINITE);
                     if (!reader->running)
                     {
                         INSSBuffer_Release(sample);
                         goto out;
                     }
                 }
-                else
-                {
-                    for (;;)
-                    {
-                        REFERENCE_TIME current_time = get_current_time(reader);
-
-                        if (pts <= current_time - start_time)
-                            break;
-
-                        SleepConditionVariableCS(&reader->stream_cv, &reader->stream_cs,
-                                (pts - (current_time - start_time)) / 10000);
-
-                        if (!reader->running)
-                        {
-                            INSSBuffer_Release(sample);
-                            goto out;
-                        }
-                    }
-                }
-
-                if (stream->read_compressed)
-                    hr = IWMReaderCallbackAdvanced_OnStreamSample(reader->reader.callback_advanced,
-                            i + 1, pts, duration, flags, sample, reader->context);
-                else
-                    hr = IWMReaderCallback_OnSample(callback, i, pts, duration,
-                            flags, sample, reader->context);
-                TRACE("Callback returned %#lx.\n", hr);
-                INSSBuffer_Release(sample);
-                all_eos = false;
-            }
-            else if (hr != NS_E_NO_MORE_SAMPLES)
-            {
-                ERR("Failed to get sample, hr %#lx.\n", hr);
-                LeaveCriticalSection(&reader->stream_cs);
-                return 0;
             }
+
+            if (stream->read_compressed)
+                hr = IWMReaderCallbackAdvanced_OnStreamSample(reader->reader.callback_advanced,
+                        stream_number, pts, duration, flags, sample, reader->context);
+            else
+                hr = IWMReaderCallback_OnSample(callback, stream_number - 1, pts, duration,
+                        flags, sample, reader->context);
+            TRACE("Callback returned %#lx.\n", hr);
+            INSSBuffer_Release(sample);
         }
-
-        if (all_eos)
+        else if (hr == NS_E_NO_MORE_SAMPLES)
         {
             IWMReaderCallback_OnStatus(callback, WMT_END_OF_STREAMING, S_OK,
                     WMT_TYPE_DWORD, (BYTE *)&zero, reader->context);
@@ -171,6 +156,12 @@ static DWORD WINAPI stream_thread(void *arg)
             LeaveCriticalSection(&reader->stream_cs);
             return 0;
         }
+        else
+        {
+            ERR("Failed to get sample, hr %#lx.\n", hr);
+            LeaveCriticalSection(&reader->stream_cs);
+            return 0;
+        }
     }
 
 out:
diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c
index bfed0d1e0ca..c911cb59d1f 100644
--- a/dlls/winegstreamer/wm_reader.c
+++ b/dlls/winegstreamer/wm_reader.c
@@ -1820,30 +1820,98 @@ static const char *get_major_type_string(enum wg_major_type type)
     return NULL;
 }
 
-HRESULT wm_reader_get_stream_sample(struct wm_stream *stream,
-        INSSBuffer **ret_sample, QWORD *pts, QWORD *duration, DWORD *flags)
+/* Find the earliest buffer by PTS.
+ *
+ * Native seems to behave similarly to this with the async reader, although our
+ * unit tests show that it's not entirely consistent—some frames are received
+ * slightly out of order. It's possible that one stream is being manually offset
+ * to account for decoding latency.
+ *
+ * The behaviour with the synchronous reader, when stream 0 is requested, seems
+ * consistent with this hypothesis, but with a much larger offset—the video
+ * stream seems to be "behind" by about 150 ms.
+ *
+ * The main reason for doing this is that the video and audio stream probably
+ * don't have quite the same "frame rate", and we don't want to force one stream
+ * to decode faster just to keep up with the other. Delivering samples in PTS
+ * order should avoid that problem. */
+static WORD get_earliest_buffer(struct wm_reader *reader, struct wg_parser_buffer *ret_buffer)
 {
-    IWMReaderCallbackAdvanced *callback_advanced = stream->reader->callback_advanced;
-    struct wg_parser_stream *wg_stream = stream->wg_stream;
+    struct wg_parser_buffer buffer;
+    QWORD earliest_pts = UI64_MAX;
+    WORD stream_number = 0;
+    WORD i;
+
+    for (i = 0; i < reader->stream_count; ++i)
+    {
+        struct wm_stream *stream = &reader->streams[i];
+
+        if (stream->selection == WMT_OFF)
+            continue;
+
+        if (!wg_parser_stream_get_buffer(stream->wg_stream, &buffer))
+            continue;
+
+        if (buffer.has_pts && buffer.pts < earliest_pts)
+        {
+            stream_number = i + 1;
+            earliest_pts = buffer.pts;
+            *ret_buffer = buffer;
+        }
+    }
+
+    return stream_number;
+}
+
+HRESULT wm_reader_get_stream_sample(struct wm_reader *reader, WORD stream_number,
+        INSSBuffer **ret_sample, QWORD *pts, QWORD *duration, DWORD *flags, WORD *ret_stream_number)
+{
+    IWMReaderCallbackAdvanced *callback_advanced = reader->callback_advanced;
+    struct wg_parser_stream *wg_stream;
     struct wg_parser_buffer wg_buffer;
+    struct wm_stream *stream;
     DWORD size, capacity;
     INSSBuffer *sample;
     HRESULT hr;
     BYTE *data;
 
-    if (stream->selection == WMT_OFF)
-        return NS_E_INVALID_REQUEST;
-
-    if (stream->eos)
-        return NS_E_NO_MORE_SAMPLES;
-
     for (;;)
     {
-        if (!wg_parser_stream_get_buffer(wg_stream, &wg_buffer))
+        if (!stream_number)
         {
-            stream->eos = true;
-            TRACE("End of stream.\n");
-            return NS_E_NO_MORE_SAMPLES;
+            if (!(stream_number = get_earliest_buffer(reader, &wg_buffer)))
+            {
+                /* All streams are disabled or EOS. */
+                return NS_E_NO_MORE_SAMPLES;
+            }
+
+            stream = wm_reader_get_stream_by_stream_number(reader, stream_number);
+            wg_stream = stream->wg_stream;
+        }
+        else
+        {
+            if (!(stream = wm_reader_get_stream_by_stream_number(reader, stream_number)))
+            {
+                WARN("Invalid stream number %u; returning E_INVALIDARG.\n", stream_number);
+                return E_INVALIDARG;
+            }
+            wg_stream = stream->wg_stream;
+
+            if (stream->selection == WMT_OFF)
+            {
+                WARN("Stream %u is deselected; returning NS_E_INVALID_REQUEST.\n", stream_number);
+                return NS_E_INVALID_REQUEST;
+            }
+
+            if (stream->eos)
+                return NS_E_NO_MORE_SAMPLES;
+
+            if (!wg_parser_stream_get_buffer(wg_stream, &wg_buffer))
+            {
+                stream->eos = true;
+                TRACE("End of stream.\n");
+                return NS_E_NO_MORE_SAMPLES;
+            }
         }
 
         TRACE("Got buffer for '%s' stream %p.\n", get_major_type_string(stream->format.major_type), stream);
@@ -1920,6 +1988,7 @@ HRESULT wm_reader_get_stream_sample(struct wm_stream *stream,
             *flags |= WM_SF_CLEANPOINT;
 
         *ret_sample = sample;
+        *ret_stream_number = stream_number;
         return S_OK;
     }
 }
diff --git a/dlls/winegstreamer/wm_syncreader.c b/dlls/winegstreamer/wm_syncreader.c
index 55940e2b370..c7cccd52c4f 100644
--- a/dlls/winegstreamer/wm_syncreader.c
+++ b/dlls/winegstreamer/wm_syncreader.c
@@ -25,8 +25,6 @@ struct sync_reader
     struct wm_reader reader;
 
     IWMSyncReader2 IWMSyncReader2_iface;
-
-    WORD last_read_stream;
 };
 
 static struct sync_reader *impl_from_IWMSyncReader2(IWMSyncReader2 *iface)
@@ -84,60 +82,21 @@ static HRESULT WINAPI WMSyncReader_GetNextSample(IWMSyncReader2 *iface,
 {
     struct sync_reader *reader = impl_from_IWMSyncReader2(iface);
     HRESULT hr = NS_E_NO_MORE_SAMPLES;
-    struct wm_stream *stream;
-    WORD i;
 
     TRACE("reader %p, stream_number %u, sample %p, pts %p, duration %p,"
             " flags %p, output_number %p, ret_stream_number %p.\n",
             reader, stream_number, sample, pts, duration, flags, output_number, ret_stream_number);
 
+    if (!stream_number && !output_number && !ret_stream_number)
+        return E_INVALIDARG;
+
     EnterCriticalSection(&reader->reader.cs);
 
-    if (!stream_number)
-    {
-        if (!output_number && !ret_stream_number)
-        {
-            LeaveCriticalSection(&reader->reader.cs);
-            return E_INVALIDARG;
-        }
-
-        for (i = 0; i < reader->reader.stream_count; ++i)
-        {
-            WORD index = (i + reader->last_read_stream + 1) % reader->reader.stream_count;
-            struct wm_stream *stream = &reader->reader.streams[index];
-
-            if (stream->selection == WMT_OFF)
-                continue;
-
-            hr = wm_reader_get_stream_sample(stream, sample, pts, duration, flags);
-            if (hr == S_OK)
-            {
-                if (output_number)
-                    *output_number = index;
-                if (ret_stream_number)
-                    *ret_stream_number = index + 1;
-            }
-            if (hr != NS_E_NO_MORE_SAMPLES)
-            {
-                reader->last_read_stream = index;
-                break;
-            }
-        }
-    }
-    else
-    {
-        if (!(stream = wm_reader_get_stream_by_stream_number(&reader->reader, stream_number)))
-        {
-            LeaveCriticalSection(&reader->reader.cs);
-            return E_INVALIDARG;
-        }
-
-        hr = wm_reader_get_stream_sample(stream, sample, pts, duration, flags);
-        if (hr == S_OK && output_number)
-            *output_number = stream->index;
-        if (ret_stream_number)
-            *ret_stream_number = stream->index + 1;
-    }
+    hr = wm_reader_get_stream_sample(&reader->reader, stream_number, sample, pts, duration, flags, &stream_number);
+    if (output_number && hr == S_OK)
+        *output_number = stream_number - 1;
+    if (ret_stream_number && (hr == S_OK || stream_number))
+        *ret_stream_number = stream_number;
 
     LeaveCriticalSection(&reader->reader.cs);
     return hr;
-- 
2.35.1




More information about the wine-devel mailing list