Let winegstreamer's media source emit the same rate limits that Windows exposes for media sources generated with the standard source resolver.
Signed-off-by: Giovanni Mascellani gmascellani@codeweavers.com --- dlls/winegstreamer/media_source.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index e153c8e9161..d013a60e150 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -933,7 +933,7 @@ static HRESULT WINAPI media_source_rate_support_GetSlowestRate(IMFRateSupport *i { TRACE("%p, %d, %d, %p.\n", iface, direction, thin, rate);
- *rate = direction == MFRATE_FORWARD ? 1.0f : -1.0f; + *rate = 0.0f;
return S_OK; } @@ -942,14 +942,14 @@ static HRESULT WINAPI media_source_rate_support_GetFastestRate(IMFRateSupport *i { TRACE("%p, %d, %d, %p.\n", iface, direction, thin, rate);
- *rate = direction == MFRATE_FORWARD ? 1.0f : -1.0f; + *rate = direction == MFRATE_FORWARD ? 1e6f : -1e6f;
return S_OK; }
static HRESULT WINAPI media_source_rate_support_IsRateSupported(IMFRateSupport *iface, BOOL thin, float rate, float *nearest_support_rate) { - const float supported_rate = rate >= 0.0f ? 1.0f : -1.0f; + const float supported_rate = max(min(rate, 1e6f), -1e6f);
TRACE("%p, %d, %f, %p.\n", iface, thin, rate, nearest_support_rate);
The following behaviors from Windows are implemented: * Transitions from STOPPED to PAUSED are forbidden; * Sample requests are only emitted for transitions from STOPPED to RUNNING (i.e., not when transitioning from PAUSED); * Transition events are emitted again when transitioning from a state to the same state, except when such state is PAUSED.
Signed-off-by: Giovanni Mascellani gmascellani@codeweavers.com --- dlls/mf/samplegrabber.c | 51 ++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 21 deletions(-)
diff --git a/dlls/mf/samplegrabber.c b/dlls/mf/samplegrabber.c index f60ce2a8433..5961cac7e0f 100644 --- a/dlls/mf/samplegrabber.c +++ b/dlls/mf/samplegrabber.c @@ -32,6 +32,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(mfplat); enum sink_state { SINK_STATE_STOPPED = 0, + SINK_STATE_PAUSED, SINK_STATE_RUNNING, };
@@ -1101,55 +1102,52 @@ static ULONG WINAPI sample_grabber_clock_sink_Release(IMFClockStateSink *iface) return IMFMediaSink_Release(&grabber->IMFMediaSink_iface); }
-static void sample_grabber_set_state(struct sample_grabber *grabber, enum sink_state state) +static HRESULT sample_grabber_set_state(struct sample_grabber *grabber, enum sink_state state) { static const DWORD events[] = { MEStreamSinkStopped, /* SINK_STATE_STOPPED */ + MEStreamSinkPaused, /* SINK_STATE_PAUSED */ MEStreamSinkStarted, /* SINK_STATE_RUNNING */ }; - BOOL set_state = FALSE; + HRESULT hr = S_OK; unsigned int i;
EnterCriticalSection(&grabber->cs);
if (!grabber->is_shut_down) { - switch (grabber->state) - { - case SINK_STATE_STOPPED: - set_state = state == SINK_STATE_RUNNING; - break; - case SINK_STATE_RUNNING: - set_state = state == SINK_STATE_STOPPED; - break; - default: - ; - } - - if (set_state) + if (state == SINK_STATE_PAUSED && grabber->state == SINK_STATE_STOPPED) + hr = MF_E_INVALID_STATE_TRANSITION; + else { - grabber->state = state; - if (state == SINK_STATE_RUNNING) + if (state == SINK_STATE_RUNNING && grabber->state == SINK_STATE_STOPPED) { /* Every transition to running state sends a bunch requests to build up initial queue. */ for (i = 0; i < 4; ++i) sample_grabber_stream_request_sample(grabber); } - IMFStreamSink_QueueEvent(&grabber->IMFStreamSink_iface, events[state], &GUID_NULL, S_OK, NULL); + if (state != grabber->state || state != SINK_STATE_PAUSED) + IMFStreamSink_QueueEvent(&grabber->IMFStreamSink_iface, events[state], &GUID_NULL, S_OK, NULL); + grabber->state = state; } }
LeaveCriticalSection(&grabber->cs); + + return hr; }
static HRESULT WINAPI sample_grabber_clock_sink_OnClockStart(IMFClockStateSink *iface, MFTIME systime, LONGLONG offset) { struct sample_grabber *grabber = impl_from_IMFClockStateSink(iface); + HRESULT hr;
TRACE("%p, %s, %s.\n", iface, debugstr_time(systime), debugstr_time(offset));
- sample_grabber_set_state(grabber, SINK_STATE_RUNNING); + hr = sample_grabber_set_state(grabber, SINK_STATE_RUNNING); + if (FAILED(hr)) + return hr;
return IMFSampleGrabberSinkCallback_OnClockStart(sample_grabber_get_callback(grabber), systime, offset); } @@ -1157,10 +1155,13 @@ static HRESULT WINAPI sample_grabber_clock_sink_OnClockStart(IMFClockStateSink * static HRESULT WINAPI sample_grabber_clock_sink_OnClockStop(IMFClockStateSink *iface, MFTIME systime) { struct sample_grabber *grabber = impl_from_IMFClockStateSink(iface); + HRESULT hr;
TRACE("%p, %s.\n", iface, debugstr_time(systime));
- sample_grabber_set_state(grabber, SINK_STATE_STOPPED); + hr = sample_grabber_set_state(grabber, SINK_STATE_STOPPED); + if (FAILED(hr)) + return hr;
return IMFSampleGrabberSinkCallback_OnClockStop(sample_grabber_get_callback(grabber), systime); } @@ -1168,19 +1169,27 @@ static HRESULT WINAPI sample_grabber_clock_sink_OnClockStop(IMFClockStateSink *i static HRESULT WINAPI sample_grabber_clock_sink_OnClockPause(IMFClockStateSink *iface, MFTIME systime) { struct sample_grabber *grabber = impl_from_IMFClockStateSink(iface); + HRESULT hr;
TRACE("%p, %s.\n", iface, debugstr_time(systime));
+ hr = sample_grabber_set_state(grabber, SINK_STATE_PAUSED); + if (FAILED(hr)) + return hr; + return IMFSampleGrabberSinkCallback_OnClockPause(sample_grabber_get_callback(grabber), systime); }
static HRESULT WINAPI sample_grabber_clock_sink_OnClockRestart(IMFClockStateSink *iface, MFTIME systime) { struct sample_grabber *grabber = impl_from_IMFClockStateSink(iface); + HRESULT hr;
TRACE("%p, %s.\n", iface, debugstr_time(systime));
- sample_grabber_set_state(grabber, SINK_STATE_RUNNING); + hr = sample_grabber_set_state(grabber, SINK_STATE_RUNNING); + if (FAILED(hr)) + return hr;
return IMFSampleGrabberSinkCallback_OnClockRestart(sample_grabber_get_callback(grabber), systime); }
On 5/31/21 1:11 PM, Giovanni Mascellani wrote:
The following behaviors from Windows are implemented:
- Transitions from STOPPED to PAUSED are forbidden;
- Sample requests are only emitted for transitions from STOPPED to RUNNING (i.e., not when transitioning from PAUSED);
- Transition events are emitted again when transitioning from a state to the same state, except when such state is PAUSED.
Ideally we'd be fixing such issues separately. Anyway, this looks much closer to how it should work. One difference now is that user OnClockPause() callback is called every time, when it should only be called when event was fired.
1146 struct sample_grabber *grabber = impl_from_IMFClockStateSink(iface); 1147 HRESULT hr; 1148 1149 TRACE("%p, %s, %s.\n", iface, debugstr_time(systime), debugstr_time(offset)); 1150 1151 hr = sample_grabber_set_state(grabber, SINK_STATE_RUNNING); 1152 if (FAILED(hr)) 1153 return hr; 1154 1155 return IMFSampleGrabberSinkCallback_OnClockStart(sample_grabber_get_callback(grabber), systime, offset);
This could be nicer, without additional error paths. Maybe by invoking callback from the helper.
The current algorithm correctly only considers the absolute value of nodes' rates when computing maximum and minimum, but omits negating the result when in reverse direction.
Also, when computing the minimum intializing with zero is wrong, as it would always give final result zero. We have to initialize with a number that is bigger than at least one operand, e.g. FLT_MAX.
Signed-off-by: Giovanni Mascellani gmascellani@codeweavers.com --- dlls/mf/session.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 925a8c93d20..275b1e3dd98 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -18,6 +18,7 @@
#include <stdarg.h> #include <math.h> +#include <float.h>
#define COBJMACROS
@@ -3532,7 +3533,7 @@ static HRESULT session_get_presentation_rate(struct media_session *session, MFRA struct media_sink *sink; HRESULT hr = E_POINTER;
- *result = 0.0f; + *result = fastest ? FLT_MAX : 0.0f;
EnterCriticalSection(&session->cs);
@@ -3556,6 +3557,9 @@ static HRESULT session_get_presentation_rate(struct media_session *session, MFRA
LeaveCriticalSection(&session->cs);
+ if (direction == MFRATE_REVERSE) + *result = -*result; + return hr; }