Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com>
---
dlls/mf/session.c | 107 +++++++++++++++++++++++++++++++++------------
dlls/mf/tests/mf.c | 52 ++++++++++++++++++++++
2 files changed, 131 insertions(+), 28 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c
index 8e41942448..3a33a88a4b 100644
--- a/dlls/mf/session.c
+++ b/dlls/mf/session.c
@@ -85,6 +85,7 @@ enum clock_command
CLOCK_CMD_START = 0,
CLOCK_CMD_STOP,
CLOCK_CMD_PAUSE,
+ CLOCK_CMD_SET_RATE,
CLOCK_CMD_MAX,
};
@@ -94,6 +95,16 @@ enum clock_notification
CLOCK_NOTIFY_STOP,
CLOCK_NOTIFY_PAUSE,
CLOCK_NOTIFY_RESTART,
+ CLOCK_NOTIFY_SET_RATE,
+};
+
+struct clock_state_change_param
+{
+ union
+ {
+ LONGLONG offset;
+ float rate;
+ } u;
};
struct sink_notification
@@ -101,7 +112,7 @@ struct sink_notification
IUnknown IUnknown_iface;
LONG refcount;
MFTIME system_time;
- LONGLONG offset;
+ struct clock_state_change_param param;
enum clock_notification notification;
IMFClockStateSink *sink;
};
@@ -118,6 +129,7 @@ struct presentation_clock
IMFClockStateSink *time_source_sink;
MFCLOCK_STATE state;
struct list sinks;
+ float rate;
CRITICAL_SECTION cs;
};
@@ -1092,8 +1104,8 @@ static const IUnknownVtbl sinknotificationvtbl =
sink_notification_Release,
};
-static HRESULT create_sink_notification(MFTIME system_time, LONGLONG offset, enum clock_notification notification,
- IMFClockStateSink *sink, IUnknown **out)
+static HRESULT create_sink_notification(MFTIME system_time, struct clock_state_change_param param,
+ enum clock_notification notification, IMFClockStateSink *sink, IUnknown **out)
{
struct sink_notification *object;
@@ -1104,7 +1116,7 @@ static HRESULT create_sink_notification(MFTIME system_time, LONGLONG offset, enu
object->IUnknown_iface.lpVtbl = &sinknotificationvtbl;
object->refcount = 1;
object->system_time = system_time;
- object->offset = offset;
+ object->param = param;
object->notification = notification;
object->sink = sink;
IMFClockStateSink_AddRef(object->sink);
@@ -1114,15 +1126,15 @@ static HRESULT create_sink_notification(MFTIME system_time, LONGLONG offset, enu
return S_OK;
}
-static HRESULT clock_call_state_change(MFTIME system_time, LONGLONG offset, enum clock_notification notification,
- IMFClockStateSink *sink)
+static HRESULT clock_call_state_change(MFTIME system_time, struct clock_state_change_param param,
+ enum clock_notification notification, IMFClockStateSink *sink)
{
HRESULT hr = S_OK;
switch (notification)
{
case CLOCK_NOTIFY_START:
- hr = IMFClockStateSink_OnClockStart(sink, system_time, offset);
+ hr = IMFClockStateSink_OnClockStart(sink, system_time, param.u.offset);
break;
case CLOCK_NOTIFY_STOP:
hr = IMFClockStateSink_OnClockStop(sink, system_time);
@@ -1133,6 +1145,10 @@ static HRESULT clock_call_state_change(MFTIME system_time, LONGLONG offset, enum
case CLOCK_NOTIFY_RESTART:
hr = IMFClockStateSink_OnClockRestart(sink, system_time);
break;
+ case CLOCK_NOTIFY_SET_RATE:
+ /* System time source does not allow 0.0 rate, presentation clock allows it without raising errors. */
+ IMFClockStateSink_OnClockSetRate(sink, system_time, param.u.rate);
+ break;
default:
;
}
@@ -1140,20 +1156,22 @@ static HRESULT clock_call_state_change(MFTIME system_time, LONGLONG offset, enum
return hr;
}
-static HRESULT clock_change_state(struct presentation_clock *clock, enum clock_command command, LONGLONG offset)
+static HRESULT clock_change_state(struct presentation_clock *clock, enum clock_command command,
+ struct clock_state_change_param param)
{
static const BYTE state_change_is_allowed[MFCLOCK_STATE_PAUSED+1][CLOCK_CMD_MAX] =
- { /* S S* P */
- /* INVALID */ { 1, 1, 1 },
- /* RUNNING */ { 1, 1, 1 },
- /* STOPPED */ { 1, 1, 0 },
- /* PAUSED */ { 1, 1, 0 },
+ { /* S S* P, R */
+ /* INVALID */ { 1, 1, 1, 1 },
+ /* RUNNING */ { 1, 1, 1, 1 },
+ /* STOPPED */ { 1, 1, 0, 1 },
+ /* PAUSED */ { 1, 1, 0, 1 },
};
static const MFCLOCK_STATE states[CLOCK_CMD_MAX] =
{
- /* CLOCK_CMD_START */ MFCLOCK_STATE_RUNNING,
- /* CLOCK_CMD_STOP */ MFCLOCK_STATE_STOPPED,
- /* CLOCK_CMD_PAUSE */ MFCLOCK_STATE_PAUSED,
+ /* CLOCK_CMD_START */ MFCLOCK_STATE_RUNNING,
+ /* CLOCK_CMD_STOP */ MFCLOCK_STATE_STOPPED,
+ /* CLOCK_CMD_PAUSE */ MFCLOCK_STATE_PAUSED,
+ /* CLOCK_CMD_SET_RATE */ 0, /* Unused */
};
enum clock_notification notification;
struct clock_sink *sink;
@@ -1162,7 +1180,7 @@ static HRESULT clock_change_state(struct presentation_clock *clock, enum clock_c
MFTIME system_time;
HRESULT hr;
- if (clock->state == states[command] && clock->state != MFCLOCK_STATE_RUNNING)
+ if (command != CLOCK_CMD_SET_RATE && clock->state == states[command] && clock->state != MFCLOCK_STATE_RUNNING)
return MF_E_CLOCK_STATE_ALREADY_SET;
if (!state_change_is_allowed[clock->state][command])
@@ -1173,7 +1191,7 @@ static HRESULT clock_change_state(struct presentation_clock *clock, enum clock_c
switch (command)
{
case CLOCK_CMD_START:
- if (clock->state == MFCLOCK_STATE_PAUSED && offset == PRESENTATION_CURRENT_POSITION)
+ if (clock->state == MFCLOCK_STATE_PAUSED && param.u.offset == PRESENTATION_CURRENT_POSITION)
notification = CLOCK_NOTIFY_RESTART;
else
notification = CLOCK_NOTIFY_START;
@@ -1184,18 +1202,21 @@ static HRESULT clock_change_state(struct presentation_clock *clock, enum clock_c
case CLOCK_CMD_PAUSE:
notification = CLOCK_NOTIFY_PAUSE;
break;
+ case CLOCK_CMD_SET_RATE:
+ notification = CLOCK_NOTIFY_SET_RATE;
+ break;
default:
;
}
- if (FAILED(hr = clock_call_state_change(system_time, offset, notification, clock->time_source_sink)))
+ if (FAILED(hr = clock_call_state_change(system_time, param, notification, clock->time_source_sink)))
return hr;
clock->state = states[command];
LIST_FOR_EACH_ENTRY(sink, &clock->sinks, struct clock_sink, entry)
{
- if (SUCCEEDED(create_sink_notification(system_time, offset, notification, sink->state_sink, ¬ify_object)))
+ if (SUCCEEDED(create_sink_notification(system_time, param, notification, sink->state_sink, ¬ify_object)))
{
hr = MFCreateAsyncResult(notify_object, &clock->IMFAsyncCallback_iface, NULL, &result);
IUnknown_Release(notify_object);
@@ -1213,12 +1234,14 @@ static HRESULT clock_change_state(struct presentation_clock *clock, enum clock_c
static HRESULT WINAPI present_clock_Start(IMFPresentationClock *iface, LONGLONG start_offset)
{
struct presentation_clock *clock = impl_from_IMFPresentationClock(iface);
+ struct clock_state_change_param param = { 0 };
HRESULT hr;
TRACE("%p, %s.\n", iface, wine_dbgstr_longlong(start_offset));
EnterCriticalSection(&clock->cs);
- hr = clock_change_state(clock, CLOCK_CMD_START, start_offset);
+ param.u.offset = start_offset;
+ hr = clock_change_state(clock, CLOCK_CMD_START, param);
LeaveCriticalSection(&clock->cs);
return hr;
@@ -1227,12 +1250,13 @@ static HRESULT WINAPI present_clock_Start(IMFPresentationClock *iface, LONGLONG
static HRESULT WINAPI present_clock_Stop(IMFPresentationClock *iface)
{
struct presentation_clock *clock = impl_from_IMFPresentationClock(iface);
+ struct clock_state_change_param param = { 0 };
HRESULT hr;
TRACE("%p.\n", iface);
EnterCriticalSection(&clock->cs);
- hr = clock_change_state(clock, CLOCK_CMD_STOP, 0);
+ hr = clock_change_state(clock, CLOCK_CMD_STOP, param);
LeaveCriticalSection(&clock->cs);
return hr;
@@ -1241,12 +1265,13 @@ static HRESULT WINAPI present_clock_Stop(IMFPresentationClock *iface)
static HRESULT WINAPI present_clock_Pause(IMFPresentationClock *iface)
{
struct presentation_clock *clock = impl_from_IMFPresentationClock(iface);
+ struct clock_state_change_param param = { 0 };
HRESULT hr;
TRACE("%p.\n", iface);
EnterCriticalSection(&clock->cs);
- hr = clock_change_state(clock, CLOCK_CMD_PAUSE, 0);
+ hr = clock_change_state(clock, CLOCK_CMD_PAUSE, param);
LeaveCriticalSection(&clock->cs);
return hr;
@@ -1292,16 +1317,41 @@ static ULONG WINAPI present_clock_rate_control_Release(IMFRateControl *iface)
static HRESULT WINAPI present_clock_rate_SetRate(IMFRateControl *iface, BOOL thin, float rate)
{
- FIXME("%p, %d, %f.\n", iface, thin, rate);
+ struct presentation_clock *clock = impl_from_IMFRateControl(iface);
+ struct clock_state_change_param param;
+ HRESULT hr;
- return E_NOTIMPL;
+ TRACE("%p, %d, %f.\n", iface, thin, rate);
+
+ if (thin)
+ return MF_E_THINNING_UNSUPPORTED;
+
+ EnterCriticalSection(&clock->cs);
+ param.u.rate = rate;
+ if (SUCCEEDED(hr = clock_change_state(clock, CLOCK_CMD_SET_RATE, param)))
+ clock->rate = rate;
+ LeaveCriticalSection(&clock->cs);
+
+ return hr;
}
static HRESULT WINAPI present_clock_rate_GetRate(IMFRateControl *iface, BOOL *thin, float *rate)
{
- FIXME("%p, %p, %p.\n", iface, thin, rate);
+ struct presentation_clock *clock = impl_from_IMFRateControl(iface);
- return E_NOTIMPL;
+ TRACE("%p, %p, %p.\n", iface, thin, rate);
+
+ if (!rate)
+ return E_INVALIDARG;
+
+ if (thin)
+ *thin = FALSE;
+
+ EnterCriticalSection(&clock->cs);
+ *rate = clock->rate;
+ LeaveCriticalSection(&clock->cs);
+
+ return S_OK;
}
static const IMFRateControlVtbl presentclockratecontrolvtbl =
@@ -1439,7 +1489,7 @@ static HRESULT WINAPI present_clock_sink_callback_Invoke(IMFAsyncCallback *iface
data = impl_from_IUnknown(object);
- clock_call_state_change(data->system_time, data->offset, data->notification, data->sink);
+ clock_call_state_change(data->system_time, data->param, data->notification, data->sink);
IUnknown_Release(object);
@@ -1475,6 +1525,7 @@ HRESULT WINAPI MFCreatePresentationClock(IMFPresentationClock **clock)
object->IMFAsyncCallback_iface.lpVtbl = &presentclocksinkcallbackvtbl;
object->refcount = 1;
list_init(&object->sinks);
+ object->rate = 1.0f;
InitializeCriticalSection(&object->cs);
*clock = &object->IMFPresentationClock_iface;
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c
index 8f9a97d497..cb2db22d93 100644
--- a/dlls/mf/tests/mf.c
+++ b/dlls/mf/tests/mf.c
@@ -1430,6 +1430,7 @@ static void test_presentation_clock(void)
MFCLOCK_PROPERTIES props, props2;
IMFRateControl *rate_control;
IMFPresentationClock *clock;
+ MFSHUTDOWN_STATUS status;
IMFShutdown *shutdown;
MFTIME systime, time;
LONGLONG clock_time;
@@ -1437,7 +1438,9 @@ static void test_presentation_clock(void)
IMFTimer *timer;
unsigned int i;
DWORD value;
+ float rate;
HRESULT hr;
+ BOOL thin;
hr = MFStartup(MF_VERSION, MFSTARTUP_FULL);
ok(hr == S_OK, "Failed to start up, hr %#x.\n", hr);
@@ -1577,6 +1580,40 @@ static void test_presentation_clock(void)
hr = IMFPresentationClock_QueryInterface(clock, &IID_IMFRateControl, (void **)&rate_control);
ok(hr == S_OK, "Failed to get rate control interface, hr %#x.\n", hr);
+
+ hr = IMFRateControl_GetRate(rate_control, NULL, &rate);
+ ok(hr == S_OK, "Failed to get clock rate, hr %#x.\n", hr);
+
+ hr = IMFRateControl_GetRate(rate_control, &thin, NULL);
+ ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr);
+
+ hr = IMFRateControl_GetRate(rate_control, &thin, &rate);
+ ok(hr == S_OK, "Failed to get clock rate, hr %#x.\n", hr);
+ ok(rate == 1.0f, "Unexpected rate.\n");
+ ok(!thin, "Unexpected thinning.\n");
+
+ hr = IMFPresentationClock_Start(clock, 0);
+ ok(hr == S_OK, "Failed to stop, hr %#x.\n", hr);
+
+ hr = IMFRateControl_SetRate(rate_control, FALSE, 0.0f);
+ ok(hr == S_OK, "Failed to set clock rate, hr %#x.\n", hr);
+ hr = IMFRateControl_GetRate(rate_control, &thin, &rate);
+ ok(hr == S_OK, "Failed to get clock rate, hr %#x.\n", hr);
+ ok(rate == 0.0f, "Unexpected rate.\n");
+ hr = IMFRateControl_SetRate(rate_control, FALSE, 1.0f);
+ ok(hr == S_OK, "Failed to set clock rate, hr %#x.\n", hr);
+ hr = IMFRateControl_SetRate(rate_control, FALSE, 0.0f);
+ ok(hr == S_OK, "Failed to set clock rate, hr %#x.\n", hr);
+ hr = IMFRateControl_SetRate(rate_control, FALSE, 0.5f);
+ ok(hr == S_OK, "Failed to set clock rate, hr %#x.\n", hr);
+ hr = IMFRateControl_SetRate(rate_control, TRUE, -1.0f);
+ ok(hr == MF_E_THINNING_UNSUPPORTED, "Unexpected hr %#x.\n", hr);
+
+ hr = IMFRateControl_GetRate(rate_control, &thin, &rate);
+ ok(hr == S_OK, "Failed to get clock rate, hr %#x.\n", hr);
+ ok(rate == 0.5f, "Unexpected rate.\n");
+ ok(!thin, "Unexpected thinning.\n");
+
IMFRateControl_Release(rate_control);
hr = IMFPresentationClock_QueryInterface(clock, &IID_IMFTimer, (void **)&timer);
@@ -1585,6 +1622,21 @@ static void test_presentation_clock(void)
hr = IMFPresentationClock_QueryInterface(clock, &IID_IMFShutdown, (void **)&shutdown);
ok(hr == S_OK, "Failed to get shutdown interface, hr %#x.\n", hr);
+
+ /* Shutdown behavior. */
+ hr = IMFShutdown_GetShutdownStatus(shutdown, NULL);
+todo_wine
+ ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr);
+
+ hr = IMFShutdown_Shutdown(shutdown);
+todo_wine
+ ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr);
+
+ hr = IMFShutdown_GetShutdownStatus(shutdown, &status);
+todo_wine {
+ ok(hr == S_OK, "Failed to get status, hr %#x.\n", hr);
+ ok(status == MFSHUTDOWN_COMPLETED, "Unexpected status.\n");
+}
IMFShutdown_Release(shutdown);
IMFPresentationClock_Release(clock);
--
2.20.1