[PATCH v8 0/7] MR9489: mf: Fix seeking within sample grabber.
This fixes seeking within sample grabber by matching Windows functionality. -- v8: mf/tests: Test sample grabber pause and resume. mf/tests: Check contents of sample collection. mf/tests: Test seek with sample grabber whilst ignoring clock. mf/tests: Test samplegrabber flush then seek. mf/tests: Check StreamSinkMarker event. mf/tests: Test sample grabber {Set,Cancel}Timer. mf/tests: Add test for sample grabber seek. https://gitlab.winehq.org/wine/wine/-/merge_requests/9489
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/mf/tests/mf.c | 188 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 0c0590ed195..1a8c0528b1e 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -4189,6 +4189,193 @@ static void test_sample_grabber(void) IMFSampleGrabberSinkCallback_Release(grabber_callback); } +static void supply_samples(IMFStreamSink *stream, int num_samples) +{ + IMFMediaBuffer *buffer; + IMFSample *sample; + HRESULT hr; + int i; + + for (i = 0; i < num_samples; i++) + { + hr = MFCreateMemoryBuffer(360, &buffer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateSample(&sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFSample_AddBuffer(sample, buffer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFSample_SetSampleTime(sample, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFSample_SetSampleDuration(sample, 41667); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFStreamSink_ProcessSample(stream, sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFMediaBuffer_Release(buffer); + IMFSample_Release(sample); + } +} + +static int count_samples_requested(IMFStreamSink *stream) +{ + int samples_requested; + IMFMediaEvent *event; + MediaEventType met; + HRESULT hr; + + samples_requested = 0; + while (IMFStreamSink_GetEvent(stream, 0, &event) == S_OK) + { + hr = IMFMediaEvent_GetType(event, &met); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFMediaEvent_Release(event); + if (met == MEStreamSinkRequestSample) + samples_requested++; + else if (met == MEStreamSinkStarted) + break; + } + + return samples_requested; +} + +static void test_sample_grabber_seek(void) +{ + IMFSampleGrabberSinkCallback *grabber_callback = create_test_grabber_callback(); + IMFPresentationTimeSource *time_source; + IMFPresentationClock *clock; + IMFMediaType *media_type; + IMFStreamSink *stream; + IMFActivate *activate; + int samples_requested; + IMFMediaSink *sink; + HRESULT hr; + ULONG ref; + + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); + + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + + hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); + ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM); + ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); + + hr = MFCreateSampleGrabberSinkActivate(media_type, grabber_callback, &activate); + ok(hr == S_OK, "Failed to create grabber activate, hr %#lx.\n", hr); + + ref = IMFMediaType_Release(media_type); + + hr = IMFActivate_ActivateObject(activate, &IID_IMFMediaSink, (void **)&sink); + ok(hr == S_OK, "Failed to activate object, hr %#lx.\n", hr); + + ref = IMFActivate_Release(activate); + ok(ref == 0, "Release returned %ld\n", ref); + + hr = IMFMediaSink_GetStreamSinkByIndex(sink, 0, &stream); + ok(hr == S_OK, "Failed to get sink stream, hr %#lx.\n", hr); + + + /* Set clock. */ + hr = MFCreatePresentationClock(&clock); + ok(hr == S_OK, "Failed to create clock object, hr %#lx.\n", hr); + + hr = IMFMediaSink_SetPresentationClock(sink, clock); + ok(hr == S_OK, "Failed to set presentation clock, hr %#lx.\n", hr); + + hr = MFCreateSystemTimeSource(&time_source); + ok(hr == S_OK, "Failed to create time source, hr %#lx.\n", hr); + + hr = IMFPresentationClock_SetTimeSource(clock, time_source); + ok(hr == S_OK, "Failed to set time source, hr %#lx.\n", hr); + IMFPresentationTimeSource_Release(time_source); + + + /* test number of new sample requests on clock start */ + hr = IMFPresentationClock_Start(clock, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); + + /* test number of new sample requests on seek when in running state and 4 samples have been provided */ + supply_samples(stream, 4); + hr = IMFPresentationClock_Start(clock, 1234); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + todo_wine + ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); + + /* test number of new sample requests on seek when in running state and 3 samples have been provided */ + supply_samples(stream, 3); + hr = IMFPresentationClock_Start(clock, 1234); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + todo_wine + ok(samples_requested == 3, "Unexpected number of samples requested %d\n", samples_requested); + + /* test number of new sample requests on seek whilst stopped */ + hr = IMFPresentationClock_Stop(clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFPresentationClock_Start(clock, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + todo_wine + ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); + + /* test number of new sample requests on seek from paused state where 3 samples were previously provided */ + supply_samples(stream, 3); + + hr = IMFPresentationClock_Pause(clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFPresentationClock_Start(clock, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + todo_wine + ok(samples_requested == 3, "Unexpected number of samples requested %d\n", samples_requested); + + /* test number of new sample requests on seek from paused state where no samples were previously provided */ + hr = IMFPresentationClock_Pause(clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFPresentationClock_Start(clock, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + todo_wine + ok(samples_requested == 0, "Unexpected number of samples requested %d\n", samples_requested); + + ref = IMFPresentationClock_Release(clock); + ok(ref == 2, "Release returned %ld\n", ref); + + /* required for the sink to be fully released */ + hr = IMFMediaSink_Shutdown(sink); + ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); + + ref = IMFMediaSink_Release(sink); + todo_wine + ok(ref == 0, "Release returned %ld\n", ref); + + hr = MFShutdown(); + ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); + + IMFSampleGrabberSinkCallback_Release(grabber_callback); +} + static void test_sample_grabber_is_mediatype_supported(void) { IMFSampleGrabberSinkCallback *grabber_callback = create_test_grabber_callback(); @@ -8454,6 +8641,7 @@ START_TEST(mf) test_MFShutdownObject(); test_presentation_clock(); test_sample_grabber(); + test_sample_grabber_seek(); test_sample_grabber_is_mediatype_supported(); test_sample_grabber_orientation(MFVideoFormat_RGB32); test_sample_grabber_orientation(MFVideoFormat_NV12); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9489
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/mf/tests/mf.c | 422 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 406 insertions(+), 16 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 1a8c0528b1e..961f466f0b5 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -4189,8 +4189,336 @@ static void test_sample_grabber(void) IMFSampleGrabberSinkCallback_Release(grabber_callback); } +struct timer_cancel +{ + IUnknown IUnknown_iface; + LONG refcount; + ULONG id; +}; + +static struct timer_cancel* impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct timer_cancel, IUnknown_iface); +} + +static WINAPI HRESULT unknown_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + } + else + { + *obj = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef(iface); + return S_OK; +} + +static WINAPI ULONG unknown_AddRef(IUnknown *iface) +{ + struct timer_cancel *tc = impl_from_IUnknown(iface); + return InterlockedIncrement(&tc->refcount); +} + +static WINAPI ULONG unknown_Release(IUnknown *iface) +{ + struct timer_cancel *tc = impl_from_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&tc->refcount); + + if (!tc->refcount) + free(tc); + + return refcount; +} + +static IUnknownVtbl UnknownVtbl = +{ + unknown_QueryInterface, + unknown_AddRef, + unknown_Release, +}; + +static struct timer_cancel* create_timer_cancel(void) +{ + static ULONG id = 1; + + struct timer_cancel *tc = calloc(1, sizeof(*tc)); + tc->IUnknown_iface.lpVtbl = &UnknownVtbl; + tc->refcount = 1; + tc->id = id++; + + return tc; +} + +DEFINE_EXPECT(timer_SetTimer); +DEFINE_EXPECT(timer_CancelTimer); + +struct presentation_clock +{ + IMFPresentationClock IMFPresentationClock_iface; + IMFTimer IMFTimer_iface; + LONG refcount; + IMFClockStateSink *sample_grabber_clock_state_sink; + IMFAsyncResult *callback_result; + IUnknown *cancel_key; +}; + +static struct presentation_clock* impl_from_IMFTimer(IMFTimer *iface) +{ + return CONTAINING_RECORD(iface, struct presentation_clock, IMFTimer_iface); +} + +static WINAPI HRESULT timer_QueryInterface(IMFTimer *iface, REFIID riid, void **obj) +{ + struct presentation_clock* pc = impl_from_IMFTimer(iface); + return IMFPresentationClock_QueryInterface(&pc->IMFPresentationClock_iface, riid, obj); +} + +static WINAPI ULONG timer_AddRef(IMFTimer *iface) +{ + struct presentation_clock* pc = impl_from_IMFTimer(iface); + return IMFPresentationClock_AddRef(&pc->IMFPresentationClock_iface); +} + +static WINAPI ULONG timer_Release(IMFTimer *iface) +{ + struct presentation_clock* pc = impl_from_IMFTimer(iface); + return IMFPresentationClock_Release(&pc->IMFPresentationClock_iface); +} + +static HRESULT WINAPI timer_SetTimer(IMFTimer *iface, DWORD flags, LONGLONG time, + IMFAsyncCallback *callback, IUnknown *state, IUnknown **cancel_key) +{ + struct presentation_clock* pc = impl_from_IMFTimer(iface); + struct timer_cancel *tc; + HRESULT hr; + + CHECK_EXPECT(timer_SetTimer); + + ok(flags == 0, "Unexpected flags value %#lx\n", flags); + ok(time == 0, "Unexpected time value %I64d\n", time); + ok(pc->callback_result == NULL, "Unexpected callback value %p\n", pc->callback_result); + ok(pc->cancel_key == NULL, "Unexpected cancel key %p\n", pc->cancel_key); + + hr = MFCreateAsyncResult(NULL, callback, state, &pc->callback_result); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + tc = create_timer_cancel(); + pc->cancel_key = *cancel_key = &tc->IUnknown_iface; + + return S_OK; +} + +static HRESULT WINAPI timer_CancelTimer(IMFTimer *iface, IUnknown *cancel_key) +{ + struct presentation_clock* pc = impl_from_IMFTimer(iface); + + todo_wine + CHECK_EXPECT(timer_CancelTimer); + ok(cancel_key == pc->cancel_key, "Unexpected cancel key %p\n", cancel_key); + + IMFAsyncResult_Release(pc->callback_result); + pc->callback_result = NULL; + pc->cancel_key = NULL; + + return S_OK; +} + +static IMFTimerVtbl MFTimerVtbl = +{ + timer_QueryInterface, + timer_AddRef, + timer_Release, + timer_SetTimer, + timer_CancelTimer, +}; + +static struct presentation_clock* impl_from_IMFPresentationClock(IMFPresentationClock *iface) +{ + return CONTAINING_RECORD(iface, struct presentation_clock, IMFPresentationClock_iface); +} + +static WINAPI HRESULT presentation_clock_QueryInterface(IMFPresentationClock *iface, REFIID riid, void **obj) +{ + struct presentation_clock *pc = impl_from_IMFPresentationClock(iface); + + if (IsEqualIID(riid, &IID_IMFPresentationClock) || + IsEqualIID(riid, &IID_IMFClock) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + } + else if (IsEqualIID(riid, &IID_IMFTimer)) + { + *obj = &pc->IMFTimer_iface; + } + else + { + *obj = NULL; + return E_NOINTERFACE; + } + + IMFPresentationClock_AddRef(iface); + return S_OK; +} + +static WINAPI ULONG presentation_clock_AddRef(IMFPresentationClock *iface) +{ + struct presentation_clock *pc = impl_from_IMFPresentationClock(iface); + return InterlockedIncrement(&pc->refcount); +} + +static WINAPI ULONG presentation_clock_Release(IMFPresentationClock *iface) +{ + struct presentation_clock *pc = impl_from_IMFPresentationClock(iface); + ULONG refcount = InterlockedDecrement(&pc->refcount); + + if (!pc->refcount) + { + if (pc->sample_grabber_clock_state_sink) + IMFClockStateSink_Release(pc->sample_grabber_clock_state_sink); + if (pc->callback_result) + IMFAsyncResult_Release(pc->callback_result); + free(pc); + } + + return refcount; +} + +static WINAPI HRESULT presentation_clock_GetClockCharacteristics(IMFPresentationClock *iface, DWORD *flags) +{ + return E_NOTIMPL; +} + +static WINAPI HRESULT presentation_clock_GetCorrelatedTime(IMFPresentationClock *iface, DWORD reserved, + LONGLONG *clock_time, MFTIME *system_time) +{ + return E_NOTIMPL; +} + +static WINAPI HRESULT presentation_clock_GetContinuityKey(IMFPresentationClock *iface, DWORD *key) +{ + return E_NOTIMPL; +} + +static WINAPI HRESULT presentation_clock_GetState(IMFPresentationClock *iface, DWORD reserved, MFCLOCK_STATE *state) +{ + return E_NOTIMPL; +} + +static WINAPI HRESULT presentation_clock_GetProperties(IMFPresentationClock *iface, MFCLOCK_PROPERTIES *props) +{ + return E_NOTIMPL; +} + +static WINAPI HRESULT presentation_clock_SetTimeSource(IMFPresentationClock *iface, + IMFPresentationTimeSource *time_source) +{ + return E_NOTIMPL; +} + +static WINAPI HRESULT presentation_clock_GetTimeSource(IMFPresentationClock *iface, + IMFPresentationTimeSource **time_source) +{ + return E_NOTIMPL; +} + +static WINAPI HRESULT presentation_clock_GetTime(IMFPresentationClock *iface, MFTIME *time) +{ + return E_NOTIMPL; +} + +static WINAPI HRESULT presentation_clock_AddClockStateSink(IMFPresentationClock *iface, IMFClockStateSink *state_sink) +{ + struct presentation_clock *pc = impl_from_IMFPresentationClock(iface); + + if (pc->sample_grabber_clock_state_sink) + IMFClockStateSink_Release(pc->sample_grabber_clock_state_sink); + + IMFClockStateSink_AddRef(pc->sample_grabber_clock_state_sink = state_sink); + + return S_OK; +} + +static WINAPI HRESULT presentation_clock_RemoveClockStateSink(IMFPresentationClock *iface, + IMFClockStateSink *state_sink) +{ + struct presentation_clock *pc = impl_from_IMFPresentationClock(iface); + + if (pc->sample_grabber_clock_state_sink == state_sink) + { + IMFClockStateSink_Release(state_sink); + pc->sample_grabber_clock_state_sink = NULL; + } + + return S_OK; +} + +static WINAPI HRESULT presentation_clock_Start(IMFPresentationClock *iface, LONGLONG start_offset) +{ + struct presentation_clock *pc = impl_from_IMFPresentationClock(iface); + HRESULT hr; + + if (start_offset == PRESENTATION_CURRENT_POSITION) + hr = IMFClockStateSink_OnClockRestart(pc->sample_grabber_clock_state_sink, 0); + else + hr = IMFClockStateSink_OnClockStart(pc->sample_grabber_clock_state_sink, 0, start_offset); + return hr; +} + +static WINAPI HRESULT presentation_clock_Stop(IMFPresentationClock *iface) +{ + struct presentation_clock *pc = impl_from_IMFPresentationClock(iface); + + return IMFClockStateSink_OnClockStop(pc->sample_grabber_clock_state_sink, 0); +} + +static WINAPI HRESULT presentation_clock_Pause(IMFPresentationClock *iface) +{ + struct presentation_clock *pc = impl_from_IMFPresentationClock(iface); + + return IMFClockStateSink_OnClockPause(pc->sample_grabber_clock_state_sink, 0); +} + +static IMFPresentationClockVtbl MFPresentationClockVtbl = +{ + presentation_clock_QueryInterface, + presentation_clock_AddRef, + presentation_clock_Release, + presentation_clock_GetClockCharacteristics, + presentation_clock_GetCorrelatedTime, + presentation_clock_GetContinuityKey, + presentation_clock_GetState, + presentation_clock_GetProperties, + presentation_clock_SetTimeSource, + presentation_clock_GetTimeSource, + presentation_clock_GetTime, + presentation_clock_AddClockStateSink, + presentation_clock_RemoveClockStateSink, + presentation_clock_Start, + presentation_clock_Stop, + presentation_clock_Pause, +}; + +static struct presentation_clock* create_presentation_clock(void) +{ + struct presentation_clock* pc = calloc(1, sizeof(*pc)); + + pc->IMFPresentationClock_iface.lpVtbl = &MFPresentationClockVtbl; + pc->IMFTimer_iface.lpVtbl = &MFTimerVtbl; + pc->refcount = 1; + + return pc; +} + +static MFTIME sample_pts = 0; + static void supply_samples(IMFStreamSink *stream, int num_samples) { + IMFMediaBuffer *buffer; IMFSample *sample; HRESULT hr; @@ -4207,9 +4535,11 @@ static void supply_samples(IMFStreamSink *stream, int num_samples) hr = IMFSample_AddBuffer(sample, buffer); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFSample_SetSampleTime(sample, 0); + hr = IMFSample_SetSampleTime(sample, sample_pts); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + sample_pts += 41667; + hr = IMFSample_SetSampleDuration(sample, 41667); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -4247,7 +4577,7 @@ static int count_samples_requested(IMFStreamSink *stream) static void test_sample_grabber_seek(void) { IMFSampleGrabberSinkCallback *grabber_callback = create_test_grabber_callback(); - IMFPresentationTimeSource *time_source; + struct presentation_clock *mock_clock; IMFPresentationClock *clock; IMFMediaType *media_type; IMFStreamSink *stream; @@ -4257,7 +4587,6 @@ static void test_sample_grabber_seek(void) HRESULT hr; ULONG ref; - hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); @@ -4285,19 +4614,12 @@ static void test_sample_grabber_seek(void) /* Set clock. */ - hr = MFCreatePresentationClock(&clock); - ok(hr == S_OK, "Failed to create clock object, hr %#lx.\n", hr); + mock_clock = create_presentation_clock(); + clock = &mock_clock->IMFPresentationClock_iface; hr = IMFMediaSink_SetPresentationClock(sink, clock); ok(hr == S_OK, "Failed to set presentation clock, hr %#lx.\n", hr); - - hr = MFCreateSystemTimeSource(&time_source); - ok(hr == S_OK, "Failed to create time source, hr %#lx.\n", hr); - - hr = IMFPresentationClock_SetTimeSource(clock, time_source); - ok(hr == S_OK, "Failed to set time source, hr %#lx.\n", hr); - IMFPresentationTimeSource_Release(time_source); - + ok(!!mock_clock->sample_grabber_clock_state_sink, "AddClockStateSink not called\n"); /* test number of new sample requests on clock start */ hr = IMFPresentationClock_Start(clock, 0); @@ -4307,18 +4629,33 @@ static void test_sample_grabber_seek(void) ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); /* test number of new sample requests on seek when in running state and 4 samples have been provided */ + sample_pts = 0; + SET_EXPECT(timer_SetTimer); supply_samples(stream, 4); + CHECK_CALLED(timer_SetTimer); + + SET_EXPECT(timer_CancelTimer); hr = IMFPresentationClock_Start(clock, 1234); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + CHECK_CALLED(timer_CancelTimer); samples_requested = count_samples_requested(stream); todo_wine ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); /* test number of new sample requests on seek when in running state and 3 samples have been provided */ + sample_pts = 0; + SET_EXPECT(timer_SetTimer); supply_samples(stream, 3); + todo_wine + CHECK_CALLED(timer_SetTimer); + + SET_EXPECT(timer_CancelTimer); hr = IMFPresentationClock_Start(clock, 1234); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + CHECK_CALLED(timer_CancelTimer); samples_requested = count_samples_requested(stream); todo_wine @@ -4332,22 +4669,44 @@ static void test_sample_grabber_seek(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); samples_requested = count_samples_requested(stream); - todo_wine ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); /* test number of new sample requests on seek from paused state where 3 samples were previously provided */ + sample_pts = 0; + SET_EXPECT(timer_SetTimer); supply_samples(stream, 3); + todo_wine + CHECK_CALLED(timer_SetTimer); hr = IMFPresentationClock_Pause(clock); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + SET_EXPECT(timer_CancelTimer); hr = IMFPresentationClock_Start(clock, 0); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + CHECK_CALLED(timer_CancelTimer); samples_requested = count_samples_requested(stream); todo_wine ok(samples_requested == 3, "Unexpected number of samples requested %d\n", samples_requested); + /* test over supply */ + sample_pts = 0; + SET_EXPECT(timer_SetTimer); + supply_samples(stream, 6); + todo_wine + CHECK_CALLED(timer_SetTimer); + + SET_EXPECT(timer_CancelTimer); + hr = IMFPresentationClock_Start(clock, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + CHECK_CALLED(timer_CancelTimer); + + samples_requested = count_samples_requested(stream); + todo_wine + ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); + /* test number of new sample requests on seek from paused state where no samples were previously provided */ hr = IMFPresentationClock_Pause(clock); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -4356,13 +4715,44 @@ static void test_sample_grabber_seek(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); samples_requested = count_samples_requested(stream); - todo_wine ok(samples_requested == 0, "Unexpected number of samples requested %d\n", samples_requested); + /* test sample received in the paused state with no samples queued */ + hr = IMFPresentationClock_Pause(clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + sample_pts = 0; + SET_EXPECT(timer_SetTimer); + supply_samples(stream, 4); + todo_wine + CHECK_CALLED(timer_SetTimer); + + hr = IMFPresentationClock_Start(clock, PRESENTATION_CURRENT_POSITION); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + todo_wine + ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); + + /* test sample received in the stopped state */ + SET_EXPECT(timer_CancelTimer); + hr = IMFPresentationClock_Stop(clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + CHECK_CALLED(timer_CancelTimer); + + supply_samples(stream, 4); + + hr = IMFPresentationClock_Start(clock, PRESENTATION_CURRENT_POSITION); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); + + /* required for the sink to be fully released */ ref = IMFPresentationClock_Release(clock); ok(ref == 2, "Release returned %ld\n", ref); - /* required for the sink to be fully released */ hr = IMFMediaSink_Shutdown(sink); ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9489
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/mf/tests/mf.c | 130 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 117 insertions(+), 13 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 961f466f0b5..dbbb5496c2a 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -4255,6 +4255,9 @@ static struct timer_cancel* create_timer_cancel(void) DEFINE_EXPECT(timer_SetTimer); DEFINE_EXPECT(timer_CancelTimer); +DEFINE_EXPECT(MEStreamSinkMarker); + +static MFTIME sample_pts = 0, expected_pts = 0; struct presentation_clock { @@ -4264,6 +4267,7 @@ struct presentation_clock IMFClockStateSink *sample_grabber_clock_state_sink; IMFAsyncResult *callback_result; IUnknown *cancel_key; + HANDLE set_timer_event; }; static struct presentation_clock* impl_from_IMFTimer(IMFTimer *iface) @@ -4297,10 +4301,11 @@ static HRESULT WINAPI timer_SetTimer(IMFTimer *iface, DWORD flags, LONGLONG time HRESULT hr; CHECK_EXPECT(timer_SetTimer); + SetEvent(pc->set_timer_event); ok(flags == 0, "Unexpected flags value %#lx\n", flags); - ok(time == 0, "Unexpected time value %I64d\n", time); - ok(pc->callback_result == NULL, "Unexpected callback value %p\n", pc->callback_result); + ok(time == expected_pts, "Unexpected time value %I64d\n", time); + ok(pc->callback_result == NULL, "Unexpected callback result value %p\n", pc->callback_result); ok(pc->cancel_key == NULL, "Unexpected cancel key %p\n", pc->cancel_key); hr = MFCreateAsyncResult(NULL, callback, state, &pc->callback_result); @@ -4321,6 +4326,7 @@ static HRESULT WINAPI timer_CancelTimer(IMFTimer *iface, IUnknown *cancel_key) ok(cancel_key == pc->cancel_key, "Unexpected cancel key %p\n", cancel_key); IMFAsyncResult_Release(pc->callback_result); + pc->callback_result = NULL; pc->cancel_key = NULL; @@ -4382,6 +4388,7 @@ static WINAPI ULONG presentation_clock_Release(IMFPresentationClock *iface) IMFClockStateSink_Release(pc->sample_grabber_clock_state_sink); if (pc->callback_result) IMFAsyncResult_Release(pc->callback_result); + CloseHandle(pc->set_timer_event); free(pc); } @@ -4510,12 +4517,11 @@ static struct presentation_clock* create_presentation_clock(void) pc->IMFPresentationClock_iface.lpVtbl = &MFPresentationClockVtbl; pc->IMFTimer_iface.lpVtbl = &MFTimerVtbl; pc->refcount = 1; + pc->set_timer_event = CreateEventW(NULL, FALSE, FALSE, NULL); return pc; } -static MFTIME sample_pts = 0; - static void supply_samples(IMFStreamSink *stream, int num_samples) { @@ -4569,24 +4575,63 @@ static int count_samples_requested(IMFStreamSink *stream) samples_requested++; else if (met == MEStreamSinkStarted) break; + else if (met == MEStreamSinkMarker) + { + CHECK_EXPECT(MEStreamSinkMarker); + break; + } } return samples_requested; } +#define trigger_timer(mock_clock) _trigger_timer(__LINE__, mock_clock) + +static HRESULT _trigger_timer(int line, struct presentation_clock *mock_clock) +{ + HRESULT hr = E_FAIL; + + mock_clock->cancel_key = NULL; + + ok_(__FILE__, line)(!!mock_clock->callback_result, "Expected callback result to be set\n"); + + if (mock_clock->callback_result) + { + hr = MFInvokeCallback(mock_clock->callback_result); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFAsyncResult_Release(mock_clock->callback_result); + mock_clock->callback_result = NULL; + } + + return hr; +} + static void test_sample_grabber_seek(void) { - IMFSampleGrabberSinkCallback *grabber_callback = create_test_grabber_callback(); + struct test_grabber_callback *grabber_callback_impl; + IMFSampleGrabberSinkCallback *grabber_callback; struct presentation_clock *mock_clock; IMFPresentationClock *clock; + IMFAsyncCallback *callback; IMFMediaType *media_type; IMFStreamSink *stream; IMFActivate *activate; int samples_requested; + PROPVARIANT propvar; IMFMediaSink *sink; HRESULT hr; ULONG ref; + PropVariantInit(&propvar); + callback = create_test_callback(TRUE); + + grabber_callback = create_test_grabber_callback(); + grabber_callback_impl = impl_from_IMFSampleGrabberSinkCallback(grabber_callback); + grabber_callback_impl->ready_event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!grabber_callback_impl->ready_event, "CreateEventW failed, error %lu\n", GetLastError()); + grabber_callback_impl->done_event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!grabber_callback_impl->done_event, "CreateEventW failed, error %lu\n", GetLastError()); + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); @@ -4647,9 +4692,31 @@ static void test_sample_grabber_seek(void) /* test number of new sample requests on seek when in running state and 3 samples have been provided */ sample_pts = 0; SET_EXPECT(timer_SetTimer); - supply_samples(stream, 3); + supply_samples(stream, 2); todo_wine CHECK_CALLED(timer_SetTimer); + /* this marker gets silently discarded on the next seek */ + hr = IMFStreamSink_PlaceMarker(stream, MFSTREAMSINK_MARKER_DEFAULT, NULL, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + supply_samples(stream, 1); + + ok(!!mock_clock->callback_result, "Expected callback result to be set\n"); + hr = trigger_timer(mock_clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = WaitForSingleObject(grabber_callback_impl->ready_event, 1000); + ok(hr == WAIT_OBJECT_0, "Unexpected hr %#lx.\n", hr); + + expected_pts = 41667; + ResetEvent(mock_clock->set_timer_event); + SET_EXPECT(timer_SetTimer); + SetEvent(grabber_callback_impl->done_event); + hr = gen_wait_media_event((IMFMediaEventGenerator*)stream, callback, MEStreamSinkRequestSample, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + hr = WaitForSingleObject(mock_clock->set_timer_event, 1000); + ok(hr == WAIT_OBJECT_0, "Unexpected hr %#lx.\n", hr); + CHECK_CALLED(timer_SetTimer); SET_EXPECT(timer_CancelTimer); hr = IMFPresentationClock_Start(clock, 1234); @@ -4659,7 +4726,7 @@ static void test_sample_grabber_seek(void) samples_requested = count_samples_requested(stream); todo_wine - ok(samples_requested == 3, "Unexpected number of samples requested %d\n", samples_requested); + ok(samples_requested == 2, "Unexpected number of samples requested %d\n", samples_requested); /* test number of new sample requests on seek whilst stopped */ hr = IMFPresentationClock_Stop(clock); @@ -4671,13 +4738,47 @@ static void test_sample_grabber_seek(void) samples_requested = count_samples_requested(stream); ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); - /* test number of new sample requests on seek from paused state where 3 samples were previously provided */ - sample_pts = 0; + /* queue three samples with a marker between the first and second ... */ + sample_pts = expected_pts = 0; SET_EXPECT(timer_SetTimer); - supply_samples(stream, 3); + supply_samples(stream, 1); todo_wine CHECK_CALLED(timer_SetTimer); + hr = IMFStreamSink_PlaceMarker(stream, MFSTREAMSINK_MARKER_DEFAULT, NULL, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + supply_samples(stream, 2); + /* ... trigger the time for the first sample ... */ + todo_wine + hr = trigger_timer(mock_clock); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = WaitForSingleObject(grabber_callback_impl->ready_event, 1000); + todo_wine + ok(hr == WAIT_OBJECT_0, "Unexpected hr %#lx.\n", hr); + + if (hr == WAIT_OBJECT_0) + { + expected_pts = 41667; + ResetEvent(mock_clock->set_timer_event); + SET_EXPECT(timer_SetTimer); + SetEvent(grabber_callback_impl->done_event); + + SET_EXPECT(MEStreamSinkMarker); + samples_requested = count_samples_requested(stream); + ok(samples_requested == 1, "Unexpected number of samples requested %d\n", samples_requested); + hr = WaitForSingleObject(mock_clock->set_timer_event, 1000); + ok(hr == WAIT_OBJECT_0, "Unexpected hr %#lx.\n", hr); + CHECK_CALLED(MEStreamSinkMarker); + CHECK_CALLED(timer_SetTimer); + } + else + { + skip("skipping MEStreamSinkMarker test\n"); + } + + /* ... now pause and seek then test the number of samples requested */ hr = IMFPresentationClock_Pause(clock); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -4688,10 +4789,10 @@ static void test_sample_grabber_seek(void) samples_requested = count_samples_requested(stream); todo_wine - ok(samples_requested == 3, "Unexpected number of samples requested %d\n", samples_requested); + ok(samples_requested == 2, "Unexpected number of samples requested %d\n", samples_requested); /* test over supply */ - sample_pts = 0; + sample_pts = expected_pts = 0; SET_EXPECT(timer_SetTimer); supply_samples(stream, 6); todo_wine @@ -4721,7 +4822,7 @@ static void test_sample_grabber_seek(void) hr = IMFPresentationClock_Pause(clock); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - sample_pts = 0; + expected_pts = sample_pts; SET_EXPECT(timer_SetTimer); supply_samples(stream, 4); todo_wine @@ -4760,6 +4861,9 @@ static void test_sample_grabber_seek(void) todo_wine ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFAsyncCallback_Release(callback); + ok(ref == 0, "Release returned %ld\n", ref); + hr = MFShutdown(); ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9489
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/mf/tests/mf.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index dbbb5496c2a..31ddabf6939 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -4300,12 +4300,16 @@ static HRESULT WINAPI timer_SetTimer(IMFTimer *iface, DWORD flags, LONGLONG time struct timer_cancel *tc; HRESULT hr; + todo_wine_if(time == 83334) CHECK_EXPECT(timer_SetTimer); SetEvent(pc->set_timer_event); ok(flags == 0, "Unexpected flags value %#lx\n", flags); + todo_wine_if(time == 83334) ok(time == expected_pts, "Unexpected time value %I64d\n", time); + todo_wine_if(time == 83334) ok(pc->callback_result == NULL, "Unexpected callback result value %p\n", pc->callback_result); + todo_wine_if(time == 83334) ok(pc->cancel_key == NULL, "Unexpected cancel key %p\n", pc->cancel_key); hr = MFCreateAsyncResult(NULL, callback, state, &pc->callback_result); @@ -4577,6 +4581,7 @@ static int count_samples_requested(IMFStreamSink *stream) break; else if (met == MEStreamSinkMarker) { + todo_wine CHECK_EXPECT(MEStreamSinkMarker); break; } @@ -4728,6 +4733,30 @@ static void test_sample_grabber_seek(void) todo_wine ok(samples_requested == 2, "Unexpected number of samples requested %d\n", samples_requested); + /* test number of new sample requests after a flush then seek */ + sample_pts = expected_pts = 0; + SET_EXPECT(timer_SetTimer); + supply_samples(stream, 2); + todo_wine + CHECK_CALLED(timer_SetTimer); + + /* there is no cancel timer, or sample requests during a flush */ + hr = IMFStreamSink_Flush(stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + supply_samples(stream, 1); + + /* only on seek */ + SET_EXPECT(timer_CancelTimer); + hr = IMFPresentationClock_Start(clock, 1234); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + CHECK_CALLED(timer_CancelTimer); + + samples_requested = count_samples_requested(stream); + todo_wine + ok(samples_requested == 3, "Unexpected number of samples requested %d\n", samples_requested); + /* test number of new sample requests on seek whilst stopped */ hr = IMFPresentationClock_Stop(clock); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -4736,13 +4765,13 @@ static void test_sample_grabber_seek(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); samples_requested = count_samples_requested(stream); + todo_wine ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); /* queue three samples with a marker between the first and second ... */ sample_pts = expected_pts = 0; SET_EXPECT(timer_SetTimer); supply_samples(stream, 1); - todo_wine CHECK_CALLED(timer_SetTimer); hr = IMFStreamSink_PlaceMarker(stream, MFSTREAMSINK_MARKER_DEFAULT, NULL, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -4848,6 +4877,7 @@ static void test_sample_grabber_seek(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); samples_requested = count_samples_requested(stream); + todo_wine ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); /* required for the sink to be fully released */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9489
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/mf/tests/mf.c | 238 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 231 insertions(+), 7 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 31ddabf6939..84e3a287cc5 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -3105,6 +3105,8 @@ static void test_media_session_rate_control(void) ok(hr == S_OK, "Shutdown failure, hr %#lx.\n", hr); } +DEFINE_EXPECT(OnProcessSample); + struct test_grabber_callback { IMFSampleGrabberSinkCallback IMFSampleGrabberSinkCallback_iface; @@ -3113,6 +3115,8 @@ struct test_grabber_callback IMFCollection *samples; HANDLE ready_event; HANDLE done_event; + + BOOL do_event; }; static struct test_grabber_callback *impl_from_IMFSampleGrabberSinkCallback(IMFSampleGrabberSinkCallback *iface) @@ -3204,7 +3208,7 @@ static HRESULT WINAPI test_grabber_callback_OnProcessSample(IMFSampleGrabberSink return S_OK; } - if (!grabber->ready_event) + if (!grabber->ready_event && grabber->do_event) return E_NOTIMPL; sample = create_sample(buffer, sample_size); @@ -3218,9 +3222,16 @@ static HRESULT WINAPI test_grabber_callback_OnProcessSample(IMFSampleGrabberSink IMFCollection_AddElement(grabber->samples, (IUnknown *)sample); IMFSample_Release(sample); - SetEvent(grabber->ready_event); - res = WaitForSingleObject(grabber->done_event, 1000); - ok(!res, "WaitForSingleObject returned %#lx\n", res); + if (grabber->do_event) + { + SetEvent(grabber->ready_event); + res = WaitForSingleObject(grabber->done_event, 1000); + ok(!res, "WaitForSingleObject returned %#lx\n", res); + } + else + { + CHECK_EXPECT2(OnProcessSample); + } return S_OK; } @@ -3257,6 +3268,7 @@ static IMFSampleGrabberSinkCallback *create_test_grabber_callback(void) grabber->refcount = 1; hr = MFCreateCollection(&grabber->samples); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + grabber->do_event = TRUE; return &grabber->IMFSampleGrabberSinkCallback_iface; } @@ -4561,7 +4573,10 @@ static void supply_samples(IMFStreamSink *stream, int num_samples) } } -static int count_samples_requested(IMFStreamSink *stream) +static BOOL ignore_clock = FALSE; + +#define count_samples_requested(stream) _count_samples_requested(__LINE__, stream) +static int _count_samples_requested(int line, IMFStreamSink *stream) { int samples_requested; IMFMediaEvent *event; @@ -4572,16 +4587,19 @@ static int count_samples_requested(IMFStreamSink *stream) while (IMFStreamSink_GetEvent(stream, 0, &event) == S_OK) { hr = IMFMediaEvent_GetType(event, &met); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); IMFMediaEvent_Release(event); if (met == MEStreamSinkRequestSample) samples_requested++; else if (met == MEStreamSinkStarted) + { + ok_(__FILE__, line)(!expect_MEStreamSinkMarker, "Expected MEStreamSinkMarker, got MEStreamSinkStarted\n"); break; + } else if (met == MEStreamSinkMarker) { - todo_wine + todo_wine_if(!ignore_clock) CHECK_EXPECT(MEStreamSinkMarker); break; } @@ -4891,6 +4909,212 @@ static void test_sample_grabber_seek(void) todo_wine ok(ref == 0, "Release returned %ld\n", ref); + /* test with MF_SAMPLEGRABBERSINK_IGNORE_CLOCK */ + + ignore_clock = TRUE; + grabber_callback = create_test_grabber_callback(); + grabber_callback_impl = impl_from_IMFSampleGrabberSinkCallback(grabber_callback); + grabber_callback_impl->do_event = FALSE; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); + + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Failed to create media type, hr %#lx.\n", hr); + + hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); + ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM); + ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); + + EXPECT_REF(media_type, 1); + hr = MFCreateSampleGrabberSinkActivate(media_type, grabber_callback, &activate); + ok(hr == S_OK, "Failed to create grabber activate, hr %#lx.\n", hr); + EXPECT_REF(media_type, 2); + + hr = IMFActivate_SetUINT32(activate, &MF_SAMPLEGRABBERSINK_IGNORE_CLOCK, TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + ref = IMFMediaType_Release(media_type); + ok(ref == 1, "Release returned %ld\n", ref); + + hr = IMFActivate_ActivateObject(activate, &IID_IMFMediaSink, (void **)&sink); + ok(hr == S_OK, "Failed to activate object, hr %#lx.\n", hr); + + ref = IMFActivate_Release(activate); + ok(ref == 0, "Release returned %ld\n", ref); + + hr = IMFMediaSink_GetStreamSinkByIndex(sink, 0, &stream); + ok(hr == S_OK, "Failed to get sink stream, hr %#lx.\n", hr); + + + /* Set clock. */ + mock_clock = create_presentation_clock(); + clock = &mock_clock->IMFPresentationClock_iface; + + hr = IMFMediaSink_SetPresentationClock(sink, clock); + ok(hr == S_OK, "Failed to set presentation clock, hr %#lx.\n", hr); + + /* test number of new sample requests on clock start */ + hr = IMFPresentationClock_Start(clock, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); + + /* test number of new sample requests on seek when in running state and 4 samples have been provided */ + sample_pts = 0; + SET_EXPECT(OnProcessSample); + supply_samples(stream, 4); + CHECK_CALLED(OnProcessSample); + CLEAR_CALLED(OnProcessSample); + + hr = IMFPresentationClock_Start(clock, 1234); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); + + /* test number of new sample requests on seek when in running state and 3 samples have been provided */ + sample_pts = 0; + SET_EXPECT(OnProcessSample); + supply_samples(stream, 2); + CHECK_CALLED(OnProcessSample); + CLEAR_CALLED(OnProcessSample); + hr = IMFStreamSink_PlaceMarker(stream, MFSTREAMSINK_MARKER_DEFAULT, NULL, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + SET_EXPECT(OnProcessSample); + supply_samples(stream, 1); + CHECK_CALLED(OnProcessSample); + CLEAR_CALLED(OnProcessSample); + + hr = IMFPresentationClock_Start(clock, 1234); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + SET_EXPECT(MEStreamSinkMarker); + samples_requested = count_samples_requested(stream); + ok(samples_requested == 2, "Unexpected number of samples requested %d\n", samples_requested); + CHECK_CALLED(MEStreamSinkMarker); + CLEAR_CALLED(OnProcessSample); + + samples_requested = count_samples_requested(stream); + ok(samples_requested == 1, "Unexpected number of samples requested %d\n", samples_requested); + + /* test number of new sample requests after a flush then seek */ + sample_pts = expected_pts = 0; + SET_EXPECT(OnProcessSample); + supply_samples(stream, 2); + CHECK_CALLED(OnProcessSample); + CLEAR_CALLED(OnProcessSample); + + /* there is no cancel timer, or sample requests during a flush */ + hr = IMFStreamSink_Flush(stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + SET_EXPECT(OnProcessSample); + supply_samples(stream, 1); + CHECK_CALLED(OnProcessSample); + CLEAR_CALLED(OnProcessSample); + + /* only on seek */ + hr = IMFPresentationClock_Start(clock, 1234); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + ok(samples_requested == 3, "Unexpected number of samples requested %d\n", samples_requested); + + /* test number of new sample requests on seek whilst stopped */ + hr = IMFPresentationClock_Stop(clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFPresentationClock_Start(clock, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); + + /* test number of new sample requests on seek whilst paused and 3 samples provided */ + sample_pts = expected_pts = 0; + SET_EXPECT(OnProcessSample); + supply_samples(stream, 3); + CHECK_CALLED(OnProcessSample); + CLEAR_CALLED(OnProcessSample); + hr = IMFStreamSink_PlaceMarker(stream, MFSTREAMSINK_MARKER_DEFAULT, NULL, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + SET_EXPECT(OnProcessSample); + supply_samples(stream, 2); + CHECK_CALLED(OnProcessSample); + CLEAR_CALLED(OnProcessSample); + + SET_EXPECT(MEStreamSinkMarker); + samples_requested = count_samples_requested(stream); + ok(samples_requested == 3, "Unexpected number of samples requested %d\n", samples_requested); + CHECK_CALLED(MEStreamSinkMarker); + + hr = IMFPresentationClock_Pause(clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* test over supply */ + SET_EXPECT(OnProcessSample); + supply_samples(stream, 6); + CHECK_CALLED(OnProcessSample); + CLEAR_CALLED(OnProcessSample); + + hr = IMFPresentationClock_Start(clock, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + ok(samples_requested == 8, "Unexpected number of samples requested %d\n", samples_requested); + + /* test number of new sample requests on seek whilst paused and no samples provided */ + hr = IMFPresentationClock_Pause(clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFPresentationClock_Start(clock, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + ok(samples_requested == 0, "Unexpected number of samples requested %d\n", samples_requested); + + /* test sample received in the paused state with no samples queued */ + hr = IMFPresentationClock_Pause(clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + expected_pts = sample_pts; + SET_EXPECT(OnProcessSample); + supply_samples(stream, 4); + CHECK_CALLED(OnProcessSample); + CLEAR_CALLED(OnProcessSample); + + hr = IMFPresentationClock_Start(clock, PRESENTATION_CURRENT_POSITION); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); + + /* test sample received in the stopped state */ + hr = IMFPresentationClock_Stop(clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + supply_samples(stream, 4); + + hr = IMFPresentationClock_Start(clock, PRESENTATION_CURRENT_POSITION); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); + + ref = IMFPresentationClock_Release(clock); + ok(ref == 2, "Release returned %ld\n", ref); + + /* required for the sink to be fully released */ + hr = IMFMediaSink_Shutdown(sink); + ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); + + ref = IMFMediaSink_Release(sink); + todo_wine + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFAsyncCallback_Release(callback); ok(ref == 0, "Release returned %ld\n", ref); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9489
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/mf/tests/mf.c | 87 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 84e3a287cc5..6796acbb534 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -3117,6 +3117,7 @@ struct test_grabber_callback HANDLE done_event; BOOL do_event; + BOOL need_sample_time; }; static struct test_grabber_callback *impl_from_IMFSampleGrabberSinkCallback(IMFSampleGrabberSinkCallback *iface) @@ -3215,7 +3216,7 @@ static HRESULT WINAPI test_grabber_callback_OnProcessSample(IMFSampleGrabberSink hr = IMFSample_SetSampleFlags(sample, sample_flags); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); /* FIXME: sample time is inconsistent across windows versions, ignore it */ - hr = IMFSample_SetSampleTime(sample, 0); + hr = IMFSample_SetSampleTime(sample, grabber->need_sample_time ? sample_time : 0); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSample_SetSampleDuration(sample, sample_duration); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -4642,8 +4643,47 @@ static void test_sample_grabber_seek(void) int samples_requested; PROPVARIANT propvar; IMFMediaSink *sink; + IMFSample *sample; + LONGLONG pts; + DWORD count; HRESULT hr; ULONG ref; + int i; + + static const LONGLONG use_clock_samples[] = + { + 0, + 0 + }; + + static const LONGLONG ignore_clock_samples[] = + { + 0, + 41667, + 83334, + 125001, + 0, + 41667, + 83334, + 0, + 41667, + 83334, + 0, + 41667, + 83334, + 125001, + 166668, + 208335, + 250002, + 291669, + 333336, + 375003, + 416670, + 458337, + 500004, + 541671, + 583338, + }; PropVariantInit(&propvar); callback = create_test_callback(TRUE); @@ -4654,6 +4694,7 @@ static void test_sample_grabber_seek(void) ok(!!grabber_callback_impl->ready_event, "CreateEventW failed, error %lu\n", GetLastError()); grabber_callback_impl->done_event = CreateEventW(NULL, FALSE, FALSE, NULL); ok(!!grabber_callback_impl->done_event, "CreateEventW failed, error %lu\n", GetLastError()); + grabber_callback_impl->need_sample_time = TRUE; hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); @@ -4898,6 +4939,29 @@ static void test_sample_grabber_seek(void) todo_wine ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); + /* check contents of collection */ + hr = IMFCollection_GetElementCount(grabber_callback_impl->samples, &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(count == ARRAY_SIZE(use_clock_samples), "Unexpected total of samples delivered %ld\n", count); + + for (i = 0; i < ARRAY_SIZE(use_clock_samples); i++) + { + hr = IMFCollection_GetElement(grabber_callback_impl->samples, i, (IUnknown**)&sample); + todo_wine_if(i) + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (hr == S_OK) + { + hr = IMFSample_GetSampleTime(sample, &pts); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pts == use_clock_samples[i], "%d: Unexpected pts %I64d, expected %I64d\n", i, pts, use_clock_samples[i]); + + ref = IMFSample_Release(sample); + ok(ref == 1, "Release returned %ld\n", ref); + } + } + /* required for the sink to be fully released */ ref = IMFPresentationClock_Release(clock); ok(ref == 2, "Release returned %ld\n", ref); @@ -4915,6 +4979,7 @@ static void test_sample_grabber_seek(void) grabber_callback = create_test_grabber_callback(); grabber_callback_impl = impl_from_IMFSampleGrabberSinkCallback(grabber_callback); grabber_callback_impl->do_event = FALSE; + grabber_callback_impl->need_sample_time = TRUE; hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); @@ -5104,10 +5169,28 @@ static void test_sample_grabber_seek(void) samples_requested = count_samples_requested(stream); ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); + /* check contents of collection */ + hr = IMFCollection_GetElementCount(grabber_callback_impl->samples, &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == ARRAY_SIZE(ignore_clock_samples), "Unexpected total of samples delivered %ld\n", count); + + for (i = 0; i < ARRAY_SIZE(ignore_clock_samples); i++) + { + hr = IMFCollection_GetElement(grabber_callback_impl->samples, i, (IUnknown**)&sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFSample_GetSampleTime(sample, &pts); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pts == ignore_clock_samples[i], "%d: Unexpected pts %I64d, expected %I64d\n", i, pts, ignore_clock_samples[i]); + + ref = IMFSample_Release(sample); + ok(ref == 1, "Release returned %ld\n", ref); + } + + /* required for the sink to be fully released */ ref = IMFPresentationClock_Release(clock); ok(ref == 2, "Release returned %ld\n", ref); - /* required for the sink to be fully released */ hr = IMFMediaSink_Shutdown(sink); ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9489
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/mf/tests/mf.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 6796acbb534..d3e191d8721 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -4923,13 +4923,33 @@ static void test_sample_grabber_seek(void) todo_wine ok(samples_requested == 4, "Unexpected number of samples requested %d\n", samples_requested); - /* test sample received in the stopped state */ + /* test pause and resume with 4 samples queued */ + hr = IMFPresentationClock_Pause(clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFPresentationClock_Start(clock, PRESENTATION_CURRENT_POSITION); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + samples_requested = count_samples_requested(stream); + ok(samples_requested == 0, "Unexpected number of samples requested %d\n", samples_requested); + + /* test pause and seek with 4 samples queued */ + hr = IMFPresentationClock_Pause(clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + SET_EXPECT(timer_CancelTimer); - hr = IMFPresentationClock_Stop(clock); + hr = IMFPresentationClock_Start(clock, 1234567); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); todo_wine CHECK_CALLED(timer_CancelTimer); + samples_requested = count_samples_requested(stream); + ok(samples_requested == 0, "Unexpected number of samples requested %d\n", samples_requested); + + /* test sample received in the stopped state */ + hr = IMFPresentationClock_Stop(clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + supply_samples(stream, 4); hr = IMFPresentationClock_Start(clock, PRESENTATION_CURRENT_POSITION); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9489
v8: - Change MR to include tests only (a new MR will be raised with the fixes after this one is merged) - Add basic Start/Stop/Pause implementation to mock presentation clock and use this instead of direct calls to `IMFClockStateSink` - Remove some redundant checks - Make the comments for a couple of the tests easier to understand - Fix one of the tests that seem to have got trampled on by another test - Rebase to master -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9489#note_127171
On Fri Jan 16 06:54:20 2026 +0000, Nikolay Sivov wrote:
Could you move tests to a separate MR? The commit list is pretty long here. Yep, done. I was thinking to ask if you wanted that after I broke up the implementation of the fixes in to separate commits.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9489#note_127172
On Fri Jan 16 06:55:36 2026 +0000, Nikolay Sivov wrote:
Normal flow is to call clock->Start(), callbacks are called internally. Why changing that? I could have gone either way with this. I think in the end I took the option which seemed to have less code. But it's pretty trivial to implement, so I've gone ahead and done so.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9489#note_127173
On Fri Jan 16 06:55:50 2026 +0000, Nikolay Sivov wrote:
I would remove this one. Done.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9489#note_127174
In this sequence, how could you tell that requests are coming on Pause -\> Start transition (is that what we are trying to test?)?
Yes, this is what I'm testing. The comment there was a bit poor, so I fixed it. I guess it becomes more obvious it's the result of that transition in later tests when I start checking for `CancelTimer` and `SetTimer`. Although, I noticed this test seemed to get trampled on by the over sample test, so I have now fixed it.
Does it rely on a fact that we pushed fewer than requested?
Yep. It seems the sample grabber always wants to request four samples after a Pause -> Start transition (Start being a seek as opposed to a Restart). But it expects the client to keep track of outstanding requests (i.e. the media session). So when it requested four samples (as shown at the end of the previous test) but only received three samples, it knew one was already outstanding and therefore only requested three (for a total of four). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9489#note_127175
On Fri Jan 16 06:46:06 2026 +0000, Brendan McGrath wrote:
changed this line in [version 8 of the diff](/wine/wine/-/merge_requests/9489/diffs?diff_id=238211&start_sha=56ff4dc8a51d930d036d3a4eb9597fa8c7734add#38af2002cdac18e4819839775ba914609caa73df_4698_4714) OK, removed.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9489#note_127176
On Fri Jan 16 06:46:06 2026 +0000, Brendan McGrath wrote:
changed this line in [version 8 of the diff](/wine/wine/-/merge_requests/9489/diffs?diff_id=238211&start_sha=56ff4dc8a51d930d036d3a4eb9597fa8c7734add#38af2002cdac18e4819839775ba914609caa73df_4695_4712) OK, removed.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9489#note_127177
This merge request was approved by Nikolay Sivov. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9489
participants (3)
-
Brendan McGrath -
Brendan McGrath (@redmcg) -
Nikolay Sivov (@nsivov)