Commit 446bb1eb authored by Zebediah Figura's avatar Zebediah Figura Committed by Alexandre Julliard

winegstreamer: Deliver samples in PTS order instead of alternating.

For the async reader, and when requesting stream 0. Signed-off-by: 's avatarZebediah Figura <zfigura@codeweavers.com> Signed-off-by: 's avatarAlexandre Julliard <julliard@winehq.org>
parent ddd9a423
...@@ -180,8 +180,8 @@ HRESULT wm_reader_get_output_props(struct wm_reader *reader, DWORD output, ...@@ -180,8 +180,8 @@ HRESULT wm_reader_get_output_props(struct wm_reader *reader, DWORD output,
IWMOutputMediaProps **props); IWMOutputMediaProps **props);
struct wm_stream *wm_reader_get_stream_by_stream_number(struct wm_reader *reader, struct wm_stream *wm_reader_get_stream_by_stream_number(struct wm_reader *reader,
WORD stream_number); WORD stream_number);
HRESULT wm_reader_get_stream_sample(struct wm_stream *stream, HRESULT wm_reader_get_stream_sample(struct wm_reader *reader, WORD stream_number,
INSSBuffer **sample, QWORD *pts, QWORD *duration, DWORD *flags); INSSBuffer **ret_sample, QWORD *pts, QWORD *duration, DWORD *flags, WORD *ret_stream_number);
HRESULT wm_reader_get_stream_selection(struct wm_reader *reader, HRESULT wm_reader_get_stream_selection(struct wm_reader *reader,
WORD stream_number, WMT_STREAM_SELECTION *selection); WORD stream_number, WMT_STREAM_SELECTION *selection);
void wm_reader_init(struct wm_reader *reader, const struct wm_reader_ops *ops); void wm_reader_init(struct wm_reader *reader, const struct wm_reader_ops *ops);
......
...@@ -72,11 +72,11 @@ static void open_stream(struct async_reader *reader, IWMReaderCallback *callback ...@@ -72,11 +72,11 @@ static void open_stream(struct async_reader *reader, IWMReaderCallback *callback
static DWORD WINAPI stream_thread(void *arg) static DWORD WINAPI stream_thread(void *arg)
{ {
struct async_reader *reader = arg; struct async_reader *reader = arg;
WORD i, stream_count = reader->reader.stream_count;
IWMReaderCallback *callback = reader->callback; IWMReaderCallback *callback = reader->callback;
REFERENCE_TIME start_time; REFERENCE_TIME start_time;
static const DWORD zero; static const DWORD zero;
QWORD pts, duration; QWORD pts, duration;
WORD stream_number;
INSSBuffer *sample; INSSBuffer *sample;
DWORD flags; DWORD flags;
HRESULT hr; HRESULT hr;
...@@ -87,71 +87,56 @@ static DWORD WINAPI stream_thread(void *arg) ...@@ -87,71 +87,56 @@ static DWORD WINAPI stream_thread(void *arg)
while (reader->running) 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) if (reader->user_clock)
continue; {
QWORD user_time = reader->user_time;
hr = wm_reader_get_stream_sample(stream, &sample, &pts, &duration, &flags); if (pts > user_time && reader->reader.callback_advanced)
if (hr == S_OK) 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
{ {
if (reader->user_clock) for (;;)
{ {
QWORD user_time = reader->user_time; 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) if (!reader->running)
{ {
INSSBuffer_Release(sample); INSSBuffer_Release(sample);
goto out; 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 (all_eos) 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);
}
else if (hr == NS_E_NO_MORE_SAMPLES)
{ {
IWMReaderCallback_OnStatus(callback, WMT_END_OF_STREAMING, S_OK, IWMReaderCallback_OnStatus(callback, WMT_END_OF_STREAMING, S_OK,
WMT_TYPE_DWORD, (BYTE *)&zero, reader->context); WMT_TYPE_DWORD, (BYTE *)&zero, reader->context);
...@@ -171,6 +156,12 @@ static DWORD WINAPI stream_thread(void *arg) ...@@ -171,6 +156,12 @@ static DWORD WINAPI stream_thread(void *arg)
LeaveCriticalSection(&reader->stream_cs); LeaveCriticalSection(&reader->stream_cs);
return 0; return 0;
} }
else
{
ERR("Failed to get sample, hr %#lx.\n", hr);
LeaveCriticalSection(&reader->stream_cs);
return 0;
}
} }
out: out:
......
...@@ -1820,30 +1820,98 @@ static const char *get_major_type_string(enum wg_major_type type) ...@@ -1820,30 +1820,98 @@ static const char *get_major_type_string(enum wg_major_type type)
return NULL; return NULL;
} }
HRESULT wm_reader_get_stream_sample(struct wm_stream *stream, /* Find the earliest buffer by PTS.
INSSBuffer **ret_sample, QWORD *pts, QWORD *duration, DWORD *flags) *
* 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)
{
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 = stream->reader->callback_advanced; IWMReaderCallbackAdvanced *callback_advanced = reader->callback_advanced;
struct wg_parser_stream *wg_stream = stream->wg_stream; struct wg_parser_stream *wg_stream;
struct wg_parser_buffer wg_buffer; struct wg_parser_buffer wg_buffer;
struct wm_stream *stream;
DWORD size, capacity; DWORD size, capacity;
INSSBuffer *sample; INSSBuffer *sample;
HRESULT hr; HRESULT hr;
BYTE *data; BYTE *data;
if (stream->selection == WMT_OFF)
return NS_E_INVALID_REQUEST;
if (stream->eos)
return NS_E_NO_MORE_SAMPLES;
for (;;) for (;;)
{ {
if (!wg_parser_stream_get_buffer(wg_stream, &wg_buffer)) if (!stream_number)
{
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
{ {
stream->eos = true; if (!(stream = wm_reader_get_stream_by_stream_number(reader, stream_number)))
TRACE("End of stream.\n"); {
return NS_E_NO_MORE_SAMPLES; 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); 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, ...@@ -1920,6 +1988,7 @@ HRESULT wm_reader_get_stream_sample(struct wm_stream *stream,
*flags |= WM_SF_CLEANPOINT; *flags |= WM_SF_CLEANPOINT;
*ret_sample = sample; *ret_sample = sample;
*ret_stream_number = stream_number;
return S_OK; return S_OK;
} }
} }
......
...@@ -25,8 +25,6 @@ struct sync_reader ...@@ -25,8 +25,6 @@ struct sync_reader
struct wm_reader reader; struct wm_reader reader;
IWMSyncReader2 IWMSyncReader2_iface; IWMSyncReader2 IWMSyncReader2_iface;
WORD last_read_stream;
}; };
static struct sync_reader *impl_from_IWMSyncReader2(IWMSyncReader2 *iface) static struct sync_reader *impl_from_IWMSyncReader2(IWMSyncReader2 *iface)
...@@ -84,60 +82,21 @@ static HRESULT WINAPI WMSyncReader_GetNextSample(IWMSyncReader2 *iface, ...@@ -84,60 +82,21 @@ static HRESULT WINAPI WMSyncReader_GetNextSample(IWMSyncReader2 *iface,
{ {
struct sync_reader *reader = impl_from_IWMSyncReader2(iface); struct sync_reader *reader = impl_from_IWMSyncReader2(iface);
HRESULT hr = NS_E_NO_MORE_SAMPLES; 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," TRACE("reader %p, stream_number %u, sample %p, pts %p, duration %p,"
" flags %p, output_number %p, ret_stream_number %p.\n", " flags %p, output_number %p, ret_stream_number %p.\n",
reader, stream_number, sample, pts, duration, flags, output_number, ret_stream_number); 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); EnterCriticalSection(&reader->reader.cs);
if (!stream_number) hr = wm_reader_get_stream_sample(&reader->reader, stream_number, sample, pts, duration, flags, &stream_number);
{ if (output_number && hr == S_OK)
if (!output_number && !ret_stream_number) *output_number = stream_number - 1;
{ if (ret_stream_number && (hr == S_OK || stream_number))
LeaveCriticalSection(&reader->reader.cs); *ret_stream_number = stream_number;
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;
}
LeaveCriticalSection(&reader->reader.cs); LeaveCriticalSection(&reader->reader.cs);
return hr; return hr;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment