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:
parent
d5f8eb333c
commit
d9cd07fd4e
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue