This is a more correct version of 6849, plus some other changes not included in 6849, and should supersede the remaining patches in that merge request.
It is also a large and risky change and may not be suitable this close to code freeze.
From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/quartz/dsoundrender.c | 53 +++++++++++--------------------------- 1 file changed, 15 insertions(+), 38 deletions(-)
diff --git a/dlls/quartz/dsoundrender.c b/dlls/quartz/dsoundrender.c index f240afbf1f8..1ac9baebfda 100644 --- a/dlls/quartz/dsoundrender.c +++ b/dlls/quartz/dsoundrender.c @@ -149,7 +149,7 @@ static void update_positions(struct dsound_render *filter, DWORD *seqwritepos, D }
static HRESULT get_write_pos(struct dsound_render *filter, - DWORD *ret_writepos, REFERENCE_TIME write_at, DWORD *pfree, DWORD *skip) + DWORD *ret_writepos, REFERENCE_TIME write_at, DWORD *pfree) { DWORD writepos, min_writepos, playpos; REFERENCE_TIME max_lag = 50 * 10000; @@ -168,7 +168,6 @@ static HRESULT get_write_pos(struct dsound_render *filter, if (writepos == min_writepos) max_lag = 0;
- *skip = 0; if (write_at < 0) { *ret_writepos = writepos; @@ -184,36 +183,21 @@ static HRESULT get_write_pos(struct dsound_render *filter, /* cur: current time of play position */ /* writepos_t: current time of our pointer play position */ delta_t = write_at - writepos_t; - if (delta_t >= -max_lag && delta_t <= max_lag) + TRACE("Last sample end %s, this sample start %s.\n", + debugstr_time(writepos_t), debugstr_time(write_at)); + if (delta_t <= max_lag) { - TRACE("Continuing from old position\n"); - *ret_writepos = writepos; - } - else if (delta_t < 0) - { - REFERENCE_TIME past, min_writepos_t; - WARN("Delta too big %s/%s, overwriting old data or even skipping.\n", + /* If the stream time of a sample is in the past (i.e. the sample is + * late, in which case write_at < min_writepos), or earlier than the + * last sample's end time, or there is a gap between the last sample's + * end time and this sample less than a certain threshold, native + * simply treats the two as continuous, ignoring this sample's play time + * and rendering the whole sample even if it's late. */ + TRACE("Difference %s is less than threshold %s; treating sample as continuous.\n", debugstr_time(delta_t), debugstr_time(max_lag)); - if (min_writepos >= playpos) - min_writepos_t = cur + time_from_pos(filter, min_writepos - playpos); - else - min_writepos_t = cur + time_from_pos(filter, filter->buf_size - playpos + min_writepos); - past = min_writepos_t - write_at; - if (past >= 0) - { - DWORD skipbytes = pos_from_time(filter, past); - WARN("Skipping %lu bytes.\n", skipbytes); - *skip = skipbytes; - *ret_writepos = min_writepos; - } - else - { - DWORD aheadbytes = pos_from_time(filter, -past); - WARN("Advancing %lu bytes.\n", aheadbytes); - *ret_writepos = (min_writepos + aheadbytes) % filter->buf_size; - } + *ret_writepos = writepos; } - else /* delta_t > 0 */ + else { DWORD aheadbytes; WARN("Delta too big %s/%s, too far ahead.\n", debugstr_time(delta_t), debugstr_time(max_lag)); @@ -259,11 +243,11 @@ static HRESULT send_sample_data(struct dsound_render *filter,
while (size && filter->filter.state != State_Stopped) { - DWORD writepos, skip = 0, free, size1, size2, ret; + DWORD writepos, free, size1, size2, ret; BYTE *buf1, *buf2;
if (filter->filter.state == State_Running) - hr = get_write_pos(filter, &writepos, tStart, &free, &skip); + hr = get_write_pos(filter, &writepos, tStart, &free); else hr = S_FALSE;
@@ -278,13 +262,6 @@ static HRESULT send_sample_data(struct dsound_render *filter, } tStart = -1;
- if (skip) - FIXME("Sample dropped %lu of %lu bytes.\n", skip, size); - if (skip >= size) - return S_OK; - data += skip; - size -= skip; - hr = IDirectSoundBuffer_Lock(filter->dsbuffer, writepos, min(free, size), (void **)&buf1, &size1, (void **)&buf2, &size2, 0); if (hr != DS_OK)
From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/quartz/dsoundrender.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/quartz/dsoundrender.c b/dlls/quartz/dsoundrender.c index 1ac9baebfda..81ec532e420 100644 --- a/dlls/quartz/dsoundrender.c +++ b/dlls/quartz/dsoundrender.c @@ -237,7 +237,7 @@ static HRESULT handle_eos(struct dsound_render *filter) }
static HRESULT send_sample_data(struct dsound_render *filter, - REFERENCE_TIME tStart, REFERENCE_TIME tStop, const BYTE *data, DWORD size) + REFERENCE_TIME tStart, const BYTE *data, DWORD size) { HRESULT hr;
@@ -347,7 +347,7 @@ static HRESULT render_sample(struct dsound_render *filter, IMediaSample *pSample }
cbSrcStream = IMediaSample_GetActualDataLength(pSample); - return send_sample_data(filter, tStart, tStop, pbSrcStream, cbSrcStream); + return send_sample_data(filter, tStart, pbSrcStream, cbSrcStream); }
static HRESULT WINAPI dsound_render_sink_Receive(struct strmbase_sink *iface, IMediaSample *sample)
From: Elizabeth Figura zfigura@codeweavers.com
Ignoring their start time. This matches native behaviour. --- dlls/quartz/dsoundrender.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/dlls/quartz/dsoundrender.c b/dlls/quartz/dsoundrender.c index 81ec532e420..bf750d417ac 100644 --- a/dlls/quartz/dsoundrender.c +++ b/dlls/quartz/dsoundrender.c @@ -321,9 +321,9 @@ static HRESULT configure_buffer(struct dsound_render *filter, IMediaSample *pSam
static HRESULT render_sample(struct dsound_render *filter, IMediaSample *pSample) { + REFERENCE_TIME start = -1, stop = -1; LPBYTE pbSrcStream = NULL; LONG cbSrcStream = 0; - REFERENCE_TIME tStart, tStop; HRESULT hr;
hr = IMediaSample_GetPointer(pSample, &pbSrcStream); @@ -333,11 +333,11 @@ static HRESULT render_sample(struct dsound_render *filter, IMediaSample *pSample return hr; }
- hr = IMediaSample_GetTime(pSample, &tStart, &tStop); - if (FAILED(hr)) + if (IMediaSample_IsDiscontinuity(pSample) == S_OK + && FAILED(hr = IMediaSample_GetTime(pSample, &start, &stop))) { ERR("Failed to get sample time, hr %#lx.\n", hr); - tStart = tStop = -1; + start = stop = -1; }
if (IMediaSample_IsPreroll(pSample) == S_OK) @@ -347,7 +347,7 @@ static HRESULT render_sample(struct dsound_render *filter, IMediaSample *pSample }
cbSrcStream = IMediaSample_GetActualDataLength(pSample); - return send_sample_data(filter, tStart, pbSrcStream, cbSrcStream); + return send_sample_data(filter, start, pbSrcStream, cbSrcStream); }
static HRESULT WINAPI dsound_render_sink_Receive(struct strmbase_sink *iface, IMediaSample *sample)
From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/quartz/tests/dsoundrender.c | 183 ++++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 5 deletions(-)
diff --git a/dlls/quartz/tests/dsoundrender.c b/dlls/quartz/tests/dsoundrender.c index 269c590f8ee..c1952d0dd89 100644 --- a/dlls/quartz/tests/dsoundrender.c +++ b/dlls/quartz/tests/dsoundrender.c @@ -595,6 +595,7 @@ struct testfilter { struct strmbase_filter filter; struct strmbase_source source; + IMediaSeeking IMediaSeeking_iface; };
static inline struct testfilter *impl_from_strmbase_filter(struct strmbase_filter *iface) @@ -625,8 +626,17 @@ static const struct strmbase_filter_ops testfilter_ops =
static HRESULT testsource_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) { + struct testfilter *filter = impl_from_strmbase_filter(iface->filter); + ok(!IsEqualGUID(iid, &IID_IQualityControl), "Unexpected query for IQualityControl.\n"); - return E_NOINTERFACE; + + if (IsEqualGUID(iid, &IID_IMediaSeeking)) + *out = &filter->IMediaSeeking_iface; + else + return E_NOINTERFACE; + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; }
static HRESULT WINAPI testsource_DecideAllocator(struct strmbase_source *iface, @@ -642,11 +652,163 @@ static const struct strmbase_source_ops testsource_ops = .pfnDecideAllocator = testsource_DecideAllocator, };
+static struct testfilter *impl_from_IMediaSeeking(IMediaSeeking *iface) +{ + return CONTAINING_RECORD(iface, struct testfilter, IMediaSeeking_iface); +} + +static HRESULT WINAPI testseek_QueryInterface(IMediaSeeking *iface, REFIID iid, void **out) +{ + struct testfilter *filter = impl_from_IMediaSeeking(iface); + return IUnknown_QueryInterface(filter->filter.outer_unk, iid, out); +} + +static ULONG WINAPI testseek_AddRef(IMediaSeeking *iface) +{ + struct testfilter *filter = impl_from_IMediaSeeking(iface); + return IUnknown_AddRef(filter->filter.outer_unk); +} + +static ULONG WINAPI testseek_Release(IMediaSeeking *iface) +{ + struct testfilter *filter = impl_from_IMediaSeeking(iface); + return IUnknown_Release(filter->filter.outer_unk); +} + +static HRESULT WINAPI testseek_GetCapabilities(IMediaSeeking *iface, DWORD *caps) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testseek_CheckCapabilities(IMediaSeeking *iface, DWORD *caps) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testseek_IsFormatSupported(IMediaSeeking *iface, const GUID *format) +{ + if (winetest_debug > 1) trace("IsFormatSupported(%s)\n", debugstr_guid(format)); + return E_NOTIMPL; +} + +static HRESULT WINAPI testseek_QueryPreferredFormat(IMediaSeeking *iface, GUID *format) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testseek_GetTimeFormat(IMediaSeeking *iface, GUID *format) +{ + if (winetest_debug > 1) trace("GetTimeFormat()\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testseek_IsUsingTimeFormat(IMediaSeeking *iface, const GUID *format) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testseek_SetTimeFormat(IMediaSeeking *iface, const GUID *format) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testseek_GetDuration(IMediaSeeking *iface, LONGLONG *duration) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testseek_GetStopPosition(IMediaSeeking *iface, LONGLONG *stop) +{ + if (winetest_debug > 1) trace("GetStopPosition()\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testseek_GetCurrentPosition(IMediaSeeking *iface, LONGLONG *current) +{ + if (winetest_debug > 1) trace("GetCurrentPosition()\n"); + return 0xdeadbeef; +} + +static HRESULT WINAPI testseek_ConvertTimeFormat(IMediaSeeking *iface, LONGLONG *target, + const GUID *target_format, LONGLONG source, const GUID *source_format) +{ + todo_wine ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testseek_SetPositions(IMediaSeeking *iface, LONGLONG *current, + DWORD current_flags, LONGLONG *stop, DWORD stop_flags) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testseek_GetPositions(IMediaSeeking *iface, LONGLONG *current, LONGLONG *stop) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testseek_GetAvailable(IMediaSeeking *iface, LONGLONG *earliest, LONGLONG *latest) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testseek_SetRate(IMediaSeeking *iface, double rate) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testseek_GetRate(IMediaSeeking *iface, double *rate) +{ + if (winetest_debug > 1) trace("GetRate()\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testseek_GetPreroll(IMediaSeeking *iface, LONGLONG *preroll) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static const IMediaSeekingVtbl testseek_vtbl = +{ + testseek_QueryInterface, + testseek_AddRef, + testseek_Release, + testseek_GetCapabilities, + testseek_CheckCapabilities, + testseek_IsFormatSupported, + testseek_QueryPreferredFormat, + testseek_GetTimeFormat, + testseek_IsUsingTimeFormat, + testseek_SetTimeFormat, + testseek_GetDuration, + testseek_GetStopPosition, + testseek_GetCurrentPosition, + testseek_ConvertTimeFormat, + testseek_SetPositions, + testseek_GetPositions, + testseek_GetAvailable, + testseek_SetRate, + testseek_GetRate, + testseek_GetPreroll, +}; + static void testfilter_init(struct testfilter *filter) { static const GUID clsid = {0xabacab}; strmbase_filter_init(&filter->filter, NULL, &clsid, &testfilter_ops); strmbase_source_init(&filter->source, &filter->filter, L"", &testsource_ops); + filter->IMediaSeeking_iface.lpVtbl = &testseek_vtbl; }
static void test_allocator(IMemInputPin *input) @@ -756,9 +918,10 @@ static HRESULT send_frame(IMemInputPin *sink) return ret; }
-static void test_filter_state(IMemInputPin *input, IMediaControl *control) +static void test_filter_state(IMemInputPin *input, IMediaSeeking *seeking, IMediaControl *control) { OAFilterState state; + LONGLONG time; HRESULT hr;
hr = send_frame(input); @@ -816,6 +979,9 @@ static void test_filter_state(IMemInputPin *input, IMediaControl *control) hr = send_frame(input); ok(hr == S_OK, "Got hr %#lx.\n", hr);
+ hr = IMediaSeeking_GetCurrentPosition(seeking, &time); + todo_wine ok(hr == 0xdeadbeef, "Got hr %#lx.\n", hr); + hr = IMediaControl_GetState(control, 0, &state); ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -910,10 +1076,11 @@ static unsigned int check_ec_complete(IMediaEvent *eventsrc, DWORD timeout) return ret; }
-static void test_eos(IPin *pin, IMemInputPin *input, IMediaControl *control) +static void test_eos(IPin *pin, IMemInputPin *input, IMediaSeeking *seeking, IMediaControl *control) { IMediaEvent *eventsrc; OAFilterState state; + LONGLONG time; HRESULT hr; BOOL ret;
@@ -966,6 +1133,9 @@ static void test_eos(IPin *pin, IMemInputPin *input, IMediaControl *control) ret = check_ec_complete(eventsrc, 2000); todo_wine ok(ret == 1, "Expected EC_COMPLETE.\n");
+ hr = IMediaSeeking_GetCurrentPosition(seeking, &time); + ok(hr == 0xdeadbeef, "Got hr %#lx.\n", hr); + hr = IMediaControl_Stop(control); ok(hr == S_OK, "Got hr %#lx.\n", hr); ret = check_ec_complete(eventsrc, 0); @@ -1051,6 +1221,7 @@ static void test_connect_pin(void) IBaseFilter *filter = create_dsound_render(); struct testfilter source; IMemAllocator *allocator; + IMediaSeeking *seeking; IMediaControl *control; IFilterGraph2 *graph; IMemInputPin *input; @@ -1067,6 +1238,7 @@ static void test_connect_pin(void) IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
IBaseFilter_FindPin(filter, sink_id, &pin); + IBaseFilter_QueryInterface(filter, &IID_IMediaSeeking, (void **)&seeking);
peer = (IPin *)0xdeadbeef; hr = IPin_ConnectedTo(pin, &peer); @@ -1120,9 +1292,9 @@ static void test_connect_pin(void) hr = IMemInputPin_ReceiveCanBlock(input); ok(hr == S_OK, "Got hr %#lx.\n", hr);
- test_filter_state(input, control); + test_filter_state(input, seeking, control); test_flushing(pin, input, control); - test_eos(pin, input, control); + test_eos(pin, input, seeking, control);
hr = IFilterGraph2_Disconnect(graph, pin); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -1146,6 +1318,7 @@ static void test_connect_pin(void) IMediaControl_Release(control); ref = IFilterGraph2_Release(graph); ok(!ref, "Got outstanding refcount %ld.\n", ref); + IMediaSeeking_Release(seeking); ref = IBaseFilter_Release(filter); ok(!ref, "Got outstanding refcount %ld.\n", ref); ref = IBaseFilter_Release(&source.filter.IBaseFilter_iface);
From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/quartz/dsoundrender.c | 7 ------- dlls/quartz/tests/dsoundrender.c | 4 ++-- 2 files changed, 2 insertions(+), 9 deletions(-)
diff --git a/dlls/quartz/dsoundrender.c b/dlls/quartz/dsoundrender.c index bf750d417ac..7ee4a680b5b 100644 --- a/dlls/quartz/dsoundrender.c +++ b/dlls/quartz/dsoundrender.c @@ -353,7 +353,6 @@ static HRESULT render_sample(struct dsound_render *filter, IMediaSample *pSample static HRESULT WINAPI dsound_render_sink_Receive(struct strmbase_sink *iface, IMediaSample *sample) { struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin); - REFERENCE_TIME start, stop; HRESULT hr;
TRACE("filter %p, sample %p.\n", filter, sample); @@ -367,9 +366,6 @@ static HRESULT WINAPI dsound_render_sink_Receive(struct strmbase_sink *iface, IM if (FAILED(hr = configure_buffer(filter, sample))) return hr;
- if (filter->filter.clock && SUCCEEDED(IMediaSample_GetTime(sample, &start, &stop))) - strmbase_passthrough_update_time(&filter->passthrough, start); - if (filter->filter.state == State_Paused) SetEvent(filter->state_event);
@@ -469,7 +465,6 @@ static HRESULT dsound_render_sink_eos(struct strmbase_sink *iface) (LONG_PTR)&filter->filter.IBaseFilter_iface); IMediaEventSink_Release(event_sink); } - strmbase_passthrough_eos(&filter->passthrough); SetEvent(filter->state_event);
handle_eos(filter); @@ -496,7 +491,6 @@ static HRESULT dsound_render_sink_end_flush(struct strmbase_sink *iface) EnterCriticalSection(&filter->filter.stream_cs);
filter->eos = FALSE; - strmbase_passthrough_invalidate_time(&filter->passthrough); ResetEvent(filter->flush_event);
if (filter->dsbuffer) @@ -638,7 +632,6 @@ static HRESULT dsound_render_cleanup_stream(struct strmbase_filter *iface) { struct dsound_render *filter = impl_from_strmbase_filter(iface);
- strmbase_passthrough_invalidate_time(&filter->passthrough); SetEvent(filter->state_event); SetEvent(filter->flush_event);
diff --git a/dlls/quartz/tests/dsoundrender.c b/dlls/quartz/tests/dsoundrender.c index c1952d0dd89..1864c0fcf01 100644 --- a/dlls/quartz/tests/dsoundrender.c +++ b/dlls/quartz/tests/dsoundrender.c @@ -738,7 +738,7 @@ static HRESULT WINAPI testseek_GetCurrentPosition(IMediaSeeking *iface, LONGLONG static HRESULT WINAPI testseek_ConvertTimeFormat(IMediaSeeking *iface, LONGLONG *target, const GUID *target_format, LONGLONG source, const GUID *source_format) { - todo_wine ok(0, "Unexpected call.\n"); + ok(0, "Unexpected call.\n"); return E_NOTIMPL; }
@@ -980,7 +980,7 @@ static void test_filter_state(IMemInputPin *input, IMediaSeeking *seeking, IMedi ok(hr == S_OK, "Got hr %#lx.\n", hr);
hr = IMediaSeeking_GetCurrentPosition(seeking, &time); - todo_wine ok(hr == 0xdeadbeef, "Got hr %#lx.\n", hr); + ok(hr == 0xdeadbeef, "Got hr %#lx.\n", hr);
hr = IMediaControl_GetState(control, 0, &state); ok(hr == S_OK, "Got hr %#lx.\n", hr);
From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/quartz/dsoundrender.c | 6 ------ dlls/quartz/tests/dsoundrender.c | 3 +++ 2 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/dlls/quartz/dsoundrender.c b/dlls/quartz/dsoundrender.c index 7ee4a680b5b..1dd6d456ae5 100644 --- a/dlls/quartz/dsoundrender.c +++ b/dlls/quartz/dsoundrender.c @@ -340,12 +340,6 @@ static HRESULT render_sample(struct dsound_render *filter, IMediaSample *pSample start = stop = -1; }
- if (IMediaSample_IsPreroll(pSample) == S_OK) - { - TRACE("Preroll!\n"); - return S_OK; - } - cbSrcStream = IMediaSample_GetActualDataLength(pSample); return send_sample_data(filter, start, pbSrcStream, cbSrcStream); } diff --git a/dlls/quartz/tests/dsoundrender.c b/dlls/quartz/tests/dsoundrender.c index 1864c0fcf01..49e435b8cce 100644 --- a/dlls/quartz/tests/dsoundrender.c +++ b/dlls/quartz/tests/dsoundrender.c @@ -906,6 +906,9 @@ static HRESULT send_frame(IMemInputPin *sink) hr = IMediaSample_SetTime(sample, &start_time, &end_time); ok(hr == S_OK, "Got hr %#lx.\n", hr);
+ hr = IMediaSample_SetPreroll(sample, TRUE); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + params->sink = sink; params->sample = sample; thread = CreateThread(NULL, 0, frame_thread, params, 0, NULL);
From: Elizabeth Figura zfigura@codeweavers.com
As tests show, the DirectSound renderer will return immediately from Receive() and EndOfStream(), holding the sample to be rendered arbitrarily later. This is also necessary to properly clear the buffer if the renderer is starved for a long period of time. --- dlls/quartz/dsoundrender.c | 106 ++++++++++++++++++++++++++++--- dlls/quartz/main.c | 27 ++++++++ dlls/quartz/quartz_private.h | 3 + dlls/quartz/tests/dsoundrender.c | 10 +-- 4 files changed, 131 insertions(+), 15 deletions(-)
diff --git a/dlls/quartz/dsoundrender.c b/dlls/quartz/dsoundrender.c index 1dd6d456ae5..c9fd5ace1dc 100644 --- a/dlls/quartz/dsoundrender.c +++ b/dlls/quartz/dsoundrender.c @@ -50,6 +50,13 @@ struct dsound_render
struct strmbase_sink sink;
+ HANDLE render_thread; + CRITICAL_SECTION render_cs; + CONDITION_VARIABLE render_cv; + IMediaSample **queued_samples; + size_t queued_sample_count, queued_samples_capacity; + bool render_thread_shutdown; + /* Signaled when the filter has completed a state change. The filter waits * for this event in IBaseFilter::GetState(). */ HANDLE state_event; @@ -243,7 +250,7 @@ static HRESULT send_sample_data(struct dsound_render *filter,
while (size && filter->filter.state != State_Stopped) { - DWORD writepos, free, size1, size2, ret; + DWORD writepos, free, size1, size2; BYTE *buf1, *buf2;
if (filter->filter.state == State_Running) @@ -253,11 +260,11 @@ static HRESULT send_sample_data(struct dsound_render *filter,
if (hr != S_OK) { - ret = WaitForSingleObject(filter->flush_event, 10); - if (filter->sink.flushing || filter->filter.state == State_Stopped) - return filter->filter.state == State_Paused ? S_OK : VFW_E_WRONG_STATE; - if (ret != WAIT_TIMEOUT) - ERR("WaitForSingleObject() returned %ld.\n", ret); + if (!WaitForSingleObject(filter->flush_event, 10)) + { + TRACE("Flush signaled; discarding sample.\n"); + return VFW_E_WRONG_STATE; + } continue; } tStart = -1; @@ -344,6 +351,42 @@ static HRESULT render_sample(struct dsound_render *filter, IMediaSample *pSample return send_sample_data(filter, start, pbSrcStream, cbSrcStream); }
+static DWORD WINAPI render_thread_run(void *arg) +{ + struct dsound_render *filter = arg; + + TRACE("Render thread started.\n"); + + EnterCriticalSection(&filter->render_cs); + + while (!filter->render_thread_shutdown) + { + IMediaSample *sample; + + if (!filter->queued_sample_count) + { + SleepConditionVariableCS(&filter->render_cv, &filter->render_cs, INFINITE); + continue; + } + + sample = filter->queued_samples[0]; + if (--filter->queued_sample_count) + memmove(filter->queued_samples, filter->queued_samples + 1, + filter->queued_sample_count * sizeof(*filter->queued_samples)); + + LeaveCriticalSection(&filter->render_cs); + + render_sample(filter, sample); + IMediaSample_Release(sample); + + EnterCriticalSection(&filter->render_cs); + } + + LeaveCriticalSection(&filter->render_cs); + TRACE("Render thread exiting.\n"); + return 0; +} + static HRESULT WINAPI dsound_render_sink_Receive(struct strmbase_sink *iface, IMediaSample *sample) { struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin); @@ -363,7 +406,21 @@ static HRESULT WINAPI dsound_render_sink_Receive(struct strmbase_sink *iface, IM if (filter->filter.state == State_Paused) SetEvent(filter->state_event);
- return render_sample(filter, sample); + EnterCriticalSection(&filter->render_cs); + + if (!array_reserve((void **)&filter->queued_samples, &filter->queued_samples_capacity, + filter->queued_sample_count + 1, sizeof(*filter->queued_samples))) + { + LeaveCriticalSection(&filter->render_cs); + return E_OUTOFMEMORY; + } + + filter->queued_samples[filter->queued_sample_count++] = sample; + IMediaSample_AddRef(sample); + + LeaveCriticalSection(&filter->render_cs); + WakeConditionVariable(&filter->render_cv); + return S_OK; }
static HRESULT dsound_render_sink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) @@ -536,6 +593,9 @@ static void dsound_render_destroy(struct strmbase_filter *iface) CloseHandle(filter->state_event); CloseHandle(filter->flush_event);
+ filter->render_cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&filter->render_cs); + strmbase_passthrough_cleanup(&filter->passthrough); strmbase_filter_cleanup(&filter->filter); free(filter); @@ -577,12 +637,19 @@ static HRESULT dsound_render_init_stream(struct strmbase_filter *iface) { struct dsound_render *filter = impl_from_strmbase_filter(iface);
- if (filter->sink.pin.peer) - ResetEvent(filter->state_event); filter->eos = FALSE; ResetEvent(filter->flush_event);
- return filter->sink.pin.peer ? S_FALSE : S_OK; + if (!filter->sink.pin.peer) + return S_OK; + + ResetEvent(filter->state_event); + + filter->render_thread_shutdown = false; + if (!(filter->render_thread = CreateThread(NULL, 0, render_thread_run, filter, 0, NULL))) + return HRESULT_FROM_WIN32(GetLastError()); + + return S_FALSE; }
static HRESULT dsound_render_start_stream(struct strmbase_filter *iface, REFERENCE_TIME start) @@ -629,6 +696,21 @@ static HRESULT dsound_render_cleanup_stream(struct strmbase_filter *iface) SetEvent(filter->state_event); SetEvent(filter->flush_event);
+ if (filter->render_thread) + { + EnterCriticalSection(&filter->render_cs); + filter->render_thread_shutdown = true; + LeaveCriticalSection(&filter->render_cs); + WakeConditionVariable(&filter->render_cv); + WaitForSingleObject(filter->render_thread, INFINITE); + CloseHandle(filter->render_thread); + filter->render_thread = NULL; + + for (unsigned int i = 0; i < filter->queued_sample_count; ++i) + IMediaSample_Release(filter->queued_samples[i]); + filter->queued_sample_count = 0; + } + return S_OK; }
@@ -1020,6 +1102,10 @@ HRESULT dsound_render_create(IUnknown *outer, IUnknown **out) object->IAMDirectSound_iface.lpVtbl = &direct_sound_vtbl; object->IQualityControl_iface.lpVtbl = &quality_control_vtbl;
+ InitializeCriticalSectionEx(&object->render_cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO); + object->render_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": dsound_render.render_cs"); + InitializeConditionVariable(&object->render_cv); + TRACE("Created DirectSound renderer %p.\n", object); *out = &object->filter.IUnknown_inner;
diff --git a/dlls/quartz/main.c b/dlls/quartz/main.c index b226ec353d1..d8da7bde5fa 100644 --- a/dlls/quartz/main.c +++ b/dlls/quartz/main.c @@ -28,6 +28,33 @@ extern BOOL WINAPI QUARTZ_DllMain(HINSTANCE, DWORD, LPVOID); extern HRESULT WINAPI QUARTZ_DllRegisterServer(void); extern HRESULT WINAPI QUARTZ_DllUnregisterServer(void);
+bool array_reserve(void **elements, size_t *capacity, size_t count, size_t size) +{ + unsigned int new_capacity, max_capacity; + void *new_elements; + + if (count <= *capacity) + return true; + + max_capacity = ~(size_t)0 / size; + if (count > max_capacity) + return false; + + new_capacity = max(4, *capacity); + while (new_capacity < count && new_capacity <= max_capacity / 2) + new_capacity *= 2; + if (new_capacity < count) + new_capacity = max_capacity; + + if (!(new_elements = realloc(*elements, new_capacity * size))) + return false; + + *elements = new_elements; + *capacity = new_capacity; + + return true; +} + BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) { if (reason == DLL_PROCESS_DETACH && !reserved) diff --git a/dlls/quartz/quartz_private.h b/dlls/quartz/quartz_private.h index e219ed01583..5f568f84f75 100644 --- a/dlls/quartz/quartz_private.h +++ b/dlls/quartz/quartz_private.h @@ -21,6 +21,7 @@ #define __QUARTZ_PRIVATE_INCLUDED__
#include <stdarg.h> +#include <stdbool.h> #include <wchar.h>
#define COBJMACROS @@ -48,6 +49,8 @@ #define MEDIATIME_FROM_BYTES(x) ((LONGLONG)(x) * 10000000) #define BYTES_FROM_MEDIATIME(time) ((time) / 10000000)
+bool array_reserve(void **elements, size_t *capacity, size_t count, size_t size); + HRESULT acm_wrapper_create(IUnknown *outer, IUnknown **out); HRESULT async_reader_create(IUnknown *outer, IUnknown **out); HRESULT avi_dec_create(IUnknown *outer, IUnknown **out); diff --git a/dlls/quartz/tests/dsoundrender.c b/dlls/quartz/tests/dsoundrender.c index 49e435b8cce..322c1a37ce8 100644 --- a/dlls/quartz/tests/dsoundrender.c +++ b/dlls/quartz/tests/dsoundrender.c @@ -947,7 +947,7 @@ static void test_filter_state(IMemInputPin *input, IMediaSeeking *seeking, IMedi ok(hr == VFW_S_STATE_INTERMEDIATE, "Got hr %#lx.\n", hr);
hr = send_frame(input); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(hr == S_OK, "Got hr %#lx.\n", hr);
hr = IMediaControl_GetState(control, 1000, &state); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -965,7 +965,7 @@ static void test_filter_state(IMemInputPin *input, IMediaSeeking *seeking, IMedi ok(hr == VFW_S_STATE_INTERMEDIATE, "Got hr %#lx.\n", hr);
hr = send_frame(input); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(hr == S_OK, "Got hr %#lx.\n", hr);
hr = IMediaControl_GetState(control, 1000, &state); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -1020,7 +1020,7 @@ static void test_flushing(IPin *pin, IMemInputPin *input, IMediaControl *control ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
hr = send_frame(input); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(hr == S_OK, "Got hr %#lx.\n", hr);
hr = IMediaControl_GetState(control, 0, &state); ok(hr == S_OK, "Got hr %#lx.\n", hr); @@ -1121,7 +1121,7 @@ static void test_eos(IPin *pin, IMemInputPin *input, IMediaSeeking *seeking, IMe hr = IMediaControl_Pause(control); ok(hr == S_FALSE, "Got hr %#lx.\n", hr); hr = send_frame(input); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IMediaControl_GetState(control, 1000, &state); ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IMediaControl_Run(control); @@ -1168,7 +1168,7 @@ static void test_eos(IPin *pin, IMemInputPin *input, IMediaSeeking *seeking, IMe hr = IMediaControl_Pause(control); ok(hr == S_FALSE, "Got hr %#lx.\n", hr); hr = send_frame(input); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IMediaControl_GetState(control, 1000, &state); ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IMediaControl_Run(control);
From: Elizabeth Figura zfigura@codeweavers.com
Inspired by a patch by Rémi Bernon.
This allows us to clear the buffer a bit earlier than waiting for an underrun does, and avoids the spurious FIXME message in that case. It also unifies code paths a bit.
This change also correctly clears the buffer for 8-bit formats. --- dlls/quartz/dsoundrender.c | 58 ++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 27 deletions(-)
diff --git a/dlls/quartz/dsoundrender.c b/dlls/quartz/dsoundrender.c index c9fd5ace1dc..46782022963 100644 --- a/dlls/quartz/dsoundrender.c +++ b/dlls/quartz/dsoundrender.c @@ -228,21 +228,6 @@ end: return S_OK; }
-static HRESULT handle_eos(struct dsound_render *filter) -{ - while (filter->filter.state == State_Running) - { - DWORD pos1, pos2; - update_positions(filter, &pos1, &pos2); - if (pos1 == pos2) - break; - - WaitForSingleObject(filter->flush_event, 10); - } - - return S_OK; -} - static HRESULT send_sample_data(struct dsound_render *filter, REFERENCE_TIME tStart, const BYTE *data, DWORD size) { @@ -276,14 +261,27 @@ static HRESULT send_sample_data(struct dsound_render *filter, ERR("Failed to lock sound buffer, hr %#lx.\n", hr); break; } - memcpy(buf1, data, size1); - if (size2) - memcpy(buf2, data+size1, size2); + + if (data) + { + memcpy(buf1, data, size1); + if (size2) + memcpy(buf2, data + size1, size2); + data += size1 + size2; + } + else + { + const WAVEFORMATEX *wfx = (const WAVEFORMATEX *)filter->sink.pin.mt.pbFormat; + char silence = (wfx->wBitsPerSample == 8 ? 0x80 : 0); + + memset(buf1, silence, size1); + if (size2) + memset(buf2, silence, size2); + } IDirectSoundBuffer_Unlock(filter->dsbuffer, buf1, size1, buf2, size2); filter->writepos = (writepos + size1 + size2) % filter->buf_size; TRACE("Wrote %lu bytes at %lu, next at %lu - (%lu/%lu)\n", size1 + size2, writepos, filter->writepos, free, size); - data += size1 + size2; size -= size1 + size2; } return S_OK; @@ -363,6 +361,17 @@ static DWORD WINAPI render_thread_run(void *arg) { IMediaSample *sample;
+ if (filter->eos) + { + 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"); + return 0; + } + if (!filter->queued_sample_count) { SleepConditionVariableCS(&filter->render_cv, &filter->render_cs, INFINITE); @@ -503,10 +512,11 @@ 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; - void *buffer; - DWORD size;
+ EnterCriticalSection(&filter->render_cs); filter->eos = TRUE; + LeaveCriticalSection(&filter->render_cs); + WakeConditionVariable(&filter->render_cv);
if (filter->filter.state == State_Running && graph && SUCCEEDED(IFilterGraph_QueryInterface(graph, @@ -518,12 +528,6 @@ static HRESULT dsound_render_sink_eos(struct strmbase_sink *iface) } SetEvent(filter->state_event);
- handle_eos(filter); - - IDirectSoundBuffer_Lock(filter->dsbuffer, 0, 0, &buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER); - memset(buffer, 0, size); - IDirectSoundBuffer_Unlock(filter->dsbuffer, buffer, size, NULL, 0); - return S_OK; }
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=150266
Your paranoid android.
=== build (build log) ===
error: patch failed: dlls/quartz/dsoundrender.c:228 Task: Patch failed to apply
=== debian11 (build log) ===
error: patch failed: dlls/quartz/dsoundrender.c:228 Task: Patch failed to apply
=== debian11b (build log) ===
error: patch failed: dlls/quartz/dsoundrender.c:228 Task: Patch failed to apply