-- v2: quartz/dsoundrender: Do not send EC_COMPLETE while flushing. quartz/dsoundrender: Send all queued samples to dsound before issuing EC_COMPLETE. quartz/test: Check for EC_COMPLETE more often in test_eos(). quartz/dsoundrender: Get rid of DSoundRenderer_Max_Fill buffer data queue limit.
From: Paul Gofman pgofman@codeweavers.com
And wait when there is no free space available in buffer instead. --- dlls/quartz/dsoundrender.c | 47 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 24 deletions(-)
diff --git a/dlls/quartz/dsoundrender.c b/dlls/quartz/dsoundrender.c index 59609a3c849..bb308ac1566 100644 --- a/dlls/quartz/dsoundrender.c +++ b/dlls/quartz/dsoundrender.c @@ -34,11 +34,6 @@
WINE_DEFAULT_DEBUG_CHANNEL(quartz);
-/* NOTE: buffer can still be filled completely, - * but we start waiting until only this amount is buffered - */ -static const REFERENCE_TIME DSoundRenderer_Max_Fill = 150 * 10000; - struct dsound_render { struct strmbase_filter filter; @@ -155,7 +150,7 @@ static void update_positions(struct dsound_render *filter, DWORD *seqwritepos, D *seqwritepos = filter->writepos; }
-static HRESULT get_write_pos(struct dsound_render *filter, +static void get_write_pos(struct dsound_render *filter, DWORD *ret_writepos, REFERENCE_TIME write_at, DWORD *pfree) { DWORD writepos, min_writepos, playpos; @@ -210,8 +205,6 @@ static HRESULT get_write_pos(struct dsound_render *filter, WARN("Delta too big %s/%s, too far ahead.\n", debugstr_time(delta_t), debugstr_time(max_lag)); aheadbytes = pos_from_time(filter, delta_t); WARN("Advancing %lu bytes.\n", aheadbytes); - if (delta_t >= DSoundRenderer_Max_Fill) - return S_FALSE; *ret_writepos = (min_writepos + aheadbytes) % filter->buf_size; } end: @@ -219,13 +212,6 @@ end: *pfree = playpos - *ret_writepos; else *pfree = filter->buf_size + playpos - *ret_writepos; - if (time_from_pos(filter, filter->buf_size - *pfree) >= DSoundRenderer_Max_Fill) - { - TRACE("Blocked: too full %s / %s\n", debugstr_time(time_from_pos(filter, filter->buf_size - *pfree)), - debugstr_time(DSoundRenderer_Max_Fill)); - return S_FALSE; - } - return S_OK; }
static HRESULT send_sample_data(struct dsound_render *filter, @@ -239,9 +225,30 @@ static HRESULT send_sample_data(struct dsound_render *filter, BYTE *buf1, *buf2;
if (filter->filter.state == State_Running) - hr = get_write_pos(filter, &writepos, tStart, &free); + { + get_write_pos(filter, &writepos, tStart, &free); + hr = free ? S_OK : S_FALSE; + } else + { hr = S_FALSE; + } + + if (hr == S_OK) + { + hr = IDirectSoundBuffer_Lock(filter->dsbuffer, writepos, min(free, size), + (void **)&buf1, &size1, (void **)&buf2, &size2, 0); + if (hr != DS_OK) + { + ERR("Failed to lock sound buffer, hr %#lx.\n", hr); + break; + } + if (!(size1 + size2)) + { + IDirectSoundBuffer_Unlock(filter->dsbuffer, buf1, size1, buf2, size2); + hr = S_FALSE; + } + }
if (hr != S_OK) { @@ -254,14 +261,6 @@ static HRESULT send_sample_data(struct dsound_render *filter, } tStart = -1;
- hr = IDirectSoundBuffer_Lock(filter->dsbuffer, writepos, min(free, size), - (void **)&buf1, &size1, (void **)&buf2, &size2, 0); - if (hr != DS_OK) - { - ERR("Failed to lock sound buffer, hr %#lx.\n", hr); - break; - } - if (data) { memcpy(buf1, data, size1);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/quartz/tests/dsoundrender.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-)
diff --git a/dlls/quartz/tests/dsoundrender.c b/dlls/quartz/tests/dsoundrender.c index 322c1a37ce8..f3ae13231db 100644 --- a/dlls/quartz/tests/dsoundrender.c +++ b/dlls/quartz/tests/dsoundrender.c @@ -1120,10 +1120,19 @@ static void test_eos(IPin *pin, IMemInputPin *input, IMediaSeeking *seeking, IMe
hr = IMediaControl_Pause(control); ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ret = check_ec_complete(eventsrc, 0); + ok(!ret, "Got unexpected EC_COMPLETE.\n"); + hr = send_frame(input); ok(hr == S_OK, "Got hr %#lx.\n", hr); + ret = check_ec_complete(eventsrc, 0); + ok(!ret, "Got unexpected EC_COMPLETE.\n"); + hr = IMediaControl_GetState(control, 1000, &state); ok(hr == S_OK, "Got hr %#lx.\n", hr); + ret = check_ec_complete(eventsrc, 0); + ok(!ret, "Got unexpected EC_COMPLETE.\n"); + hr = IMediaControl_Run(control); ok(hr == S_OK, "Got hr %#lx.\n", hr); ret = check_ec_complete(eventsrc, 0); @@ -1150,25 +1159,38 @@ static void test_eos(IPin *pin, IMemInputPin *input, IMediaSeeking *seeking, IMe ok(hr == S_FALSE, "Got hr %#lx.\n", hr); hr = send_frame(input); ok(hr == S_OK, "Got hr %#lx.\n", hr); + ret = check_ec_complete(eventsrc, 0); + ok(!ret, "Got unexpected EC_COMPLETE.\n");
hr = IPin_BeginFlush(pin); ok(hr == S_OK, "Got hr %#lx.\n", hr); + ret = check_ec_complete(eventsrc, 0); + ok(!ret, "Got unexpected EC_COMPLETE.\n"); hr = IPin_EndOfStream(pin); todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ret = check_ec_complete(eventsrc, 100); + todo_wine ok(!ret, "Got unexpected EC_COMPLETE.\n"); hr = IPin_EndFlush(pin); ok(hr == S_OK, "Got hr %#lx.\n", hr); + ret = check_ec_complete(eventsrc, 0); + ok(!ret, "Got unexpected EC_COMPLETE.\n");
hr = IMediaControl_Stop(control); ok(hr == S_OK, "Got hr %#lx.\n", hr); ret = check_ec_complete(eventsrc, 0); - todo_wine ok(!ret, "Got unexpected EC_COMPLETE.\n"); + ok(!ret, "Got unexpected EC_COMPLETE.\n");
/* Test sending EOS and then flushing or stopping. */
hr = IMediaControl_Pause(control); ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ret = check_ec_complete(eventsrc, 0); + ok(!ret, "Got unexpected EC_COMPLETE.\n"); + hr = send_frame(input); ok(hr == S_OK, "Got hr %#lx.\n", hr); + ret = check_ec_complete(eventsrc, 0); + ok(!ret, "Got unexpected EC_COMPLETE.\n"); hr = IMediaControl_GetState(control, 1000, &state); ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IMediaControl_Run(control); @@ -1183,11 +1205,18 @@ static void test_eos(IPin *pin, IMemInputPin *input, IMediaSeeking *seeking, IMe
hr = IPin_BeginFlush(pin); ok(hr == S_OK, "Got hr %#lx.\n", hr); + ret = check_ec_complete(eventsrc, 0); + ok(!ret, "Got unexpected EC_COMPLETE.\n"); hr = IPin_EndFlush(pin); ok(hr == S_OK, "Got hr %#lx.\n", hr); + ret = check_ec_complete(eventsrc, 0); + ok(!ret, "Got unexpected EC_COMPLETE.\n");
hr = send_frame(input); ok(hr == S_OK, "Got hr %#lx.\n", hr); + ret = check_ec_complete(eventsrc, 0); + ok(!ret, "Got unexpected EC_COMPLETE.\n"); + hr = IPin_EndOfStream(pin); ok(hr == S_OK, "Got hr %#lx.\n", hr); ret = check_ec_complete(eventsrc, 0);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/quartz/dsoundrender.c | 47 ++++++++++++++++++++------------ dlls/quartz/tests/dsoundrender.c | 8 +++--- 2 files changed, 33 insertions(+), 22 deletions(-)
diff --git a/dlls/quartz/dsoundrender.c b/dlls/quartz/dsoundrender.c index bb308ac1566..c7039663494 100644 --- a/dlls/quartz/dsoundrender.c +++ b/dlls/quartz/dsoundrender.c @@ -59,7 +59,7 @@ struct dsound_render * to immediately unblock the streaming thread. */ HANDLE flush_event; REFERENCE_TIME stream_start; - BOOL eos; + BOOL eos, eos_complete_pending;
IDirectSound8 *dsound; LPDIRECTSOUNDBUFFER dsbuffer; @@ -348,6 +348,22 @@ static HRESULT render_sample(struct dsound_render *filter, IMediaSample *pSample return send_sample_data(filter, start, pbSrcStream, cbSrcStream); }
+static void complete_eos(struct dsound_render *filter) +{ + IFilterGraph *graph = filter->filter.graph; + IMediaEventSink *event_sink; + + if (filter->filter.state == State_Running && graph + && SUCCEEDED(IFilterGraph_QueryInterface(graph, + &IID_IMediaEventSink, (void **)&event_sink))) + { + IMediaEventSink_Notify(event_sink, EC_COMPLETE, S_OK, + (LONG_PTR)&filter->filter.IBaseFilter_iface); + IMediaEventSink_Release(event_sink); + } + SetEvent(filter->state_event); +} + static DWORD WINAPI render_thread_run(void *arg) { struct dsound_render *filter = arg; @@ -360,14 +376,18 @@ static DWORD WINAPI render_thread_run(void *arg) { IMediaSample *sample;
- if (filter->eos) + if (filter->eos && (filter->filter.state == State_Stopped || !filter->queued_sample_count)) { + BOOL complete_pending; + + complete_pending = filter->eos_complete_pending; + filter->eos_complete_pending = FALSE; LeaveCriticalSection(&filter->render_cs); - TRACE("Got EOS.\n"); /* Clear the buffer. */ send_sample_data(filter, -1, NULL, filter->buf_size); - - TRACE("Render thread exiting.\n"); + if (complete_pending) + complete_eos(filter); + TRACE("Render thread exiting on EOS.\n"); return 0; }
@@ -509,24 +529,15 @@ static void dsound_render_sink_disconnect(struct strmbase_sink *iface) static HRESULT dsound_render_sink_eos(struct strmbase_sink *iface) { struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin); - IFilterGraph *graph = filter->filter.graph; - IMediaEventSink *event_sink; + BOOL complete_pending;
EnterCriticalSection(&filter->render_cs); filter->eos = TRUE; + filter->eos_complete_pending = complete_pending = (filter->filter.state == State_Running); LeaveCriticalSection(&filter->render_cs); WakeConditionVariable(&filter->render_cv); - - if (filter->filter.state == State_Running && graph - && SUCCEEDED(IFilterGraph_QueryInterface(graph, - &IID_IMediaEventSink, (void **)&event_sink))) - { - IMediaEventSink_Notify(event_sink, EC_COMPLETE, S_OK, - (LONG_PTR)&filter->filter.IBaseFilter_iface); - IMediaEventSink_Release(event_sink); - } - SetEvent(filter->state_event); - + if (!complete_pending) + complete_eos(filter); return S_OK; }
diff --git a/dlls/quartz/tests/dsoundrender.c b/dlls/quartz/tests/dsoundrender.c index f3ae13231db..6486a152f32 100644 --- a/dlls/quartz/tests/dsoundrender.c +++ b/dlls/quartz/tests/dsoundrender.c @@ -1141,9 +1141,9 @@ static void test_eos(IPin *pin, IMemInputPin *input, IMediaSeeking *seeking, IMe hr = IPin_EndOfStream(pin); ok(hr == S_OK, "Got hr %#lx.\n", hr); ret = check_ec_complete(eventsrc, 0); - todo_wine ok(!ret, "Got unexpected EC_COMPLETE.\n"); + ok(!ret, "Got unexpected EC_COMPLETE.\n"); ret = check_ec_complete(eventsrc, 2000); - todo_wine ok(ret == 1, "Expected EC_COMPLETE.\n"); + ok(ret == 1, "Expected EC_COMPLETE.\n");
hr = IMediaSeeking_GetCurrentPosition(seeking, &time); ok(hr == 0xdeadbeef, "Got hr %#lx.\n", hr); @@ -1201,7 +1201,7 @@ static void test_eos(IPin *pin, IMemInputPin *input, IMediaSeeking *seeking, IMe hr = IPin_EndOfStream(pin); ok(hr == S_OK, "Got hr %#lx.\n", hr); ret = check_ec_complete(eventsrc, 0); - todo_wine ok(!ret, "Got unexpected EC_COMPLETE.\n"); + ok(!ret, "Got unexpected EC_COMPLETE.\n");
hr = IPin_BeginFlush(pin); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -1210,7 +1210,7 @@ static void test_eos(IPin *pin, IMemInputPin *input, IMediaSeeking *seeking, IMe hr = IPin_EndFlush(pin); ok(hr == S_OK, "Got hr %#lx.\n", hr); ret = check_ec_complete(eventsrc, 0); - ok(!ret, "Got unexpected EC_COMPLETE.\n"); + todo_wine ok(!ret, "Got unexpected EC_COMPLETE.\n");
hr = send_frame(input); ok(hr == S_OK, "Got hr %#lx.\n", hr);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/quartz/dsoundrender.c | 3 ++- dlls/quartz/tests/dsoundrender.c | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/dlls/quartz/dsoundrender.c b/dlls/quartz/dsoundrender.c index c7039663494..e72b743e39e 100644 --- a/dlls/quartz/dsoundrender.c +++ b/dlls/quartz/dsoundrender.c @@ -380,7 +380,7 @@ static DWORD WINAPI render_thread_run(void *arg) { BOOL complete_pending;
- complete_pending = filter->eos_complete_pending; + complete_pending = WaitForSingleObject(filter->flush_event, 0) && filter->eos_complete_pending; filter->eos_complete_pending = FALSE; LeaveCriticalSection(&filter->render_cs); /* Clear the buffer. */ @@ -554,6 +554,7 @@ static HRESULT dsound_render_sink_end_flush(struct strmbase_sink *iface) struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin);
EnterCriticalSection(&filter->filter.stream_cs); + filter->eos_complete_pending = FALSE; if (filter->eos && filter->filter.state != State_Stopped) { WaitForSingleObject(filter->render_thread, INFINITE); diff --git a/dlls/quartz/tests/dsoundrender.c b/dlls/quartz/tests/dsoundrender.c index 6486a152f32..b036050ff69 100644 --- a/dlls/quartz/tests/dsoundrender.c +++ b/dlls/quartz/tests/dsoundrender.c @@ -1168,8 +1168,8 @@ static void test_eos(IPin *pin, IMemInputPin *input, IMediaSeeking *seeking, IMe ok(!ret, "Got unexpected EC_COMPLETE.\n"); hr = IPin_EndOfStream(pin); todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); - ret = check_ec_complete(eventsrc, 100); - todo_wine ok(!ret, "Got unexpected EC_COMPLETE.\n"); + ret = check_ec_complete(eventsrc, 0); + ok(!ret, "Got unexpected EC_COMPLETE.\n"); hr = IPin_EndFlush(pin); ok(hr == S_OK, "Got hr %#lx.\n", hr); ret = check_ec_complete(eventsrc, 0); @@ -1210,7 +1210,7 @@ static void test_eos(IPin *pin, IMemInputPin *input, IMediaSeeking *seeking, IMe hr = IPin_EndFlush(pin); ok(hr == S_OK, "Got hr %#lx.\n", hr); ret = check_ec_complete(eventsrc, 0); - todo_wine ok(!ret, "Got unexpected EC_COMPLETE.\n"); + ok(!ret, "Got unexpected EC_COMPLETE.\n");
hr = send_frame(input); ok(hr == S_OK, "Got hr %#lx.\n", hr);
v2: - get rid of DSoundRenderer_Max_Fill entirely and wait when buffer has no free space instead; - treat State_Paused the same way as State_Running in render_thread_run(). Only delay EOS completion in State_Run however, doing otherwise breaks the tests as in that case EC_COMPLETE event is available immediately.
This merge request was approved by Elizabeth Figura.