[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