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; 5. Refactored `if` statement in `session_start` to be a `switch` again
-- v7: 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 1070e9c1b2d..d531b55e065 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; @@ -4407,21 +4428,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; @@ -4643,6 +4670,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); @@ -4684,6 +4712,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 d531b55e065..3ecd12db1b8 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; } @@ -1699,10 +1821,6 @@ static HRESULT WINAPI test_source_Start(IMFMediaSource *iface, IMFPresentationDe
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)) @@ -1715,12 +1833,16 @@ static HRESULT WINAPI test_source_Start(IMFMediaSource *iface, IMFPresentationDe 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); @@ -1772,10 +1894,6 @@ static HRESULT WINAPI test_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)) @@ -1787,6 +1905,10 @@ static HRESULT WINAPI test_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);
@@ -4018,10 +4140,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; @@ -4038,8 +4160,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); @@ -4051,7 +4189,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); @@ -4065,6 +4203,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 = @@ -7269,6 +7412,644 @@ static void test_media_session_thinning(void) ok(hr == S_OK, "Shutdown failure, 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_callback *test_callback; + struct test_source *media_source; + 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_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); + + 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(); @@ -7306,4 +8087,5 @@ START_TEST(mf) test_media_session_Close(); test_media_session_source_shutdown(); test_media_session_thinning(); + 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 | 146 +++++++++++++++++++++++++++++++-------------- dlls/mf/tests/mf.c | 7 --- 2 files changed, 100 insertions(+), 53 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 2e95d763984..55b66b8c4f5 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -234,6 +234,7 @@ enum presentation_flags SESSION_FLAG_NEEDS_PREROLL = 0x8, SESSION_FLAG_SOURCE_SHUTDOWN = 0x10, SESSION_FLAG_PENDING_RATE_CHANGE = 0x20, + SESSION_FLAG_RESTARTING = 0x40, };
struct media_session @@ -1014,6 +1015,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; @@ -1045,8 +1082,45 @@ 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) + { + session->command_state = COMMAND_STATE_RESTARTING_SOURCES; + + /* 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); + if (hr == MF_E_SHUTDOWN) + return session_handle_source_shutdown(session); + return session_command_complete_with_event(session, MESessionStarted, hr, NULL); + } + } + + session->presentation.time_format = *time_format; + session->presentation.start_position.vt = VT_EMPTY; + PropVariantCopy(&session->presentation.start_position, start_position); + + 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) { @@ -1054,8 +1128,6 @@ static void session_start(struct media_session *session, const GUID *time_format break; }
- /* fallthrough */ - case SESSION_STATE_PAUSED: session->command_state = COMMAND_STATE_STARTING_SOURCES;
session->presentation.time_format = *time_format; @@ -1092,38 +1164,7 @@ static void session_start(struct media_session *session, const GUID *time_format } } break; - case SESSION_STATE_STARTED: - session->command_state = COMMAND_STATE_RESTARTING_SOURCES; - - /* 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); - if (hr == MF_E_SHUTDOWN) - return session_handle_source_shutdown(session); - return session_command_complete_with_event(session, MESessionStarted, hr, NULL); - } - }
- session->presentation.time_format = *time_format; - session->presentation.start_position.vt = VT_EMPTY; - PropVariantCopy(&session->presentation.start_position, start_position); - break; case SESSION_STATE_CLOSED: case SESSION_STATE_SHUT_DOWN: session_command_complete_with_event(session, MESessionStarted, MF_E_INVALIDREQUEST, NULL); @@ -3158,15 +3199,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; @@ -3201,17 +3233,39 @@ static void session_set_source_object_state(struct media_session *session, IUnkn } session->command_state = COMMAND_STATE_PREROLLING_SINKS; } - else if (SUCCEEDED(session_start_clock(session))) - session->command_state = COMMAND_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->command_state = COMMAND_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 COMMAND_STATE_RESTARTING_SOURCES: if (!session_is_source_nodes_state(session, OBJ_STATE_STOPPED)) break;
- session_flush_nodes(session); session->state = SESSION_STATE_STOPPED; session->command_state = COMMAND_STATE_STARTING_SOURCES; + 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 3ecd12db1b8..8550157073b 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -6878,7 +6878,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); @@ -7958,9 +7957,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 @@ -8021,13 +8018,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/mf/session.c | 16 ++++++++++------ dlls/mf/tests/mf.c | 3 --- 2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 55b66b8c4f5..87af5b95eab 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1466,12 +1466,6 @@ 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 (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)) { /* Attempt to get time source from the sinks. */ @@ -3152,6 +3146,7 @@ static void session_set_source_object_state(struct media_session *session, IUnkn struct media_source *src; struct media_sink *sink; enum object_state state; + struct topo_node *node; BOOL changed = FALSE; DWORD i, count; HRESULT hr; @@ -3197,6 +3192,15 @@ static void session_set_source_object_state(struct media_session *session, IUnkn
session_set_topo_status(session, S_OK, MF_TOPOSTATUS_STARTED_SOURCE);
+ if (event_type == MESourceStarted || event_type == MEStreamStarted) + { + 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); + } + } + session_set_presentation_clock(session);
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 8550157073b..a02f2859f91 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -7656,7 +7656,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; @@ -7960,7 +7959,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 */ @@ -8023,7 +8021,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
This mirrors Windows behaviour, in that a flush only takes place when it is explicitly requested. --- 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 9fb81854130..8a4734fe772 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/mfmediaengine/video_frame_sink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dlls/mfmediaengine/video_frame_sink.c b/dlls/mfmediaengine/video_frame_sink.c index 8a4734fe772..f710466fed4 100644 --- a/dlls/mfmediaengine/video_frame_sink.c +++ b/dlls/mfmediaengine/video_frame_sink.c @@ -1012,7 +1012,8 @@ 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_STOPPED || sink->state == SINK_STATE_PAUSED || + (sink->state == SINK_STATE_RUNNING && offset != PRESENTATION_CURRENT_POSITION))) video_frame_sink_stream_request_sample(sink);
if (state != sink->state || state != SINK_STATE_PAUSED)
Maybe we should move this somewhere else then
Good point. I've moved this in to `session_set_source_object_state`.
does first Start() with non-zero position qualify as seeking, does it produce NOTIFY_START_OF_STREAM
I believe it should. The documentation for `MFT_MESSAGE_NOTIFY_START_OF_STREAM` states:
Notifies a Media Foundation transform (MFT) that the first sample is about to be processed
So it should be notified on the first start no matter the starting position.
In the current version, I have it so it'll be called when `event_type == MESourceStarted || event_type == MEStreamStarted`. And our `IMFMediaSource` implementations currently always send `MESourceStarted` if the source state was stopped.
Note that I only added the commit with this change to fix a failing test, but more tests could probably be added. Although that would be separate MR.
@nsivov Please let me know if you think this one is now ready. Thanks.
This merge request was approved by Nikolay Sivov.