From: Charlotte Pabst cpabst@codeweavers.com
--- dlls/mfsrcsnk/tests/Makefile.in | 3 +- dlls/mfsrcsnk/tests/mfsrcsnk.c | 176 ++++++++++++++++++++++++++ dlls/mfsrcsnk/tests/resource.rc | 30 +++++ dlls/mfsrcsnk/tests/test_thinning.mp4 | Bin 0 -> 7199 bytes 4 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 dlls/mfsrcsnk/tests/resource.rc create mode 100644 dlls/mfsrcsnk/tests/test_thinning.mp4
diff --git a/dlls/mfsrcsnk/tests/Makefile.in b/dlls/mfsrcsnk/tests/Makefile.in index 55a84256266..89889d6f723 100644 --- a/dlls/mfsrcsnk/tests/Makefile.in +++ b/dlls/mfsrcsnk/tests/Makefile.in @@ -2,4 +2,5 @@ TESTDLL = mfsrcsnk.dll IMPORTS = ole32 mfsrcsnk mfplat mf uuid mfuuid
SOURCES = \ - mfsrcsnk.c + mfsrcsnk.c \ + resource.rc diff --git a/dlls/mfsrcsnk/tests/mfsrcsnk.c b/dlls/mfsrcsnk/tests/mfsrcsnk.c index b7645d113b2..3309659df1e 100644 --- a/dlls/mfsrcsnk/tests/mfsrcsnk.c +++ b/dlls/mfsrcsnk/tests/mfsrcsnk.c @@ -616,6 +616,181 @@ static void wait_for_source_stop_(int line, struct event_callback *callback) ok_(__FILE__, line)(stream_stopped, "Expected MEStreamStopped.\n"); }
+static int compare_LONGLONG(const void *va, const void *vb) +{ + LONGLONG a = *(const LONGLONG *) va, b = *(const LONGLONG *) vb; + return a < b ? -1 : a == b ? 0 : 1; +} + +static void test_sample_times_at_rate(struct event_callback *callback, IMFMediaSource *source, + IMFRateControl *rate_control, IMFPresentationDescriptor *pres_desc, FLOAT rate, BOOL thin) +{ + const UINT keyframe_interval = 5; + const UINT total_samples = 60; + const float sample_duration = 333666.666; + + IMFMediaStream *stream = NULL; + IMFMediaEvent *event; + MediaEventType type; + LONGLONG *sample_times = calloc(total_samples, sizeof(LONGLONG)); + BOOL got_rate_change = FALSE, got_thin_mode = FALSE, got_stream_end = FALSE; + UINT got_samples = 0, first_valid_sample = 0; + PROPVARIANT value; + HRESULT hr, status; + + value.vt = VT_EMPTY; + hr = IMFMediaSource_Start(source, pres_desc, &GUID_NULL, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + wait_for_source_start(callback, &stream); + + hr = IMFMediaStream_RequestSample(stream, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFRateControl_SetRate(rate_control, thin, rate); + todo_wine_if(thin && hr == MF_E_THINNING_UNSUPPORTED) + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + while (!got_stream_end && (event = event_callback_recv_event(callback, 1000))) + { + hr = IMFMediaEvent_GetType(event, &type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEvent_GetValue(event, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEvent_GetStatus(event, &status); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + switch (type) + { + case MESourceRateChanged: + ok(status == S_OK, "Unexpected status %#lx.\n", status); + ok(!got_rate_change, "Unexpected MESourceRateChanged.\n"); + todo_wine + ok(value.vt == VT_R4, "Unexpected vt %d\n", value.vt); + todo_wine + ok(value.fltVal == rate, "Unexpected rate %f\n", value.fltVal); + + got_rate_change = TRUE; + break; + + case MEStreamThinMode: + /* According to MS docs, this should be VT_BOOL, but windows gives VT_INT. */ + if (value.vt == VT_INT) + { + value.vt = VT_BOOL; + value.boolVal = value.iVal ? VARIANT_TRUE : VARIANT_FALSE; + } + + ok(status == S_OK, "Unexpected status %#lx.\n", status); + ok(!got_thin_mode, "Unexpected MEStreamThinMode.\n"); + ok(value.vt == VT_BOOL, "Unexpected vt %d\n", value.vt); + ok(!!value.boolVal == !!thin, "Unexpected thin %d\n", !!value.boolVal); + + got_thin_mode = TRUE; + first_valid_sample = got_samples; + break; + + case MEMediaSample: + ok(status == S_OK, "Unexpected status %#lx.\n", status); + ok(got_samples < total_samples, "Unexpected MEMediaSample.\n"); + ok(value.vt == VT_UNKNOWN, "Unexpected vt %d\n", value.vt); + ok(!!value.punkVal, "Missing sample.\n"); + + hr = IMFSample_GetSampleTime((IMFSample *) value.punkVal, + &sample_times[got_samples++]); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaStream_RequestSample(stream, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + break; + + case MEEndOfStream: + ok(status == S_OK, "Unexpected status %#lx.\n", status); + got_stream_end = TRUE; + break; + } + + PropVariantClear(&value); + IMFMediaEvent_Release(event); + } + + hr = IMFMediaSource_Stop(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + wait_for_source_stop(callback); + + IMFMediaStream_Release(stream); + + ok(got_stream_end, "Expected MEEndOfStream.\n"); + todo_wine_if(thin) + ok(got_rate_change, "Expected MESourceRateChanged.\n"); + todo_wine + ok(got_thin_mode, "Expected MEStreamThinMode.\n"); + ok(got_samples >= total_samples/keyframe_interval, + "Expected at least %d samples, got %d.\n", total_samples/keyframe_interval, got_samples); + + qsort(sample_times+first_valid_sample, got_samples-first_valid_sample, + sizeof(LONGLONG), &compare_LONGLONG); + for (UINT i = first_valid_sample+1; i < got_samples; ++i) + { + LONGLONG expect_interval = sample_duration * (thin ? keyframe_interval : 1); + LONGLONG interval = sample_times[i] - sample_times[i-1]; + + LONGLONG tolerance = 10; + LONGLONG diff = expect_interval - interval; + + todo_wine_if(thin) + ok((diff < 0 ? -diff : diff) <= tolerance, + "Expected interval %lld, got %lld.\n", expect_interval, interval); + } + + free(sample_times); +} + +static void test_thinning(void) +{ + static const GUID CLSID_MPEG4ByteStreamHandler = + {0x271c3902,0x6095,0x4c45,{0xa2,0x2f,0x20,0x09,0x18,0x16,0xee,0x9e}}; + IMFMediaSource *source = NULL; + IMFRateControl *rate_control; + IMFPresentationDescriptor *pres_desc; + struct event_callback *callback; + HRESULT hr; + + hr = create_source(&CLSID_MPEG4ByteStreamHandler, + create_resource_byte_stream(L"test_thinning.mp4"), &source); + if (FAILED(hr)) + { + win_skip("Failed to create MPEG4 source: %#lx.\n", hr); + return; + } + + callback = create_event_callback(); + + hr = IMFMediaSource_BeginGetEvent(source, &callback->iface, (IUnknown *) source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFGetService((IUnknown *)source, &MF_RATE_CONTROL_SERVICE, + &IID_IMFRateControl, (void **)&rate_control); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSource_CreatePresentationDescriptor(source, &pres_desc); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + test_sample_times_at_rate(callback, source, rate_control, pres_desc, 2.0, TRUE); + test_sample_times_at_rate(callback, source, rate_control, pres_desc, 3.0, FALSE); + + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + event_callback_shutdown(callback); + + IMFRateControl_Release(rate_control); + IMFPresentationDescriptor_Release(pres_desc); + IMFMediaSource_Release(source); + IMFAsyncCallback_Release(&callback->iface); +} + START_TEST(mfsrcsnk) { HRESULT hr; @@ -624,6 +799,7 @@ START_TEST(mfsrcsnk) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
test_wave_sink(); + test_thinning();
hr = MFShutdown(); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); diff --git a/dlls/mfsrcsnk/tests/resource.rc b/dlls/mfsrcsnk/tests/resource.rc new file mode 100644 index 00000000000..fd391b19f88 --- /dev/null +++ b/dlls/mfsrcsnk/tests/resource.rc @@ -0,0 +1,30 @@ +/* + * Resources for mfsrcsnk test suite. + * + * Copyright 2025 Charlotte Pabst for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "windef.h" + +/* Generated with: + * gst-launch-1.0 videotestsrc num-buffers=60 pattern=smpte100 ! \ + * video/x-raw,format=I420,width=64,height=64,framerate=30000/1001 ! \ + * videoflip method=clockwise ! videoconvert ! \ + * x264enc key-int-max=5 ! mp4mux ! filesink location=dlls/mfsrcsnk/tests/test_thinning.mp4 + */ +/* @makedep: test_thinning.mp4 */ +test_thinning.mp4 RCDATA test_thinning.mp4 diff --git a/dlls/mfsrcsnk/tests/test_thinning.mp4 b/dlls/mfsrcsnk/tests/test_thinning.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..7e9f7206b5686a2aa79bbe2808dee42c76016d3e GIT binary patch literal 7199 zcmZQzU{FXasVvAXFfn3aU|;~zxdkSMnZ^0JnZ@}aF^;sN)Kmrr(c0XU#1aMu1}07c z1_lPH^b`h>H8)+<4n#FGGY2&=Fn(!ZC^c~J5;@Jlz`&aE`pqUC1_q|5Y^?wPKfL4m za__U79^0qs+)%iw{O)~)k(r5tu0nEtQL2I=nCYQsWM-o88{q1$Yog#7=B$vMpOTua zpsV1VUr?EonpUD<WME*dYh+*q(wI?FQedU8UtV6WSC*NQnxB)Hr<Y%pt`9O-FQX(k zM?qI1zn~;DKd;zIAvrNAG1=Bop(r)Y)>t7WH7O@QIosCI%D~D%Au%s8r?NQJ)}X@J z%Amr~&{!ci)ixuwLZP@cDL2*DT%n-2($)|}#uug78tNGsSQ+RU7%1dsR-~rHgG@A3 z$W4teO3X`7wKX(TNX{tA&rOWaO|>;tC@D(K$;m9XHB_*uut-TRu{BgkF3hzxP)JEl zOsUGxOSLsJ)G;(vNJ}g(i7zP5&MW|_fEZ9%5TBoxR-9U5t81iCl2MeJm{M$Oq>z)J zpPiVInwSy~6EjpO&dE$pMF<!u<Q0MKNY2bnECIPAGp{7IC?_#FHO1CIAt|S{D6uj= zIX|}`vBcIuAvr&<xTGjCGcPqIJ~OYRC=sM4ttc@!wb<5JAt}C~vM4b(GsV_OAt^pF zC9weHi=_CZ%*0}lC7DI3V1JdTW~OJ9B-t7&<QJso#i!>N*cvE61Pg496tYt*GxJJp zO`(kV+{`>%Lxtkx)V$Q>(h^$}0|l_vpzth8EzYntP$)`{hx^sm&|IM?*)}<;NFg_= zq$m~Sr=-l1B9QNl3`{H(K&+Dd98mNq6oPzaYoKRlpio!<vIk5jR@j<ZDijtJm!uZh znkbYdmBr^KR)F-`f=vJkB$cKWXI7=!8X6f{DC8yP#Ag(x*yiQur7EOl=H#S;0yVQB zz9_LIGvC%w&%{6>vC!7g3X~)m8W<QD{-<`dFi8D>^WV>Y+mi~ZEyWk>zh9rdQBnGO z*yXRkcB_9$IrJgpz`LZ0Iy>)d^?Ms_@$l;9?t{l>hl_FPXq4GI-*cbxdu#gS?&&T4 zEotTdW?3lxEZw&KuhnVRuHsZj5%%1Uwwc8xXSW}Zn-r2f`_0?l0O4H)PgOPta0%@% zE1MRyb^F9f>(V81;aSe!)yKBq-}B!*pv!GzZDqU7!h%-8b1oAP&iZ$0MV46Ai#2CD zBpR{<kKH)<Tlt-$<00YLr{_Ls-%(9mpYtK@l}1_hA-mNcyoRmojknnv70-X<D0bq( z-!i4QizdYhub;`GGC}F|Hs(bQ;^BwBF14FC`RtOnSAuQ91%m+t0|S@iER`I`{C``u z--E>h7#J8h9p^b!G|MxTIKqWE80RIHIOa1NB8wzvIodNF2a6D2#xU}wb~G`t)xW9t zvtRa?ago8}tJgoaH9ucqsjI>NosYTd2FLHhsCUs(`yT$dUHjmO!P`5WSDIwjur=Rm z)G>O0R5?xD;P+Z~e@%-QCz|fRIPtNL@o$!$e);pC&M#+P@^otI?CjxU-jr7T{e+s` zwG-9(%cgf2J6<`Us#u~J_f+<e-MTAQITP#{r~dVk&DxmcymH3*hhghtp2hWjmK5yc zSsGatH@WlvuiqvqFOx4{@m)Bv^stD-?k{&)11_^J-l5!oe8t?H;6~G^_bV)?A9Fe2 zapR0`PKoXfSx1GiB}$1ulqFZmTCL#;*tYp;R<cy|j+o`Q7Cz2?c4TY%Y6i)yrdO{N z-)L`2iFUJhk8F7<a>eH%IQT#@!3~ay`hVAY-5SAy@Th3|&u|_|2pkpl4Cg`Vn28gt zig8|YmLpt<w8&^-ko-Ry8Mq?@DVRa|0u&iYLa@j{i62O0K!iw(jM1EdKW89?JZX^u z(MVckjOGk@WIO<uNQ8?iNH9aoq{I?XnFPrOP@&PBL4`VF;3H!+XW-8nNZ9~ZOrc~0 zNX|e~1*^azsz@uQM(YgxkpT%)Z~=rTGLTeJ5*ed81Ak;7rFlG&fusr+84#h-oH5$Z z7^a;*NCHPGrjUg2^fMr;NXQuigSQ9HZ>+t|ZTbq9qkL8UF{!-2u1W1&RDAqY48tBF zS+~ZLLlX~gd-~6z$97|M&+~ilSG8DpeKk<`G=Fxaql&$^S5DT)%cG8Wu9>TH&p{@Z zoP-_A4m%$cGP=GiO4Iz-sXdb=A9>zW-?iU0Mu|7i?FLWulNbI+3uadm(YS54XNY$I zs{_Xn{^($<%FWL&V_;y&$t}xB0S(kX@$(h|VFrd$0|tgya~K#IKnNrS!Hf_JPNMNa zR>S2m8B7cetS3u~60;c?7-CAY!FIBP>;qv2xK*#_U}#0N2h~vw3}Aaf1PC)QsHCQp z6f-a|$fV{Jg9evD#xa8Z!@zzCqL<}GZc1h%XrMYb1?&z6kQ+c4=J(JeAZ-k~87VnM zU={-d187_r#s<-0pmAc4#JrT8RFE2$$+?+%X$%YuB4xQ?Ly?`Wk^+&FNhwMNsR8K+ z$@7#J<tRWr&k|f*l9a>1z;M2}q!=6_5OIe6iDk*4F)f%L5RJeN3=Rw)3=Cil5(SwG zqH$x1|Nj{n7>p9jlARe-7)1U*#5eNJ2p@Q75J@U2f&?hY#SEeQ7#J9Ylk;<m@{)5Z z7}&s&fq_AwAhEar>U5A}B#KMG@d0uWh;PHd!2FVdfx)4;q!<)WAaNe3dNu|I25yiU z3=9mSpeX?c1_pTs1_l)d1_n(A1_nI_1_omW1_ldI{1%rKCxbLY^+L@C#V{jFaY=C% zR1QShFfg!4f}#fGWCjKXAqZx)huR|y6&Ge;7?NV3K~PY5A^YP)G9uh<AYsIQ2|^3^ zLTDHt6hxqK1IG&^11O(>^W&&GNH`6Na1kjkNzMnyBslIEKm#F>r70z#axO486-<Mq z6=7vsZbnW~Zb~Mo>;j3yu}UVS;FMWek`FRaBqb578I-{oK=WY?3=FnVgH1^>m;+=m E0NSC6RsaA1
literal 0 HcmV?d00001