mf: Add rate support for presentation clock.

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Nikolay Sivov 2019-06-03 13:11:33 +03:00 committed by Alexandre Julliard
parent d5f8eb333c
commit d9cd07fd4e
2 changed files with 131 additions and 28 deletions

View File

@ -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_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, &notify_object)))
if (SUCCEEDED(create_sink_notification(system_time, param, notification, sink->state_sink, &notify_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;

View File

@ -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);