For the async reader, and when requesting stream 0.
Signed-off-by: Zebediah Figura zfigura@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;