This MR adds tests for how scrubbing and rate changes work within the Media Session.
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mf/tests/mf.c | 271 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 265 insertions(+), 6 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 0c0590ed195..40e6ebc6ccd 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -757,11 +757,14 @@ static void test_handler_clear_current_type(struct test_handler *handler) struct test_media_sink { IMFMediaSink IMFMediaSink_iface; + IMFMediaSinkPreroll IMFMediaSinkPreroll_iface; LONG refcount; IMFMediaTypeHandler *handler; IMFPresentationClock *clock; IMFStreamSink *stream; BOOL shutdown; + DWORD characteristics; + HANDLE preroll_event; };
static struct test_media_sink *impl_from_IMFMediaSink(IMFMediaSink *iface) @@ -771,15 +774,26 @@ static struct test_media_sink *impl_from_IMFMediaSink(IMFMediaSink *iface)
static HRESULT WINAPI test_media_sink_QueryInterface(IMFMediaSink *iface, REFIID riid, void **obj) { + struct test_media_sink *sink = impl_from_IMFMediaSink(iface); + if (IsEqualIID(riid, &IID_IMFMediaSink) || IsEqualIID(riid, &IID_IUnknown)) { - IMFMediaSink_AddRef((*obj = iface)); - return S_OK; + *obj = iface; + } + else if (IsEqualIID(riid, &IID_IMFMediaSinkPreroll)) + { + *obj = &sink->IMFMediaSinkPreroll_iface; + } + else + { + *obj = NULL; + return E_NOINTERFACE; }
- *obj = NULL; - return E_NOINTERFACE; + IUnknown_AddRef((IUnknown*)*obj); + return S_OK; + }
static ULONG WINAPI test_media_sink_AddRef(IMFMediaSink *iface) @@ -799,6 +813,8 @@ static ULONG WINAPI test_media_sink_Release(IMFMediaSink *iface) IMFMediaSink_Shutdown(iface); if (sink->handler) IMFMediaTypeHandler_Release(sink->handler); + if (sink->preroll_event) + CloseHandle(sink->preroll_event); free(sink); }
@@ -807,7 +823,8 @@ static ULONG WINAPI test_media_sink_Release(IMFMediaSink *iface)
static HRESULT WINAPI test_media_sink_GetCharacteristics(IMFMediaSink *iface, DWORD *characteristics) { - *characteristics = 0; + struct test_media_sink *sink = impl_from_IMFMediaSink(iface); + *characteristics = sink->characteristics; return S_OK; }
@@ -982,6 +999,48 @@ static const IMFMediaSinkVtbl test_media_sink_vtbl = test_media_sink_Shutdown, };
+DEFINE_EXPECT(test_media_sink_preroll_NotifyPreroll); + +static struct test_media_sink *impl_from_IMFMediaSinkPreroll(IMFMediaSinkPreroll *iface) +{ + return CONTAINING_RECORD(iface, struct test_media_sink, IMFMediaSinkPreroll_iface); +} + +static HRESULT WINAPI test_media_sink_preroll_QueryInterface(IMFMediaSinkPreroll *iface, REFIID riid, void **obj) +{ + struct test_media_sink *sink = impl_from_IMFMediaSinkPreroll(iface); + return IMFMediaSink_QueryInterface(&sink->IMFMediaSink_iface, riid, obj); +} + +static ULONG WINAPI test_media_sink_preroll_AddRef(IMFMediaSinkPreroll *iface) +{ + struct test_media_sink *sink = impl_from_IMFMediaSinkPreroll(iface); + return IMFMediaSink_AddRef(&sink->IMFMediaSink_iface); +} + +static ULONG WINAPI test_media_sink_preroll_Release(IMFMediaSinkPreroll *iface) +{ + struct test_media_sink *sink = impl_from_IMFMediaSinkPreroll(iface); + return IMFMediaSink_Release(&sink->IMFMediaSink_iface); +} + +static HRESULT WINAPI test_media_sink_preroll_NotifyPreroll(IMFMediaSinkPreroll *iface, MFTIME time) +{ + struct test_media_sink *sink = impl_from_IMFMediaSinkPreroll(iface); + todo_wine_if(!expect_test_media_sink_preroll_NotifyPreroll) + CHECK_EXPECT(test_media_sink_preroll_NotifyPreroll); + SetEvent(sink->preroll_event); + return S_OK; +} + +static const IMFMediaSinkPrerollVtbl test_media_sink_preroll_vtbl = +{ + test_media_sink_preroll_QueryInterface, + test_media_sink_preroll_AddRef, + test_media_sink_preroll_Release, + test_media_sink_preroll_NotifyPreroll, +}; + struct test_stream_sink { IMFStreamSink IMFStreamSink_iface; @@ -1266,6 +1325,7 @@ static struct test_media_sink *create_test_media_sink(IMFMediaTypeHandler *handl
sink = calloc(1, sizeof(*sink)); sink->IMFMediaSink_iface.lpVtbl = &test_media_sink_vtbl; + sink->IMFMediaSinkPreroll_iface.lpVtbl = &test_media_sink_preroll_vtbl; sink->refcount = 1; if (handler) IMFMediaTypeHandler_AddRef(sink->handler = handler); @@ -8039,7 +8099,7 @@ static HRESULT WINAPI test_transform_ProcessMessage(IMFTransform *iface, MFT_MES return S_OK;
case MFT_MESSAGE_COMMAND_FLUSH: - CHECK_EXPECT(test_transform_ProcessMessage_FLUSH); + CHECK_EXPECT2(test_transform_ProcessMessage_FLUSH); add_object_state(&actual_object_state_record, MFT_FLUSH); return S_OK;
@@ -8436,6 +8496,204 @@ static void test_media_session_seek(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); }
+static void test_media_session_scrubbing(void) +{ + IMFClockStateSink test_seek_clock_sink = {&test_seek_clock_sink_vtbl}; + MFT_OUTPUT_STREAM_INFO output_stream_info = {0}; + IMFPresentationClock *presentation_clock; + struct test_callback *test_callback; + struct test_media_sink *media_sink; + struct test_source *media_source; + struct test_handler *handler; + IMFRateControl *rate_control; + IMFAsyncCallback *callback; + IMFMediaSession *session; + IMFMediaSource *source; + IMFTopology *topology; + PROPVARIANT propvar; + IMFMediaType *type; + IMFTransform *mft; + IMFClock *clock; + UINT32 status; + HRESULT hr; + INT i; + + /* Allocate and initialise required resources */ + handler = create_test_handler(); + media_sink = create_test_media_sink(&handler->IMFMediaTypeHandler_iface); + media_sink->characteristics = MEDIASINK_CAN_PREROLL | MEDIASINK_FIXED_STREAMS; + media_sink->preroll_event = CreateEventA(NULL, FALSE, FALSE, NULL); + IMFMediaTypeHandler_Release(&handler->IMFMediaTypeHandler_iface); + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + source = create_test_source(TRUE); + media_source = impl_test_source_from_IMFMediaSource(source); + for (i = 0; i < media_source->stream_count; i++) + media_source->streams[i]->test_expect = TRUE; + + hr = MFCreateMediaType(&type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, &MFVideoFormat_RGB32); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT64(type, &MF_MT_FRAME_SIZE, (UINT64)640 << 32 | 480); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + mft = NULL; + hr = test_transform_create(1, &type, 1, &type, FALSE, &mft); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + test_transform_set_output_stream_info(mft, &output_stream_info); + IMFMediaType_Release(type); + + hr = MFGetService((IUnknown*)session, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateControl, (void**)&rate_control); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + PropVariantInit(&propvar); + + /* Create and set-up the required topology */ + SET_EXPECT(test_transform_ProcessMessage_BEGIN_STREAMING); + topology = create_test_topology_unk(source, (IUnknown*)media_sink->stream, (IUnknown*) mft, NULL); + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopology_Release(topology); + + hr = IMFMediaSession_GetClock(session, &clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFClock_QueryInterface(clock, &IID_IMFPresentationClock, (void **)&presentation_clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationClock_AddClockStateSink(presentation_clock, &test_seek_clock_sink); + ok(hr == S_OK, "Failed to add a sink, hr %#lx.\n", hr); + IMFClock_Release(clock); + + callback = create_test_callback(TRUE); + test_callback = impl_from_IMFAsyncCallback(callback); + hr = wait_media_event(session, callback, MESessionTopologyStatus, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEvent_GetUINT32(test_callback->media_event, &MF_EVENT_TOPOLOGY_STATUS, &status); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(status == MF_TOPOSTATUS_READY, "Unexpected status %d.\n", status); + PropVariantClear(&propvar); + CHECK_CALLED(test_transform_ProcessMessage_BEGIN_STREAMING); + + /* Test that when rate is zero (i.e. we're scrubbing), no preroll occurs */ + hr = IMFRateControl_SetRate(rate_control, FALSE, 0.0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + SET_EXPECT(test_media_sink_GetPresentationClock); + SET_EXPECT(test_media_sink_SetPresentationClock); + SET_EXPECT(test_transform_ProcessMessage_START_OF_STREAM); + SET_EXPECT(test_media_sink_GetStreamSinkCount); + propvar.vt = VT_I8; /* hVal will be zero */ + hr = IMFMediaSession_Start(session, NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = WaitForSingleObject(media_sink->preroll_event, 100); + todo_wine + ok(hr == WAIT_TIMEOUT, "Unexpected hr %#lx.\n", hr); + + todo_wine + CHECK_CALLED(test_media_sink_GetPresentationClock); + CHECK_CALLED(test_media_sink_SetPresentationClock); + todo_wine + CHECK_CALLED(test_transform_ProcessMessage_START_OF_STREAM); + todo_wine + CHECK_CALLED(test_media_sink_GetStreamSinkCount); + + hr = IMFStreamSink_QueueEvent(media_sink->stream, MEStreamSinkScrubSampleComplete, &GUID_NULL, S_OK, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + + hr = IMFStreamSink_QueueEvent(media_sink->stream, MEStreamSinkStarted, &GUID_NULL, S_OK, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + + hr = wait_media_event_until_blocking(session, callback, MESessionStarted, 1000, &propvar); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + + SET_EXPECT(test_transform_ProcessMessage_FLUSH); + SET_EXPECT(test_stream_sink_Flush); + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFStreamSink_QueueEvent(media_sink->stream, MEStreamSinkStopped, &GUID_NULL, S_OK, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + + hr = wait_media_event_until_blocking(session, callback, MESessionStopped, 1000, &propvar); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + todo_wine + CHECK_CALLED(test_transform_ProcessMessage_FLUSH); + todo_wine + CHECK_CALLED(test_stream_sink_Flush); + + /* Test that during a standard start (i.e. rate == 1.0), preroll is called on the sink */ + hr = IMFRateControl_SetRate(rate_control, FALSE, 1.0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + SET_EXPECT(test_media_sink_GetPresentationClock); + SET_EXPECT(test_transform_ProcessMessage_START_OF_STREAM); + SET_EXPECT(test_media_sink_preroll_NotifyPreroll); + propvar.vt = VT_I8; /* hVal will be zero */ + hr = IMFMediaSession_Start(session, NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = WaitForSingleObject(media_sink->preroll_event, 100); + todo_wine + ok(hr == WAIT_OBJECT_0, "Unexpected hr %#lx.\n", hr); + + todo_wine + CHECK_CALLED(test_media_sink_GetPresentationClock); + todo_wine + CHECK_CALLED(test_transform_ProcessMessage_START_OF_STREAM); + CHECK_CALLED(test_media_sink_preroll_NotifyPreroll); + + SET_EXPECT(test_media_sink_GetStreamSinkCount); + hr = IMFStreamSink_QueueEvent(media_sink->stream, MEStreamSinkPrerolled, &GUID_NULL, S_OK, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + + hr = IMFStreamSink_QueueEvent(media_sink->stream, MEStreamSinkStarted, &GUID_NULL, S_OK, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + + hr = wait_media_event_until_blocking(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + todo_wine + CHECK_CALLED(test_media_sink_GetStreamSinkCount); + + /* Release all the used resources */ + IMFPresentationClock_RemoveClockStateSink(presentation_clock, &test_seek_clock_sink); + IMFPresentationClock_Release(presentation_clock); + IMFAsyncCallback_Release(callback); + IMFTransform_Release(mft); + + IMFRateControl_Release(rate_control); + + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(media_sink->shutdown, "Media sink didn't shutdown.\n"); + + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFMediaSession_Release(session); + IMFMediaSource_Release(source); + IMFMediaSink_Release(&media_sink->IMFMediaSink_iface); + + hr = MFShutdown(); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} + START_TEST(mf) { init_functions(); @@ -8475,4 +8733,5 @@ START_TEST(mf) test_media_session_source_shutdown(); test_media_session_thinning(); test_media_session_seek(); + test_media_session_scrubbing(); }
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mf/tests/mf.c | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 40e6ebc6ccd..82ecd017100 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -2359,6 +2359,8 @@ static IMFMediaSource *create_test_source(BOOL seekable) return &source->IMFMediaSource_iface; }
+DEFINE_EXPECT(test_seek_clock_sink_OnClockSetRate); + static HRESULT WINAPI test_seek_clock_sink_QueryInterface(IMFClockStateSink *iface, REFIID riid, void **obj) { if (IsEqualIID(riid, &IID_IMFClockStateSink) || @@ -2409,6 +2411,7 @@ static HRESULT WINAPI test_seek_clock_sink_OnClockRestart(IMFClockStateSink *ifa
static HRESULT WINAPI test_seek_clock_sink_OnClockSetRate(IMFClockStateSink *iface, MFTIME system_time, float rate) { + CHECK_EXPECT(test_seek_clock_sink_OnClockSetRate); add_object_state(&actual_object_state_record, SINK_ON_CLOCK_SETRATE); return S_OK; } @@ -8581,11 +8584,12 @@ static void test_media_session_scrubbing(void) CHECK_CALLED(test_transform_ProcessMessage_BEGIN_STREAMING);
/* Test that when rate is zero (i.e. we're scrubbing), no preroll occurs */ + SET_EXPECT(test_media_sink_GetPresentationClock); + SET_EXPECT(test_media_sink_SetPresentationClock); + SET_EXPECT(test_seek_clock_sink_OnClockSetRate); hr = IMFRateControl_SetRate(rate_control, FALSE, 0.0); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- SET_EXPECT(test_media_sink_GetPresentationClock); - SET_EXPECT(test_media_sink_SetPresentationClock); SET_EXPECT(test_transform_ProcessMessage_START_OF_STREAM); SET_EXPECT(test_media_sink_GetStreamSinkCount); propvar.vt = VT_I8; /* hVal will be zero */ @@ -8603,6 +8607,7 @@ static void test_media_session_scrubbing(void) CHECK_CALLED(test_transform_ProcessMessage_START_OF_STREAM); todo_wine CHECK_CALLED(test_media_sink_GetStreamSinkCount); + CHECK_CALLED(test_seek_clock_sink_OnClockSetRate);
hr = IMFStreamSink_QueueEvent(media_sink->stream, MEStreamSinkScrubSampleComplete, &GUID_NULL, S_OK, &propvar); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -8636,10 +8641,11 @@ static void test_media_session_scrubbing(void) CHECK_CALLED(test_stream_sink_Flush);
/* Test that during a standard start (i.e. rate == 1.0), preroll is called on the sink */ + SET_EXPECT(test_seek_clock_sink_OnClockSetRate); + SET_EXPECT(test_media_sink_GetPresentationClock); hr = IMFRateControl_SetRate(rate_control, FALSE, 1.0); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- SET_EXPECT(test_media_sink_GetPresentationClock); SET_EXPECT(test_transform_ProcessMessage_START_OF_STREAM); SET_EXPECT(test_media_sink_preroll_NotifyPreroll); propvar.vt = VT_I8; /* hVal will be zero */ @@ -8655,6 +8661,8 @@ static void test_media_session_scrubbing(void) todo_wine CHECK_CALLED(test_transform_ProcessMessage_START_OF_STREAM); CHECK_CALLED(test_media_sink_preroll_NotifyPreroll); + todo_wine + CHECK_CALLED(test_seek_clock_sink_OnClockSetRate);
SET_EXPECT(test_media_sink_GetStreamSinkCount); hr = IMFStreamSink_QueueEvent(media_sink->stream, MEStreamSinkPrerolled, &GUID_NULL, S_OK, &propvar); @@ -8671,6 +8679,37 @@ static void test_media_session_scrubbing(void) todo_wine CHECK_CALLED(test_media_sink_GetStreamSinkCount);
+ /* Test that a rate change whilst in the PLAY state is a no-op */ + hr = IMFRateControl_SetRate(rate_control, FALSE, 0.0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* But registers with the sink in the PAUSE state */ + hr = IMFMediaSession_Pause(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFStreamSink_QueueEvent(media_sink->stream, MEStreamSinkPaused, &GUID_NULL, S_OK, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + + hr = wait_media_event_until_blocking(session, callback, MESessionPaused, 1000, &propvar); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + + SET_EXPECT(test_media_sink_GetPresentationClock); + SET_EXPECT(test_seek_clock_sink_OnClockSetRate); + hr = IMFRateControl_SetRate(rate_control, FALSE, 0.0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = wait_media_event_until_blocking(session, callback, MESessionRateChanged, 1000, &propvar); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + todo_wine + CHECK_CALLED(test_media_sink_GetPresentationClock); + todo_wine + CHECK_CALLED(test_seek_clock_sink_OnClockSetRate); + /* Release all the used resources */ IMFPresentationClock_RemoveClockStateSink(presentation_clock, &test_seek_clock_sink); IMFPresentationClock_Release(presentation_clock);