The Microsoft WMV decoder has a bug/feature where orientation of the first output type determines the orientation for all subsequent RGB output types.
If the transform is created new, then the orientation is determined by the value of the MF_MT_DEFAULT_STRIDE.
This MR adds tests for that behavior and implements it in Wine.
From: Brendan McGrath brendan@redmandi.com
The Microsoft WMV decoder has a bug/feature where orientation of the first output type determines the orientation for all subsequent RGB output types.
If the transform is created new, then the orientation is determined by the value of the MF_MT_DEFAULT_STRIDE. --- dlls/mf/tests/transform.c | 75 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-)
diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index e828f719ffd..07f967dd801 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -6290,6 +6290,13 @@ static void test_wmv_decoder(void) .todo_time = TRUE, .todo_duration = TRUE, }; const struct sample_desc output_sample_desc_rgb = + { + .attributes = output_sample_attributes, + .sample_time = 0, .sample_duration = 333333, + .buffer_count = 1, .buffers = &output_buffer_desc_rgb, + .todo_duration = TRUE, + }; + const struct sample_desc output_sample_desc_rgb_todo_time = { .attributes = output_sample_attributes, .sample_time = 0, .sample_duration = 333333, @@ -6306,6 +6313,7 @@ static void test_wmv_decoder(void) const struct sample_desc *output_sample_desc; const WCHAR *result_bitmap; ULONG delta; + BOOL new_transform; BOOL todo; } transform_tests[] = @@ -6338,7 +6346,7 @@ static void test_wmv_decoder(void) .expect_output_type_desc = expect_output_type_desc_rgb, .expect_input_info = &expect_input_info_rgb, .expect_output_info = &expect_output_info_rgb, - .output_sample_desc = &output_sample_desc_rgb, + .output_sample_desc = &output_sample_desc_rgb_todo_time, .result_bitmap = L"rgb32frame-flip.bmp", .delta = 5, }, @@ -6349,7 +6357,7 @@ static void test_wmv_decoder(void) .expect_output_type_desc = expect_output_type_desc_rgb_negative_stride, .expect_input_info = &expect_input_info_rgb, .expect_output_info = &expect_output_info_rgb, - .output_sample_desc = &output_sample_desc_rgb, + .output_sample_desc = &output_sample_desc_rgb_todo_time, .result_bitmap = L"rgb32frame-flip.bmp", .delta = 5, }, @@ -6360,11 +6368,60 @@ static void test_wmv_decoder(void) .expect_output_type_desc = expect_output_type_desc_rgb, .expect_input_info = &expect_input_info_rgb, .expect_output_info = &expect_output_info_rgb, + .output_sample_desc = &output_sample_desc_rgb_todo_time, + .result_bitmap = L"rgb32frame-flip.bmp", + .delta = 5, + }, + + { + /* WMV1 -> RGB (w/ new transform) */ + .output_type_desc = output_type_desc_rgb, + .expect_output_type_desc = expect_output_type_desc_rgb, + .expect_input_info = &expect_input_info_rgb, + .expect_output_info = &expect_output_info_rgb, + .output_sample_desc = &output_sample_desc_rgb, + .result_bitmap = L"rgb32frame.bmp", + .delta = 5, + .new_transform = TRUE, + .todo = TRUE, + }, + + { + /* WMV1 -> RGB (negative stride, but reusing MFT w/ positive stride) */ + .output_type_desc = output_type_desc_rgb_negative_stride, + .expect_output_type_desc = expect_output_type_desc_rgb_negative_stride, + .expect_input_info = &expect_input_info_rgb, + .expect_output_info = &expect_output_info_rgb, + .output_sample_desc = &output_sample_desc_rgb_todo_time, + .result_bitmap = L"rgb32frame.bmp", + .delta = 5, + .todo = TRUE, + }, + + { + /* WMV1 -> RGB (negative stride w/ new transform) */ + .output_type_desc = output_type_desc_rgb_negative_stride, + .expect_output_type_desc = expect_output_type_desc_rgb_negative_stride, + .expect_input_info = &expect_input_info_rgb, + .expect_output_info = &expect_output_info_rgb, .output_sample_desc = &output_sample_desc_rgb, .result_bitmap = L"rgb32frame-flip.bmp", .delta = 5, + .new_transform = TRUE, },
+ { + /* WMV1 -> RGB (positive stride w/ new transform) */ + .output_type_desc = output_type_desc_rgb_positive_stride, + .expect_output_type_desc = expect_output_type_desc_rgb, + .expect_input_info = &expect_input_info_rgb, + .expect_output_info = &expect_output_info_rgb, + .output_sample_desc = &output_sample_desc_rgb, + .result_bitmap = L"rgb32frame.bmp", + .delta = 5, + .new_transform = TRUE, + .todo = TRUE, + }, };
MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Video, MFVideoFormat_NV12}; @@ -6470,10 +6527,23 @@ static void test_wmv_decoder(void) ok(hr == MF_E_NO_MORE_TYPES, "GetOutputAvailableType returned %#lx\n", hr); ok(i == ARRAY_SIZE(expect_available_outputs), "%lu input media types\n", i);
+ check_mft_set_output_type(transform, output_type_desc_rgb, S_OK); + for (j = 0; j < ARRAY_SIZE(transform_tests); j++) { winetest_push_context("transform #%lu", j);
+ if (transform_tests[j].new_transform) + { + ret = IMFTransform_Release(transform); + ok(ret == 0, "Release returned %lu\n", ret); + + if (FAILED(hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, + &IID_IMFTransform, (void **)&transform))) + goto failed; + check_mft_set_input_type(transform, input_type_desc, S_OK); + } + check_mft_set_output_type_required(transform, transform_tests[j].output_type_desc); check_mft_set_output_type(transform, transform_tests[j].output_type_desc, S_OK); check_mft_get_output_current_type_(__LINE__, transform, transform_tests[j].expect_output_type_desc, FALSE, TRUE); @@ -6535,6 +6605,7 @@ static void test_wmv_decoder(void)
ret = check_mf_sample_collection(output_samples, transform_tests[j].output_sample_desc, transform_tests[j].result_bitmap); + todo_wine_if(transform_tests[j].todo) ok(ret <= transform_tests[j].delta, "got %lu%% diff\n", ret); IMFCollection_Release(output_samples);
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mf/tests/transform.c | 3 -- dlls/winegstreamer/video_decoder.c | 63 +++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 5 deletions(-)
diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 07f967dd801..b8a78e949d0 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -6383,7 +6383,6 @@ static void test_wmv_decoder(void) .result_bitmap = L"rgb32frame.bmp", .delta = 5, .new_transform = TRUE, - .todo = TRUE, },
{ @@ -6395,7 +6394,6 @@ static void test_wmv_decoder(void) .output_sample_desc = &output_sample_desc_rgb_todo_time, .result_bitmap = L"rgb32frame.bmp", .delta = 5, - .todo = TRUE, },
{ @@ -6420,7 +6418,6 @@ static void test_wmv_decoder(void) .result_bitmap = L"rgb32frame.bmp", .delta = 5, .new_transform = TRUE, - .todo = TRUE, }, };
diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index 83fb713d5da..4086b586d76 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -110,6 +110,8 @@ struct video_decoder
DMO_MEDIA_TYPE dmo_input_type; DMO_MEDIA_TYPE dmo_output_type; + + INT32 previous_stride; };
static inline struct video_decoder *impl_from_IUnknown(IUnknown *iface) @@ -238,8 +240,41 @@ static struct video_decoder *impl_from_IMFTransform(IMFTransform *iface) return CONTAINING_RECORD(iface, struct video_decoder, IMFTransform_iface); }
-static HRESULT normalize_stride(IMFMediaType *media_type, IMFMediaType **ret) +static HRESULT set_previous_stride(struct video_decoder *decoder) +{ + INT32 requested_stride; + LONG default_stride; + UINT64 ratio; + UINT32 width; + GUID subtype; + HRESULT hr; + + if (FAILED(hr = IMFMediaType_GetGUID(decoder->output_type, &MF_MT_SUBTYPE, &subtype))) + return hr; + + if (FAILED(IMFMediaType_GetUINT64(decoder->output_type, &MF_MT_FRAME_SIZE, &ratio))) + ratio = (UINT64)1920 << 32 | 1080; + + width = ratio >> 32; + + if (FAILED(hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &default_stride))) + return hr; + + if (FAILED(IMFMediaType_GetUINT32(decoder->output_type, &MF_MT_DEFAULT_STRIDE, (UINT32*)&requested_stride))) + requested_stride = 0; + + if (default_stride > 0 || requested_stride < 0) + decoder->previous_stride = -abs(default_stride); + else + decoder->previous_stride = abs(default_stride); + + return S_OK; +} + +static HRESULT normalize_stride(struct video_decoder *decoder, IMFMediaType **ret) { + IMFMediaType *media_type = decoder->output_type; + INT32 default_stride, requested_stride; DMO_MEDIA_TYPE amt; HRESULT hr;
@@ -249,6 +284,27 @@ static HRESULT normalize_stride(IMFMediaType *media_type, IMFMediaType **ret) vih->bmiHeader.biHeight = abs(vih->bmiHeader.biHeight); hr = MFCreateMediaTypeFromRepresentation(AM_MEDIA_TYPE_REPRESENTATION, &amt, ret); FreeMediaType(&amt); + + /* Stride must always be positive for YUV formats. Otherwise: + * 1. if this is the first time an output type has been specified, it must either: + * a. match the sign of the requested MF_MT_DEFAULT_STRIDE value; or + * b. default to positive; or + * 2. If an output type was previously specified and input sent it must: + * a. be negative if it was previously a YUV format; or + * b. match the sign of the previously requested MF_MT_DEFAULT_STRIDE attribute + * (defaulting to positive if it had not been provided) + */ + + if (SUCCEEDED(hr = IMFMediaType_GetUINT32(*ret, &MF_MT_DEFAULT_STRIDE, (UINT32*)&default_stride))) + { + if (FAILED(IMFMediaType_GetUINT32(media_type, &MF_MT_DEFAULT_STRIDE, (UINT32*)&requested_stride))) + requested_stride = 0; + + /* default stride less than 0 means an RGB format */ + if (default_stride < 0 && (decoder->previous_stride > 0 + || (!decoder->previous_stride && requested_stride >= 0))) + hr = IMFMediaType_SetUINT32(*ret, &MF_MT_DEFAULT_STRIDE, abs(default_stride)); + } }
return hr; @@ -686,7 +742,7 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF output_type = decoder->output_type; IMFMediaType_AddRef(output_type); } - else if (FAILED(hr = normalize_stride(decoder->output_type, &output_type))) + else if (FAILED(hr = normalize_stride(decoder, &output_type))) { IMFMediaType_Release(decoder->output_type); decoder->output_type = NULL; @@ -818,6 +874,9 @@ static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFS if (decoder->sample_time == -1 && FAILED(IMFSample_GetSampleTime(sample, (LONGLONG *)&decoder->sample_time))) decoder->sample_time = 0;
+ if (!decoder->previous_stride) + set_previous_stride(decoder); + return wg_transform_push_mf(decoder->wg_transform, sample, decoder->wg_sample_queue); }