[PATCH 2/3] mf: Implement timer functionality for presentation clock.

Nikolay Sivov nsivov at codeweavers.com
Thu Feb 20 07:41:20 CST 2020


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/mf/session.c | 285 +++++++++++++++++++++++++++++++++++++++++++---
 include/mfidl.idl |   5 +
 2 files changed, 275 insertions(+), 15 deletions(-)

diff --git a/dlls/mf/session.c b/dlls/mf/session.c
index 1f790a6ed3..b80499477c 100644
--- a/dlls/mf/session.c
+++ b/dlls/mf/session.c
@@ -173,19 +173,32 @@ struct sink_notification
     IMFClockStateSink *sink;
 };
 
+struct clock_timer
+{
+    IUnknown IUnknown_iface;
+    LONG refcount;
+    IMFAsyncResult *result;
+    IMFAsyncCallback *callback;
+    MFWORKITEM_KEY key;
+    struct list entry;
+};
+
 struct presentation_clock
 {
     IMFPresentationClock IMFPresentationClock_iface;
     IMFRateControl IMFRateControl_iface;
     IMFTimer IMFTimer_iface;
     IMFShutdown IMFShutdown_iface;
-    IMFAsyncCallback IMFAsyncCallback_iface;
+    IMFAsyncCallback sink_callback;
+    IMFAsyncCallback timer_callback;
     LONG refcount;
     IMFPresentationTimeSource *time_source;
     IMFClockStateSink *time_source_sink;
     MFCLOCK_STATE state;
     struct list sinks;
+    struct list timers;
     float rate;
+    LONGLONG frequency;
     CRITICAL_SECTION cs;
 };
 
@@ -250,9 +263,19 @@ 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)
+static struct presentation_clock *impl_from_sink_callback_IMFAsyncCallback(IMFAsyncCallback *iface)
+{
+    return CONTAINING_RECORD(iface, struct presentation_clock, sink_callback);
+}
+
+static struct presentation_clock *impl_from_timer_callback_IMFAsyncCallback(IMFAsyncCallback *iface)
 {
-    return CONTAINING_RECORD(iface, struct presentation_clock, IMFAsyncCallback_iface);
+    return CONTAINING_RECORD(iface, struct presentation_clock, timer_callback);
+}
+
+static struct clock_timer *impl_clock_timer_from_IUnknown(IUnknown *iface)
+{
+    return CONTAINING_RECORD(iface, struct clock_timer, IUnknown_iface);
 }
 
 static struct sink_notification *impl_from_IUnknown(IUnknown *iface)
@@ -1631,6 +1654,7 @@ static ULONG WINAPI present_clock_Release(IMFPresentationClock *iface)
 {
     struct presentation_clock *clock = impl_from_IMFPresentationClock(iface);
     ULONG refcount = InterlockedDecrement(&clock->refcount);
+    struct clock_timer *timer, *timer2;
     struct clock_sink *sink, *sink2;
 
     TRACE("%p, refcount %u.\n", iface, refcount);
@@ -1647,6 +1671,11 @@ static ULONG WINAPI present_clock_Release(IMFPresentationClock *iface)
             IMFClockStateSink_Release(sink->state_sink);
             heap_free(sink);
         }
+        LIST_FOR_EACH_ENTRY_SAFE(timer, timer2, &clock->timers, struct clock_timer, entry)
+        {
+            list_remove(&timer->entry);
+            IUnknown_Release(&timer->IUnknown_iface);
+        }
         DeleteCriticalSection(&clock->cs);
         heap_free(clock);
     }
@@ -1726,11 +1755,14 @@ static HRESULT WINAPI present_clock_SetTimeSource(IMFPresentationClock *iface,
         IMFPresentationTimeSource *time_source)
 {
     struct presentation_clock *clock = impl_from_IMFPresentationClock(iface);
+    MFCLOCK_PROPERTIES props;
+    IMFClock *source_clock;
     HRESULT hr;
 
     TRACE("%p, %p.\n", iface, time_source);
 
     EnterCriticalSection(&clock->cs);
+
     if (clock->time_source)
         IMFPresentationTimeSource_Release(clock->time_source);
     if (clock->time_source_sink)
@@ -1745,6 +1777,16 @@ static HRESULT WINAPI present_clock_SetTimeSource(IMFPresentationClock *iface,
         IMFPresentationTimeSource_AddRef(clock->time_source);
     }
 
+    if (SUCCEEDED(IMFPresentationTimeSource_GetUnderlyingClock(time_source, &source_clock)))
+    {
+        if (SUCCEEDED(IMFClock_GetProperties(source_clock, &props)))
+            clock->frequency = props.qwClockFrequency;
+        IMFClock_Release(source_clock);
+    }
+
+    if (!clock->frequency)
+        clock->frequency = MFCLOCK_FREQUENCY_HNS;
+
     LeaveCriticalSection(&clock->cs);
 
     return hr;
@@ -1986,6 +2028,7 @@ static HRESULT clock_change_state(struct presentation_clock *clock, enum clock_c
     enum clock_notification notification;
     struct clock_sink *sink;
     IUnknown *notify_object;
+    MFCLOCK_STATE old_state;
     IMFAsyncResult *result;
     MFTIME system_time;
     HRESULT hr;
@@ -2012,13 +2055,47 @@ static HRESULT clock_change_state(struct presentation_clock *clock, enum clock_c
     if (FAILED(hr = clock_call_state_change(system_time, param, notification, clock->time_source_sink)))
         return hr;
 
+    old_state = clock->state;
     clock->state = states[command];
 
+    /* Dump all pending timer requests immediately on start; otherwise try to cancel scheduled items when
+       transitioning from running state. */
+    if ((clock->state == MFCLOCK_STATE_RUNNING) ^ (old_state == MFCLOCK_STATE_RUNNING))
+    {
+        struct clock_timer *timer, *timer2;
+
+        if (clock->state == MFCLOCK_STATE_RUNNING)
+        {
+            LIST_FOR_EACH_ENTRY_SAFE(timer, timer2, &clock->timers, struct clock_timer, entry)
+            {
+                list_remove(&timer->entry);
+                hr = MFCreateAsyncResult(&timer->IUnknown_iface, &clock->timer_callback, NULL, &result);
+                IUnknown_Release(&timer->IUnknown_iface);
+                if (SUCCEEDED(hr))
+                {
+                    MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_TIMER, result);
+                    IMFAsyncResult_Release(result);
+                }
+            }
+        }
+        else
+        {
+            LIST_FOR_EACH_ENTRY(timer, &clock->timers, struct clock_timer, entry)
+            {
+                if (timer->key)
+                {
+                    MFCancelWorkItem(timer->key);
+                    timer->key = 0;
+                }
+            }
+        }
+    }
+
     LIST_FOR_EACH_ENTRY(sink, &clock->sinks, struct clock_sink, entry)
     {
         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->sink_callback, NULL, &result);
             IUnknown_Release(notify_object);
             if (SUCCEEDED(hr))
             {
@@ -2181,19 +2258,151 @@ static ULONG WINAPI present_clock_timer_Release(IMFTimer *iface)
     return IMFPresentationClock_Release(&clock->IMFPresentationClock_iface);
 }
 
+static HRESULT present_clock_schedule_timer(struct presentation_clock *clock, DWORD flags, LONGLONG time,
+        struct clock_timer *timer)
+{
+    IMFAsyncResult *result;
+    MFTIME systime, clocktime;
+    LONGLONG frequency;
+    HRESULT hr;
+
+    if (!(flags & MFTIMER_RELATIVE))
+    {
+        if (FAILED(hr = IMFPresentationTimeSource_GetCorrelatedTime(clock->time_source, 0, &clocktime, &systime)))
+        {
+            WARN("Failed to get clock time, hr %#x.\n", hr);
+            return hr;
+        }
+        time -= clocktime;
+    }
+
+    frequency = clock->frequency / 1000;
+    time /= frequency;
+
+    /* Scheduled item is using clock instance callback, with timer instance as an object. Clock callback will
+       call user callback and cleanup timer list. */
+
+    if (FAILED(hr = MFCreateAsyncResult(&timer->IUnknown_iface, &clock->timer_callback, NULL, &result)))
+        return hr;
+
+    hr = MFScheduleWorkItemEx(result, -time, &timer->key);
+    IMFAsyncResult_Release(result);
+
+    return hr;
+}
+
+static HRESULT WINAPI clock_timer_QueryInterface(IUnknown *iface, REFIID riid, void **obj)
+{
+    if (IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = iface;
+        IUnknown_AddRef(iface);
+        return S_OK;
+    }
+
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI clock_timer_AddRef(IUnknown *iface)
+{
+    struct clock_timer *timer = impl_clock_timer_from_IUnknown(iface);
+    return InterlockedIncrement(&timer->refcount);
+}
+
+static ULONG WINAPI clock_timer_Release(IUnknown *iface)
+{
+    struct clock_timer *timer = impl_clock_timer_from_IUnknown(iface);
+    ULONG refcount = InterlockedDecrement(&timer->refcount);
+
+    if (!refcount)
+    {
+        IMFAsyncResult_Release(timer->result);
+        IMFAsyncCallback_Release(timer->callback);
+        heap_free(timer);
+    }
+
+    return refcount;
+}
+
+static const IUnknownVtbl clock_timer_vtbl =
+{
+    clock_timer_QueryInterface,
+    clock_timer_AddRef,
+    clock_timer_Release,
+};
+
 static HRESULT WINAPI present_clock_timer_SetTimer(IMFTimer *iface, DWORD flags, LONGLONG time,
         IMFAsyncCallback *callback, IUnknown *state, IUnknown **cancel_key)
 {
-    FIXME("%p, %#x, %s, %p, %p, %p.\n", iface, flags, wine_dbgstr_longlong(time), callback, state, cancel_key);
+    struct presentation_clock *clock = impl_from_IMFTimer(iface);
+    struct clock_timer *clock_timer;
+    HRESULT hr;
 
-    return E_NOTIMPL;
+    TRACE("%p, %#x, %s, %p, %p, %p.\n", iface, flags, wine_dbgstr_longlong(time), callback, state, cancel_key);
+
+    if (!(clock_timer = heap_alloc_zero(sizeof(*clock_timer))))
+        return E_OUTOFMEMORY;
+
+    if (FAILED(hr = MFCreateAsyncResult(NULL, NULL, state, &clock_timer->result)))
+    {
+        heap_free(clock_timer);
+        return hr;
+    }
+
+    clock_timer->IUnknown_iface.lpVtbl = &clock_timer_vtbl;
+    clock_timer->refcount = 1;
+    clock_timer->callback = callback;
+    IMFAsyncCallback_AddRef(clock_timer->callback);
+
+    EnterCriticalSection(&clock->cs);
+
+    if (clock->state == MFCLOCK_STATE_RUNNING)
+        hr = present_clock_schedule_timer(clock, flags, time, clock_timer);
+    else if (clock->state == MFCLOCK_STATE_STOPPED)
+        hr = MF_S_CLOCK_STOPPED;
+
+    if (SUCCEEDED(hr))
+        list_add_tail(&clock->timers, &clock_timer->entry);
+
+    LeaveCriticalSection(&clock->cs);
+
+    if (SUCCEEDED(hr) && cancel_key)
+    {
+        *cancel_key = &clock_timer->IUnknown_iface;
+        IUnknown_AddRef(*cancel_key);
+    }
+
+    return hr;
 }
 
 static HRESULT WINAPI present_clock_timer_CancelTimer(IMFTimer *iface, IUnknown *cancel_key)
 {
-    FIXME("%p, %p.\n", iface, cancel_key);
+    struct presentation_clock *clock = impl_from_IMFTimer(iface);
+    struct clock_timer *timer;
 
-    return E_NOTIMPL;
+    TRACE("%p, %p.\n", iface, cancel_key);
+
+    EnterCriticalSection(&clock->cs);
+
+    LIST_FOR_EACH_ENTRY(timer, &clock->timers, struct clock_timer, entry)
+    {
+        if (&timer->IUnknown_iface == cancel_key)
+        {
+            list_remove(&timer->entry);
+            if (timer->key)
+            {
+                MFCancelWorkItem(timer->key);
+                timer->key = 0;
+            }
+            IUnknown_Release(&timer->IUnknown_iface);
+            break;
+        }
+    }
+
+    LeaveCriticalSection(&clock->cs);
+
+    return S_OK;
 }
 
 static const IMFTimerVtbl presentclocktimervtbl =
@@ -2246,7 +2455,7 @@ static const IMFShutdownVtbl presentclockshutdownvtbl =
     present_clock_shutdown_GetShutdownStatus,
 };
 
-static HRESULT WINAPI present_clock_sink_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **out)
+static HRESULT WINAPI present_clock_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **out)
 {
     if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
             IsEqualIID(riid, &IID_IUnknown))
@@ -2263,17 +2472,17 @@ static HRESULT WINAPI present_clock_sink_callback_QueryInterface(IMFAsyncCallbac
 
 static ULONG WINAPI present_clock_sink_callback_AddRef(IMFAsyncCallback *iface)
 {
-    struct presentation_clock *clock = impl_from_IMFAsyncCallback(iface);
+    struct presentation_clock *clock = impl_from_sink_callback_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);
+    struct presentation_clock *clock = impl_from_sink_callback_IMFAsyncCallback(iface);
     return IMFPresentationClock_Release(&clock->IMFPresentationClock_iface);
 }
 
-static HRESULT WINAPI present_clock_sink_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
+static HRESULT WINAPI present_clock_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
 {
     return E_NOTIMPL;
 }
@@ -2298,13 +2507,57 @@ static HRESULT WINAPI present_clock_sink_callback_Invoke(IMFAsyncCallback *iface
 
 static const IMFAsyncCallbackVtbl presentclocksinkcallbackvtbl =
 {
-    present_clock_sink_callback_QueryInterface,
+    present_clock_callback_QueryInterface,
     present_clock_sink_callback_AddRef,
     present_clock_sink_callback_Release,
-    present_clock_sink_callback_GetParameters,
+    present_clock_callback_GetParameters,
     present_clock_sink_callback_Invoke,
 };
 
+static ULONG WINAPI present_clock_timer_callback_AddRef(IMFAsyncCallback *iface)
+{
+    struct presentation_clock *clock = impl_from_timer_callback_IMFAsyncCallback(iface);
+    return IMFPresentationClock_AddRef(&clock->IMFPresentationClock_iface);
+}
+
+static ULONG WINAPI present_clock_timer_callback_Release(IMFAsyncCallback *iface)
+{
+    struct presentation_clock *clock = impl_from_timer_callback_IMFAsyncCallback(iface);
+    return IMFPresentationClock_Release(&clock->IMFPresentationClock_iface);
+}
+
+static HRESULT WINAPI present_clock_timer_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
+{
+    struct presentation_clock *clock = impl_from_timer_callback_IMFAsyncCallback(iface);
+    struct clock_timer *timer;
+    IUnknown *object;
+    HRESULT hr;
+
+    if (FAILED(hr = IMFAsyncResult_GetObject(result, &object)))
+        return hr;
+
+    timer = impl_clock_timer_from_IUnknown(object);
+
+    EnterCriticalSection(&clock->cs);
+    list_remove(&timer->entry);
+    LeaveCriticalSection(&clock->cs);
+
+    IMFAsyncCallback_Invoke(timer->callback, timer->result);
+
+    IUnknown_Release(object);
+
+    return S_OK;
+}
+
+static const IMFAsyncCallbackVtbl presentclocktimercallbackvtbl =
+{
+    present_clock_callback_QueryInterface,
+    present_clock_timer_callback_AddRef,
+    present_clock_timer_callback_Release,
+    present_clock_callback_GetParameters,
+    present_clock_timer_callback_Invoke,
+};
+
 /***********************************************************************
  *      MFCreatePresentationClock (mf.@)
  */
@@ -2322,9 +2575,11 @@ 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->sink_callback.lpVtbl = &presentclocksinkcallbackvtbl;
+    object->timer_callback.lpVtbl = &presentclocktimercallbackvtbl;
     object->refcount = 1;
     list_init(&object->sinks);
+    list_init(&object->timers);
     object->rate = 1.0f;
     InitializeCriticalSection(&object->cs);
 
diff --git a/include/mfidl.idl b/include/mfidl.idl
index da43399828..b301edbbdd 100644
--- a/include/mfidl.idl
+++ b/include/mfidl.idl
@@ -152,6 +152,11 @@ interface IMFRateControl : IUnknown
             [in, out, unique] float *rate);
 }
 
+typedef enum MFTIMER_FLAGS
+{
+    MFTIMER_RELATIVE = 0x00000001,
+} MFTIMER_FLAGS;
+
 [
     object,
     uuid(e56e4cbd-8f70-49d8-a0f8-edb3d6ab9bf2),
-- 
2.25.0




More information about the wine-devel mailing list