winegstreamer: Deliver samples in PTS order instead of alternating.

For the async reader, and when requesting stream 0.

Signed-off-by: Zebediah Figura <zfigura@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zebediah Figura 2022-03-09 18:56:15 -06:00 committed by Alexandre Julliard
parent ddd9a4239a
commit 446bb1ebf6
4 changed files with 137 additions and 118 deletions

View file

@ -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);

View file

@ -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:

View file

@ -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 consistentsome 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 offsetthe 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;
}
}

View file

@ -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;