Windows uses a smaller alignment than gstreamer for some formats, for example NV12. This means we cannot use MFCalculateImageSize() to get the output sample size. Commit 7b79e3a87b1e switched to calling it instead of GetOutputStreamInfo() to fix some game bugs.
-- v6: winegstreamer: Call wg_format_get_max_size() to get the video decoder output sample size. winegstreamer: Do not pass a sample size to wg_transform_read_mf(). mf/tests: Add a video processor NV12 test with a width alignment of 2. mfplat/tests: Add NV12 650 x 850 to image_size_tests.
From: Conor McCarthy cmccarthy@codeweavers.com
--- dlls/mfplat/tests/mfplat.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 0a2471d3849..21bcc86c8d1 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -5672,6 +5672,7 @@ image_size_tests[] = { &MFVideoFormat_NV12, 3, 2, 12, 9, 192, 9, 64 }, { &MFVideoFormat_NV12, 4, 2, 12, 0, 192, 12, 64 }, { &MFVideoFormat_NV12, 320, 240, 115200, 0, 115200, 115200, 320 }, + { &MFVideoFormat_NV12, 650, 850, 828750, 0, 897600, 828750, 704 },
/* YUV 4:2:0, 12 bpp, planar, half stride (the secondary plane has * the same height, half the width and half the stride of the
From: Conor McCarthy cmccarthy@codeweavers.com
GStreamer has a minimum NV12 alignment of 4. --- dlls/mf/tests/nv12frame-crop-2d.bmp | Bin 0 -> 54582 bytes dlls/mf/tests/nv12frame-crop.bmp | Bin 0 -> 49686 bytes dlls/mf/tests/resource.rc | 8 ++++ dlls/mf/tests/transform.c | 64 ++++++++++++++++++++++++++-- 4 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 dlls/mf/tests/nv12frame-crop-2d.bmp create mode 100644 dlls/mf/tests/nv12frame-crop.bmp
diff --git a/dlls/mf/tests/nv12frame-crop-2d.bmp b/dlls/mf/tests/nv12frame-crop-2d.bmp new file mode 100644 index 0000000000000000000000000000000000000000..133a90514817454166495b4ae57a794db5b257db GIT binary patch literal 54582 zcmZ?rHS1*n12YB&1`P%VhByWWh6Dx%21W)2us8!$9>Rf;uLA@BQ-(wTzYYxj|9bUm z((u`{|6dOVOtJq1|6d6V{J)w+d^RxfKV>-d|JlIM|7TaPCJmoG`~NHw9<Vr|Lby|w zZYfRo2>qk+PefiGHt~&F-d$O}`u}P|`0UyLtC28r9;7Q<Q}h2>B>Y-a^ZzI&B@8V8 zzt*%wsq3)DKT=$S<6Tqp|7s+R90vo*9y)Q48SX^2Iq1;FxPl(n*!W0s4@w6E8Ry7$ zQ!W0{+kbQjccR)ZbSU4hpv5gHehFdZI2cGa;rJiC`EGc}zvch4nwF%s?LmD3q_#aV z?Fyp8kes|shkQpk?UGWjldqOG`G1hb|A>iC(#tz=`wv^4k4KD{yh)ZCq_#aV^~zw$ zLxaa1(D)~z%mcUoNNfWT6F2lwGw8w{y&Xx2PeMG=s;(Or@lPn6(d7q0xP#+!5Y$Vw zssB;OX=qgzT++1s55}uk|Gz{EK7026rNMwTHUD24V0?_kvLDnp1h?(!(7q?sh9El* z2RO_JI?j>p9}@A8qg{yXkCDNqetCBcPdgtx_KQWHP`<<|k0Tz*tsjPMoKw4Q!c*Rn zm;dQgzY|Iq^oxJ$=R30NE^^Ywkd1%*<48E$eB^`?E%e|CclxDW{B6v^o&QJUpVsLF zd;DAeU#)40b6o(=bvq=E-{Wj25NQt1GIR*!J6gvpvF6hy{;AyVgS88Y_2bY{NB?#P zRonkGif34Sqtk;nUTM*mz-J}}<sJ6?kGkFmH0Fbh(c@vD_|%Jk`sX{Gbr-&P#w9mo z;~#(fj!-*;s$qyLZ1IUx5bo4VyZGDpgzOpE{7+f@uRMG9|A1gk&HpQhG`9S|Qq%JP z%GIm?FC(<}7g-)U7D#EkonrmacE-?-gMmy#B*s59-eGNkfvWG(?HzjYj|g{A-Xj{* zu$)^4i(6u7Xjy=>UIwk<qa!9d{8q09mvtnor(w90RBw^%ej@adRQ`{~|G>02h=@m$ z(>B!1K^gx<j{lLAhsepx&@du5Y=?rr5gGp@AYO@0dnoZwOdEj6yh%|TpU5@@v3?x! z>hP3(DD6LJ{Z42<=!j|m(W{)m6F;=B<3@D+4`8^X#4Ekh85Vm`%Kwp;|3~AWwDCV^ z`k=V{r&1dNB@dAkmss+{NNXz$NdBiZ{-N!|iK|!tAH^hu!P&F_Ct|_4*7@QRr>JcY zihF4MQCg0Sn7GFhXV~JApcsz)La4ouE>CH^QA_`jiGOOvAEoAuwD_kqyr`ugN4O7` z^oyRq3Gu0we<?MGVE9oh{wOtPq{Tm|j;8}Ax4t8oZU%XLQQQ55@;kab!F)?7?nm2y zwCT5ySx(_<=i?G5*#4(d{1QrM)XoPqGmp%8Ay~eln@eW7g`U3&@d?EXz2s>YU!;a3 zdOQpipHz24)f0+uba{HkFClwK&L6aj7ic&Rn)FDbd(h*T5T8)I&`W+a{z(}7!;>Zm z#W%V<!TO(2`;SWULp8q*Hg}`PFCjj?@;4!SvRAMEKcev2v;VWHjI)Sb^Ml+MAQUh3 zlBZkTQ#s7Zjzb!x_blj~E}=Y+EKjfaC1lTti~oj(|JhW=Sq%;USEFMBX@XF^P)(jd zJAv$cL~4Gfa+p&)4k<AYYy6|92Q>cKhKBzn;r7n|XF+)B`u}H1!Sss%;Tpf~o&U2z zc<K88**I|4()IsWqhkVX0=l*T38XVh(*20g3yOOXMvr@>@(*kK4=ul=*AFD+eFAYv ziCKd-4nbiK!sy{nh>sL6^s3uNMEnoJaK{?I15gjqrT(W;8Gw=hL3s{@3B@<M`~c*; zVHN+NwjBtQ(zXMsCD6Vj)Xu;fhUCQM0OUK2v^zj<pi2G+#XSgv;*=Cjt@t0Hup96= z1eJ9lj9#7*;v?k|to1*=+W!>R3j-eh1C{QO;*YE{5Xs$C$^R6_Be{Kmz=nqZ1A^N- z{|AEb()IrXNx^5&{tp}s7%ALB3H0^Q`+gukx$#A@K2RM4!q7T~=&&CQVGeQwQn(LD zy2WDuX#CSLPYO)>A4m+w5>K>BFGR(Ed*}bvAPgNRBzg>&!uB1Z_CL{ODKTL_Le+!{ zO!~i?5O$vae>DixD*go~{Xa_vCMqut<#=_T{yz|eNoiw%)Pm|7B5^2zwm$N_Ka5Q% zU&7_dh*MJ1!H|uABIAbW@(tZQ64E?T`5&!(ASI4ysg`E(56W{O49ZKSVDxwxDE?55 zf9L7{&w?<teNU*J0dM~gR5+vCOG3EQEbWr$hC!A8L2(blqw!B%KY_%!#+K&5@egX- zfiSdvhp`?2O&p#tY23btw<B=a1Fs*c5DwVFW+aGFCH`sL&coG)81nH0YGZ&fDQygp zT4=rVn%dyXY9w%$AfUGOuSe}44S#qD5KMk&N7EmbLtr%j!BgMr)q_9%U7|KPhU9%B z{7-G`FOAxd6aj?O-)Q(#IRr-2-)Q=Shrr;^e=DgCmcc#0nwa{J+SacewI3-02&cc% z@TYPJjHbWQ^al@t!Jq#oQXRlOPgMC&b?YaN+K&hU!s%}`{HYxRh|njd{X=crCyu6n zqzD*I|Aayyo7x}?Dfo#P|Dm?^*`xL&MF64LKYMmG{HYuQqv>xn{lP<E@aMlkYJ*U? z=ZR|nQ``E$QTvf1fN=U74Sy<!z-am#O@HtZ82tH9liI)%?s=lhe`;H=Ich&r1Q1St zqv22G5ExB=qv;PG0)s#Q!JSGI24Vlx#9FwaqxKUD0eDc+gh9ymvu9~yE!@yi`w4{r zJg8{GAY}V!{L>`Z;f4|p{{||9_Rgj25pdLgLLorq;BOcWe?;nAx}H$zpFJxuiMr5v z`l$Vg5Eu=ALLori)F&{B)bzJZfVyzGBO>;R$$!+feVM?h{iETJ2mwO5@9bG~CF;Uh zUxe+%q<`w#Zmu+H|7iFlLV$4kKY5S3@YF4Y?Zl*i>e_zt-l+Yf;g1Lb!s$QiJ#}H( zQ-tlrq`w#9`%0I{?YJ9gaG06{~?dM@Nomec4E$b#4EUH){WA_#;AqaQa86r3Gu$ ZenKHYi%>_HNyzrIXAx>?!5aMb0|0jrK}!Gt
literal 0 HcmV?d00001
diff --git a/dlls/mf/tests/nv12frame-crop.bmp b/dlls/mf/tests/nv12frame-crop.bmp new file mode 100644 index 0000000000000000000000000000000000000000..bf1d38c0387bc7e997393fb5d989cc134144bd21 GIT binary patch literal 49686 zcmZ?rHS1*n12YB&1`P%VhByWWh6Dx%21W)2us8!$9>Rf;uNxZvk75eKpuO||mG;j6 ztCy}P36F+31z`?Kv!mfYF!^>g+(*M5l>bHw9!>9~>3yW6b&x+t)B9+82c?6Nf=AQ) zXnG$hX&vOx(eyr~()%TWN&iPN1!3Sk{r@rN=_HNKfW~r0!<>RJ2c_B3a37d_I~wkz z;SS1wBL$D9_tEq|QqnrepQGu0G`)k;!AQZQ>3uZ4kCe0y^5<xJA5!Uk<uZZ)qnLs) zSnl|L*>Xpc$7e>voPsb1rP<MNADDbQ8t$Xv4$6Nc1&^lp(eyr2(mKeWqv?G#y@S%h zNWr7&eKfs~l(Y`==V*E#Qt5r7xzhhpOhFjL`u?97>r3+Z%xIWX5aysXI~wi-lW#}E zeKg!b`ER7)(eyr=-bYGW2l;a}y^p4MP&ybXcr?9_ruUJO)<OOpP47c0y=R}i_kR>q z5C*4i{m(jei{$Z{(J-eV%t2{(G~5R!-;Rd+Xt;y&-$=ou>3uZ4kCe0y^5<xJA5HI| zbTCrzXnG$_?;|CxgZw#~-iK6r4~%;Me-u*?24zqGhn77hd3<Ix%qa+SP?{YL_kqc` zqv1Xp?x6fPQt)VcA5HHgC9Q+}Ihx)_(>o{~j1)YY-bd5>NJ;A;e~za2A(h@WKji%% z#T10W<A(p1j~hrHpBW8v3c?(eW=F$)VDjx~xQ~WADF2NVJeuA|)B8wC>mYxQruWhG z4oU|j1&^lp(eyr2(mKeWqv?G}rFYO>LZg_pyRk>ZYc#w@)4*sN7)=ACX<#%BjHZFn zG%%V5M$^D(8W>FjqiJ9?4UDD%YNmnL)CN~pBLQj~{(97OTtPY-GNU1bD+F+f(=p9l zqBb~&RLrcVw&9mXO~)0aqaiaIGPptjmpC2M%t~s5Wys|WwGCf6YC5hU9Sxb$kiiuK zxWwt0W+qY{AeS>#H+<r#>A1plG-O6Y23H8+5~p99$)+~QLUuc~4bL7m9aoT!hRkTl z;0gg;;&e<ifz$?}$mI;R4G$bO9aoT!hRkTl;0gg;;&e<in$!lC$mI;R4c8ns9aoT! zhRkTl;0gg;;&e<i$fd$4do%<_Ltr!nXb}PpR0i#xOV=YHl?`tgH62%o(lcZPCQ%nU zPe-JfrR%9{w!oxO({Y6;Jws-h0CnMVN8~g^U9*=7jGB%sMClna=1SCsvA)P@hPq~( zD~+0tD@5rTGAHj*7oNI>oMxzN_Q`vrrsE1xdWKBYd+Nfnr^soBx@JecA2l6Uh|)7; jKIBmsK5jrxGt@QvL*A(AxI&bkA%h&lqwLWTASMI=IopSZ
literal 0 HcmV?d00001
diff --git a/dlls/mf/tests/resource.rc b/dlls/mf/tests/resource.rc index ff7fb6d8b0c..1639b8863d5 100644 --- a/dlls/mf/tests/resource.rc +++ b/dlls/mf/tests/resource.rc @@ -93,6 +93,14 @@ nv12frame.bmp RCDATA nv12frame.bmp /* @makedep: nv12frame-2d.bmp */ nv12frame-2d.bmp RCDATA nv12frame-2d.bmp
+/* Generated from running the tests on Windows */ +/* @makedep: nv12frame-crop.bmp */ +nv12frame-crop.bmp RCDATA nv12frame-crop.bmp + +/* Generated from running the tests on Windows */ +/* @makedep: nv12frame-crop-2d.bmp */ +nv12frame-crop-2d.bmp RCDATA nv12frame-crop-2d.bmp + /* Generated from running the tests on Windows */ /* @makedep: nv12frame-grabber.bmp */ nv12frame-grabber.bmp RCDATA nv12frame-grabber.bmp diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 28b746f1622..b9c35537551 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -8681,9 +8681,10 @@ static void test_video_processor(BOOL use_2d_buffer) };
const MFVideoArea actual_aperture = {.Area={82,84}}; - const DWORD actual_width = 96, actual_height = 96, nv12_aligned_width = 128; + const DWORD actual_width = 96, actual_height = 96, crop_width = 94, nv12_aligned_width = 128; const DWORD extra_width = actual_width + 0x30; const DWORD nv12_aligned_extra_width = 192; + const MFVideoArea crop_aperture = {.Area={94,96}}; const struct attribute_desc rgb32_with_aperture[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), @@ -8724,6 +8725,13 @@ static void test_video_processor(BOOL use_2d_buffer) ATTR_RATIO(MF_MT_FRAME_SIZE, extra_width, actual_height, .required = TRUE), {0}, }; + const struct attribute_desc nv12_crop[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12, .required = TRUE), + ATTR_RATIO(MF_MT_FRAME_SIZE, crop_width, actual_height, .required = TRUE), + {0}, + }; const struct attribute_desc rgb32_default_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), @@ -8755,6 +8763,15 @@ static void test_video_processor(BOOL use_2d_buffer) ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 4), {0}, }; + const struct attribute_desc rgb32_crop[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32, .required = TRUE), + ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height, .required = TRUE), + ATTR_UINT32(MF_MT_DEFAULT_STRIDE, actual_width * 4), + ATTR_BLOB(MF_MT_MINIMUM_DISPLAY_APERTURE, &crop_aperture, 16), + {0}, + }; const struct attribute_desc rgb555_default_stride[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video, .required = TRUE), @@ -8935,6 +8952,30 @@ static void test_video_processor(BOOL use_2d_buffer) .sample_time = 0, .sample_duration = 10000000, .buffer_count = 1, .buffers = &nv12_extra_width_buffer_2d_desc, }; + const struct buffer_desc nv12_crop_buffer_desc = + { + .length = crop_width * actual_height * 3 / 2, + .compare = compare_nv12, .compare_rect = {.top = 12, .right = 82, .bottom = 96}, + .dump = dump_nv12, .size = {.cx = crop_width, .cy = actual_height}, + }; + const struct sample_desc nv12_crop_sample_desc = + { + .attributes = output_sample_attributes, + .sample_time = 0, .sample_duration = 10000000, + .buffer_count = 1, .buffers = &nv12_crop_buffer_desc, + }; + const struct buffer_desc nv12_crop_buffer_2d_desc = + { + .length = nv12_aligned_width * actual_height * 3 / 2, + .compare = compare_nv12, .compare_rect = {.top = 12, .right = 82, .bottom = 96}, + .dump = dump_nv12, .size = {.cx = nv12_aligned_width, .cy = actual_height}, + }; + const struct sample_desc nv12_crop_sample_2d_desc = + { + .attributes = output_sample_attributes, + .sample_time = 0, .sample_duration = 10000000, + .buffer_count = 1, .buffers = &nv12_crop_buffer_2d_desc, + };
const struct transform_desc { @@ -9156,6 +9197,15 @@ static void test_video_processor(BOOL use_2d_buffer) .output_sample_desc = &rgb32_sample_desc, .output_sample_2d_desc = &rgb32_sample_desc, .todo = TRUE, }, + { /* Test 25 */ + .input_type_desc = rgb32_crop, .input_bitmap = L"rgb32frame.bmp", + .input_buffer_desc = use_2d_buffer ? rgb32_crop : NULL, + .output_type_desc = nv12_crop, .output_bitmap = L"nv12frame-crop.bmp", .output_bitmap_2d = L"nv12frame-crop-2d.bmp", + .output_buffer_desc = use_2d_buffer ? nv12_crop : NULL, + .output_sample_desc = &nv12_crop_sample_desc, .output_sample_2d_desc = &nv12_crop_sample_2d_desc, + .delta = 2, /* Windows returns 1, Wine needs 2 */ + .todo = TRUE, /* Would need special handling to convert gstreamer NV12 buffer to Windows alignment */ + }, };
MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Video, MFVideoFormat_NV12}; @@ -9516,6 +9566,11 @@ static void test_video_processor(BOOL use_2d_buffer) output_info.cbSize = actual_width * actual_height * 3 / 2; check_mft_get_output_stream_info(transform, S_OK, &output_info); } + else if (test->output_sample_desc == &nv12_crop_sample_desc) + { + output_info.cbSize = crop_width * actual_height * 3 / 2; + check_mft_get_output_stream_info(transform, S_OK, &output_info); + } else if (test->output_sample_desc == &rgb555_sample_desc) { output_info.cbSize = actual_width * actual_height * 2; @@ -9589,10 +9644,11 @@ static void test_video_processor(BOOL use_2d_buffer) output_sample = create_sample_(NULL, output_info.cbSize, test->output_buffer_desc); hr = check_mft_process_output(transform, output_sample, &output_status);
+ todo_wine_if(test->output_sample_desc == &nv12_crop_sample_desc) ok(hr == S_OK || broken(hr == MF_E_SHUTDOWN) /* w8 */, "ProcessOutput returned %#lx\n", hr); if (hr != S_OK) { - win_skip("ProcessOutput returned MF_E_SHUTDOWN, skipping tests.\n"); + skip("ProcessOutput returned %#lx, skipping tests.\n", hr); } else { @@ -9616,8 +9672,6 @@ static void test_video_processor(BOOL use_2d_buffer) ok(ret <= test->delta || broken(test->broken), "2d got %lu%% diff\n", ret); }
- IMFCollection_Release(output_samples); - output_sample = create_sample_(NULL, output_info.cbSize, test->output_buffer_desc); hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "ProcessOutput returned %#lx\n", hr); @@ -9626,6 +9680,8 @@ static void test_video_processor(BOOL use_2d_buffer) ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr); ok(length == 0, "got length %lu\n", length); } + ret = IMFCollection_Release(output_samples); + ok(ret == 0, "Release returned %lu\n", ret); ret = IMFSample_Release(output_sample); ok(ret == 0, "Release returned %lu\n", ret);
From: Conor McCarthy cmccarthy@codeweavers.com
The size is not used. We could perhaps check it against the maximum size of the MF sample, but it would be more relevant to check the maximum size against the size GStreamer requires, which is unknown in this function. --- dlls/winegstreamer/aac_decoder.c | 7 +------ dlls/winegstreamer/color_convert.c | 7 +------ dlls/winegstreamer/gst_private.h | 3 +-- dlls/winegstreamer/resampler.c | 7 +------ dlls/winegstreamer/video_decoder.c | 3 +-- dlls/winegstreamer/video_encoder.c | 2 +- dlls/winegstreamer/video_processor.c | 5 +---- dlls/winegstreamer/wg_sample.c | 3 +-- dlls/winegstreamer/wma_decoder.c | 7 +------ 9 files changed, 9 insertions(+), 35 deletions(-)
diff --git a/dlls/winegstreamer/aac_decoder.c b/dlls/winegstreamer/aac_decoder.c index 727c516f044..9e6c5c20cb2 100644 --- a/dlls/winegstreamer/aac_decoder.c +++ b/dlls/winegstreamer/aac_decoder.c @@ -544,7 +544,6 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { struct aac_decoder *decoder = impl_from_IMFTransform(iface); - MFT_OUTPUT_STREAM_INFO info; HRESULT hr;
TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status); @@ -559,11 +558,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, if (!samples->pSample) return E_INVALIDARG;
- if (FAILED(hr = IMFTransform_GetOutputStreamInfo(iface, 0, &info))) - return hr; - - if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, samples->pSample, - info.cbSize, &samples->dwStatus, NULL))) + if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, samples->pSample, &samples->dwStatus, NULL))) wg_sample_queue_flush(decoder->wg_sample_queue, false); else samples->dwStatus = MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE; diff --git a/dlls/winegstreamer/color_convert.c b/dlls/winegstreamer/color_convert.c index c5c7141f4ce..938d8b7b6f2 100644 --- a/dlls/winegstreamer/color_convert.c +++ b/dlls/winegstreamer/color_convert.c @@ -651,7 +651,6 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { struct color_convert *impl = impl_from_IMFTransform(iface); - MFT_OUTPUT_STREAM_INFO info; HRESULT hr;
TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status); @@ -666,11 +665,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, if (!samples->pSample) return E_INVALIDARG;
- if (FAILED(hr = IMFTransform_GetOutputStreamInfo(iface, 0, &info))) - return hr; - - if (SUCCEEDED(hr = wg_transform_read_mf(impl->wg_transform, samples->pSample, - info.cbSize, &samples->dwStatus, NULL))) + if (SUCCEEDED(hr = wg_transform_read_mf(impl->wg_transform, samples->pSample, &samples->dwStatus, NULL))) wg_sample_queue_flush(impl->wg_sample_queue, false);
return hr; diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 60e0c3af8bc..d50c9fa63fb 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -166,8 +166,7 @@ HRESULT wg_transform_push_quartz(wg_transform_t transform, struct wg_sample *sam struct wg_sample_queue *queue); HRESULT wg_transform_push_dmo(wg_transform_t transform, IMediaBuffer *media_buffer, DWORD flags, REFERENCE_TIME time_stamp, REFERENCE_TIME time_length, struct wg_sample_queue *queue); -HRESULT wg_transform_read_mf(wg_transform_t transform, IMFSample *sample, - DWORD sample_size, DWORD *flags, bool *preserve_timestamps); +HRESULT wg_transform_read_mf(wg_transform_t transform, IMFSample *sample, DWORD *flags, bool *preserve_timestamps); HRESULT wg_transform_read_quartz(wg_transform_t transform, struct wg_sample *sample); HRESULT wg_transform_read_dmo(wg_transform_t transform, DMO_OUTPUT_DATA_BUFFER *buffer);
diff --git a/dlls/winegstreamer/resampler.c b/dlls/winegstreamer/resampler.c index 5e38243ac2b..910d109c2c6 100644 --- a/dlls/winegstreamer/resampler.c +++ b/dlls/winegstreamer/resampler.c @@ -519,7 +519,6 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { struct resampler *impl = impl_from_IMFTransform(iface); - MFT_OUTPUT_STREAM_INFO info; HRESULT hr;
TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status); @@ -537,11 +536,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, return MF_E_TRANSFORM_NEED_MORE_INPUT; }
- if (FAILED(hr = IMFTransform_GetOutputStreamInfo(iface, 0, &info))) - return hr; - - if (SUCCEEDED(hr = wg_transform_read_mf(impl->wg_transform, samples->pSample, - info.cbSize, &samples->dwStatus, NULL))) + if (SUCCEEDED(hr = wg_transform_read_mf(impl->wg_transform, samples->pSample, &samples->dwStatus, NULL))) wg_sample_queue_flush(impl->wg_sample_queue, false);
return hr; diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index ba8b2d545f7..d96f82b62eb 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -994,8 +994,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, } }
- if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, sample, - sample_size, &samples->dwStatus, &preserve_timestamps))) + if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, sample, &samples->dwStatus, &preserve_timestamps))) { wg_sample_queue_flush(decoder->wg_sample_queue, false);
diff --git a/dlls/winegstreamer/video_encoder.c b/dlls/winegstreamer/video_encoder.c index f26bec66c84..41291928660 100644 --- a/dlls/winegstreamer/video_encoder.c +++ b/dlls/winegstreamer/video_encoder.c @@ -541,7 +541,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, if (!samples->pSample) return E_INVALIDARG;
- if (SUCCEEDED(hr = wg_transform_read_mf(encoder->wg_transform, samples->pSample, 0, &samples->dwStatus, NULL))) + if (SUCCEEDED(hr = wg_transform_read_mf(encoder->wg_transform, samples->pSample, &samples->dwStatus, NULL))) wg_sample_queue_flush(encoder->wg_sample_queue, false);
return hr; diff --git a/dlls/winegstreamer/video_processor.c b/dlls/winegstreamer/video_processor.c index 3e327228e65..587be98c98b 100644 --- a/dlls/winegstreamer/video_processor.c +++ b/dlls/winegstreamer/video_processor.c @@ -695,7 +695,6 @@ static HRESULT WINAPI video_processor_ProcessOutput(IMFTransform *iface, DWORD f MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { struct video_processor *impl = impl_from_IMFTransform(iface); - MFT_OUTPUT_STREAM_INFO info; IMFSample *output_sample; HRESULT hr; BOOL playback_mode, provide_samples; @@ -709,8 +708,6 @@ static HRESULT WINAPI video_processor_ProcessOutput(IMFTransform *iface, DWORD f return MF_E_TRANSFORM_TYPE_NOT_SET;
samples->dwStatus = 0; - if (FAILED(hr = IMFTransform_GetOutputStreamInfo(iface, 0, &info))) - return hr;
if (FAILED(IMFAttributes_GetUINT32(impl->attributes, &MF_XVP_PLAYBACK_MODE, (UINT32 *) &playback_mode))) playback_mode = FALSE; @@ -731,7 +728,7 @@ static HRESULT WINAPI video_processor_ProcessOutput(IMFTransform *iface, DWORD f IMFSample_AddRef(output_sample); }
- if (FAILED(hr = wg_transform_read_mf(impl->wg_transform, output_sample, info.cbSize, &samples->dwStatus, NULL))) + if (FAILED(hr = wg_transform_read_mf(impl->wg_transform, output_sample, &samples->dwStatus, NULL))) goto done; wg_sample_queue_flush(impl->wg_sample_queue, false);
diff --git a/dlls/winegstreamer/wg_sample.c b/dlls/winegstreamer/wg_sample.c index 83f7163d0c9..2fc2679337f 100644 --- a/dlls/winegstreamer/wg_sample.c +++ b/dlls/winegstreamer/wg_sample.c @@ -338,8 +338,7 @@ HRESULT wg_transform_push_mf(wg_transform_t transform, IMFSample *sample, return hr; }
-HRESULT wg_transform_read_mf(wg_transform_t transform, IMFSample *sample, - DWORD sample_size, DWORD *flags, bool *preserve_timestamps) +HRESULT wg_transform_read_mf(wg_transform_t transform, IMFSample *sample, DWORD *flags, bool *preserve_timestamps) { struct wg_sample *wg_sample; IMFMediaBuffer *buffer; diff --git a/dlls/winegstreamer/wma_decoder.c b/dlls/winegstreamer/wma_decoder.c index 312dec2034e..78f45f489c0 100644 --- a/dlls/winegstreamer/wma_decoder.c +++ b/dlls/winegstreamer/wma_decoder.c @@ -535,7 +535,6 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { struct wma_decoder *decoder = impl_from_IMFTransform(iface); - MFT_OUTPUT_STREAM_INFO info; HRESULT hr;
TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status); @@ -553,11 +552,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, return MF_E_TRANSFORM_NEED_MORE_INPUT; }
- if (FAILED(hr = IMFTransform_GetOutputStreamInfo(iface, 0, &info))) - return hr; - - if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, samples->pSample, - info.cbSize, &samples->dwStatus, NULL))) + if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, samples->pSample, &samples->dwStatus, NULL))) wg_sample_queue_flush(decoder->wg_sample_queue, false);
return hr;
From: Conor McCarthy cmccarthy@codeweavers.com
Windows uses a smaller alignment than GStreamer for some formats, for example NV12. This means we cannot use MFCalculateImageSize() to get the output sample size. Commit 7b79e3a87b1e switched to calling it instead of GetOutputStreamInfo() to fix some game bugs.
Fixes video decode failure in a Resident Evil Village cutscene. --- dlls/winegstreamer/video_decoder.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-)
diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index d96f82b62eb..e89dca00465 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -103,6 +103,7 @@ struct video_decoder wg_transform_t wg_transform; struct wg_transform_attrs wg_transform_attrs; struct wg_sample_queue *wg_sample_queue; + struct wg_format wg_output_format; /* for calculating sample size */
IMFVideoSampleAllocatorEx *allocator; BOOL allocator_initialized; @@ -759,6 +760,13 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF else hr = try_create_wg_transform(decoder, output_type);
+ mf_media_type_to_wg_format(output_type, &decoder->wg_output_format); + if (!decoder->wg_output_format.major_type) + { + FIXME("Failed to get output wg format.\n"); + hr = MF_E_INVALIDMEDIATYPE; + } + IMFMediaType_Release(output_type);
if (FAILED(hr)) @@ -948,9 +956,8 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, UINT32 sample_size; LONGLONG duration; IMFSample *sample; - UINT64 frame_size, frame_rate; + UINT64 frame_rate; bool preserve_timestamps; - GUID subtype; DWORD size; HRESULT hr;
@@ -966,15 +973,13 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, if (!(sample = samples->pSample) && !(decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES)) return E_INVALIDARG;
- if (FAILED(hr = IMFMediaType_GetGUID(decoder->output_type, &MF_MT_SUBTYPE, &subtype))) - return hr; - if (FAILED(hr = IMFMediaType_GetUINT64(decoder->output_type, &MF_MT_FRAME_SIZE, &frame_size))) - return hr; - if (FAILED(hr = MFCalculateImageSize(&subtype, frame_size >> 32, (UINT32)frame_size, &sample_size))) - return hr; - if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) { + /* GetOutputStreamInfo() is bugged for some games, and MFCalculateImageSize() + * for some formats uses a smaller stride alignment than GStreamer does */ + if (!(sample_size = wg_format_get_max_size(&decoder->wg_output_format))) + return E_INVALIDARG; + if (decoder->temp_buffer) { if (FAILED(IMFMediaBuffer_GetMaxLength(decoder->temp_buffer, &size)) || size < sample_size) @@ -994,6 +999,9 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, } }
+ /* In cases where the caller supplies the sample, and GStreamer requires a + * larger stride alignment than Windows, e.g. NV12, this call may fail, and + * the output buffer would need to be converted to the smaller alignment. */ if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, sample, &samples->dwStatus, &preserve_timestamps))) { wg_sample_queue_flush(decoder->wg_sample_queue, false);
Rebased. Anything to do here? I have no information on the exact issue behind the change away from `GetOutputSizeInfo()`, so I'm not certain if taking the max of `MFCalculateImageSize()` and `GetOutputSizeInfo()` would be a regression.
I may be misunderstanding, but it doesn't look like we're solving the problem in the same way as Windows. Or, at least, you haven't confirmed that this patch is doing so.
Our current implementation returns aligned frames, whereas MFCalculateImageSize() returns an unaligned size. There are two possible fixes: either change our implementation to not use MFCalculateImageSize(), or change our implementation to return unaligned frames. It would surprise me if Windows returned aligned frames when MFCalculateImageSize() returns an unaligned size, and if so, the fix taken here would seem to be the wrong one.
Whether this is actually the case has not been confirmed, though.
Either way I feel like there probably should be some tests that are actually fixed in this series.
On Mon Jul 28 13:42:00 2025 +0000, Elizabeth Figura wrote:
I may be misunderstanding, but it doesn't look like we're solving the problem in the same way as Windows. Or, at least, you haven't confirmed that this patch is doing so. Our current implementation returns aligned frames, whereas MFCalculateImageSize() returns an unaligned size. There are two possible fixes: either change our implementation to not use MFCalculateImageSize(), or change our implementation to return unaligned frames. It would surprise me if Windows returned aligned frames when MFCalculateImageSize() returns an unaligned size, and if so, the fix taken here would seem to be the wrong one. Whether this is actually the case has not been confirmed, though. Either way I feel like there probably should be some tests that are actually fixed in this series.
I'm not sure what you mean. The only sense in which this returns a size is the buffer size in the sample, whose alignment is a gstreamer requirement. We would need to modify gstreamer to change that.
I wasn't able to make an h264 frame which worked, so I have no test for it. I'll probably leave the tests added here and remove the implementation changes, because I don't know why `GetOutputSizeInfo()` is not used. It would solve the issue.
I'm not sure what you mean. The only sense in which this returns a size is the buffer size in the sample,
Yes, that's the size I mean.
whose alignment is a gstreamer requirement. We would need to modify gstreamer to change that.
Is that actually the case? GStreamer has a default alignment for any given format, but we can and do change that. That's the point of all that complicated alignment logic we have. Is some component rejecting alignment *below* the default?
I wasn't able to make an h264 frame which worked, so I have no test for it.
Hmm, what happens when you try?
If you can't get one that works, can you at least try the one from whatever application triggers the bug, to confirm that this is the correct way to solve this?
Is that actually the case? GStreamer has a default alignment for any given format, but we can and do change that. That's the point of all that complicated alignment logic we have. Is some component rejecting alignment _below_ the default?
See my comment higher up. Alignment in gstreamer is hard-coded to 4 for NV12, and when d3d-awareness is enabled, we allocate the sample buffer, which ends up being too small for gstreamer to use.
Hmm, what happens when you try?
I was getting wires crossed with some other testing I did a while ago. The issue is NV12 output from Theora, which we don't need to deal with in upstream Wine, and it also only occurs with d3d-aware output, which limits the scope. I tested d3d-aware h264 -> NV12 and there is no issue. It looks like upstream should be left as it is, at least for the moment.