H.264 uses a 16-pixel alignment, and the stream sink media type should have the aligned height after the session has started.
-- v5: mf/tests: Test H.264 decoder alignment.
From: Conor McCarthy cmccarthy@codeweavers.com
H.264 uses a 16-pixel alignment, and the stream sink media type should have the aligned height after the session has started. --- dlls/mf/tests/mf.c | 223 +++++++++++++++++++++++++++++++ dlls/mf/tests/resource.rc | 9 ++ dlls/mf/tests/test-unaligned.mp4 | Bin 0 -> 3988 bytes 3 files changed, 232 insertions(+) create mode 100644 dlls/mf/tests/test-unaligned.mp4
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index a02f2859f91..80414392c68 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -3039,6 +3039,12 @@ static HRESULT WINAPI test_grabber_callback_OnProcessSample(IMFSampleGrabberSink HRESULT hr; DWORD res;
+ if (grabber->ready_event && !grabber->done_event) + { + SetEvent(grabber->ready_event); + return S_OK; + } + if (!grabber->ready_event) return E_NOTIMPL;
@@ -8040,6 +8046,222 @@ static void test_media_session_seek(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); }
+static IMFTransform *topology_get_transform(IMFTopology *topology) +{ + IMFTransform *transform; + IMFTopologyNode *node; + MF_TOPOLOGY_TYPE type; + HRESULT hr; + WORD count; + UINT i; + + hr = IMFTopology_GetNodeCount(topology, &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + for (i = 0; i < count; ++i) + { + hr = IMFTopology_GetNode(topology, i, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_GetNodeType(node, &type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (type == MF_TOPOLOGY_TRANSFORM_NODE) + { + hr = IMFTopologyNode_GetObject(node, (IUnknown **)&transform); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopologyNode_Release(node); + return transform; + } + IMFTopologyNode_Release(node); + } + + return NULL; +} + +#define check_transform_output_type(a, b, c) check_transform_output_type_(__LINE__, a, b, c) +static void check_transform_output_type_(int line, IMFTransform *transform, UINT expected_w, UINT expected_h) +{ + IMFMediaType *output_type; + UINT64 frame_size; + HRESULT hr; + + if (!transform) + { + skip("Wine does not currently use a transform.\n"); + return; + } + hr = IMFTransform_GetOutputCurrentType(transform, 0, &output_type); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_GetUINT64(output_type, &MF_MT_FRAME_SIZE, &frame_size); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine_if(expected_h > 72) + ok_(__FILE__, line)(frame_size == (((UINT64)expected_w << 32) | expected_h), "Unexpected frame size %#llx\n", frame_size); + IMFMediaType_Release(output_type); +} + +#define check_type_handler_output_type(a, b, c) check_type_handler_output_type_(__LINE__, a, b, c) +static void check_type_handler_output_type_(int line, IMFMediaTypeHandler *handler, UINT expected_w, UINT expected_h) +{ + IMFMediaType *output_type; + UINT64 frame_size; + HRESULT hr; + + hr = IMFMediaTypeHandler_GetCurrentMediaType(handler, &output_type); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_GetUINT64(output_type, &MF_MT_FRAME_SIZE, &frame_size); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine_if(expected_h > 72) + ok_(__FILE__, line)(frame_size == (((UINT64)expected_w << 32) | expected_h), "Unexpected frame size %#llx\n", frame_size); + IMFMediaType_Release(output_type); +} + +static void test_h264_output_alignment(void) +{ + media_type_desc video_nv12_desc = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), + }; + struct test_grabber_callback *grabber_callback; + IMFTopology *topology, *resolved_topology; + struct test_callback *test_callback; + IMFMediaTypeHandler *handler; + IMFActivate *sink_activate; + IMFTopoLoader *topo_loader; + IMFStreamSink *stream_sink; + IMFAsyncCallback *callback; + IMFMediaType *output_type; + MediaEventType event_type; + IMFMediaSession *session; + IMFMediaSink *media_sink; + IMFTransform *transform; + IMFMediaSource *source; + PROPVARIANT propvar; + UINT64 frame_size; + HANDLE objects[2]; + UINT32 status; + HRESULT hr; + DWORD ret; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); + + if (!(source = create_media_source(L"test-unaligned.mp4", L"video/mp4"))) + { + todo_wine /* Gitlab CI Debian runner */ + win_skip("MP4 media source is not supported, skipping tests.\n"); + MFShutdown(); + return; + } + + grabber_callback = impl_from_IMFSampleGrabberSinkCallback(create_test_grabber_callback()); + grabber_callback->ready_event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!grabber_callback->ready_event, "CreateEventW failed, error %lu\n", GetLastError()); + + hr = MFCreateMediaType(&output_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(output_type, video_nv12_desc, -1); + hr = MFCreateSampleGrabberSinkActivate(output_type, &grabber_callback->IMFSampleGrabberSinkCallback_iface, &sink_activate); + ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); + IMFMediaType_Release(output_type); + + IMFActivate_ActivateObject(sink_activate, &IID_IMFMediaSink, (void **)&media_sink); + hr = IMFMediaSink_GetStreamSinkByIndex(media_sink, 0, &stream_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFActivate_Release(sink_activate); + topology = create_test_topology_unk(source, (IUnknown *)stream_sink, NULL, NULL); + hr = MFCreateTopoLoader(&topo_loader); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopoLoader_Load(topo_loader, topology, &resolved_topology, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopology_Release(topology); + IMFTopoLoader_Release(topo_loader); + + hr = IMFStreamSink_GetMediaTypeHandler(stream_sink, &handler); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaTypeHandler_GetCurrentMediaType(handler, &output_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_GetUINT64(output_type, &MF_MT_FRAME_SIZE, &frame_size); + ok(hr == MF_E_ATTRIBUTENOTFOUND, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(output_type); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_SetTopology(session, 0, resolved_topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + transform = topology_get_transform(resolved_topology); + IMFTopology_Release(resolved_topology); + + callback = create_test_callback(TRUE); + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + test_callback = impl_from_IMFAsyncCallback(callback); + hr = wait_media_event(session, callback, MESessionTopologyStatus, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEvent_GetUINT32(test_callback->media_event, &MF_EVENT_TOPOLOGY_STATUS, &status); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(status == MF_TOPOSTATUS_READY, "Unexpected status %d.\n", status); + + check_transform_output_type(transform, 64, 72); + check_type_handler_output_type(handler, 64, 72); + + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_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); + check_transform_output_type(transform, 64, 72); + check_type_handler_output_type(handler, 64, 72); + + /* frame size change occurs before MESessionStarted and delivery of the first sample to the sink */ + objects[0] = test_callback->event; + objects[1] = grabber_callback->ready_event; + do + { + hr = IMFMediaEventGenerator_BeginGetEvent((IMFMediaEventGenerator *)session, + &test_callback->IMFAsyncCallback_iface, (IUnknown *)session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ret = WaitForMultipleObjects(2, objects, FALSE, 5000); + ok(ret == WAIT_OBJECT_0 || ret == WAIT_OBJECT_0 + 1, "WaitForMultipleObjects returned %lu\n", ret); + if (ret == WAIT_OBJECT_0) + { + hr = IMFMediaEvent_GetType(test_callback->media_event, &event_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + } while (ret == WAIT_OBJECT_0 && event_type != MESessionStarted); + + check_transform_output_type(transform, 64, 80); + check_type_handler_output_type(handler, 64, 80); + + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Close(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionClosed, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFMediaTypeHandler_Release(handler); + if (transform) + IMFTransform_Release(transform); + IMFAsyncCallback_Release(callback); + IMFMediaSession_Release(session); + IMFStreamSink_Release(stream_sink); + IMFMediaSink_Release(media_sink); + IMFSampleGrabberSinkCallback_Release(&grabber_callback->IMFSampleGrabberSinkCallback_iface); + IMFMediaSource_Release(source); + + hr = MFShutdown(); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} + START_TEST(mf) { init_functions(); @@ -8078,4 +8300,5 @@ START_TEST(mf) test_media_session_source_shutdown(); test_media_session_thinning(); test_media_session_seek(); + test_h264_output_alignment(); } diff --git a/dlls/mf/tests/resource.rc b/dlls/mf/tests/resource.rc index dff5719dd3b..2a69a842ce8 100644 --- a/dlls/mf/tests/resource.rc +++ b/dlls/mf/tests/resource.rc @@ -185,3 +185,12 @@ rgb24frame.bmp RCDATA rgb24frame.bmp */ /* @makedep: test.mp4 */ test.mp4 RCDATA test.mp4 + +/* Generated with: + * gst-launch-1.0 videotestsrc num-buffers=60 pattern=smpte100 ! \ + * video/x-raw,format=I420,width=72,height=64,framerate=30000/1001 ! \ + * videoflip method=clockwise ! videoconvert ! \ + * x264enc ! mp4mux ! filesink location=dlls/mf/tests/test-unaligned.mp4 + */ +/* @makedep: test-unaligned.mp4 */ +test-unaligned.mp4 RCDATA test-unaligned.mp4 diff --git a/dlls/mf/tests/test-unaligned.mp4 b/dlls/mf/tests/test-unaligned.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..5920da40daaf55dc69a5a02577891c70906fed94 GIT binary patch literal 3988 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 V7(l&F1_lOOsKKVB7|a1O7yw{rbdCT3
literal 0 HcmV?d00001
From: Conor McCarthy cmccarthy@codeweavers.com
The transform returns MF_E_TRANSFORM_STREAM_CHANGE to trigger a change of output type to the aligned frame size. --- dlls/mf/tests/transform.c | 112 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+)
diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index f82dfa1b090..0a144254b0e 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -236,6 +236,25 @@ static void load_resource(const WCHAR *filename, const BYTE **data, DWORD *lengt *length = SizeofResource(GetModuleHandleW(NULL), resource); }
+static IMFMediaType *transform_find_available_output_type(IMFTransform *transform, const GUID *desired) +{ + IMFMediaType *output_type = NULL; + GUID subtype; + HRESULT hr; + UINT i = 0; + + while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, i++, &output_type))) + { + IMFMediaType_GetGUID(output_type, &MF_MT_SUBTYPE, &subtype); + if (IsEqualGUID(&subtype, desired)) + break; + IMFMediaType_Release(output_type); + output_type = NULL; + } + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + return output_type; +} + #define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__) static void _expect_ref(IUnknown* obj, ULONG expected_refcount, int line) { @@ -5568,6 +5587,98 @@ failed: CoUninitialize(); }
+static void test_h264_decoder_alignment(void) +{ + static const DWORD actual_width = 82, actual_height = 84; + static const DWORD aligned_width = 96, aligned_height = 96; + + const struct attribute_desc input_type_desc[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_H264, .required = TRUE), + ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), + {0}, + }; + + IMFSample *input_sample, *output_sample; + const BYTE *h264_encoded_data; + ULONG h264_encoded_data_len; + IMFMediaType *output_type; + IMFTransform *transform; + DWORD output_status; + UINT64 frame_size; + HRESULT hr; + + hr = CoInitialize(NULL); + ok(hr == S_OK, "Failed to initialize, hr %#lx.\n", hr); + + winetest_push_context("h264dec alignment"); + + if (FAILED(hr = CoCreateInstance(&CLSID_MSH264DecoderMFT, NULL, CLSCTX_INPROC_SERVER, + &IID_IMFTransform, (void **)&transform))) + goto failed; + + load_resource(L"h264data.bin", &h264_encoded_data, &h264_encoded_data_len); + + check_mft_set_input_type(transform, input_type_desc, S_OK); + + output_type = transform_find_available_output_type(transform, &MFVideoFormat_NV12); + hr = IMFMediaType_GetUINT64(output_type, &MF_MT_FRAME_SIZE, &frame_size); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(frame_size == (((UINT64)actual_width << 32) | actual_height), "Unexpected frame size %#llx\n", frame_size); + + hr = IMFTransform_SetOutputType(transform, 0, output_type, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(output_type); + + hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + output_sample = create_sample(NULL, aligned_width * aligned_height * 3 / 2); + output_type = NULL; + do + { + MFT_OUTPUT_DATA_BUFFER output = {.pSample = output_sample}; + + hr = IMFTransform_ProcessOutput(transform, 0, 1, &output, &output_status); + ok(hr == S_OK || hr == MF_E_TRANSFORM_STREAM_CHANGE || hr == MF_E_TRANSFORM_NEED_MORE_INPUT, + "ProcessOutput returned %#lx\n", hr); + + if (hr == S_OK) + { + IMFSample_Release(output_sample); + output_sample = create_sample(NULL, aligned_width * aligned_height * 3 / 2); + } + else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT && h264_encoded_data_len > 4) + { + input_sample = next_h264_sample(&h264_encoded_data, &h264_encoded_data_len); + hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); + ok(hr == S_OK, "ProcessInput returned %#lx\n", hr); + IMFSample_Release(input_sample); + } + else if (hr == MF_E_TRANSFORM_STREAM_CHANGE) + { + /* The H.264 decoder sends a format change once the aligned frame size is known. */ + output_type = transform_find_available_output_type(transform, &MFVideoFormat_NV12); + hr = IMFMediaType_GetUINT64(output_type, &MF_MT_FRAME_SIZE, &frame_size); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(frame_size == (((UINT64)aligned_width << 32) | aligned_height), "Unexpected frame size %#llx\n", frame_size); + + hr = IMFTransform_SetOutputType(transform, 0, output_type, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(output_type); + } + } while (hr == S_OK); + ok(!!output_type, "Stream change not received.\n"); + + IMFSample_Release(output_sample); + IMFTransform_Release(transform); + +failed: + winetest_pop_context(); + CoUninitialize(); +} + static void test_h264_decoder_concat_streams(void) { const struct buffer_desc output_buffer_desc[] = @@ -11134,6 +11245,7 @@ START_TEST(transform) test_h264_encoder(); test_h264_decoder(); test_h264_decoder_timestamps(); + test_h264_decoder_alignment(); test_wmv_encoder(); test_wmv_decoder(); test_wmv_decoder_timestamps();
I found the trigger for the output type change. The H.264 decoder returns `MF_E_TRANSFORM_STREAM_CHANGE` to change the format, presumably because the aligned size has become known. GStreamer does this too, so our decoder transform is already compliant. Wine's media source currently decodes the stream, so there is no transform to trigger this change in the session engine, hence the test failure there. After that is fixed, the only remaining issue will be to pass the format change down to the sink.
I added a transform test.
On Mon Sep 8 10:38:52 2025 +0000, Conor McCarthy wrote:
I found the trigger for the output type change. The H.264 decoder returns `MF_E_TRANSFORM_STREAM_CHANGE` to change the format, presumably because the aligned size has become known. GStreamer does this too, so our decoder transform is already compliant. Wine's media source currently decodes the stream, so there is no transform to trigger this change in the session engine, hence the test failure there. After that is fixed, the only remaining issue will be to pass the format change down to the sink. I added a transform test.
We could use MEStreamFormatChanged to report format change at the source.
On Mon Sep 8 10:38:52 2025 +0000, Nikolay Sivov wrote:
We could use MEStreamFormatChanged to report format change at the source.
Wine media source shouldn't decode in the first place, upstream still uses GStreamer-based media source by default but the winedmo based one does the right thing. https://gitlab.winehq.org/wine/wine/-/merge_requests/5988 could perhaps be merged to make them both behave similarly.
I think upstream MF pipelines should be good enough for it to work by default, but we need to be careful and we should probably make sure to upstream any remaining relevant Proton patch before doing so.