This MR fixes seek in VRChat by copying the sequence of flushes/stop/starts that Windows does.
The order on Windows is: 1. Stop sources; 2. Flush MFTs; 3. Start sources; 4. Request output down the chain of sink inputs; 6. Flush sinks; and 7. Start the clock
This takes place whether we pause before we seek or seek without pause.
Changes in version 2: 1. Add test to see when SAR requests new samples; 2. Add test to examine when step 4 from above takes place; 3. Adjust the seek implementation in `mf/session.c` to match findings from the new tests 4. Adjust `mfmediaengine` to reflect findings from the new tests
-- v2: mfmediaengine: Request sample if we are seeking. mfmediaengine: Don't perform implicit flush on state change. mf: Don't send MFT_MESSAGE_NOTIFY_START_OF_STREAM when seeking. mf: Restart transforms and sinks on seek. mf/tests: Test sequence of calls during a Pause and Seek. mf/tests: Test when SAR requests a new sample.
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mf/tests/mf.c | 159 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 149 insertions(+), 10 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 37c1b0be456..d2686236393 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -1033,6 +1033,7 @@ struct test_callback HANDLE event; IMFMediaEvent *media_event; BOOL check_media_event; + BOOL timed_out; };
static struct test_callback *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) @@ -1136,8 +1137,8 @@ static IMFAsyncCallback *create_test_callback(BOOL check_media_event) return &callback->IMFAsyncCallback_iface; }
-#define wait_media_event(a, b, c, d, e) wait_media_event_(__LINE__, a, b, c, d, e) -static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, +#define gen_wait_media_event(a, b, c, d, e) gen_wait_media_event_(__LINE__, a, b, c, d, e) +static HRESULT gen_wait_media_event_(int line, IMFMediaEventGenerator *event_generator, IMFAsyncCallback *callback, MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) { struct test_callback *impl = impl_from_IMFAsyncCallback(callback); @@ -1148,9 +1149,10 @@ static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCal
do { - hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEventGenerator_BeginGetEvent(event_generator, &impl->IMFAsyncCallback_iface, (IUnknown *)event_generator); + ok_(__FILE__, line)(hr == S_OK || (impl->timed_out && (hr == MF_E_MULTIPLE_SUBSCRIBERS || hr == MF_S_MULTIPLE_BEGIN)), "Unexpected hr %#lx.\n", hr); ret = WaitForSingleObject(impl->event, timeout); + impl->timed_out = FALSE; ok_(__FILE__, line)(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", ret); hr = IMFMediaEvent_GetType(impl->media_event, &type); ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -1171,8 +1173,15 @@ static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCal return status; }
-#define wait_media_event_until_blocking(a, b, c, d, e) wait_media_event_until_blocking_(__LINE__, a, b, c, d, e) -static HRESULT wait_media_event_until_blocking_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, +#define wait_media_event(a, b, c, d, e) wait_media_event_(__LINE__, a, b, c, d, e) +static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, + MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) +{ + return gen_wait_media_event_(line, (IMFMediaEventGenerator*) session, callback, expect_type, timeout, value); +} + +#define gen_wait_media_event_until_blocking(a, b, c, d, e) gen_wait_media_event_until_blocking_(__LINE__, a, b, c, d, e) +static HRESULT gen_wait_media_event_until_blocking_(int line, IMFMediaEventGenerator *event_generator, IMFAsyncCallback *callback, MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) { struct test_callback *impl = impl_from_IMFAsyncCallback(callback); @@ -1183,10 +1192,15 @@ static HRESULT wait_media_event_until_blocking_(int line, IMFMediaSession *sessi
do { - hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEventGenerator_BeginGetEvent(event_generator, &impl->IMFAsyncCallback_iface, (IUnknown *)event_generator); + ok_(__FILE__, line)(hr == S_OK || (impl->timed_out && (hr == MF_E_MULTIPLE_SUBSCRIBERS || hr == MF_S_MULTIPLE_BEGIN)), "Unexpected hr %#lx.\n", hr); ret = WaitForSingleObject(impl->event, timeout); - if (ret == WAIT_TIMEOUT) return WAIT_TIMEOUT; + if (ret == WAIT_TIMEOUT) + { + impl->timed_out = TRUE; + return WAIT_TIMEOUT; + } + impl->timed_out = FALSE; hr = IMFMediaEvent_GetType(impl->media_event, &type); ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); } while (type != expect_type); @@ -1206,6 +1220,13 @@ static HRESULT wait_media_event_until_blocking_(int line, IMFMediaSession *sessi return status; }
+#define wait_media_event_until_blocking(a, b, c, d, e) wait_media_event_until_blocking_(__LINE__, a, b, c, d, e) +static HRESULT wait_media_event_until_blocking_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, + MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) +{ + return gen_wait_media_event_until_blocking_(line, (IMFMediaEventGenerator*) session, callback, expect_type, timeout, value); +} + static IMFMediaSource *create_media_source(const WCHAR *name, const WCHAR *mime) { IMFSourceResolver *resolver; @@ -4221,21 +4242,27 @@ static void test_sar(void)
IMFPresentationClock *present_clock, *present_clock2; IMFMediaType *mediatype, *mediatype2, *mediatype3; + UINT32 channel_count, rate, bytes_per_second; IMFClockStateSink *state_sink, *state_sink2; IMFMediaTypeHandler *handler, *handler2; IMFPresentationTimeSource *time_source; IMFSimpleAudioVolume *simple_volume; IMFAudioStreamVolume *stream_volume; + IMFAsyncCallback *callback; IMFMediaSink *sink, *sink2; IMFStreamSink *stream_sink; - UINT32 channel_count, rate; IMFAttributes *attributes; + IMFMediaBuffer *buffer; DWORD id, flags, count; IMFActivate *activate; + IMFMediaEvent *event; MFCLOCK_STATE state; + PROPVARIANT propvar; + IMFSample *sample; IMFClock *clock; IUnknown *unk; HRESULT hr; + BYTE *buff; GUID guid; BOOL mute; LONG ref; @@ -4457,6 +4484,7 @@ if (SUCCEEDED(hr)) hr = IMFMediaTypeHandler_GetCurrentMediaType(handler, &mediatype); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(mediatype == mediatype2, "Unexpected instance.\n"); + IMFMediaType_GetUINT32(mediatype, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &bytes_per_second); IMFMediaType_Release(mediatype);
IMFMediaType_Release(mediatype2); @@ -4498,6 +4526,117 @@ if (SUCCEEDED(hr)) hr = IMFClockStateSink_OnClockStop(state_sink, 0); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ callback = create_test_callback(TRUE); + + /* Flush events */ + while (SUCCEEDED(IMFStreamSink_GetEvent(stream_sink, MF_EVENT_FLAG_NO_WAIT, &event))) + IMFMediaEvent_Release(event); + + hr = IMFClockStateSink_OnClockStart(state_sink, 0, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + propvar.vt = VT_EMPTY; + hr = gen_wait_media_event((IMFMediaEventGenerator*)stream_sink, callback, MEStreamSinkStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = gen_wait_media_event((IMFMediaEventGenerator*)stream_sink, callback, MEStreamSinkRequestSample, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateMemoryBuffer(bytes_per_second, &buffer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaBuffer_Lock(buffer, &buff, NULL, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + memset(buff, 0, bytes_per_second); + hr = IMFMediaBuffer_Unlock(buffer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaBuffer_SetCurrentLength(buffer, bytes_per_second); + 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 = IMFStreamSink_ProcessSample(stream_sink, sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFSample_Release(sample); + + hr = gen_wait_media_event((IMFMediaEventGenerator*)stream_sink, callback, MEStreamSinkRequestSample, 1000, &propvar); + 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); + IMFMediaBuffer_Release(buffer); + + hr = IMFSample_SetSampleTime(sample, 10000000); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFStreamSink_ProcessSample(stream_sink, sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFSample_Release(sample); + + hr = IMFClockStateSink_OnClockPause(state_sink, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = gen_wait_media_event((IMFMediaEventGenerator*)stream_sink, callback, MEStreamSinkPaused, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFStreamSink_Flush(stream_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* confirm no new sample is requested after a flush */ + hr = gen_wait_media_event_until_blocking((IMFMediaEventGenerator*)stream_sink, callback, MEStreamSinkRequestSample, 1000, &propvar); + todo_wine + ok(hr == WAIT_TIMEOUT, "Unexpected hr %#lx.\n", hr); + + hr = IMFClockStateSink_OnClockStart(state_sink, 0, 123456); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = gen_wait_media_event_until_blocking((IMFMediaEventGenerator*)stream_sink, callback, MEStreamSinkStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* they are only requested after a call to OnClockStart */ + hr = gen_wait_media_event_until_blocking((IMFMediaEventGenerator*)stream_sink, callback, MEStreamSinkRequestSample, 1000, &propvar); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = gen_wait_media_event_until_blocking((IMFMediaEventGenerator*)stream_sink, callback, MEStreamSinkRequestSample, 1000, &propvar); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* but if the original requests aren't satisfied ... */ + hr = IMFClockStateSink_OnClockPause(state_sink, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = gen_wait_media_event((IMFMediaEventGenerator*)stream_sink, callback, MEStreamSinkPaused, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFStreamSink_Flush(stream_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* ... there is still no new sample request after a flush ... */ + hr = gen_wait_media_event_until_blocking((IMFMediaEventGenerator*)stream_sink, callback, MEStreamSinkRequestSample, 1000, &propvar); + ok(hr == WAIT_TIMEOUT, "Unexpected hr %#lx.\n", hr); + + hr = IMFClockStateSink_OnClockStart(state_sink, 0, 654321); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = gen_wait_media_event_until_blocking((IMFMediaEventGenerator*)stream_sink, callback, MEStreamSinkStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* ... and still none after a call to OnClockStart. The client must keep track of these pending requests. */ + hr = gen_wait_media_event_until_blocking((IMFMediaEventGenerator*)stream_sink, callback, MEStreamSinkRequestSample, 1000, &propvar); + ok(hr == WAIT_TIMEOUT, "Unexpected hr %#lx.\n", hr); + + IMFAsyncCallback_Release(callback); + IMFClockStateSink_Release(state_sink);
IMFStreamSink_Release(stream_sink);
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mf/tests/mf.c | 918 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 850 insertions(+), 68 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index d2686236393..b1605a9ae1e 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -720,6 +720,8 @@ static const struct test_handler test_handler = {.IMFMediaTypeHandler_iface.lpVt struct test_media_sink { IMFMediaSink IMFMediaSink_iface; + IMFPresentationClock *clock; + IMFStreamSink *stream; BOOL shutdown; };
@@ -770,14 +772,34 @@ static HRESULT WINAPI test_media_sink_RemoveStreamSink(IMFMediaSink *iface, DWOR return E_NOTIMPL; }
+DEFINE_EXPECT(test_media_sink_GetStreamSinkCount); +DEFINE_EXPECT(test_media_sink_SetPresentationClock); +DEFINE_EXPECT(test_media_sink_GetPresentationClock); + static HRESULT WINAPI test_media_sink_GetStreamSinkCount(IMFMediaSink *iface, DWORD *count) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + HRESULT hr; + if (expect_test_media_sink_GetStreamSinkCount) + { + *count = 1; + hr = S_OK; + } + else + { + hr = E_NOTIMPL; + } + CHECK_EXPECT(test_media_sink_GetStreamSinkCount); + return hr; }
static HRESULT WINAPI test_media_sink_GetStreamSinkByIndex(IMFMediaSink *iface, DWORD index, IMFStreamSink **sink) { + struct test_media_sink *sink_impl = impl_from_IMFMediaSink(iface); + if (sink_impl->stream) + { + IMFStreamSink_AddRef(*sink = sink_impl->stream); + return S_OK; + } ok(0, "Unexpected call.\n"); return E_NOTIMPL; } @@ -790,13 +812,37 @@ static HRESULT WINAPI test_media_sink_GetStreamSinkById(IMFMediaSink *iface, DWO
static HRESULT WINAPI test_media_sink_SetPresentationClock(IMFMediaSink *iface, IMFPresentationClock *clock) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + struct test_media_sink *sink = impl_from_IMFMediaSink(iface); + HRESULT hr; + + if (expect_test_media_sink_SetPresentationClock) + { + if (sink->clock) IMFPresentationClock_Release(clock); + IMFPresentationClock_AddRef(sink->clock = clock); + hr = S_OK; + } + else + { + hr = E_NOTIMPL; + } + CHECK_EXPECT(test_media_sink_SetPresentationClock); + return hr; }
static HRESULT WINAPI test_media_sink_GetPresentationClock(IMFMediaSink *iface, IMFPresentationClock **clock) { - ok(0, "Unexpected call.\n"); + struct test_media_sink *sink = impl_from_IMFMediaSink(iface); + + CHECK_EXPECT2(test_media_sink_GetPresentationClock); + if (expect_test_media_sink_GetPresentationClock) + { + if (sink->clock) + { + IMFPresentationClock_AddRef(*clock = sink->clock); + return S_OK; + } + return MF_E_NO_CLOCK; + } return E_NOTIMPL; }
@@ -808,6 +854,54 @@ static HRESULT WINAPI test_media_sink_Shutdown(IMFMediaSink *iface) return S_OK; }
+enum object_state +{ + SOURCE_START, + SOURCE_PAUSE, + SOURCE_STOP, + SOURCE_SHUTDOWN, + SOURCE_REQUEST_SAMPLE, + SINK_ON_CLOCK_START, + SINK_ON_CLOCK_PAUSE, + SINK_ON_CLOCK_STOP, + SINK_ON_CLOCK_RESTART, + SINK_ON_CLOCK_SETRATE, + SINK_FLUSH, + SINK_PROCESS_SAMPLE, + MFT_BEGIN, + MFT_START, + MFT_FLUSH, + MFT_PROCESS_INPUT, + MFT_PROCESS_OUTPUT, +}; + +#define MAX_OBJECT_STATE 1024 + +struct object_state_record +{ + enum object_state states[MAX_OBJECT_STATE]; + unsigned int state_count; +}; +static struct object_state_record actual_object_state_record; + +#define add_object_state(a, b) _add_object_state(__LINE__, a, b) +static void _add_object_state(int line, struct object_state_record *record, enum object_state state) +{ + ok_(__FILE__, line)(record->state_count < MAX_OBJECT_STATE, "exceeded state_count maximum %d.\n", MAX_OBJECT_STATE); + if (record->state_count < MAX_OBJECT_STATE) + record->states[record->state_count++] = state; +} + +#define compare_object_states(a, b) _compare_object_states(__LINE__, a, b) +static void _compare_object_states(int line, const struct object_state_record *r1, + const struct object_state_record *r2) +{ + ok_(__FILE__, line)(r1->state_count == r2->state_count, "State count not equal.\n"); + if (r1->state_count == r2->state_count) + ok_(__FILE__, line)(!memcmp(r1->states, r2->states, sizeof(enum object_state) * r1->state_count), "Got different states.\n"); +} + + static const IMFMediaSinkVtbl test_media_sink_vtbl = { test_media_sink_QueryInterface, @@ -835,6 +929,8 @@ struct test_stream_sink
IMFAttributes *attributes; IUnknown *device_manager; + + IMFMediaEventQueue *event_queue; };
static struct test_stream_sink *impl_from_IMFStreamSink(IMFStreamSink *iface) @@ -889,6 +985,11 @@ static HRESULT WINAPI test_stream_sink_GetEvent(IMFStreamSink *iface, DWORD flag
static HRESULT WINAPI test_stream_sink_BeginGetEvent(IMFStreamSink *iface, IMFAsyncCallback *callback, IUnknown *state) { + struct test_stream_sink *sink = impl_from_IMFStreamSink(iface); + + if (sink->event_queue) + return IMFMediaEventQueue_BeginGetEvent(sink->event_queue, callback, state); + ok(0, "Unexpected call.\n"); return E_NOTIMPL; } @@ -896,6 +997,11 @@ static HRESULT WINAPI test_stream_sink_BeginGetEvent(IMFStreamSink *iface, IMFAs static HRESULT WINAPI test_stream_sink_EndGetEvent(IMFStreamSink *iface, IMFAsyncResult *result, IMFMediaEvent **event) { + struct test_stream_sink *sink = impl_from_IMFStreamSink(iface); + + if (sink->event_queue) + return IMFMediaEventQueue_EndGetEvent(sink->event_queue, result, event); + ok(0, "Unexpected call.\n"); return E_NOTIMPL; } @@ -903,6 +1009,11 @@ static HRESULT WINAPI test_stream_sink_EndGetEvent(IMFStreamSink *iface, IMFAsyn static HRESULT WINAPI test_stream_sink_QueueEvent(IMFStreamSink *iface, MediaEventType event_type, REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) { + struct test_stream_sink *sink = impl_from_IMFStreamSink(iface); + + if (sink->event_queue) + return IMFMediaEventQueue_QueueEventParamVar(sink->event_queue, event_type, ext_type, hr, value); + ok(0, "Unexpected call.\n"); return E_NOTIMPL; } @@ -942,10 +1053,22 @@ static HRESULT WINAPI test_stream_sink_GetMediaTypeHandler(IMFStreamSink *iface, return E_NOTIMPL; }
+DEFINE_EXPECT(test_stream_sink_ProcessSample); +DEFINE_EXPECT(test_stream_sink_Flush); + static HRESULT WINAPI test_stream_sink_ProcessSample(IMFStreamSink *iface, IMFSample *sample) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + HRESULT hr; + + if (expect_test_stream_sink_ProcessSample) + hr = S_OK; + else + hr = E_NOTIMPL; + + CHECK_EXPECT(test_stream_sink_ProcessSample); + add_object_state(&actual_object_state_record, SINK_PROCESS_SAMPLE); + + return hr; }
static HRESULT WINAPI test_stream_sink_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type, @@ -957,8 +1080,17 @@ static HRESULT WINAPI test_stream_sink_PlaceMarker(IMFStreamSink *iface, MFSTREA
static HRESULT WINAPI test_stream_sink_Flush(IMFStreamSink *iface) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + HRESULT hr; + + if (expect_test_stream_sink_Flush) + hr = S_OK; + else + hr = E_NOTIMPL; + + CHECK_EXPECT(test_stream_sink_Flush); + add_object_state(&actual_object_state_record, SINK_FLUSH); + + return hr; }
static const IMFStreamSinkVtbl test_stream_sink_vtbl = @@ -1273,45 +1405,6 @@ static IMFMediaSource *create_media_source(const WCHAR *name, const WCHAR *mime) return source; }
-enum object_state -{ - SOURCE_START, - SOURCE_PAUSE, - SOURCE_STOP, - SOURCE_SHUTDOWN, - SINK_ON_CLOCK_START, - SINK_ON_CLOCK_PAUSE, - SINK_ON_CLOCK_STOP, - SINK_ON_CLOCK_RESTART, - SINK_ON_CLOCK_SETRATE, -}; - -#define MAX_OBJECT_STATE 1024 - -struct object_state_record -{ - enum object_state states[MAX_OBJECT_STATE]; - unsigned int state_count; -}; -static struct object_state_record actual_object_state_record; - -#define add_object_state(a, b) _add_object_state(__LINE__, a, b) -static void _add_object_state(int line, struct object_state_record *record, enum object_state state) -{ - ok_(__FILE__, line)(record->state_count < MAX_OBJECT_STATE, "exceeded state_count maximum %d.\n", MAX_OBJECT_STATE); - if (record->state_count < MAX_OBJECT_STATE) - record->states[record->state_count++] = state; -} - -#define compare_object_states(a, b) _compare_object_states(__LINE__, a, b) -static void _compare_object_states(int line, const struct object_state_record *r1, - const struct object_state_record *r2) -{ - ok_(__FILE__, line)(r1->state_count == r2->state_count, "State count not equal.\n"); - if (r1->state_count == r2->state_count) - ok_(__FILE__, line)(!memcmp(r1->states, r2->states, sizeof(enum object_state) * r1->state_count), "Got different states.\n"); -} - enum source_state { SOURCE_STOPPED, @@ -1328,6 +1421,9 @@ struct test_media_stream LONGLONG sample_duration; LONGLONG sample_time; BOOL is_new; + BOOL test_expect; + BOOL delay_sample; + IMFSample *delayed_sample; LONG refcount; };
@@ -1367,6 +1463,8 @@ static ULONG WINAPI test_media_stream_Release(IMFMediaStream *iface)
if (!refcount) { + if (stream->delayed_sample) + IMFSample_Release(stream->delayed_sample); IMFMediaEventQueue_Release(stream->event_queue); free(stream); } @@ -1419,6 +1517,8 @@ static HRESULT WINAPI test_media_stream_GetStreamDescriptor(IMFMediaStream *ifac return S_OK; }
+DEFINE_EXPECT(test_media_stream_RequestSample); + static HRESULT WINAPI test_media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) { struct test_media_stream *stream = impl_from_IMFMediaStream(iface); @@ -1426,6 +1526,20 @@ static HRESULT WINAPI test_media_stream_RequestSample(IMFMediaStream *iface, IUn IMFSample *sample; HRESULT hr;
+ if (stream->test_expect) + { + if (expect_test_media_stream_RequestSample) + hr = S_OK; + else + hr = E_NOTIMPL; + + CHECK_EXPECT(test_media_stream_RequestSample); + add_object_state(&actual_object_state_record, SOURCE_REQUEST_SAMPLE); + + if (FAILED(hr)) + return hr; + } + hr = MFCreateSample(&sample); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (stream->sample_duration) @@ -1457,10 +1571,18 @@ static HRESULT WINAPI test_media_stream_RequestSample(IMFMediaStream *iface, IUn ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); IMFMediaBuffer_Release(buffer);
- hr = IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, - (IUnknown *)sample); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFSample_Release(sample); + if (stream->delay_sample) + { + if (stream->delayed_sample) IMFSample_Release(stream->delayed_sample); + stream->delayed_sample = sample; + } + else + { + hr = IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, + (IUnknown *)sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFSample_Release(sample); + }
return S_OK; } @@ -1662,10 +1784,6 @@ static HRESULT WINAPI test_seek_source_Start(IMFMediaSource *iface, IMFPresentat
EnterCriticalSection(&source->cs);
- event_type = source->state != SOURCE_STOPPED ? MESourceSeeked : MESourceStarted; - hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - for (i = 0; i < source->stream_count; ++i) { if (!is_stream_selected(pd, i)) @@ -1678,12 +1796,16 @@ static HRESULT WINAPI test_seek_source_Start(IMFMediaSource *iface, IMFPresentat hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, &var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- event_type = source->state != SOURCE_STOPPED ? MEStreamSeeked : MEStreamStarted; + event_type = start_position->vt == VT_I8 ? MEStreamSeeked : MEStreamStarted; hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, - S_OK, NULL); + S_OK, start_position); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); }
+ event_type = start_position->vt == VT_I8 ? MESourceSeeked : MESourceStarted; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, start_position); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + source->state = SOURCE_RUNNING;
LeaveCriticalSection(&source->cs); @@ -1735,10 +1857,6 @@ static HRESULT WINAPI test_seek_source_Pause(IMFMediaSource *iface)
EnterCriticalSection(&source->cs);
- event_type = MESourcePaused; - hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - for (i = 0; i < source->stream_count; ++i) { if (!is_stream_selected(source->pd, i)) @@ -1750,6 +1868,10 @@ static HRESULT WINAPI test_seek_source_Pause(IMFMediaSource *iface) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); }
+ event_type = MESourcePaused; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + source->state = SOURCE_PAUSED; LeaveCriticalSection(&source->cs);
@@ -3832,10 +3954,10 @@ static void test_sample_grabber_is_mediatype_supported(void) IMFSampleGrabberSinkCallback_Release(grabber_callback); }
-/* create a test topology with the specified source and sink, and return duration if required */ -static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *sink_activate, UINT64 *duration) +/* create a test topology with the specified source, sink, and option MFT. Return duration if required */ +static IMFTopology *create_test_topology_unk(IMFMediaSource *source, IUnknown *sink, IUnknown *mft, UINT64 *duration) { - IMFTopologyNode *src_node, *sink_node; + IMFTopologyNode *src_node, *sink_node, *mft_node; IMFPresentationDescriptor *pd; IMFTopology *topology = NULL; IMFStreamDescriptor *sd; @@ -3852,8 +3974,24 @@ static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *si ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFTopology_AddNode(topology, src_node); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (mft) + { + hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mft_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, mft_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(src_node, 0, mft_node, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(mft_node, 0, sink_node, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetObject(mft_node, mft); + ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); + } + else + { + hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); @@ -3865,7 +4003,7 @@ static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *si ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); } init_source_node(source, -1, src_node, pd, sd); - hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)sink_activate); + hr = IMFTopologyNode_SetObject(sink_node, sink); ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); hr = IMFTopologyNode_SetUINT32(sink_node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_ALLOW_DECODER); ok(hr == S_OK, "Failed to set connect method, hr %#lx.\n", hr); @@ -3879,6 +4017,11 @@ static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *si return topology; }
+static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *sink_activate, UINT64 *duration) +{ + return create_test_topology_unk(source, (IUnknown*)sink_activate, NULL, duration); +} + static void test_sample_grabber_orientation(GUID subtype) { media_type_desc video_rgb32_desc = @@ -6941,6 +7084,644 @@ static void test_media_session_Close(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); }
+struct test_transform +{ + IMFTransform IMFTransform_iface; + LONG refcount; + + const MFT_OUTPUT_STREAM_INFO *output_stream_info; + + UINT input_count; + IMFMediaType **input_types; + IMFMediaType *input_type; + + UINT output_count; + IMFMediaType **output_types; + IMFMediaType *output_type; + + IMFSample *output; +}; + +static struct test_transform *test_transform_from_IMFTransform(IMFTransform *iface) +{ + return CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); +} + +static HRESULT WINAPI test_transform_QueryInterface(IMFTransform *iface, REFIID iid, void **out) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + + if (IsEqualGUID(iid, &IID_IUnknown) + || IsEqualGUID(iid, &IID_IMFTransform)) + { + IMFTransform_AddRef(&transform->IMFTransform_iface); + *out = &transform->IMFTransform_iface; + return S_OK; + } + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_transform_AddRef(IMFTransform *iface) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + ULONG refcount = InterlockedIncrement(&transform->refcount); + return refcount; +} + +static ULONG WINAPI test_transform_Release(IMFTransform *iface) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + ULONG refcount = InterlockedDecrement(&transform->refcount); + + if (!refcount) + { + if (transform->input_type) + IMFMediaType_Release(transform->input_type); + if (transform->output_type) + IMFMediaType_Release(transform->output_type); + free(transform); + } + + return refcount; +} + +static HRESULT WINAPI test_transform_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, + DWORD *input_maximum, DWORD *output_minimum, DWORD *output_maximum) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) +{ + *inputs = *outputs = 1; + return S_OK; +} + +static HRESULT WINAPI test_transform_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, + DWORD output_size, DWORD *outputs) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) +{ + ok(0, "Unexpected %s call.\n", __func__); + return E_NOTIMPL; +} + +static void test_transform_set_output_stream_info(IMFTransform *iface, const MFT_OUTPUT_STREAM_INFO *info) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + transform->output_stream_info = info; +} + +static HRESULT WINAPI test_transform_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + + ok(!!transform->output_stream_info, "Unexpected %s iface %p call.\n", __func__, iface); + if (!transform->output_stream_info) + return E_NOTIMPL; + + *info = *transform->output_stream_info; + return S_OK; +} + +static HRESULT WINAPI test_transform_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetInputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_DeleteInputStream(IMFTransform *iface, DWORD id) +{ + ok(0, "Unexpected %s call.\n", __func__); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +{ + ok(0, "Unexpected %s call.\n", __func__); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + + if (index >= transform->input_count) + { + *type = NULL; + return MF_E_NO_MORE_TYPES; + } + + *type = transform->input_types[index]; + IMFMediaType_AddRef(*type); + return S_OK; +} + +static HRESULT WINAPI test_transform_GetOutputAvailableType(IMFTransform *iface, DWORD id, + DWORD index, IMFMediaType **type) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + + if (index >= transform->output_count) + { + *type = NULL; + return MF_E_NO_MORE_TYPES; + } + + *type = transform->output_types[index]; + IMFMediaType_AddRef(*type); + return S_OK; +} + +static HRESULT WINAPI test_transform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + if (transform->input_type) + IMFMediaType_Release(transform->input_type); + if ((transform->input_type = type)) + IMFMediaType_AddRef(transform->input_type); + return S_OK; +} + +static HRESULT WINAPI test_transform_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + if (transform->output_type) + IMFMediaType_Release(transform->output_type); + if ((transform->output_type = type)) + IMFMediaType_AddRef(transform->output_type); + return S_OK; +} + +static HRESULT WINAPI test_transform_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + if (!(*type = transform->input_type)) + return MF_E_TRANSFORM_TYPE_NOT_SET; + IMFMediaType_AddRef(*type); + return S_OK; +} + +static HRESULT WINAPI test_transform_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + if (!(*type = transform->output_type)) + return MF_E_TRANSFORM_TYPE_NOT_SET; + IMFMediaType_AddRef(*type); + return S_OK; +} + +static HRESULT WINAPI test_transform_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) +{ + ok(0, "Unexpected %s call.\n", __func__); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetOutputStatus(IMFTransform *iface, DWORD *flags) +{ + ok(0, "Unexpected %s call.\n", __func__); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) +{ + ok(0, "Unexpected %s call.\n", __func__); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) +{ + ok(0, "Unexpected %s call.\n", __func__); + return E_NOTIMPL; +} + +DEFINE_EXPECT(test_transform_ProcessMessage_BEGIN_STREAMING); +DEFINE_EXPECT(test_transform_ProcessMessage_START_OF_STREAM); +DEFINE_EXPECT(test_transform_ProcessMessage_FLUSH); + +static HRESULT WINAPI test_transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) +{ + switch (message) + { + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: + CHECK_EXPECT(test_transform_ProcessMessage_BEGIN_STREAMING); + add_object_state(&actual_object_state_record, MFT_BEGIN); + return S_OK; + + case MFT_MESSAGE_NOTIFY_START_OF_STREAM: + todo_wine_if(!expect_test_transform_ProcessMessage_START_OF_STREAM) + CHECK_EXPECT(test_transform_ProcessMessage_START_OF_STREAM); + add_object_state(&actual_object_state_record, MFT_START); + return S_OK; + + case MFT_MESSAGE_COMMAND_FLUSH: + CHECK_EXPECT(test_transform_ProcessMessage_FLUSH); + add_object_state(&actual_object_state_record, MFT_FLUSH); + return S_OK; + + default: + ok(0, "Unexpected %s call %#x.\n", __func__, message); + return E_NOTIMPL; + } +} + +DEFINE_EXPECT(test_transform_ProcessInput); +DEFINE_EXPECT(test_transform_ProcessOutput); + +static HRESULT WINAPI test_transform_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + HRESULT hr; + + if (expect_test_transform_ProcessInput) + { + if (transform->output) + { + hr = MF_E_NOTACCEPTING; + } + else + { + IMFSample_AddRef(transform->output = sample); + hr = S_OK; + } + } + else + { + hr = E_NOTIMPL; + } + + CHECK_EXPECT(test_transform_ProcessInput); + add_object_state(&actual_object_state_record, MFT_PROCESS_INPUT); + + return hr; +} + +static HRESULT WINAPI test_transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, + MFT_OUTPUT_DATA_BUFFER *data, DWORD *status) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + HRESULT hr; + + if (expect_test_transform_ProcessOutput) + { + if (transform->output) + { + *status = 0; + data->pSample = transform->output; + transform->output = NULL; + hr = S_OK; + } + else + { + hr = MF_E_TRANSFORM_NEED_MORE_INPUT; + } + } + else + { + hr = E_NOTIMPL; + } + + CHECK_EXPECT2(test_transform_ProcessOutput); + add_object_state(&actual_object_state_record, MFT_PROCESS_OUTPUT); + + return hr; +} + +static const IMFTransformVtbl test_transform_vtbl = +{ + test_transform_QueryInterface, + test_transform_AddRef, + test_transform_Release, + test_transform_GetStreamLimits, + test_transform_GetStreamCount, + test_transform_GetStreamIDs, + test_transform_GetInputStreamInfo, + test_transform_GetOutputStreamInfo, + test_transform_GetAttributes, + test_transform_GetInputStreamAttributes, + test_transform_GetOutputStreamAttributes, + test_transform_DeleteInputStream, + test_transform_AddInputStreams, + test_transform_GetInputAvailableType, + test_transform_GetOutputAvailableType, + test_transform_SetInputType, + test_transform_SetOutputType, + test_transform_GetInputCurrentType, + test_transform_GetOutputCurrentType, + test_transform_GetInputStatus, + test_transform_GetOutputStatus, + test_transform_SetOutputBounds, + test_transform_ProcessEvent, + test_transform_ProcessMessage, + test_transform_ProcessInput, + test_transform_ProcessOutput, +}; + +static HRESULT WINAPI test_transform_create(UINT input_count, IMFMediaType **input_types, + UINT output_count, IMFMediaType **output_types, BOOL d3d_aware, IMFTransform **out) +{ + struct test_transform *transform; + + if (!(transform = calloc(1, sizeof(*transform)))) + return E_OUTOFMEMORY; + transform->IMFTransform_iface.lpVtbl = &test_transform_vtbl; + transform->refcount = 1; + + transform->input_count = input_count; + transform->input_types = input_types; + transform->input_type = input_types[0]; + IMFMediaType_AddRef(transform->input_type); + transform->output_count = output_count; + transform->output_types = output_types; + transform->output_type = output_types[0]; + IMFMediaType_AddRef(transform->output_type); + + *out = &transform->IMFTransform_iface; + return S_OK; +} + +static void test_media_session_seek(void) +{ + static const struct object_state_record expected_start_state_records = {{SOURCE_START, MFT_START, SINK_ON_CLOCK_START}, 3}; + static const struct object_state_record expected_sample_request_and_delivery_records = {{MFT_PROCESS_OUTPUT, SOURCE_REQUEST_SAMPLE, MFT_PROCESS_INPUT, MFT_PROCESS_OUTPUT, SINK_PROCESS_SAMPLE}, 5}; + static const struct object_state_record expected_sample_request_only_records = {{MFT_PROCESS_OUTPUT, SOURCE_REQUEST_SAMPLE}, 2}; + static const struct object_state_record expected_paused_state_records = {{SINK_ON_CLOCK_PAUSE, SOURCE_PAUSE}, 2}; + static const struct object_state_record expected_seek_start_no_pending_request_records = {{SOURCE_STOP, MFT_FLUSH, SOURCE_START, SINK_FLUSH, SINK_ON_CLOCK_START}, 5}; + static const struct object_state_record expected_seek_start_pending_request_records = {{SOURCE_STOP, MFT_FLUSH, SOURCE_START, MFT_PROCESS_OUTPUT, SOURCE_REQUEST_SAMPLE, SINK_FLUSH, SINK_ON_CLOCK_START}, 7}; + + IMFClockStateSink test_seek_clock_sink = {&test_seek_clock_sink_vtbl}; + struct test_stream_sink stream_sink = test_stream_sink; + struct test_media_sink media_sink = test_media_sink; + MFT_OUTPUT_STREAM_INFO output_stream_info = {0}; + struct test_handler handler = test_handler; + IMFPresentationClock *presentation_clock; + struct test_seek_source *media_source; + struct test_callback *test_callback; + IMFAsyncCallback *callback; + IMFMediaSession *session; + IMFMediaSource *source; + IMFTopology *topology; + PROPVARIANT propvar; + IMFMediaType *type; + IMFTransform *mft; + IMFClock *clock; + UINT32 status; + HRESULT hr; + INT i; + + stream_sink.handler = &handler.IMFMediaTypeHandler_iface; + stream_sink.media_sink = &media_sink.IMFMediaSink_iface; + media_sink.stream = &stream_sink.IMFStreamSink_iface; + MFCreateEventQueue(&stream_sink.event_queue); + + 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_seek_source(TRUE); + media_source = impl_test_seek_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); + + SET_EXPECT(test_transform_ProcessMessage_BEGIN_STREAMING); + topology = create_test_topology_unk(source, (IUnknown*)&stream_sink.IMFStreamSink_iface, (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); + PropVariantInit(&propvar); + 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); + + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + SET_EXPECT(test_media_sink_GetPresentationClock); + SET_EXPECT(test_media_sink_SetPresentationClock); + SET_EXPECT(test_media_sink_GetStreamSinkCount); + SET_EXPECT(test_transform_ProcessMessage_START_OF_STREAM); + hr = IMFMediaSession_Start(session, NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + 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_STARTED_SOURCE, "Unexpected status %d.\n", status); + PropVariantClear(&propvar); + + hr = IMFStreamSink_QueueEvent(&stream_sink.IMFStreamSink_iface, MEStreamSinkStarted, &GUID_NULL, S_OK, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + Sleep(20); + + todo_wine + CHECK_CALLED(test_media_sink_GetPresentationClock); + CHECK_CALLED(test_media_sink_SetPresentationClock); + todo_wine + CHECK_CALLED(test_media_sink_GetStreamSinkCount); + CHECK_CALLED(test_transform_ProcessMessage_START_OF_STREAM); + + compare_object_states(&actual_object_state_record, &expected_start_state_records); + + /* Test a sample request with sample delivery, then pause and then start with a seek */ + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + SET_EXPECT(test_media_stream_RequestSample); + SET_EXPECT(test_transform_ProcessOutput); + SET_EXPECT(test_transform_ProcessInput); + SET_EXPECT(test_stream_sink_ProcessSample); + IMFStreamSink_QueueEvent(&stream_sink.IMFStreamSink_iface, MEStreamSinkRequestSample, &GUID_NULL, S_OK, &propvar); + + Sleep(20); + + CHECK_CALLED(test_media_stream_RequestSample); + CHECK_CALLED(test_transform_ProcessOutput); + CHECK_CALLED(test_transform_ProcessInput); + CHECK_CALLED(test_stream_sink_ProcessSample); + + compare_object_states(&actual_object_state_record, &expected_sample_request_and_delivery_records); + + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + hr = IMFMediaSession_Pause(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + Sleep(20); + + hr = IMFStreamSink_QueueEvent(&stream_sink.IMFStreamSink_iface, MEStreamSinkPaused, &GUID_NULL, S_OK, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = wait_media_event(session, callback, MESessionPaused, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + compare_object_states(&actual_object_state_record, &expected_paused_state_records); + + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + SET_EXPECT(test_media_sink_GetPresentationClock); + SET_EXPECT(test_media_sink_GetStreamSinkCount); + SET_EXPECT(test_stream_sink_Flush); + SET_EXPECT(test_transform_ProcessMessage_FLUSH); + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + + Sleep(20); + + hr = IMFStreamSink_QueueEvent(&stream_sink.IMFStreamSink_iface, MEStreamSinkStarted, &GUID_NULL, S_OK, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + todo_wine + CHECK_CALLED(test_media_sink_GetPresentationClock); + todo_wine + CHECK_CALLED(test_media_sink_GetStreamSinkCount); + todo_wine + CHECK_CALLED(test_stream_sink_Flush); + todo_wine + CHECK_CALLED(test_transform_ProcessMessage_FLUSH); + + todo_wine + compare_object_states(&actual_object_state_record, &expected_seek_start_no_pending_request_records); + + /* Test a sample request only (i.e. with no sample delivery), then pause and then start with a seek */ + for (i = 0; i < media_source->stream_count; i++) + media_source->streams[i]->delay_sample = TRUE; + + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + SET_EXPECT(test_media_stream_RequestSample); + SET_EXPECT(test_transform_ProcessOutput); + IMFStreamSink_QueueEvent(&stream_sink.IMFStreamSink_iface, MEStreamSinkRequestSample, &GUID_NULL, S_OK, &propvar); + + Sleep(20); + + CHECK_CALLED(test_media_stream_RequestSample); + CHECK_CALLED(test_transform_ProcessOutput); + + compare_object_states(&actual_object_state_record, &expected_sample_request_only_records); + + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + hr = IMFMediaSession_Pause(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + Sleep(20); + + hr = IMFStreamSink_QueueEvent(&stream_sink.IMFStreamSink_iface, MEStreamSinkPaused, &GUID_NULL, S_OK, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = wait_media_event(session, callback, MESessionPaused, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + compare_object_states(&actual_object_state_record, &expected_paused_state_records); + + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + SET_EXPECT(test_media_sink_GetPresentationClock); + SET_EXPECT(test_media_sink_GetStreamSinkCount); + SET_EXPECT(test_stream_sink_Flush); + SET_EXPECT(test_transform_ProcessMessage_FLUSH); + SET_EXPECT(test_transform_ProcessOutput); + SET_EXPECT(test_media_stream_RequestSample); + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + + Sleep(20); + + hr = IMFStreamSink_QueueEvent(&stream_sink.IMFStreamSink_iface, MEStreamSinkStarted, &GUID_NULL, S_OK, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + todo_wine + CHECK_CALLED(test_media_sink_GetPresentationClock); + todo_wine + CHECK_CALLED(test_media_sink_GetStreamSinkCount); + todo_wine + CHECK_CALLED(test_stream_sink_Flush); + todo_wine + CHECK_CALLED(test_transform_ProcessMessage_FLUSH); + todo_wine + CHECK_CALLED(test_transform_ProcessOutput); + todo_wine + CHECK_CALLED(test_media_stream_RequestSample); + + todo_wine + compare_object_states(&actual_object_state_record, &expected_seek_start_pending_request_records); + + IMFPresentationClock_RemoveClockStateSink(presentation_clock, &test_seek_clock_sink); + IMFPresentationClock_Release(presentation_clock); + IMFAsyncCallback_Release(callback); + IMFTransform_Release(mft); + + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFMediaSession_Release(session); + + IMFMediaEventQueue_Release(stream_sink.event_queue); + if (media_sink.clock) IMFPresentationClock_Release(media_sink.clock); + + hr = MFShutdown(); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} + START_TEST(mf) { init_functions(); @@ -6977,4 +7758,5 @@ START_TEST(mf) test_MFEnumDeviceSources(); test_media_session_Close(); test_media_session_source_shutdown(); + test_media_session_seek(); }
From: Brendan McGrath bmcgrath@codeweavers.com
The order should be: 1. Stop sources; 2. Flush MFTs; 3. Start sources; 4. Request output down the chain of sink inputs; 6. Flush sinks; and 7. Start the clock
This takes place whether we pause before we seek or seek without pause. --- dlls/mf/session.c | 147 ++++++++++++++++++++++++++++++--------------- dlls/mf/tests/mf.c | 7 --- 2 files changed, 99 insertions(+), 55 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 483ea6f904f..a10655fc57a 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -222,6 +222,7 @@ enum presentation_flags SESSION_FLAG_END_OF_PRESENTATION = 0x10, SESSION_FLAG_PENDING_RATE_CHANGE = 0x20, SESSION_FLAG_PENDING_COMMAND = 0x40, + SESSION_FLAG_RESTARTING = 0x80, };
struct media_session @@ -1006,6 +1007,42 @@ static HRESULT session_subscribe_sources(struct media_session *session) return hr; }
+static void session_flush_transforms(struct media_session *session) +{ + struct topo_node *node; + UINT i; + + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->type == MF_TOPOLOGY_TRANSFORM_NODE) + { + IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_COMMAND_FLUSH, 0); + for (i = 0; i < node->u.transform.output_count; i++) + node->u.transform.outputs[i].requests = 0; /* these requests might have been flushed */ + } + } +} + +static void session_request_sample(struct media_session *session, IMFStreamSink *sink_stream); + +static void session_flush_sinks(struct media_session *session) +{ + struct topo_node *node; + + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->type == MF_TOPOLOGY_OUTPUT_NODE) + { + if (node->u.sink.requests) + { + node->u.sink.requests--; + session_request_sample(session, node->object.sink_stream); + } + IMFStreamSink_Flush(node->object.sink_stream); + } + } +} + static void session_flush_nodes(struct media_session *session) { struct topo_node *node; @@ -1071,18 +1108,50 @@ static void session_start(struct media_session *session, const GUID *time_format
switch (session->state) { - case SESSION_STATE_STOPPED: + case SESSION_STATE_PAUSED: + case SESSION_STATE_STARTED: + if (!IsEqualGUID(time_format, &GUID_NULL) || start_position->vt != VT_EMPTY) + { + /* We are seeking to a new position, check for invalid positions */ + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) + { + hr = IMFPresentationDescriptor_GetUINT64(source->pd, &MF_PD_DURATION, (UINT64 *)&duration); + if (SUCCEEDED(hr) && IsEqualGUID(time_format, &GUID_NULL) + && start_position->vt == VT_I8 && start_position->hVal.QuadPart > duration) + { + WARN("Start position %s out of range, hr %#lx.\n", wine_dbgstr_longlong(start_position->hVal.QuadPart), hr); + session_command_complete_with_event(session, MESessionStarted, MF_E_INVALID_POSITION, NULL); + return; + } + }
+ /* Stop sources */ + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) + { + if (FAILED(hr = IMFMediaSource_Stop(source->source))) + { + WARN("Failed to stop media source %p, hr %#lx.\n", source->source, hr); + session_command_complete_with_event(session, MESessionStarted, hr, NULL); + return; + } + } + + session->presentation.time_format = *time_format; + session->presentation.start_position.vt = VT_EMPTY; + PropVariantCopy(&session->presentation.start_position, start_position); + + /* SESSION_STATE_STARTED -> SESSION_STATE_RESTARTING_SOURCES -> SESSION_STATE_STARTED */ + session->state = SESSION_STATE_RESTARTING_SOURCES; + break; + } + /* fallthrough; we're resuming from the current position */ + case SESSION_STATE_STOPPED: /* Start request with no current topology. */ if (session->presentation.topo_status == MF_TOPOSTATUS_INVALID) { session_command_complete_with_event(session, MESessionStarted, MF_E_INVALIDREQUEST, NULL); break; } - - /* fallthrough */ - case SESSION_STATE_PAUSED: - session->presentation.time_format = *time_format; session->presentation.start_position.vt = VT_EMPTY; PropVariantCopy(&session->presentation.start_position, start_position); @@ -1117,38 +1186,7 @@ static void session_start(struct media_session *session, const GUID *time_format
session->state = SESSION_STATE_STARTING_SOURCES; break; - case SESSION_STATE_STARTED: - /* Check for invalid positions */ - LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) - { - hr = IMFPresentationDescriptor_GetUINT64(source->pd, &MF_PD_DURATION, (UINT64 *)&duration); - if (SUCCEEDED(hr) && IsEqualGUID(time_format, &GUID_NULL) - && start_position->vt == VT_I8 && start_position->hVal.QuadPart > duration) - { - WARN("Start position %s out of range, hr %#lx.\n", wine_dbgstr_longlong(start_position->hVal.QuadPart), hr); - session_command_complete_with_event(session, MESessionStarted, MF_E_INVALID_POSITION, NULL); - return; - } - } - - /* Stop sources */ - LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) - { - if (FAILED(hr = IMFMediaSource_Stop(source->source))) - { - WARN("Failed to stop media source %p, hr %#lx.\n", source->source, hr); - session_command_complete_with_event(session, MESessionStarted, hr, NULL); - return; - } - } - - session->presentation.time_format = *time_format; - session->presentation.start_position.vt = VT_EMPTY; - PropVariantCopy(&session->presentation.start_position, start_position);
- /* SESSION_STATE_STARTED -> SESSION_STATE_RESTARTING_SOURCES -> SESSION_STATE_STARTED */ - session->state = SESSION_STATE_RESTARTING_SOURCES; - break; default: session_command_complete_with_event(session, MESessionStarted, MF_E_INVALIDREQUEST, NULL); break; @@ -3245,15 +3283,6 @@ static void session_set_source_object_state(struct media_session *session, IUnkn
session_set_presentation_clock(session);
- /* If sinks are already started, start session immediately. This can happen when doing a - * seek from SESSION_STATE_STARTED */ - if (session_is_output_nodes_state(session, OBJ_STATE_STARTED) - && SUCCEEDED(session_start_clock(session))) - { - session_set_started(session); - return; - } - if ((session->presentation.flags & SESSION_FLAG_NEEDS_PREROLL) && session_is_output_nodes_state(session, OBJ_STATE_STOPPED)) { MFTIME preroll_time = 0; @@ -3288,15 +3317,37 @@ static void session_set_source_object_state(struct media_session *session, IUnkn } session->state = SESSION_STATE_PREROLLING_SINKS; } - else if (SUCCEEDED(session_start_clock(session))) - session->state = SESSION_STATE_STARTING_SINKS; + else + { + if (session->presentation.flags & SESSION_FLAG_RESTARTING) + { + session->presentation.flags &= ~SESSION_FLAG_RESTARTING; + session_flush_sinks(session); + } + + if (SUCCEEDED(hr = session_start_clock(session))) + { + /* If sinks are already started, start session immediately. This can happen when doing a + * seek from SESSION_STATE_STARTED (i.e. a seek without pause/stop) */ + if (session_is_output_nodes_state(session, OBJ_STATE_STARTED)) + session_set_started(session); + else + session->state = SESSION_STATE_STARTING_SINKS; + } + else + { + WARN("Failed to start session clock %p, hr %#lx.\n", session, hr); + session_command_complete_with_event(session, MESessionStarted, hr, NULL); + } + }
break; case SESSION_STATE_RESTARTING_SOURCES: if (!session_is_source_nodes_state(session, OBJ_STATE_STOPPED)) break;
- session_flush_nodes(session); + session->presentation.flags |= SESSION_FLAG_RESTARTING; + session_flush_transforms(session);
/* Start sources */ LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index b1605a9ae1e..3d69e5640cf 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -6692,7 +6692,6 @@ static void test_media_session_Start(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = wait_media_event(session, callback, MESessionStarted, 5000, &propvar); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine_if(initial_state == SOURCE_PAUSED) compare_object_states(&actual_object_state_record, &expected_object_state_records[initial_state]);
hr = IMFMediaSession_Stop(session); @@ -7630,9 +7629,7 @@ static void test_media_session_seek(void) CHECK_CALLED(test_media_sink_GetPresentationClock); todo_wine CHECK_CALLED(test_media_sink_GetStreamSinkCount); - todo_wine CHECK_CALLED(test_stream_sink_Flush); - todo_wine CHECK_CALLED(test_transform_ProcessMessage_FLUSH);
todo_wine @@ -7693,13 +7690,9 @@ static void test_media_session_seek(void) CHECK_CALLED(test_media_sink_GetPresentationClock); todo_wine CHECK_CALLED(test_media_sink_GetStreamSinkCount); - todo_wine CHECK_CALLED(test_stream_sink_Flush); - todo_wine CHECK_CALLED(test_transform_ProcessMessage_FLUSH); - todo_wine CHECK_CALLED(test_transform_ProcessOutput); - todo_wine CHECK_CALLED(test_media_stream_RequestSample);
todo_wine
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mfmediaengine/video_frame_sink.c | 3 --- 1 file changed, 3 deletions(-)
diff --git a/dlls/mfmediaengine/video_frame_sink.c b/dlls/mfmediaengine/video_frame_sink.c index 23d8e19e71e..867bedf893f 100644 --- a/dlls/mfmediaengine/video_frame_sink.c +++ b/dlls/mfmediaengine/video_frame_sink.c @@ -1013,10 +1013,7 @@ static HRESULT video_frame_sink_set_state(struct video_frame_sink *sink, enum si }
if (state == SINK_STATE_RUNNING && sink->state != SINK_STATE_RUNNING) - { - video_frame_sink_sample_queue_flush(sink); video_frame_sink_stream_request_sample(sink); - }
if (state != sink->state || state != SINK_STATE_PAUSED) {
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mf/session.c | 18 ++++++++++++------ dlls/mf/tests/mf.c | 3 --- 2 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index a10655fc57a..c86012c5056 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1483,7 +1483,7 @@ static void session_set_consumed_clock(IUnknown *object, IMFPresentationClock *c } }
-static void session_set_presentation_clock(struct media_session *session) +static void session_set_presentation_clock(struct media_session *session, BOOL seeking) { IMFPresentationTimeSource *time_source = NULL; struct media_source *source; @@ -1491,10 +1491,13 @@ static void session_set_presentation_clock(struct media_session *session) struct topo_node *node; HRESULT hr;
- LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + if (!seeking) { - if (node->type == MF_TOPOLOGY_TRANSFORM_NODE) - IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0); + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->type == MF_TOPOLOGY_TRANSFORM_NODE) + IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0); + } }
if (!(session->presentation.flags & SESSION_FLAG_PRESENTATION_CLOCK_SET)) @@ -1608,7 +1611,7 @@ static void session_complete_rate_change(struct media_session *session) return;
session->presentation.flags &= ~SESSION_FLAG_PENDING_RATE_CHANGE; - session_set_presentation_clock(session); + session_set_presentation_clock(session, FALSE);
hr = IMFRateControl_SetRate(session->clock_rate_control, session->presentation.thin, session->presentation.rate); @@ -3239,6 +3242,7 @@ static void session_set_source_object_state(struct media_session *session, IUnkn BOOL changed = FALSE; DWORD i, count; HRESULT hr; + BOOL seeking = FALSE;
if ((state = session_get_object_state_for_event(event_type)) == OBJ_STATE_INVALID) return; @@ -3246,6 +3250,7 @@ static void session_set_source_object_state(struct media_session *session, IUnkn switch (event_type) { case MESourceSeeked: + seeking = TRUE; case MESourceStarted: case MESourcePaused: case MESourceStopped: @@ -3261,6 +3266,7 @@ static void session_set_source_object_state(struct media_session *session, IUnkn } break; case MEStreamSeeked: + seeking = TRUE; case MEStreamStarted: case MEStreamPaused: case MEStreamStopped: @@ -3281,7 +3287,7 @@ static void session_set_source_object_state(struct media_session *session, IUnkn
session_set_topo_status(session, S_OK, MF_TOPOSTATUS_STARTED_SOURCE);
- session_set_presentation_clock(session); + session_set_presentation_clock(session, seeking);
if ((session->presentation.flags & SESSION_FLAG_NEEDS_PREROLL) && session_is_output_nodes_state(session, OBJ_STATE_STOPPED)) { diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 3d69e5640cf..420faf88349 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -7328,7 +7328,6 @@ static HRESULT WINAPI test_transform_ProcessMessage(IMFTransform *iface, MFT_MES return S_OK;
case MFT_MESSAGE_NOTIFY_START_OF_STREAM: - todo_wine_if(!expect_test_transform_ProcessMessage_START_OF_STREAM) CHECK_EXPECT(test_transform_ProcessMessage_START_OF_STREAM); add_object_state(&actual_object_state_record, MFT_START); return S_OK; @@ -7632,7 +7631,6 @@ static void test_media_session_seek(void) CHECK_CALLED(test_stream_sink_Flush); CHECK_CALLED(test_transform_ProcessMessage_FLUSH);
- todo_wine compare_object_states(&actual_object_state_record, &expected_seek_start_no_pending_request_records);
/* Test a sample request only (i.e. with no sample delivery), then pause and then start with a seek */ @@ -7695,7 +7693,6 @@ static void test_media_session_seek(void) CHECK_CALLED(test_transform_ProcessOutput); CHECK_CALLED(test_media_stream_RequestSample);
- todo_wine compare_object_states(&actual_object_state_record, &expected_seek_start_pending_request_records);
IMFPresentationClock_RemoveClockStateSink(presentation_clock, &test_seek_clock_sink);
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mfmediaengine/video_frame_sink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/mfmediaengine/video_frame_sink.c b/dlls/mfmediaengine/video_frame_sink.c index 867bedf893f..3e377bbc2bb 100644 --- a/dlls/mfmediaengine/video_frame_sink.c +++ b/dlls/mfmediaengine/video_frame_sink.c @@ -1012,7 +1012,7 @@ static HRESULT video_frame_sink_set_state(struct video_frame_sink *sink, enum si video_frame_sink_set_flag(sink, FLAGS_FIRST_FRAME, FALSE); }
- if (state == SINK_STATE_RUNNING && sink->state != SINK_STATE_RUNNING) + if (state == SINK_STATE_RUNNING && (sink->state != SINK_STATE_RUNNING || offset != PRESENTATION_CURRENT_POSITION)) video_frame_sink_stream_request_sample(sink);
if (state != sink->state || state != SINK_STATE_PAUSED)
On Sun May 4 23:21:00 2025 +0000, Brendan McGrath wrote:
This doesn't look right. Why would we pretend that sink requested
anything? I don't see why this would be necessary or different comparing to a regular start sequence. I agree, and my original fix made it the sinks responsibility to request a new sample. But in my testing on Windows, I couldn't see the sink ever make a request for a new sample after the flush. So my assumption was that the design on Windows was that it was the client's responsibility to request a new sample (i.e. if you call flush, you also provide the next sample). But maybe my test was wrong. I'll see if I can write one that I can include in this MR (and I'll then fix the implementation if necessary).
helpers names are not great
Agree actually. I used restart as they were called from the `SESSION_STATE_RESTARTING_SOURCES` path. But I shouldn't be naming functions based on where they are called from. I'll rename them to `session_flush_sinks` and `session_flush_transforms`.
I've just pushed a number of changes (which I've described in the MR description). But I found that the sink won't request a new sample if it has a request outstanding. Even after it is flushed and started in a new position. Under this circumstance, it is the clients responsibility to honor that outstanding request. But if no request is outstanding, then it does fall to the sink to make a new request. I've modified my MR to reflect those findings.