Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 121 ++++++++++++++++++++++++++++++++++++++++++--- dlls/mf/tests/mf.c | 67 +++++++++++++++++++++++++ include/mferror.h | 3 +- 3 files changed, 182 insertions(+), 9 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 2961abe879..27b2c4cce6 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 9fa0bda839..bae674d7fa 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 be35e6e20f..f7f0934d6c 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 */
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 76 ++++++++++++++++++++++++++++++++++++++++--- dlls/mf/tests/mf.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 4 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 27b2c4cce6..d2d1d495a0 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -29,6 +29,7 @@
#include "wine/debug.h" #include "wine/heap.h" +#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
@@ -39,6 +40,12 @@ struct media_session IMFMediaEventQueue *event_queue; };
+struct clock_sink +{ + struct list entry; + IMFClockStateSink *state_sink; +}; + struct presentation_clock { IMFPresentationClock IMFPresentationClock_iface; @@ -49,6 +56,7 @@ struct presentation_clock IMFPresentationTimeSource *time_source; IMFClockStateSink *time_source_sink; MFCLOCK_STATE state; + struct list sinks; CRITICAL_SECTION cs; };
@@ -335,6 +343,7 @@ static ULONG WINAPI present_clock_Release(IMFPresentationClock *iface) { struct presentation_clock *clock = impl_from_IMFPresentationClock(iface); ULONG refcount = InterlockedDecrement(&clock->refcount); + struct clock_sink *sink, *sink2;
TRACE("%p, refcount %u.\n", iface, refcount);
@@ -344,6 +353,12 @@ static ULONG WINAPI present_clock_Release(IMFPresentationClock *iface) IMFPresentationTimeSource_Release(clock->time_source); if (clock->time_source_sink) IMFClockStateSink_Release(clock->time_source_sink); + LIST_FOR_EACH_ENTRY_SAFE(sink, sink2, &clock->sinks, struct clock_sink, entry) + { + list_remove(&sink->entry); + IMFClockStateSink_Release(sink->state_sink); + heap_free(sink); + } DeleteCriticalSection(&clock->cs); heap_free(clock); } @@ -453,17 +468,69 @@ static HRESULT WINAPI present_clock_GetTime(IMFPresentationClock *iface, MFTIME
static HRESULT WINAPI present_clock_AddClockStateSink(IMFPresentationClock *iface, IMFClockStateSink *state_sink) { - FIXME("%p, %p.\n", iface, state_sink); + struct presentation_clock *clock = impl_from_IMFPresentationClock(iface); + struct clock_sink *sink, *cur; + HRESULT hr = S_OK;
- return E_NOTIMPL; + TRACE("%p, %p.\n", iface, state_sink); + + if (!state_sink) + return E_INVALIDARG; + + sink = heap_alloc(sizeof(*sink)); + if (!sink) + return E_OUTOFMEMORY; + + sink->state_sink = state_sink; + IMFClockStateSink_AddRef(sink->state_sink); + + EnterCriticalSection(&clock->cs); + LIST_FOR_EACH_ENTRY(cur, &clock->sinks, struct clock_sink, entry) + { + if (cur->state_sink == state_sink) + { + hr = E_INVALIDARG; + break; + } + } + if (SUCCEEDED(hr)) + list_add_tail(&clock->sinks, &sink->entry); + LeaveCriticalSection(&clock->cs); + + if (FAILED(hr)) + { + IMFClockStateSink_Release(sink->state_sink); + heap_free(sink); + } + + return hr; }
static HRESULT WINAPI present_clock_RemoveClockStateSink(IMFPresentationClock *iface, IMFClockStateSink *state_sink) { - FIXME("%p, %p.\n", iface, state_sink); + struct presentation_clock *clock = impl_from_IMFPresentationClock(iface); + struct clock_sink *sink;
- return E_NOTIMPL; + TRACE("%p, %p.\n", iface, state_sink); + + if (!state_sink) + return E_INVALIDARG; + + EnterCriticalSection(&clock->cs); + LIST_FOR_EACH_ENTRY(sink, &clock->sinks, struct clock_sink, entry) + { + if (sink->state_sink == state_sink) + { + IMFClockStateSink_Release(sink->state_sink); + list_remove(&sink->entry); + heap_free(sink); + break; + } + } + LeaveCriticalSection(&clock->cs); + + return S_OK; }
enum clock_command @@ -731,6 +798,7 @@ HRESULT WINAPI MFCreatePresentationClock(IMFPresentationClock **clock) object->IMFTimer_iface.lpVtbl = &presentclocktimervtbl; object->IMFShutdown_iface.lpVtbl = &presentclockshutdownvtbl; object->refcount = 1; + list_init(&object->sinks); InitializeCriticalSection(&object->cs);
*clock = &object->IMFPresentationClock_iface; diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index bae674d7fa..42b23d6c3e 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -742,6 +742,67 @@ enum clock_action CLOCK_PAUSE, };
+static HRESULT WINAPI test_clock_sink_QueryInterface(IMFClockStateSink *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFClockStateSink) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFClockStateSink_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_clock_sink_AddRef(IMFClockStateSink *iface) +{ + return 2; +} + +static ULONG WINAPI test_clock_sink_Release(IMFClockStateSink *iface) +{ + return 1; +} + +static HRESULT WINAPI test_clock_sink_OnClockStart(IMFClockStateSink *iface, MFTIME system_time, LONGLONG offset) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_clock_sink_OnClockStop(IMFClockStateSink *iface, MFTIME system_time) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_clock_sink_OnClockPause(IMFClockStateSink *iface, MFTIME system_time) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_clock_sink_OnClockRestart(IMFClockStateSink *iface, MFTIME system_time) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_clock_sink_OnClockSetRate(IMFClockStateSink *iface, MFTIME system_time, float rate) +{ + return E_NOTIMPL; +} + +static const IMFClockStateSinkVtbl test_clock_sink_vtbl = +{ + test_clock_sink_QueryInterface, + test_clock_sink_AddRef, + test_clock_sink_Release, + test_clock_sink_OnClockStart, + test_clock_sink_OnClockStop, + test_clock_sink_OnClockPause, + test_clock_sink_OnClockRestart, + test_clock_sink_OnClockSetRate, +}; + static void test_presentation_clock(void) { static const struct clock_state_test @@ -766,6 +827,7 @@ static void test_presentation_clock(void) { 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 }, }; + IMFClockStateSink test_sink = { &test_clock_sink_vtbl }; IMFPresentationTimeSource *time_source; IMFRateControl *rate_control; IMFPresentationClock *clock; @@ -809,6 +871,25 @@ todo_wine todo_wine ok(hr == MF_E_CLOCK_NO_TIME_SOURCE, "Unexpected hr %#x.\n", hr);
+ /* Sinks. */ + hr = IMFPresentationClock_AddClockStateSink(clock, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + hr = IMFPresentationClock_AddClockStateSink(clock, &test_sink); + ok(hr == S_OK, "Failed to add a sink, hr %#x.\n", hr); + + hr = IMFPresentationClock_AddClockStateSink(clock, &test_sink); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + hr = IMFPresentationClock_RemoveClockStateSink(clock, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + hr = IMFPresentationClock_RemoveClockStateSink(clock, &test_sink); + ok(hr == S_OK, "Failed to remove sink, hr %#x.\n", hr); + + hr = IMFPresentationClock_RemoveClockStateSink(clock, &test_sink); + ok(hr == S_OK, "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);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 237 +++++++++++++++++++++++++++++++++++++++++---- dlls/mf/tests/mf.c | 3 + include/mfidl.idl | 2 + 3 files changed, 224 insertions(+), 18 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index d2d1d495a0..7f8a283e76 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -46,12 +46,39 @@ struct clock_sink IMFClockStateSink *state_sink; };
+enum clock_command +{ + CLOCK_CMD_START = 0, + CLOCK_CMD_STOP, + CLOCK_CMD_PAUSE, + CLOCK_CMD_MAX, +}; + +enum clock_notification +{ + CLOCK_NOTIFY_START, + CLOCK_NOTIFY_STOP, + CLOCK_NOTIFY_PAUSE, + CLOCK_NOTIFY_RESTART, +}; + +struct sink_notification +{ + IUnknown IUnknown_iface; + LONG refcount; + MFTIME system_time; + LONGLONG offset; + enum clock_notification notification; + IMFClockStateSink *sink; +}; + struct presentation_clock { IMFPresentationClock IMFPresentationClock_iface; IMFRateControl IMFRateControl_iface; IMFTimer IMFTimer_iface; IMFShutdown IMFShutdown_iface; + IMFAsyncCallback IMFAsyncCallback_iface; LONG refcount; IMFPresentationTimeSource *time_source; IMFClockStateSink *time_source_sink; @@ -85,6 +112,16 @@ static struct presentation_clock *impl_from_IMFShutdown(IMFShutdown *iface) return CONTAINING_RECORD(iface, struct presentation_clock, IMFShutdown_iface); }
+static struct presentation_clock *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct presentation_clock, IMFAsyncCallback_iface); +} + +static struct sink_notification *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct sink_notification, IUnknown_iface); +} + static HRESULT WINAPI mfsession_QueryInterface(IMFMediaSession *iface, REFIID riid, void **out) { struct media_session *session = impl_from_IMFMediaSession(iface); @@ -533,15 +570,102 @@ static HRESULT WINAPI present_clock_RemoveClockStateSink(IMFPresentationClock *i return S_OK; }
-enum clock_command +static HRESULT WINAPI sink_notification_QueryInterface(IUnknown *iface, REFIID riid, void **out) { - CLOCK_CMD_START = 0, - CLOCK_CMD_STOP, - CLOCK_CMD_PAUSE, - CLOCK_CMD_MAX, + if (IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI sink_notification_AddRef(IUnknown *iface) +{ + struct sink_notification *notification = impl_from_IUnknown(iface); + ULONG refcount = InterlockedIncrement(¬ification->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI sink_notification_Release(IUnknown *iface) +{ + struct sink_notification *notification = impl_from_IUnknown(iface); + ULONG refcount = InterlockedDecrement(¬ification->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + IMFClockStateSink_Release(notification->sink); + heap_free(notification); + } + + return refcount; +} + +static const IUnknownVtbl sinknotificationvtbl = +{ + sink_notification_QueryInterface, + sink_notification_AddRef, + sink_notification_Release, };
-static HRESULT clock_change_state(struct presentation_clock *clock, enum clock_command command) +static HRESULT create_sink_notification(MFTIME system_time, LONGLONG offset, enum clock_notification notification, + IMFClockStateSink *sink, IUnknown **out) +{ + struct sink_notification *object; + + object = heap_alloc(sizeof(*object)); + if (!object) + return E_OUTOFMEMORY; + + object->IUnknown_iface.lpVtbl = &sinknotificationvtbl; + object->refcount = 1; + object->system_time = system_time; + object->offset = offset; + object->notification = notification; + object->sink = sink; + IMFClockStateSink_AddRef(object->sink); + + *out = &object->IUnknown_iface; + + return S_OK; +} + +static HRESULT clock_call_state_change(MFTIME system_time, LONGLONG offset, enum clock_notification notification, + IMFClockStateSink *sink) +{ + HRESULT hr = S_OK; + + switch (notification) + { + case CLOCK_NOTIFY_START: + hr = IMFClockStateSink_OnClockStart(sink, system_time, offset); + break; + case CLOCK_NOTIFY_STOP: + hr = IMFClockStateSink_OnClockStop(sink, system_time); + break; + case CLOCK_NOTIFY_PAUSE: + hr = IMFClockStateSink_OnClockPause(sink, system_time); + break; + case CLOCK_NOTIFY_RESTART: + hr = IMFClockStateSink_OnClockRestart(sink, system_time); + break; + default: + ; + } + + return hr; +} + +static HRESULT clock_change_state(struct presentation_clock *clock, enum clock_command command, LONGLONG offset) { static const BYTE state_change_is_allowed[MFCLOCK_STATE_PAUSED+1][CLOCK_CMD_MAX] = { /* S S* P */ @@ -556,40 +680,57 @@ static HRESULT clock_change_state(struct presentation_clock *clock, enum clock_c /* CLOCK_CMD_STOP */ MFCLOCK_STATE_STOPPED, /* CLOCK_CMD_PAUSE */ MFCLOCK_STATE_PAUSED, }; + enum clock_notification notification; + struct clock_sink *sink; + IUnknown *notify_object; + IMFAsyncResult *result; + MFTIME system_time; 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;
+ system_time = MFGetSystemTime(); + switch (command) { case CLOCK_CMD_START: - if (clock->state == MFCLOCK_STATE_PAUSED) - hr = IMFClockStateSink_OnClockRestart(clock->time_source_sink, 0); + if (clock->state == MFCLOCK_STATE_PAUSED && offset == PRESENTATION_CURRENT_POSITION) + notification = CLOCK_NOTIFY_RESTART; else - hr = IMFClockStateSink_OnClockStart(clock->time_source_sink, 0, 0); + notification = CLOCK_NOTIFY_START; break; case CLOCK_CMD_STOP: - hr = IMFClockStateSink_OnClockStop(clock->time_source_sink, 0); + notification = CLOCK_NOTIFY_STOP; break; case CLOCK_CMD_PAUSE: - hr = IMFClockStateSink_OnClockPause(clock->time_source_sink, 0); + notification = CLOCK_NOTIFY_PAUSE; break; default: ; }
- if (FAILED(hr)) + if (FAILED(hr = clock_call_state_change(system_time, offset, notification, clock->time_source_sink))) return hr;
clock->state = states[command];
- /* FIXME: notify registered sinks. */ + 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))) + { + hr = MFCreateAsyncResult(notify_object, &clock->IMFAsyncCallback_iface, NULL, &result); + IUnknown_Release(notify_object); + if (SUCCEEDED(hr)) + { + MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_STANDARD, result); + IMFAsyncResult_Release(result); + } + } + }
return S_OK; } @@ -602,7 +743,7 @@ static HRESULT WINAPI present_clock_Start(IMFPresentationClock *iface, LONGLONG TRACE("%p, %s.\n", iface, wine_dbgstr_longlong(start_offset));
EnterCriticalSection(&clock->cs); - hr = clock_change_state(clock, CLOCK_CMD_START); + hr = clock_change_state(clock, CLOCK_CMD_START, start_offset); LeaveCriticalSection(&clock->cs);
return hr; @@ -616,7 +757,7 @@ static HRESULT WINAPI present_clock_Stop(IMFPresentationClock *iface) TRACE("%p.\n", iface);
EnterCriticalSection(&clock->cs); - hr = clock_change_state(clock, CLOCK_CMD_STOP); + hr = clock_change_state(clock, CLOCK_CMD_STOP, 0); LeaveCriticalSection(&clock->cs);
return hr; @@ -630,7 +771,7 @@ static HRESULT WINAPI present_clock_Pause(IMFPresentationClock *iface) TRACE("%p.\n", iface);
EnterCriticalSection(&clock->cs); - hr = clock_change_state(clock, CLOCK_CMD_PAUSE); + hr = clock_change_state(clock, CLOCK_CMD_PAUSE, 0); LeaveCriticalSection(&clock->cs);
return hr; @@ -780,6 +921,65 @@ static const IMFShutdownVtbl presentclockshutdownvtbl = present_clock_shutdown_GetShutdownStatus, };
+static HRESULT WINAPI present_clock_sink_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **out) +{ + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", wine_dbgstr_guid(riid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI present_clock_sink_callback_AddRef(IMFAsyncCallback *iface) +{ + struct presentation_clock *clock = impl_from_IMFAsyncCallback(iface); + return IMFPresentationClock_AddRef(&clock->IMFPresentationClock_iface); +} + +static ULONG WINAPI present_clock_sink_callback_Release(IMFAsyncCallback *iface) +{ + struct presentation_clock *clock = impl_from_IMFAsyncCallback(iface); + return IMFPresentationClock_Release(&clock->IMFPresentationClock_iface); +} + +static HRESULT WINAPI present_clock_sink_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI present_clock_sink_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct sink_notification *data; + IUnknown *object; + HRESULT hr; + + if (FAILED(hr = IMFAsyncResult_GetObject(result, &object))) + return hr; + + data = impl_from_IUnknown(object); + + clock_call_state_change(data->system_time, data->offset, data->notification, data->sink); + + IUnknown_Release(object); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl presentclocksinkcallbackvtbl = +{ + present_clock_sink_callback_QueryInterface, + present_clock_sink_callback_AddRef, + present_clock_sink_callback_Release, + present_clock_sink_callback_GetParameters, + present_clock_sink_callback_Invoke, +}; + /*********************************************************************** * MFCreatePresentationClock (mf.@) */ @@ -797,6 +997,7 @@ HRESULT WINAPI MFCreatePresentationClock(IMFPresentationClock **clock) object->IMFRateControl_iface.lpVtbl = &presentclockratecontrolvtbl; object->IMFTimer_iface.lpVtbl = &presentclocktimervtbl; object->IMFShutdown_iface.lpVtbl = &presentclockshutdownvtbl; + object->IMFAsyncCallback_iface.lpVtbl = &presentclocksinkcallbackvtbl; object->refcount = 1; list_init(&object->sinks); InitializeCriticalSection(&object->cs); diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 42b23d6c3e..4dfefdd459 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -826,6 +826,9 @@ static void test_presentation_clock(void) { 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 }, + { CLOCK_START, MFCLOCK_STATE_RUNNING, MFCLOCK_STATE_RUNNING }, + { CLOCK_PAUSE, MFCLOCK_STATE_PAUSED, MFCLOCK_STATE_PAUSED }, + { CLOCK_START, MFCLOCK_STATE_RUNNING, MFCLOCK_STATE_RUNNING }, }; IMFClockStateSink test_sink = { &test_clock_sink_vtbl }; IMFPresentationTimeSource *time_source; diff --git a/include/mfidl.idl b/include/mfidl.idl index 252099e145..04a6d71bf9 100644 --- a/include/mfidl.idl +++ b/include/mfidl.idl @@ -412,6 +412,8 @@ interface IMFPresentationTimeSource : IMFClock HRESULT GetUnderlyingClock([out] IMFClock **clock); }
+cpp_quote("#define PRESENTATION_CURRENT_POSITION 0x7fffffffffffffff") + [ object, uuid(868ce85c-8ea9-4f55-ab82-b009a910a805)