Patch 1.
The tests on Windows show that when the timer callback is queued behind the clock time it is queued and executed quite soon with 0-15ms delay which corresponds to default timer granularity. The timeout tolerance in the test is quite bigger to avoid a flaky test. Wine currently may execute the callback right away or delay it indefinitely depending on the time difference and current system time.
Patch 2. It turns out when the session is paused and then restarted and all the samples were delivered to sample grabber before restart, nothing is going to request the samples again when the session is restarted. I am attaching a test in a patch which hopefully confirms how that should work. I am not including that in the patches as the test involves delays (to make sure all the async events settle at certain points) and inherently flaky. Also, _ IMFStreamSink_GetEvent(stream, ...) in the test crashes on Win7 for some unknown reason.
[0001-mf-tests-Add-test-for-pausing-and-restarting-sample-.patch](/uploads/58c1738d48b3392e7ec430a0757210d0/0001-mf-tests-Add-test-for-pausing-and-restarting-sample-.patch)
-- v5: mf/samplegrabber: Handle samples receieved in paused state. mf/samplegrabber: Process samples in paused state in ignore_clock mode. mf: Handle timer time behind clock time in present_clock_schedule_timer().
From: Paul Gofman pgofman@codeweavers.com
--- dlls/mf/clock.c | 5 +++- dlls/mf/tests/mf.c | 59 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 51 insertions(+), 13 deletions(-)
diff --git a/dlls/mf/clock.c b/dlls/mf/clock.c index e9877533a5a..aef4366c6d9 100644 --- a/dlls/mf/clock.c +++ b/dlls/mf/clock.c @@ -819,7 +819,10 @@ static HRESULT present_clock_schedule_timer(struct presentation_clock *clock, DW WARN("Failed to get clock time, hr %#lx.\n", hr); return hr; } - time -= clocktime; + if (time > clocktime) + time -= clocktime; + else + time = 0; }
frequency = clock->frequency / 1000; diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 35ccde2f9cb..3a5d91ab87d 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -1927,6 +1927,7 @@ struct test_callback
HANDLE event; IMFMediaEvent *media_event; + BOOL check_media_event; };
static struct test_callback *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) @@ -1987,15 +1988,18 @@ static HRESULT WINAPI testcallback_Invoke(IMFAsyncCallback *iface, IMFAsyncResul if (callback->media_event) IMFMediaEvent_Release(callback->media_event);
- hr = IMFAsyncResult_GetObject(result, &object); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + if (callback->check_media_event) + { + hr = IMFAsyncResult_GetObject(result, &object); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr);
- hr = IMFAsyncResult_GetState(result, &object); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaEventGenerator_EndGetEvent((IMFMediaEventGenerator *)object, - result, &callback->media_event); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IUnknown_Release(object); + hr = IMFAsyncResult_GetState(result, &object); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEventGenerator_EndGetEvent((IMFMediaEventGenerator *)object, + result, &callback->media_event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IUnknown_Release(object); + }
SetEvent(callback->event);
@@ -2011,7 +2015,7 @@ static const IMFAsyncCallbackVtbl testcallbackvtbl = testcallback_Invoke, };
-static IMFAsyncCallback *create_test_callback(void) +static IMFAsyncCallback *create_test_callback(BOOL check_media_event) { struct test_callback *callback;
@@ -2019,6 +2023,7 @@ static IMFAsyncCallback *create_test_callback(void) return NULL;
callback->refcount = 1; + callback->check_media_event = check_media_event; callback->IMFAsyncCallback_iface.lpVtbl = &testcallbackvtbl; callback->event = CreateEventW(NULL, FALSE, FALSE, NULL); ok(!!callback->event, "CreateEventW failed, error %lu\n", GetLastError()); @@ -2107,8 +2112,8 @@ static void test_media_session_events(void) hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); ok(hr == S_OK, "Startup failure, hr %#lx.\n", hr);
- callback = create_test_callback(); - callback2 = create_test_callback(); + callback = create_test_callback(TRUE); + callback2 = create_test_callback(TRUE);
hr = MFCreateMediaSession(NULL, &session); ok(hr == S_OK, "Failed to create media session, hr %#lx.\n", hr); @@ -2163,7 +2168,7 @@ static void test_media_session_events(void) IMFAsyncCallback_Release(callback2);
- callback = create_test_callback(); + callback = create_test_callback(TRUE);
hr = MFCreateMediaSession(NULL, &session); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -3838,15 +3843,20 @@ static void test_presentation_clock(void) }; IMFClockStateSink test_sink = { &test_clock_sink_vtbl }; IMFPresentationTimeSource *time_source; + struct test_callback *timer_callback; MFCLOCK_PROPERTIES props, props2; IMFRateControl *rate_control; IMFPresentationClock *clock; + IMFAsyncCallback *callback; + IUnknown *timer_cancel_key; MFSHUTDOWN_STATUS status; IMFShutdown *shutdown; MFTIME systime, time; LONGLONG clock_time; MFCLOCK_STATE state; + IMFTimer *timer; unsigned int i; + DWORD t1, t2; DWORD value; float rate; HRESULT hr; @@ -4070,6 +4080,31 @@ static void test_presentation_clock(void)
IMFRateControl_Release(rate_control);
+ + hr = IMFPresentationClock_QueryInterface(clock, &IID_IMFTimer, (void **)&timer); + ok(hr == S_OK, "got hr %#lx.\n", hr); + + hr = IMFPresentationClock_Start(clock, 200000); + ok(hr == S_OK, "got hr %#lx.\n", hr); + + hr = IMFPresentationClock_GetCorrelatedTime(clock, 0, &time, &systime); + ok(hr == S_OK, "got hr %#lx.\n", hr); + + callback = create_test_callback(FALSE); + timer_callback = impl_from_IMFAsyncCallback(callback); + hr = IMFTimer_SetTimer(timer, 0, 100000, callback, NULL, &timer_cancel_key); + ok(hr == S_OK, "got hr %#lx.\n", hr); + + t1 = GetTickCount(); + ok(WaitForSingleObject(timer_callback->event, 4000) == WAIT_OBJECT_0, "WaitForSingleObject failed.\n"); + t2 = GetTickCount(); + + ok(t2 - t1 < 200, "unexpected time difference %lu.\n", t2 - t1); + + IUnknown_Release(timer_cancel_key); + IMFTimer_Release(timer); + IMFAsyncCallback_Release(callback); + hr = IMFPresentationClock_QueryInterface(clock, &IID_IMFShutdown, (void **)&shutdown); ok(hr == S_OK, "Failed to get shutdown interface, hr %#lx.\n", hr);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/mf/samplegrabber.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/mf/samplegrabber.c b/dlls/mf/samplegrabber.c index 2d9f8109aca..7b47b517f02 100644 --- a/dlls/mf/samplegrabber.c +++ b/dlls/mf/samplegrabber.c @@ -424,7 +424,7 @@ static HRESULT WINAPI sample_grabber_stream_ProcessSample(IMFStreamSink *iface,
if (grabber->is_shut_down) hr = MF_E_STREAMSINK_REMOVED; - else if (grabber->state == SINK_STATE_RUNNING) + else if (grabber->state == SINK_STATE_RUNNING || (grabber->state == SINK_STATE_PAUSED && grabber->ignore_clock)) { hr = IMFSample_GetSampleTime(sample, &sampletime);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/mf/samplegrabber.c | 50 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-)
diff --git a/dlls/mf/samplegrabber.c b/dlls/mf/samplegrabber.c index 7b47b517f02..8803856a620 100644 --- a/dlls/mf/samplegrabber.c +++ b/dlls/mf/samplegrabber.c @@ -19,6 +19,7 @@ #define COBJMACROS
#include <float.h> +#include <assert.h>
#include "mfidl.h" #include "mf_private.h" @@ -85,6 +86,8 @@ struct sample_grabber UINT64 sample_time_offset; enum sink_state state; CRITICAL_SECTION cs; + UINT32 sample_request_on_start_count; + IMFSample *samples_queued_while_paused[4]; };
static IMFSampleGrabberSinkCallback *sample_grabber_get_callback(const struct sample_grabber *sink) @@ -442,6 +445,14 @@ static HRESULT WINAPI sample_grabber_stream_ProcessSample(IMFStreamSink *iface, hr = stream_queue_sample(grabber, sample); } } + else if (grabber->state == SINK_STATE_PAUSED) + { + if (grabber->sample_request_on_start_count < 4) + { + IMFSample_AddRef(sample); + grabber->samples_queued_while_paused[grabber->sample_request_on_start_count++] = sample; + } + }
LeaveCriticalSection(&grabber->cs);
@@ -826,6 +837,20 @@ static void sample_grabber_release_pending_items(struct sample_grabber *grabber) } }
+static void release_samples_queued_while_paused(struct sample_grabber *grabber) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(grabber->samples_queued_while_paused); ++i) + { + if (grabber->samples_queued_while_paused[i]) + { + IMFSample_Release(grabber->samples_queued_while_paused[i]); + grabber->samples_queued_while_paused[i] = NULL; + } + } +} + static ULONG WINAPI sample_grabber_sink_Release(IMFMediaSink *iface) { struct sample_grabber *grabber = impl_from_IMFMediaSink(iface); @@ -857,6 +882,7 @@ static ULONG WINAPI sample_grabber_sink_Release(IMFMediaSink *iface) if (grabber->sample_attributes) IMFAttributes_Release(grabber->sample_attributes); sample_grabber_release_pending_items(grabber); + release_samples_queued_while_paused(grabber); DeleteCriticalSection(&grabber->cs); free(grabber); } @@ -1128,14 +1154,31 @@ static HRESULT sample_grabber_set_state(struct sample_grabber *grabber, enum sin else { if (state == SINK_STATE_STOPPED) + { sample_grabber_cancel_timer(grabber); + release_samples_queued_while_paused(grabber); + grabber->sample_request_on_start_count = 4; + }
- if (state == SINK_STATE_RUNNING && grabber->state == SINK_STATE_STOPPED) + if (state == SINK_STATE_RUNNING && grabber->state != SINK_STATE_RUNNING) { /* Every transition to running state sends a bunch requests to build up initial queue. */ - for (i = 0; i < 4; ++i) - sample_grabber_stream_request_sample(grabber); + for (i = 0; i < grabber->sample_request_on_start_count; ++i) + { + if (grabber->state == SINK_STATE_PAUSED && offset == PRESENTATION_CURRENT_POSITION) + { + assert(grabber->samples_queued_while_paused[i]); + stream_queue_sample(grabber, grabber->samples_queued_while_paused[i]); + } + else + { + sample_grabber_stream_request_sample(grabber); + } + } + release_samples_queued_while_paused(grabber); + grabber->sample_request_on_start_count = 0; } + do_callback = state != grabber->state || state != SINK_STATE_PAUSED; if (do_callback) IMFStreamSink_QueueEvent(&grabber->IMFStreamSink_iface, events[state], &GUID_NULL, S_OK, NULL); @@ -1419,6 +1462,7 @@ static HRESULT sample_grabber_create_object(IMFAttributes *attributes, void *use object->IMFStreamSink_iface.lpVtbl = &sample_grabber_stream_vtbl; object->IMFMediaTypeHandler_iface.lpVtbl = &sample_grabber_stream_type_handler_vtbl; object->timer_callback.lpVtbl = &sample_grabber_stream_timer_callback_vtbl; + object->sample_request_on_start_count = 4; object->refcount = 1; if (FAILED(IMFSampleGrabberSinkCallback_QueryInterface(context->callback, &IID_IMFSampleGrabberSinkCallback2, (void **)&object->callback2)))
v5: - rebased tests.
Nikolay Sivov (@nsivov) commented about dlls/mf/samplegrabber.c:
UINT64 sample_time_offset; enum sink_state state; CRITICAL_SECTION cs;
- UINT32 sample_request_on_start_count;
- IMFSample *samples_queued_while_paused[4];
Let's rename this to something less verbose. Like 'samples' and 'samples_count'. Same for release helper release_samples_queued_while_paused() -> release_input_samples() as an example.
Nikolay Sivov (@nsivov) commented about dlls/mf/samplegrabber.c:
hr = stream_queue_sample(grabber, sample); } }
- else if (grabber->state == SINK_STATE_PAUSED)
- {
if (grabber->sample_request_on_start_count < 4)
This should either use some global constant or ARRAY_SIZE. Maybe a constant is better.