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_START = 0,
CLOCK_CMD_STOP, CLOCK_CMD_STOP,
CLOCK_CMD_PAUSE, CLOCK_CMD_PAUSE,
CLOCK_CMD_SET_RATE,
CLOCK_CMD_MAX, CLOCK_CMD_MAX,
}; };
@ -94,6 +95,16 @@ enum clock_notification
CLOCK_NOTIFY_STOP, CLOCK_NOTIFY_STOP,
CLOCK_NOTIFY_PAUSE, CLOCK_NOTIFY_PAUSE,
CLOCK_NOTIFY_RESTART, CLOCK_NOTIFY_RESTART,
CLOCK_NOTIFY_SET_RATE,
};
struct clock_state_change_param
{
union
{
LONGLONG offset;
float rate;
} u;
}; };
struct sink_notification struct sink_notification
@ -101,7 +112,7 @@ struct sink_notification
IUnknown IUnknown_iface; IUnknown IUnknown_iface;
LONG refcount; LONG refcount;
MFTIME system_time; MFTIME system_time;
LONGLONG offset; struct clock_state_change_param param;
enum clock_notification notification; enum clock_notification notification;
IMFClockStateSink *sink; IMFClockStateSink *sink;
}; };
@ -118,6 +129,7 @@ struct presentation_clock
IMFClockStateSink *time_source_sink; IMFClockStateSink *time_source_sink;
MFCLOCK_STATE state; MFCLOCK_STATE state;
struct list sinks; struct list sinks;
float rate;
CRITICAL_SECTION cs; CRITICAL_SECTION cs;
}; };
@ -1092,8 +1104,8 @@ static const IUnknownVtbl sinknotificationvtbl =
sink_notification_Release, sink_notification_Release,
}; };
static HRESULT create_sink_notification(MFTIME system_time, LONGLONG offset, enum clock_notification notification, static HRESULT create_sink_notification(MFTIME system_time, struct clock_state_change_param param,
IMFClockStateSink *sink, IUnknown **out) enum clock_notification notification, IMFClockStateSink *sink, IUnknown **out)
{ {
struct sink_notification *object; 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->IUnknown_iface.lpVtbl = &sinknotificationvtbl;
object->refcount = 1; object->refcount = 1;
object->system_time = system_time; object->system_time = system_time;
object->offset = offset; object->param = param;
object->notification = notification; object->notification = notification;
object->sink = sink; object->sink = sink;
IMFClockStateSink_AddRef(object->sink); IMFClockStateSink_AddRef(object->sink);
@ -1114,15 +1126,15 @@ static HRESULT create_sink_notification(MFTIME system_time, LONGLONG offset, enu
return S_OK; return S_OK;
} }
static HRESULT clock_call_state_change(MFTIME system_time, LONGLONG offset, enum clock_notification notification, static HRESULT clock_call_state_change(MFTIME system_time, struct clock_state_change_param param,
IMFClockStateSink *sink) enum clock_notification notification, IMFClockStateSink *sink)
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
switch (notification) switch (notification)
{ {
case CLOCK_NOTIFY_START: case CLOCK_NOTIFY_START:
hr = IMFClockStateSink_OnClockStart(sink, system_time, offset); hr = IMFClockStateSink_OnClockStart(sink, system_time, param.u.offset);
break; break;
case CLOCK_NOTIFY_STOP: case CLOCK_NOTIFY_STOP:
hr = IMFClockStateSink_OnClockStop(sink, system_time); 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: case CLOCK_NOTIFY_RESTART:
hr = IMFClockStateSink_OnClockRestart(sink, system_time); hr = IMFClockStateSink_OnClockRestart(sink, system_time);
break; 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: default:
; ;
} }
@ -1140,20 +1156,22 @@ static HRESULT clock_call_state_change(MFTIME system_time, LONGLONG offset, enum
return hr; 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] = static const BYTE state_change_is_allowed[MFCLOCK_STATE_PAUSED+1][CLOCK_CMD_MAX] =
{ /* S S* P */ { /* S S* P, R */
/* INVALID */ { 1, 1, 1 }, /* INVALID */ { 1, 1, 1, 1 },
/* RUNNING */ { 1, 1, 1 }, /* RUNNING */ { 1, 1, 1, 1 },
/* STOPPED */ { 1, 1, 0 }, /* STOPPED */ { 1, 1, 0, 1 },
/* PAUSED */ { 1, 1, 0 }, /* PAUSED */ { 1, 1, 0, 1 },
}; };
static const MFCLOCK_STATE states[CLOCK_CMD_MAX] = static const MFCLOCK_STATE states[CLOCK_CMD_MAX] =
{ {
/* CLOCK_CMD_START */ MFCLOCK_STATE_RUNNING, /* CLOCK_CMD_START */ MFCLOCK_STATE_RUNNING,
/* CLOCK_CMD_STOP */ MFCLOCK_STATE_STOPPED, /* CLOCK_CMD_STOP */ MFCLOCK_STATE_STOPPED,
/* CLOCK_CMD_PAUSE */ MFCLOCK_STATE_PAUSED, /* CLOCK_CMD_PAUSE */ MFCLOCK_STATE_PAUSED,
/* CLOCK_CMD_SET_RATE */ 0, /* Unused */
}; };
enum clock_notification notification; enum clock_notification notification;
struct clock_sink *sink; struct clock_sink *sink;
@ -1162,7 +1180,7 @@ static HRESULT clock_change_state(struct presentation_clock *clock, enum clock_c
MFTIME system_time; MFTIME system_time;
HRESULT hr; 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; return MF_E_CLOCK_STATE_ALREADY_SET;
if (!state_change_is_allowed[clock->state][command]) 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) switch (command)
{ {
case CLOCK_CMD_START: 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; notification = CLOCK_NOTIFY_RESTART;
else else
notification = CLOCK_NOTIFY_START; notification = CLOCK_NOTIFY_START;
@ -1184,18 +1202,21 @@ static HRESULT clock_change_state(struct presentation_clock *clock, enum clock_c
case CLOCK_CMD_PAUSE: case CLOCK_CMD_PAUSE:
notification = CLOCK_NOTIFY_PAUSE; notification = CLOCK_NOTIFY_PAUSE;
break; break;
case CLOCK_CMD_SET_RATE:
notification = CLOCK_NOTIFY_SET_RATE;
break;
default: 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; return hr;
clock->state = states[command]; clock->state = states[command];
LIST_FOR_EACH_ENTRY(sink, &clock->sinks, struct clock_sink, entry) 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); hr = MFCreateAsyncResult(notify_object, &clock->IMFAsyncCallback_iface, NULL, &result);
IUnknown_Release(notify_object); 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) static HRESULT WINAPI present_clock_Start(IMFPresentationClock *iface, LONGLONG start_offset)
{ {
struct presentation_clock *clock = impl_from_IMFPresentationClock(iface); struct presentation_clock *clock = impl_from_IMFPresentationClock(iface);
struct clock_state_change_param param = {{0}};
HRESULT hr; HRESULT hr;
TRACE("%p, %s.\n", iface, wine_dbgstr_longlong(start_offset)); TRACE("%p, %s.\n", iface, wine_dbgstr_longlong(start_offset));
EnterCriticalSection(&clock->cs); 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); LeaveCriticalSection(&clock->cs);
return hr; return hr;
@ -1227,12 +1250,13 @@ static HRESULT WINAPI present_clock_Start(IMFPresentationClock *iface, LONGLONG
static HRESULT WINAPI present_clock_Stop(IMFPresentationClock *iface) static HRESULT WINAPI present_clock_Stop(IMFPresentationClock *iface)
{ {
struct presentation_clock *clock = impl_from_IMFPresentationClock(iface); struct presentation_clock *clock = impl_from_IMFPresentationClock(iface);
struct clock_state_change_param param = {{0}};
HRESULT hr; HRESULT hr;
TRACE("%p.\n", iface); TRACE("%p.\n", iface);
EnterCriticalSection(&clock->cs); EnterCriticalSection(&clock->cs);
hr = clock_change_state(clock, CLOCK_CMD_STOP, 0); hr = clock_change_state(clock, CLOCK_CMD_STOP, param);
LeaveCriticalSection(&clock->cs); LeaveCriticalSection(&clock->cs);
return hr; return hr;
@ -1241,12 +1265,13 @@ static HRESULT WINAPI present_clock_Stop(IMFPresentationClock *iface)
static HRESULT WINAPI present_clock_Pause(IMFPresentationClock *iface) static HRESULT WINAPI present_clock_Pause(IMFPresentationClock *iface)
{ {
struct presentation_clock *clock = impl_from_IMFPresentationClock(iface); struct presentation_clock *clock = impl_from_IMFPresentationClock(iface);
struct clock_state_change_param param = {{0}};
HRESULT hr; HRESULT hr;
TRACE("%p.\n", iface); TRACE("%p.\n", iface);
EnterCriticalSection(&clock->cs); EnterCriticalSection(&clock->cs);
hr = clock_change_state(clock, CLOCK_CMD_PAUSE, 0); hr = clock_change_state(clock, CLOCK_CMD_PAUSE, param);
LeaveCriticalSection(&clock->cs); LeaveCriticalSection(&clock->cs);
return hr; 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) 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) 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 = static const IMFRateControlVtbl presentclockratecontrolvtbl =
@ -1439,7 +1489,7 @@ static HRESULT WINAPI present_clock_sink_callback_Invoke(IMFAsyncCallback *iface
data = impl_from_IUnknown(object); 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); IUnknown_Release(object);
@ -1475,6 +1525,7 @@ HRESULT WINAPI MFCreatePresentationClock(IMFPresentationClock **clock)
object->IMFAsyncCallback_iface.lpVtbl = &presentclocksinkcallbackvtbl; object->IMFAsyncCallback_iface.lpVtbl = &presentclocksinkcallbackvtbl;
object->refcount = 1; object->refcount = 1;
list_init(&object->sinks); list_init(&object->sinks);
object->rate = 1.0f;
InitializeCriticalSection(&object->cs); InitializeCriticalSection(&object->cs);
*clock = &object->IMFPresentationClock_iface; *clock = &object->IMFPresentationClock_iface;

View File

@ -1430,6 +1430,7 @@ static void test_presentation_clock(void)
MFCLOCK_PROPERTIES props, props2; MFCLOCK_PROPERTIES props, props2;
IMFRateControl *rate_control; IMFRateControl *rate_control;
IMFPresentationClock *clock; IMFPresentationClock *clock;
MFSHUTDOWN_STATUS status;
IMFShutdown *shutdown; IMFShutdown *shutdown;
MFTIME systime, time; MFTIME systime, time;
LONGLONG clock_time; LONGLONG clock_time;
@ -1437,7 +1438,9 @@ static void test_presentation_clock(void)
IMFTimer *timer; IMFTimer *timer;
unsigned int i; unsigned int i;
DWORD value; DWORD value;
float rate;
HRESULT hr; HRESULT hr;
BOOL thin;
hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); hr = MFStartup(MF_VERSION, MFSTARTUP_FULL);
ok(hr == S_OK, "Failed to start up, hr %#x.\n", hr); 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); hr = IMFPresentationClock_QueryInterface(clock, &IID_IMFRateControl, (void **)&rate_control);
ok(hr == S_OK, "Failed to get rate control interface, hr %#x.\n", hr); 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); IMFRateControl_Release(rate_control);
hr = IMFPresentationClock_QueryInterface(clock, &IID_IMFTimer, (void **)&timer); 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); hr = IMFPresentationClock_QueryInterface(clock, &IID_IMFShutdown, (void **)&shutdown);
ok(hr == S_OK, "Failed to get shutdown interface, hr %#x.\n", hr); 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); IMFShutdown_Release(shutdown);
IMFPresentationClock_Release(clock); IMFPresentationClock_Release(clock);