This fixes failure to play the prologue video in Planet of the Apes: Last Frontier, which unpauses by calling Start() with a location. The exact state leading to this issue does not occur in the Start() tests, and it's not clear how to reproduce it reliably.
-- v4: mf: Reset transform nodes when seeking. mf: Initialise the grabber sample count when setting state after a seek. mf: Reset transform node outputs when seeking. mf: Drop transform node input events when unpausing at a specific position.
From: Conor McCarthy cmccarthy@codeweavers.com
--- dlls/mf/session.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 483ea6f904f..e595cadd185 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1061,13 +1061,29 @@ static void session_handle_start_error(struct media_session *session, HRESULT hr session_command_complete_with_event(session, MESessionStarted, hr, NULL); }
+static void session_reset_transforms(struct media_session *session) +{ + struct topo_node *topo_node; + UINT i; + + LIST_FOR_EACH_ENTRY(topo_node, &session->presentation.nodes, struct topo_node, entry) + { + if (topo_node->type != MF_TOPOLOGY_TRANSFORM_NODE) + continue; + + for (i = 0; i < topo_node->u.transform.input_count; i++) + { + struct transform_stream *stream = &topo_node->u.transform.inputs[i]; + stream->draining = FALSE; + } + } +} + static void session_start(struct media_session *session, const GUID *time_format, const PROPVARIANT *start_position) { struct media_source *source; - struct topo_node *topo_node; MFTIME duration; HRESULT hr; - UINT i;
switch (session->state) { @@ -1103,17 +1119,7 @@ static void session_start(struct media_session *session, const GUID *time_format } }
- LIST_FOR_EACH_ENTRY(topo_node, &session->presentation.nodes, struct topo_node, entry) - { - if (topo_node->type == MF_TOPOLOGY_TRANSFORM_NODE) - { - for (i = 0; i < topo_node->u.transform.input_count; i++) - { - struct transform_stream *stream = &topo_node->u.transform.inputs[i]; - stream->draining = FALSE; - } - } - } + session_reset_transforms(session);
session->state = SESSION_STATE_STARTING_SOURCES; break;
From: Conor McCarthy cmccarthy@codeweavers.com
The start position is empty when starting at the current position from the paused state, but if it is not empty, this is a seek operation. Behaviour in Windows is to send MFT_MESSAGE_COMMAND_FLUSH before calling Start() on the source. Other reset actions should probably be done then also. --- dlls/mf/session.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index e595cadd185..0103d7a65df 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1061,7 +1061,7 @@ static void session_handle_start_error(struct media_session *session, HRESULT hr session_command_complete_with_event(session, MESessionStarted, hr, NULL); }
-static void session_reset_transforms(struct media_session *session) +static void session_reset_transforms(struct media_session *session, BOOL drop) { struct topo_node *topo_node; UINT i; @@ -1075,6 +1075,8 @@ static void session_reset_transforms(struct media_session *session) { struct transform_stream *stream = &topo_node->u.transform.inputs[i]; stream->draining = FALSE; + if (drop) + transform_stream_drop_events(stream); } } } @@ -1082,6 +1084,7 @@ static void session_reset_transforms(struct media_session *session) static void session_start(struct media_session *session, const GUID *time_format, const PROPVARIANT *start_position) { struct media_source *source; + BOOL unpause_seek; MFTIME duration; HRESULT hr;
@@ -1109,6 +1112,11 @@ static void session_start(struct media_session *session, const GUID *time_format return; }
+ unpause_seek = start_position->vt == VT_I8; + if (unpause_seek) + session_flush_nodes(session); + session_reset_transforms(session, unpause_seek); + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) { if (FAILED(hr = IMFMediaSource_Start(source->source, source->pd, &GUID_NULL, start_position))) @@ -1119,8 +1127,6 @@ static void session_start(struct media_session *session, const GUID *time_format } }
- session_reset_transforms(session); - session->state = SESSION_STATE_STARTING_SOURCES; break; case SESSION_STATE_STARTED:
From: Conor McCarthy cmccarthy@codeweavers.com
--- dlls/mf/session.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 0103d7a65df..dc6bbec2b21 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1078,6 +1078,16 @@ static void session_reset_transforms(struct media_session *session, BOOL drop) if (drop) transform_stream_drop_events(stream); } + + if (!drop) + continue; + + for (i = 0; i < topo_node->u.transform.output_count; ++i) + { + struct transform_stream *stream = &topo_node->u.transform.outputs[i]; + transform_stream_drop_events(stream); + stream->requests = 0; + } } }
From: Conor McCarthy cmccarthy@codeweavers.com
If sample_count is zero in this case, we end up with no samples being requested. --- dlls/mf/samplegrabber.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/dlls/mf/samplegrabber.c b/dlls/mf/samplegrabber.c index de599139736..4d54afc1d31 100644 --- a/dlls/mf/samplegrabber.c +++ b/dlls/mf/samplegrabber.c @@ -1195,6 +1195,10 @@ static HRESULT sample_grabber_set_state(struct sample_grabber *grabber, enum sin
if (state == SINK_STATE_RUNNING && grabber->state != SINK_STATE_RUNNING) { + /* Unpause at a position is a seek operation which drops everything pending. */ + if (grabber->state == SINK_STATE_PAUSED && offset != PRESENTATION_CURRENT_POSITION) + grabber->sample_count = MAX_SAMPLE_QUEUE_LENGTH; + /* Every transition to running state sends a bunch requests to build up initial queue. */ for (i = 0; i < grabber->sample_count; ++i) {
From: Conor McCarthy cmccarthy@codeweavers.com
--- dlls/mf/session.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index dc6bbec2b21..e11528369e2 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1164,6 +1164,8 @@ static void session_start(struct media_session *session, const GUID *time_format } }
+ session_reset_transforms(session, TRUE); + session->presentation.time_format = *time_format; session->presentation.start_position.vt = VT_EMPTY; PropVariantCopy(&session->presentation.start_position, start_position);
On Thu Mar 20 06:46:14 2025 +0000, Nikolay Sivov wrote:
I tested in Windows by swapping the vtbl in a sample copier transform, and in the media source. Windows sends `MFT_MESSAGE_COMMAND_FLUSH` after a seeking unpause (before calling `Start()` on the source), but not after unpause at the current position, which is in line with our expectations. Wine sends `MFT_MESSAGE_NOTIFY_START_OF_STREAM` after every start, but Windows only sends it once, before calling `Start()`. That would be a separate MR though. Windows also sends `MFT_MESSAGE_NOTIFY_BEGIN_STREAMING`, which is not necessary according to spec, but we might do that too in case something depends on it.
I changed this MR to use `session_flush_nodes()` when unpausing with a seek. Flush messaging now matches that in Windows.