 
            H.264 uses a 16-pixel alignment, and the stream sink media type should have the aligned height after the session has started.
-- v14: mf/tests: Test H.264 decoder alignment. mf/tests: Test H.264 sink media type height alignment. mf/tests: Fix occasional hangs in test_media_session_source_shutdown(). mf/tests: Skip the scheme resolver tests if network connectivity is unavailable.
 
            From: Conor McCarthy cmccarthy@codeweavers.com
--- dlls/mf/tests/mf.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index c56e59fbc6c..d4f8e806447 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -43,6 +43,9 @@ #include "devpkey.h" #include "evr9.h"
+#include "initguid.h" +#include "netlistmgr.h" + #define DEFINE_EXPECT(func) \ static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
@@ -5828,6 +5831,8 @@ static void test_scheme_resolvers(void) L"http://test.winehq.org", };
+ INetworkListManager *network_list_manager; + NLM_CONNECTIVITY connectivity; IMFSourceResolver *resolver; IMFByteStream *byte_stream; IMFAttributes *attributes; @@ -5838,6 +5843,20 @@ static void test_scheme_resolvers(void) DWORD i, caps; HRESULT hr;
+ hr = CoInitialize(NULL); + ok(hr == S_OK, "Failed to initialize, hr %#lx\n", hr); + hr = CoCreateInstance(&CLSID_NetworkListManager, NULL, CLSCTX_INPROC_SERVER, &IID_INetworkListManager, (void **)&network_list_manager); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = INetworkListManager_GetConnectivity(network_list_manager, &connectivity); + ok(hr == S_OK, "got hr %#lx\n", hr); + INetworkListManager_Release(network_list_manager); + CoUninitialize(); + if (connectivity == NLM_CONNECTIVITY_DISCONNECTED) + { + skip("Internet connection unavailable, skipping scheme resolver tests.\n"); + return; + } + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); ok(hr == S_OK, "got hr %#lx\n", hr);
 
            From: Conor McCarthy cmccarthy@codeweavers.com
--- dlls/mf/tests/mf.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index d4f8e806447..c11c4ce26f2 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -7011,25 +7011,26 @@ static void test_media_session_source_shutdown(void) hr = IMFMediaSession_Pause(session); IMFMediaSource_Shutdown(source); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionPaused, 1000, &propvar); + hr = wait_media_event_until_blocking(session, callback, MESessionPaused, 1000, &propvar); /* Windows has not been observed to emit PAUSEWHILESTOPPED here, but this could - * be a matter of async command timing, and this error is not exactly wrong. */ - ok(hr == S_OK || hr == MF_E_SESSION_PAUSEWHILESTOPPED || hr == MF_E_SHUTDOWN, + * be a matter of async command timing, and this error is not exactly wrong. + * Windows occasionally never sends MESessionPaused here; ditto for Stopped and Closed. */ + ok(hr == S_OK || hr == MF_E_SESSION_PAUSEWHILESTOPPED || hr == WAIT_TIMEOUT || hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); break; case TEST_STOP: hr = IMFMediaSession_Stop(session); IMFMediaSource_Shutdown(source); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionStopped, 1000, &propvar); - ok(hr == S_OK || hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event_until_blocking(session, callback, MESessionStopped, 1000, &propvar); + ok(hr == S_OK || hr == WAIT_TIMEOUT || hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); break; case TEST_CLOSE: hr = IMFMediaSession_Close(session); IMFMediaSource_Shutdown(source); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = wait_media_event(session, callback, MESessionClosed, 1000, &propvar); - ok(hr == S_OK || hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event_until_blocking(session, callback, MESessionClosed, 1000, &propvar); + ok(hr == S_OK || hr == WAIT_TIMEOUT || hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); default: break; }
 
            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 | 195 +++++++++++++++++++++++++++++++ dlls/mf/tests/resource.rc | 9 ++ dlls/mf/tests/test-unaligned.mp4 | Bin 0 -> 3988 bytes 3 files changed, 204 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 c11c4ce26f2..aaf4c5eae58 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -6603,6 +6603,200 @@ static void test_MFCreateSequencerSegmentOffset(void) PropVariantClear(&propvar); }
+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 get_current_media_type_frame_size(a) get_current_media_type_frame_size_(__LINE__, (IUnknown *)a) +static UINT64 get_current_media_type_frame_size_(int line, IUnknown *unknown) +{ + IMFMediaTypeHandler *handler; + IMFMediaType *output_type; + IMFTransform *transform; + UINT64 frame_size; + HRESULT hr; + + /* Wine does not currently use a transform. */ + if (!unknown) + return 0; + + if (SUCCEEDED(IUnknown_QueryInterface(unknown, &IID_IMFTransform, (void **)&transform))) + { + 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); + IMFMediaType_Release(output_type); + IMFTransform_Release(transform); + return frame_size; + } + + hr = IUnknown_QueryInterface(unknown, &IID_IMFMediaTypeHandler, (void **)&handler); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", 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); + IMFMediaType_Release(output_type); + IMFMediaTypeHandler_Release(handler); + return frame_size; +} + +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; + IMFMediaSession *session; + IMFMediaSink *media_sink; + IMFTransform *transform; + IMFMediaSource *source; + PROPVARIANT propvar; + UINT64 frame_size; + UINT32 status; + HRESULT hr; + + 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()); + + 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); + + frame_size = get_current_media_type_frame_size(transform); + todo_wine + ok(frame_size == (((UINT64)64 << 32) | 72), "Unexpected frame size %#llx\n", frame_size); + frame_size = get_current_media_type_frame_size(handler); + ok(frame_size == (((UINT64)64 << 32) | 72), "Unexpected frame size %#llx\n", frame_size); + + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* frame size change occurs before MESessionStarted */ + hr = wait_media_event(session, callback, MESessionStarted, 5000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + frame_size = get_current_media_type_frame_size(transform); + todo_wine + ok(frame_size == (((UINT64)64 << 32) | 80), "Unexpected frame size %#llx\n", frame_size); + frame_size = get_current_media_type_frame_size(handler); + todo_wine + ok(frame_size == (((UINT64)64 << 32) | 80), "Unexpected frame size %#llx\n", frame_size); + + 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); +} + static void test_media_session_Start(void) { static const struct object_state_record expected_object_state_records[] = @@ -8107,6 +8301,7 @@ START_TEST(mf) test_MFRequireProtectedEnvironment(); test_mpeg4_media_sink(); test_MFCreateSequencerSegmentOffset(); + test_h264_output_alignment(); test_media_session_Start(); test_MFEnumDeviceSources(); test_media_session_Close(); 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 8a80275b8d5..e6ff4a0836d 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) + { + ok(!!output_type, "Stream change not received.\n"); + 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); + + 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[] = @@ -11154,6 +11265,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();
 
            On Mon Oct 20 14:50:25 2025 +0000, Nikolay Sivov wrote:
Bigger problem is that existing tests are already unstable. One thing is that running offline breaks in test_scheme_resolvers(), on Windows, that's easy to skip. But then I get sporadic failures in test_media_session_source_shutdown() when test is waiting forever, even if tests don't hang or crash I get different number of executed tests each time I run the test. I can understand if something async occasionally works differently, but executed test numbers are fluctuating in order of thousands. I think we need to make this stable first, before adding more. Latest Windows 11 release gives even worse results.
I added some fixes for the failures, but in more-or-less current Windows 11 I see failures/hangs in test_media_session_events(). I'm not sure what's happening there, but it looks like they just decided to change the behaviour. Maybe the failing tests can be skipped after the first failure.
The vast majority of variation in the number of executed tests is due to the number of calls to test_media_stream_RequestSample() during test_media_session_Start(), in both Wine and Windows. This looks like normal session engine behaviour.

