-- v2: mf: Update the stream sink input type when handling an output node format change.
From: Conor McCarthy cmccarthy@codeweavers.com
--- dlls/mf/session.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index abc77d9f143..a06f106f4dc 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -3765,18 +3765,43 @@ static HRESULT transform_node_handle_format_change(struct media_session *session return transform_stream_update_input_type(topo_node, input, media_type); }
+/* For H.264, the sink's input media type must be set to the aligned frame size + * before MESessionStarted is sent. The decoder sends MF_E_TRANSFORM_STREAM_CHANGE + * to trigger a refresh of its output type once the aligned frame size is known. + * This occurs before any call to ProcessOutput() can succeed on the transform. + * Satisfactory is known to use the sink frame size. */ +static HRESULT sink_node_update_input_type(struct topo_node *topo_node, IMFMediaType *media_type) +{ + IMFMediaTypeHandler *handler; + HRESULT hr; + + if (FAILED(hr = IMFStreamSink_GetMediaTypeHandler(topo_node->object.sink_stream, &handler))) + { + WARN("Failed to get type handler, hr %#lx.\n", hr); + return hr; + } + if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(handler, media_type))) + WARN("Failed to set type, hr %#lx.\n", hr); + IMFMediaTypeHandler_Release(handler); + + return hr; +} + static HRESULT session_handle_format_change(struct media_session *session, struct topo_node *topo_node, UINT input, IMFMediaType *media_type) { - HRESULT hr; + HRESULT hr = S_OK;
switch (topo_node->type) { case MF_TOPOLOGY_OUTPUT_NODE: - if (!topo_node->u.sink.allocator) - return S_OK; - if (SUCCEEDED(hr = IMFVideoSampleAllocator_UninitializeSampleAllocator(topo_node->u.sink.allocator))) - hr = IMFVideoSampleAllocator_InitializeSampleAllocator(topo_node->u.sink.allocator, 4, media_type); + if (topo_node->u.sink.allocator) + { + if (SUCCEEDED(hr = IMFVideoSampleAllocator_UninitializeSampleAllocator(topo_node->u.sink.allocator))) + hr = IMFVideoSampleAllocator_InitializeSampleAllocator(topo_node->u.sink.allocator, 4, media_type); + } + if (SUCCEEDED(hr)) + hr = sink_node_update_input_type(topo_node, media_type); return hr;
case MF_TOPOLOGY_TRANSFORM_NODE:
On Thu Oct 16 14:12:15 2025 +0000, Nikolay Sivov wrote:
The way it works now, if I'm reading this part correctly: MF_E_TRANSFORM_STREAM_CHANGE (happens due to decoder internal logic) -> transform_node_format_changed -> transform_stream_update_output_type + transform_stream_push_format_change -> MEStreamFormatChanged -> session_handle_format_change. Now, session_handle_format_change() then branches depending on destination node type. For output nodes it only cares about re-initializing sample allocator. My point is, shouldn't we amend session_handle_format_change() instead and keep existing flow, instead of fixing up sink types that early?
I moved it to `session_handle_format_change()`.
Nikolay Sivov (@nsivov) commented about dlls/mf/session.c:
+static HRESULT sink_node_update_input_type(struct topo_node *topo_node, IMFMediaType *media_type) +{
- IMFMediaTypeHandler *handler;
- HRESULT hr;
- if (FAILED(hr = IMFStreamSink_GetMediaTypeHandler(topo_node->object.sink_stream, &handler)))
- {
WARN("Failed to get type handler, hr %#lx.\n", hr);return hr;- }
- if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(handler, media_type)))
WARN("Failed to set type, hr %#lx.\n", hr);- IMFMediaTypeHandler_Release(handler);
- return hr;
+}
This could be reduce to 3 lines with topology_node_get_type_handler() - _get_() -> SetCurrentMediaType() -> Release(). It will be short enough to avoid another helper.
Nikolay Sivov (@nsivov) commented about dlls/mf/session.c:
switch (topo_node->type) { case MF_TOPOLOGY_OUTPUT_NODE:
if (!topo_node->u.sink.allocator)return S_OK;if (SUCCEEDED(hr = IMFVideoSampleAllocator_UninitializeSampleAllocator(topo_node->u.sink.allocator)))hr = IMFVideoSampleAllocator_InitializeSampleAllocator(topo_node->u.sink.allocator, 4, media_type);
if (topo_node->u.sink.allocator){if (SUCCEEDED(hr = IMFVideoSampleAllocator_UninitializeSampleAllocator(topo_node->u.sink.allocator)))hr = IMFVideoSampleAllocator_InitializeSampleAllocator(topo_node->u.sink.allocator, 4, media_type);}if (SUCCEEDED(hr))hr = sink_node_update_input_type(topo_node, media_type); return hr;
Would it make sense to first update the type and then reinitialize allocator, if type update succeeded? Allocator is normally our code, while sink could be generally anything.