diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 2961abe879c..27b2c4cce61 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -47,6 +47,7 @@ struct presentation_clock IMFShutdown IMFShutdown_iface; LONG refcount; IMFPresentationTimeSource *time_source; + IMFClockStateSink *time_source_sink; MFCLOCK_STATE state; CRITICAL_SECTION cs; }; @@ -341,6 +342,8 @@ static ULONG WINAPI present_clock_Release(IMFPresentationClock *iface) { if (clock->time_source) IMFPresentationTimeSource_Release(clock->time_source); + if (clock->time_source_sink) + IMFClockStateSink_Release(clock->time_source_sink); DeleteCriticalSection(&clock->cs); heap_free(clock); } @@ -395,9 +398,29 @@ static HRESULT WINAPI present_clock_GetProperties(IMFPresentationClock *iface, M static HRESULT WINAPI present_clock_SetTimeSource(IMFPresentationClock *iface, IMFPresentationTimeSource *time_source) { - FIXME("%p, %p.\n", iface, time_source); + struct presentation_clock *clock = impl_from_IMFPresentationClock(iface); + HRESULT hr; - return E_NOTIMPL; + TRACE("%p, %p.\n", iface, time_source); + + EnterCriticalSection(&clock->cs); + if (clock->time_source) + IMFPresentationTimeSource_Release(clock->time_source); + if (clock->time_source_sink) + IMFClockStateSink_Release(clock->time_source_sink); + clock->time_source = NULL; + clock->time_source_sink = NULL; + + hr = IMFPresentationTimeSource_QueryInterface(time_source, &IID_IMFClockStateSink, (void **)&clock->time_source_sink); + if (SUCCEEDED(hr)) + { + clock->time_source = time_source; + IMFPresentationTimeSource_AddRef(clock->time_source); + } + + LeaveCriticalSection(&clock->cs); + + return hr; } static HRESULT WINAPI present_clock_GetTimeSource(IMFPresentationClock *iface, @@ -443,25 +466,107 @@ static HRESULT WINAPI present_clock_RemoveClockStateSink(IMFPresentationClock *i return E_NOTIMPL; } +enum clock_command +{ + CLOCK_CMD_START = 0, + CLOCK_CMD_STOP, + CLOCK_CMD_PAUSE, + CLOCK_CMD_MAX, +}; + +static HRESULT clock_change_state(struct presentation_clock *clock, enum clock_command command) +{ + 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 }, + }; + 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, + }; + HRESULT hr; + + /* FIXME: use correct timestamps. */ + + if (clock->state == states[command] && clock->state != MFCLOCK_STATE_RUNNING) + return MF_E_CLOCK_STATE_ALREADY_SET; + + if (!state_change_is_allowed[clock->state][command]) + return MF_E_INVALIDREQUEST; + + switch (command) + { + case CLOCK_CMD_START: + if (clock->state == MFCLOCK_STATE_PAUSED) + hr = IMFClockStateSink_OnClockRestart(clock->time_source_sink, 0); + else + hr = IMFClockStateSink_OnClockStart(clock->time_source_sink, 0, 0); + break; + case CLOCK_CMD_STOP: + hr = IMFClockStateSink_OnClockStop(clock->time_source_sink, 0); + break; + case CLOCK_CMD_PAUSE: + hr = IMFClockStateSink_OnClockPause(clock->time_source_sink, 0); + break; + default: + ; + } + + if (FAILED(hr)) + return hr; + + clock->state = states[command]; + + /* FIXME: notify registered sinks. */ + + return S_OK; +} + static HRESULT WINAPI present_clock_Start(IMFPresentationClock *iface, LONGLONG start_offset) { - FIXME("%p, %s.\n", iface, wine_dbgstr_longlong(start_offset)); + struct presentation_clock *clock = impl_from_IMFPresentationClock(iface); + HRESULT hr; - return E_NOTIMPL; + TRACE("%p, %s.\n", iface, wine_dbgstr_longlong(start_offset)); + + EnterCriticalSection(&clock->cs); + hr = clock_change_state(clock, CLOCK_CMD_START); + LeaveCriticalSection(&clock->cs); + + return hr; } static HRESULT WINAPI present_clock_Stop(IMFPresentationClock *iface) { - FIXME("%p.\n", iface); + struct presentation_clock *clock = impl_from_IMFPresentationClock(iface); + HRESULT hr; - return E_NOTIMPL; + TRACE("%p.\n", iface); + + EnterCriticalSection(&clock->cs); + hr = clock_change_state(clock, CLOCK_CMD_STOP); + LeaveCriticalSection(&clock->cs); + + return hr; } static HRESULT WINAPI present_clock_Pause(IMFPresentationClock *iface) { - FIXME("%p.\n", iface); + struct presentation_clock *clock = impl_from_IMFPresentationClock(iface); + HRESULT hr; - return E_NOTIMPL; + TRACE("%p.\n", iface); + + EnterCriticalSection(&clock->cs); + hr = clock_change_state(clock, CLOCK_CMD_PAUSE); + LeaveCriticalSection(&clock->cs); + + return hr; } static const IMFPresentationClockVtbl presentationclockvtbl = diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 9fa0bda8399..bae674d7fa9 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -735,8 +735,37 @@ static void test_MFShutdownObject(void) ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr); } +enum clock_action +{ + CLOCK_START, + CLOCK_STOP, + CLOCK_PAUSE, +}; + static void test_presentation_clock(void) { + static const struct clock_state_test + { + enum clock_action action; + MFCLOCK_STATE clock_state; + MFCLOCK_STATE source_state; + HRESULT hr; + } + clock_state_change[] = + { + { CLOCK_STOP, MFCLOCK_STATE_STOPPED, MFCLOCK_STATE_INVALID }, + { CLOCK_PAUSE, MFCLOCK_STATE_STOPPED, MFCLOCK_STATE_INVALID, MF_E_INVALIDREQUEST }, + { CLOCK_STOP, MFCLOCK_STATE_STOPPED, MFCLOCK_STATE_INVALID, MF_E_CLOCK_STATE_ALREADY_SET }, + { CLOCK_START, MFCLOCK_STATE_RUNNING, MFCLOCK_STATE_RUNNING }, + { CLOCK_START, MFCLOCK_STATE_RUNNING, MFCLOCK_STATE_RUNNING }, + { CLOCK_PAUSE, MFCLOCK_STATE_PAUSED, MFCLOCK_STATE_PAUSED }, + { CLOCK_PAUSE, MFCLOCK_STATE_PAUSED, MFCLOCK_STATE_PAUSED, MF_E_CLOCK_STATE_ALREADY_SET }, + { CLOCK_STOP, MFCLOCK_STATE_STOPPED, MFCLOCK_STATE_STOPPED }, + { CLOCK_START, MFCLOCK_STATE_RUNNING, MFCLOCK_STATE_RUNNING }, + { CLOCK_STOP, MFCLOCK_STATE_STOPPED, MFCLOCK_STATE_STOPPED }, + { CLOCK_STOP, MFCLOCK_STATE_STOPPED, MFCLOCK_STATE_STOPPED, MF_E_CLOCK_STATE_ALREADY_SET }, + { CLOCK_PAUSE, MFCLOCK_STATE_STOPPED, MFCLOCK_STATE_STOPPED, MF_E_INVALIDREQUEST }, + }; IMFPresentationTimeSource *time_source; IMFRateControl *rate_control; IMFPresentationClock *clock; @@ -746,6 +775,7 @@ static void test_presentation_clock(void) MFCLOCK_STATE state; IMFTimer *timer; MFTIME systime; + unsigned int i; DWORD value; HRESULT hr; @@ -779,6 +809,43 @@ todo_wine todo_wine ok(hr == MF_E_CLOCK_NO_TIME_SOURCE, "Unexpected hr %#x.\n", hr); + /* Set default time source. */ + hr = MFCreateSystemTimeSource(&time_source); + ok(hr == S_OK, "Failed to create time source, hr %#x.\n", hr); + + hr = IMFPresentationClock_SetTimeSource(clock, time_source); + ok(hr == S_OK, "Failed to set time source, hr %#x.\n", hr); + + /* State changes. */ + for (i = 0; i < ARRAY_SIZE(clock_state_change); ++i) + { + switch (clock_state_change[i].action) + { + case CLOCK_STOP: + hr = IMFPresentationClock_Stop(clock); + break; + case CLOCK_PAUSE: + hr = IMFPresentationClock_Pause(clock); + break; + case CLOCK_START: + hr = IMFPresentationClock_Start(clock, 0); + break; + default: + ; + } + ok(hr == clock_state_change[i].hr, "%u: unexpected hr %#x.\n", i, hr); + + hr = IMFPresentationTimeSource_GetState(time_source, 0, &state); + ok(hr == S_OK, "%u: failed to get state, hr %#x.\n", i, hr); + ok(state == clock_state_change[i].source_state, "%u: unexpected state %d.\n", i, state); + + hr = IMFPresentationClock_GetState(clock, 0, &state); + ok(hr == S_OK, "%u: failed to get state, hr %#x.\n", i, hr); + ok(state == clock_state_change[i].clock_state, "%u: unexpected state %d.\n", i, state); + } + + IMFPresentationTimeSource_Release(time_source); + hr = IMFPresentationClock_QueryInterface(clock, &IID_IMFRateControl, (void **)&rate_control); ok(hr == S_OK, "Failed to get rate control interface, hr %#x.\n", hr); IMFRateControl_Release(rate_control); diff --git a/include/mferror.h b/include/mferror.h index be35e6e20fd..f7f0934d6ce 100644 --- a/include/mferror.h +++ b/include/mferror.h @@ -82,6 +82,7 @@ #define MF_E_TOPO_MISSING_SOURCE _HRESULT_TYPEDEF_(0xc00d521a) #define MF_E_TOPO_SINK_ACTIVATES_UNSUPPORTED _HRESULT_TYPEDEF_(0xc00d521b) -#define MF_E_CLOCK_NO_TIME_SOURCE _HRESULT_TYPEDEF_(0xc00d9c41) +#define MF_E_CLOCK_NO_TIME_SOURCE _HRESULT_TYPEDEF_(0xc00d9c41) +#define MF_E_CLOCK_STATE_ALREADY_SET _HRESULT_TYPEDEF_(0xc00d9c42) #endif /* __WINE_MFERROR_H */