It's not safe to hold the filter lock from a streaming thread, and anyway we aren't protecting anything that needs locking.
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/strmbase/transform.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/dlls/strmbase/transform.c b/dlls/strmbase/transform.c index d39dbbd3ab..7650232c44 100644 --- a/dlls/strmbase/transform.c +++ b/dlls/strmbase/transform.c @@ -495,12 +495,10 @@ static HRESULT WINAPI TransformFilter_InputPin_NewSegment(IPin * iface, REFERENC TRACE("iface %p, start %s, stop %s, rate %.16e.\n", iface, debugstr_time(tStart), debugstr_time(tStop), dRate);
- EnterCriticalSection(&pTransform->filter.csFilter); if (pTransform->pFuncsTable->pfnNewSegment) hr = pTransform->pFuncsTable->pfnNewSegment(pTransform, tStart, tStop, dRate); if (SUCCEEDED(hr)) hr = BaseInputPinImpl_NewSegment(iface, tStart, tStop, dRate); - LeaveCriticalSection(&pTransform->filter.csFilter); return hr; }
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/quartz/acmwrapper.c | 4 ---- dlls/quartz/avidec.c | 22 +++++++++------------- dlls/strmbase/transform.c | 2 +- dlls/winegstreamer/gsttffilter.c | 3 --- 4 files changed, 10 insertions(+), 21 deletions(-)
diff --git a/dlls/quartz/acmwrapper.c b/dlls/quartz/acmwrapper.c index 64e0018980..8eea6ab147 100644 --- a/dlls/quartz/acmwrapper.c +++ b/dlls/quartz/acmwrapper.c @@ -66,12 +66,10 @@ static HRESULT WINAPI ACMWrapper_Receive(TransformFilter *tf, IMediaSample *pSam LONGLONG tStart = -1, tStop = -1, tMed; LONGLONG mtStart = -1, mtStop = -1, mtMed;
- EnterCriticalSection(&This->tf.csReceive); hr = IMediaSample_GetPointer(pSample, &pbSrcStream); if (FAILED(hr)) { ERR("Cannot get pointer to sample data (%x)\n", hr); - LeaveCriticalSection(&This->tf.csReceive); return hr; }
@@ -107,7 +105,6 @@ static HRESULT WINAPI ACMWrapper_Receive(TransformFilter *tf, IMediaSample *pSam if (FAILED(hr)) { ERR("Unable to get delivery buffer (%x)\n", hr); - LeaveCriticalSection(&This->tf.csReceive); return hr; } IMediaSample_SetPreroll(pOutSample, preroll); @@ -224,7 +221,6 @@ error: This->lasttime_real = tStop; This->lasttime_sent = tMed;
- LeaveCriticalSection(&This->tf.csReceive); return hr; }
diff --git a/dlls/quartz/avidec.c b/dlls/quartz/avidec.c index cbe109df2b..32969a3473 100644 --- a/dlls/quartz/avidec.c +++ b/dlls/quartz/avidec.c @@ -111,12 +111,11 @@ static HRESULT WINAPI AVIDec_Receive(TransformFilter *tf, IMediaSample *pSample) LONGLONG tStart, tStop; DWORD flags = 0;
- EnterCriticalSection(&This->tf.csReceive); hr = IMediaSample_GetPointer(pSample, &pbSrcStream); if (FAILED(hr)) { ERR("Cannot get pointer to sample data (%x)\n", hr); - goto error; + return hr; }
cbSrcStream = IMediaSample_GetActualDataLength(pSample); @@ -129,7 +128,7 @@ static HRESULT WINAPI AVIDec_Receive(TransformFilter *tf, IMediaSample *pSample) hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->tf.source, &pOutSample, NULL, NULL, 0); if (FAILED(hr)) { ERR("Unable to get delivery buffer (%x)\n", hr); - goto error; + return hr; }
hr = IMediaSample_SetActualDataLength(pOutSample, 0); @@ -138,13 +137,14 @@ static HRESULT WINAPI AVIDec_Receive(TransformFilter *tf, IMediaSample *pSample) hr = IMediaSample_GetPointer(pOutSample, &pbDstStream); if (FAILED(hr)) { ERR("Unable to get pointer to buffer (%x)\n", hr); - goto error; + IMediaSample_Release(pOutSample); + return hr; } cbDstStream = IMediaSample_GetSize(pOutSample); if (cbDstStream < This->pBihOut->biSizeImage) { ERR("Sample size is too small %d < %d\n", cbDstStream, This->pBihOut->biSizeImage); - hr = E_FAIL; - goto error; + IMediaSample_Release(pOutSample); + return E_FAIL; }
if (IMediaSample_IsPreroll(pSample) == S_OK) @@ -161,8 +161,8 @@ static HRESULT WINAPI AVIDec_Receive(TransformFilter *tf, IMediaSample *pSample)
/* Drop sample if it's intended to be dropped */ if (flags & ICDECOMPRESS_HURRYUP) { - hr = S_OK; - goto error; + IMediaSample_Release(pOutSample); + return S_OK; }
IMediaSample_SetActualDataLength(pOutSample, This->pBihOut->biSizeImage); @@ -187,11 +187,7 @@ static HRESULT WINAPI AVIDec_Receive(TransformFilter *tf, IMediaSample *pSample) if (hr != S_OK && hr != VFW_E_NOT_CONNECTED) ERR("Error sending sample (%x)\n", hr);
-error: - if (pOutSample) - IMediaSample_Release(pOutSample); - - LeaveCriticalSection(&This->tf.csReceive); + IMediaSample_Release(pOutSample); return hr; }
diff --git a/dlls/strmbase/transform.c b/dlls/strmbase/transform.c index 7650232c44..9e2582a0db 100644 --- a/dlls/strmbase/transform.c +++ b/dlls/strmbase/transform.c @@ -82,12 +82,12 @@ static HRESULT WINAPI TransformFilter_Input_Receive(struct strmbase_sink *This, return S_FALSE; }
- LeaveCriticalSection(&pTransform->csReceive); if (pTransform->pFuncsTable->pfnReceive) hr = pTransform->pFuncsTable->pfnReceive(pTransform, pInSample); else hr = S_FALSE;
+ LeaveCriticalSection(&pTransform->csReceive); return hr; }
diff --git a/dlls/winegstreamer/gsttffilter.c b/dlls/winegstreamer/gsttffilter.c index baa4c0c2c5..9ff5f8741d 100644 --- a/dlls/winegstreamer/gsttffilter.c +++ b/dlls/winegstreamer/gsttffilter.c @@ -227,7 +227,6 @@ static HRESULT WINAPI Gstreamer_transform_ProcessData(TransformFilter *iface, IM
mark_wine_thread();
- EnterCriticalSection(&This->tf.csReceive); IMediaSample_GetPointer(sample, &data);
IMediaSample_AddRef(sample); @@ -235,7 +234,6 @@ static HRESULT WINAPI Gstreamer_transform_ProcessData(TransformFilter *iface, IM buf = gst_buffer_new_wrapped_full(0, data, bufsize, 0, bufsize, sample, release_sample_wrapper); if (!buf) { IMediaSample_Release(sample); - LeaveCriticalSection(&This->tf.csReceive); return S_OK; }
@@ -259,7 +257,6 @@ static HRESULT WINAPI Gstreamer_transform_ProcessData(TransformFilter *iface, IM GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_LIVE); if (IMediaSample_IsSyncPoint(sample) != S_OK) GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT); - LeaveCriticalSection(&This->tf.csReceive); ret = gst_pad_push(This->my_src, buf); if (ret) WARN("Sending returned: %i\n", ret);
This sort of reverts 096da45036959e57ae3aa5b0de0c06f69c651788. The deadlock mentioned there is not spelled out in detail, but likely results from the streaming thread trying to acquire the filter lock while a main thread which is holding the filter lock waits for the streaming thread to finish. However, DirectShow documentation (and manual testing) suggests that the correct way to avoid this problem is simply to never take the filter lock from the streaming thread. Since we currently don't do this anymore, it should be safe to reinstate this lock.
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/winegstreamer/gsttffilter.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/dlls/winegstreamer/gsttffilter.c b/dlls/winegstreamer/gsttffilter.c index 9ff5f8741d..ee1851dfdc 100644 --- a/dlls/winegstreamer/gsttffilter.c +++ b/dlls/winegstreamer/gsttffilter.c @@ -272,9 +272,7 @@ static HRESULT WINAPI Gstreamer_transform_ProcessEnd(TransformFilter *iface)
mark_wine_thread();
- LeaveCriticalSection(&This->tf.csReceive); ret = gst_element_set_state(This->filter, GST_STATE_READY); - EnterCriticalSection(&This->tf.csReceive); TRACE("Returned: %i\n", ret); return S_OK; }
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/winegstreamer/gstdemux.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index 479077b48f..dc8db85b72 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -1433,8 +1433,17 @@ static HRESULT WINAPI GST_Run(IBaseFilter *iface, REFERENCE_TIME tStart) }
EnterCriticalSection(&This->filter.csFilter); + + if (This->no_more_pads_event) + ResetEvent(This->no_more_pads_event); + gst_element_set_state(This->container, GST_STATE_PLAYING);
+ /* Make sure that all of our pads are connected before returning, lest we + * e.g. try to seek and fail. */ + if (This->no_more_pads_event) + WaitForSingleObject(This->no_more_pads_event, INFINITE); + for (i = 0; i < This->cStreams; i++) { hr = BaseOutputPinImpl_Active(&This->ppPins[i]->pin); if (SUCCEEDED(hr)) {
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=61157
Your paranoid android.
=== debian10 (32 bit report) ===
quartz: avisplit: Timeout filtergraph: Timeout
=== debian10 (32 bit Chinese:China report) ===
quartz: avisplit: Timeout filtergraph: Timeout
=== debian10 (32 bit WoW report) ===
quartz: avisplit: Timeout filtergraph: Timeout
=== debian10 (64 bit WoW report) ===
quartz: avisplit: Timeout filtergraph: Timeout
On 11/30/19 7:48 PM, Zebediah Figura wrote:
Signed-off-by: Zebediah Figura z.figura12@gmail.com
dlls/winegstreamer/gstdemux.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index 479077b48f..dc8db85b72 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -1433,8 +1433,17 @@ static HRESULT WINAPI GST_Run(IBaseFilter *iface, REFERENCE_TIME tStart) }
EnterCriticalSection(&This->filter.csFilter);
if (This->no_more_pads_event)
ResetEvent(This->no_more_pads_event);
gst_element_set_state(This->container, GST_STATE_PLAYING);
/* Make sure that all of our pads are connected before returning, lest we
* e.g. try to seek and fail. */
if (This->no_more_pads_event)
WaitForSingleObject(This->no_more_pads_event, INFINITE);
for (i = 0; i < This->cStreams; i++) { hr = BaseOutputPinImpl_Active(&This->ppPins[i]->pin); if (SUCCEEDED(hr)) {
Please ignore the last three patches in this series; as the test failures show I clearly missed a spot.
This may be called from the streaming thread, so it's not safe to do so.
Nor does it seem necessary. We expect that no streaming thread should ever call methods on our pad or pin, and as long as we hold the filter lock and wait for the no-more-pads signal when connecting or starting the stream, we cannot race with application threads.
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/winegstreamer/gstdemux.c | 3 --- 1 file changed, 3 deletions(-)
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index dc8db85b72..0f53efc6eb 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -971,7 +971,6 @@ static void existing_new_pad(GstElement *bin, GstPad *pad, gpointer user) return; }
- EnterCriticalSection(&This->filter.csFilter); for (x = 0; x < This->cStreams; ++x) { struct gstdemux_source *pin = This->ppPins[x]; if (!pin->their_src) { @@ -986,13 +985,11 @@ static void existing_new_pad(GstElement *bin, GstPad *pad, gpointer user) pin->their_src = pad; gst_object_ref(pin->their_src); TRACE("Relinked\n"); - LeaveCriticalSection(&This->filter.csFilter); return; } } } init_new_decoded_pad(bin, pad, This); - LeaveCriticalSection(&This->filter.csFilter); }
static gboolean query_function(GstPad *pad, GstObject *parent, GstQuery *query)
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/winegstreamer/gstdemux.c | 255 ++++++++++++++++------------------ 1 file changed, 122 insertions(+), 133 deletions(-)
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index 0f53efc6eb..e7489efc91 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -88,11 +88,6 @@ struct gstdemux_source SourceSeeking seek; };
-static inline struct gstdemux *impl_from_IBaseFilter(IBaseFilter *iface) -{ - return CONTAINING_RECORD(iface, struct gstdemux, filter.IBaseFilter_iface); -} - static inline struct gstdemux *impl_from_strmbase_filter(struct strmbase_filter *iface) { return CONTAINING_RECORD(iface, struct gstdemux, filter); @@ -1246,10 +1241,123 @@ static void gstdemux_destroy(struct strmbase_filter *iface) heap_free(filter); }
+static HRESULT gstdemux_init_stream(struct strmbase_filter *iface) +{ + struct gstdemux *filter = impl_from_strmbase_filter(iface); + HRESULT hr = VFW_E_NOT_CONNECTED, pin_hr; + GstStateChangeReturn ret; + unsigned int i; + + if (!filter->container) + return VFW_E_NOT_CONNECTED; + + if (filter->no_more_pads_event) + ResetEvent(filter->no_more_pads_event); + + if ((ret = gst_element_set_state(filter->container, GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to pause stream.\n"); + return E_FAIL; + } + + /* Make sure that all of our pads are connected before returning, lest we + * e.g. try to seek and fail. */ + if (filter->no_more_pads_event) + WaitForSingleObject(filter->no_more_pads_event, INFINITE); + + for (i = 0; i < filter->cStreams; ++i) + { + if (SUCCEEDED(pin_hr = BaseOutputPinImpl_Active(&filter->ppPins[i]->pin))) + hr = pin_hr; + } + return hr; +} + +static HRESULT gstdemux_start_stream(struct strmbase_filter *iface, REFERENCE_TIME time) +{ + struct gstdemux *filter = impl_from_strmbase_filter(iface); + GstStateChangeReturn ret; + + if (!filter->container) + return VFW_E_NOT_CONNECTED; + + if ((ret = gst_element_set_state(filter->container, GST_STATE_PLAYING)) == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to play stream.\n"); + return E_FAIL; + } + else if (ret == GST_STATE_CHANGE_ASYNC) + return S_FALSE; + return S_OK; +} + +static HRESULT gstdemux_stop_stream(struct strmbase_filter *iface) +{ + struct gstdemux *filter = impl_from_strmbase_filter(iface); + GstStateChangeReturn ret; + + if (!filter->container) + return VFW_E_NOT_CONNECTED; + + if ((ret = gst_element_set_state(filter->container, GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to pause stream.\n"); + return E_FAIL; + } + else if (ret == GST_STATE_CHANGE_ASYNC) + return S_FALSE; + return S_OK; +} + +static HRESULT gstdemux_cleanup_stream(struct strmbase_filter *iface) +{ + struct gstdemux *filter = impl_from_strmbase_filter(iface); + GstStateChangeReturn ret; + + if (!filter->container) + return S_OK; + + filter->ignore_flush = TRUE; + if ((ret = gst_element_set_state(filter->container, GST_STATE_READY)) == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to pause stream.\n"); + return E_FAIL; + } + gst_element_get_state(filter->container, NULL, NULL, GST_CLOCK_TIME_NONE); + filter->ignore_flush = FALSE; + + return S_OK; +} + +static HRESULT gstdemux_wait_state(struct strmbase_filter *iface, DWORD timeout) +{ + struct gstdemux *filter = impl_from_strmbase_filter(iface); + GstStateChangeReturn ret; + + if (!filter->container) + return S_OK; + + ret = gst_element_get_state(filter->container, NULL, NULL, + timeout == INFINITE ? GST_CLOCK_TIME_NONE : timeout * 1000); + if (ret == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to get state.\n"); + return E_FAIL; + } + else if (ret == GST_STATE_CHANGE_ASYNC) + return VFW_S_STATE_INTERMEDIATE; + return S_OK; +} + static const struct strmbase_filter_ops filter_ops = { .filter_get_pin = gstdemux_get_pin, .filter_destroy = gstdemux_destroy, + .filter_init_stream = gstdemux_init_stream, + .filter_start_stream = gstdemux_start_stream, + .filter_stop_stream = gstdemux_stop_stream, + .filter_cleanup_stream = gstdemux_cleanup_stream, + .filter_wait_state = gstdemux_wait_state, };
static HRESULT sink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt) @@ -1359,139 +1467,15 @@ IUnknown * CALLBACK Gstreamer_Splitter_create(IUnknown *outer, HRESULT *phr) return &object->filter.IUnknown_inner; }
-static HRESULT WINAPI GST_Stop(IBaseFilter *iface) -{ - struct gstdemux *This = impl_from_IBaseFilter(iface); - - TRACE("(%p)\n", This); - - mark_wine_thread(); - - if (This->container) { - This->ignore_flush = TRUE; - gst_element_set_state(This->container, GST_STATE_READY); - gst_element_get_state(This->container, NULL, NULL, -1); - This->ignore_flush = FALSE; - } - return S_OK; -} - -static HRESULT WINAPI GST_Pause(IBaseFilter *iface) -{ - struct gstdemux *This = impl_from_IBaseFilter(iface); - HRESULT hr = S_OK; - GstState now; - GstStateChangeReturn ret; - - TRACE("(%p)\n", This); - - if (!This->container) - return VFW_E_NOT_CONNECTED; - - mark_wine_thread(); - - gst_element_get_state(This->container, &now, NULL, -1); - if (now == GST_STATE_PAUSED) - return S_OK; - if (now != GST_STATE_PLAYING) - hr = IBaseFilter_Run(iface, -1); - if (FAILED(hr)) - return hr; - ret = gst_element_set_state(This->container, GST_STATE_PAUSED); - if (ret == GST_STATE_CHANGE_ASYNC) - hr = S_FALSE; - return hr; -} - -static HRESULT WINAPI GST_Run(IBaseFilter *iface, REFERENCE_TIME tStart) -{ - struct gstdemux *This = impl_from_IBaseFilter(iface); - HRESULT hr = S_OK; - ULONG i; - GstState now; - HRESULT hr_any = VFW_E_NOT_CONNECTED; - - TRACE("(%p)->(%s)\n", This, wine_dbgstr_longlong(tStart)); - - mark_wine_thread(); - - if (!This->container) - return VFW_E_NOT_CONNECTED; - - gst_element_get_state(This->container, &now, NULL, -1); - if (now == GST_STATE_PLAYING) - return S_OK; - if (now == GST_STATE_PAUSED) { - GstStateChangeReturn ret; - ret = gst_element_set_state(This->container, GST_STATE_PLAYING); - if (ret == GST_STATE_CHANGE_ASYNC) - return S_FALSE; - return S_OK; - } - - EnterCriticalSection(&This->filter.csFilter); - - if (This->no_more_pads_event) - ResetEvent(This->no_more_pads_event); - - gst_element_set_state(This->container, GST_STATE_PLAYING); - - /* Make sure that all of our pads are connected before returning, lest we - * e.g. try to seek and fail. */ - if (This->no_more_pads_event) - WaitForSingleObject(This->no_more_pads_event, INFINITE); - - for (i = 0; i < This->cStreams; i++) { - hr = BaseOutputPinImpl_Active(&This->ppPins[i]->pin); - if (SUCCEEDED(hr)) { - hr_any = hr; - } - } - hr = hr_any; - LeaveCriticalSection(&This->filter.csFilter); - - return hr; -} - -static HRESULT WINAPI GST_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState) -{ - struct gstdemux *This = impl_from_IBaseFilter(iface); - HRESULT hr = S_OK; - GstState now, pending; - GstStateChangeReturn ret; - - TRACE("(%p)->(%d, %p)\n", This, dwMilliSecsTimeout, pState); - - mark_wine_thread(); - - if (!This->container) { - *pState = State_Stopped; - return S_OK; - } - - ret = gst_element_get_state(This->container, &now, &pending, dwMilliSecsTimeout == INFINITE ? -1 : dwMilliSecsTimeout * 1000); - - if (ret == GST_STATE_CHANGE_ASYNC) - hr = VFW_S_STATE_INTERMEDIATE; - else - pending = now; - - switch (pending) { - case GST_STATE_PAUSED: *pState = State_Paused; return hr; - case GST_STATE_PLAYING: *pState = State_Running; return hr; - default: *pState = State_Stopped; return hr; - } -} - static const IBaseFilterVtbl GST_Vtbl = { BaseFilterImpl_QueryInterface, BaseFilterImpl_AddRef, BaseFilterImpl_Release, BaseFilterImpl_GetClassID, - GST_Stop, - GST_Pause, - GST_Run, - GST_GetState, + BaseFilterImpl_Stop, + BaseFilterImpl_Pause, + BaseFilterImpl_Run, + BaseFilterImpl_GetState, BaseFilterImpl_SetSyncSource, BaseFilterImpl_GetSyncSource, BaseFilterImpl_EnumPins, @@ -2630,6 +2614,11 @@ static const struct strmbase_filter_ops mpeg_splitter_ops = .filter_query_interface = mpeg_splitter_query_interface, .filter_get_pin = gstdemux_get_pin, .filter_destroy = gstdemux_destroy, + .filter_init_stream = gstdemux_init_stream, + .filter_start_stream = gstdemux_start_stream, + .filter_stop_stream = gstdemux_stop_stream, + .filter_cleanup_stream = gstdemux_cleanup_stream, + .filter_wait_state = gstdemux_wait_state, };
IUnknown * CALLBACK mpeg_splitter_create(IUnknown *outer, HRESULT *phr)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=61159
Your paranoid android.
=== debian10 (32 bit report) ===
quartz: filtergraph: Timeout
=== debian10 (32 bit Chinese:China report) ===
quartz: filtergraph: Timeout
=== debian10 (32 bit WoW report) ===
quartz: filtergraph: Timeout
=== debian10 (64 bit WoW report) ===
quartz: filtergraph: Timeout