MR !607 was trying to fix an issue with Life Is Strange Remastered, but although it fixed some race conditions with presentation end, the issue it was trying to fix is still there.
The game calls IMFMediaSession_Stop while the presentation is ending, expects that command to quickly execute, interrupting the presentation end and emitting a MESessionStopped event instead of the MESessionEnded.
Delaying the Stop command and emitting the MESessionEnded event breaks the game assumptions and it crashes.
-- v3: mf: Discard end of presentation on IMFMediaSession_Stop. mf/tests: Test IMFMediaSession_Stop command near presentation end. mf/tests: Test Start / Pause / Stop IMFMEdiaSession events. mf/tests: Split wait_media_event helper into wait_next_media_event.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/tests/audiodata.wav | Bin 0 -> 41004 bytes dlls/mf/tests/mf.c | 46 +++++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 19 deletions(-) create mode 100644 dlls/mf/tests/audiodata.wav
diff --git a/dlls/mf/tests/audiodata.wav b/dlls/mf/tests/audiodata.wav new file mode 100644 index 0000000000000000000000000000000000000000..33e41b2b7a2c5fd34d7763efa9c93fafc960afe1 GIT binary patch literal 41004 zcmeI*@sAv39mny<mda^sT?&?~V40Ryw?)cU;Cht1Wq_7mOJlDlttU-%Rlp@6Sww9y zjTuryt|=i)T6LS$xG2UYDReQ_9_{TgEl8GV%o(Fz45mX;%~fNv8e-2F(f4;gbAN!p zKTTel+urT$%=3M|&pfj;^E{v0de1$pn$CUt&fS|I{PI_Wf^%-Zo99;UcCPU9JlE#} zcmL<V`gzCSxbXB)Cb(_bnbCu}lPuADODgXvJJVPqGb_r@1UE@xW7(PdUAYmKn7vmf zwwIlm+9u&WWoL?aNc~p+9*s3pyQb{S$U<rUW7wJK7m_?T>@p9tgu6nbokPyJ>nAhO zf{ruIid2quoT<Gb>EAle1n-M8<I7@adLNV0EwMBGWr;S&&Q!KZb9?N}U@q9oda&<q z32%>`X_TaOee6v0s@!~*NZ)2V5WON(&vcxrY?A2Llg_w}XXJ<MBbql-E|@wXg%iWh zxM?Z9JM7GOp`7Ut%FZ-yl&L}Df!3WeyM=h6P25bEn3)nkn^|IJN};3!WoHI&kg<=H zovD66!b`)>)Tbo)G4%{?uSC}pPlOTm5bh%TXWn67QQ5`*qxT&dJ=<|6xG25%5+^jS zmhhvLH@csY*`e5(iA^#?Y)nMFOf}cy|I8?VXR5r%6j+a`u9n79J_pkar2XfP%fu`Z z9+6~6$C+q`xWS!fpO73H%7iP~XGHrYd4YP2!f&McFXD=Qi_4jCIr)OVkIVQ5;)ynK zFr&oGREUw8i`zIqn-;$_MdEBqgEGbXOzAp_K3H}p{r7OD{#)XP+Hom7$@ak&CHgM; z2KO}kXa2~(BG^WJ5IrlwB(D)(kmR!1ndXONd=+_zvGp=d&X}3IrAlnfDDg7&T#-C9 z!DbmBW<7{*mVi7q^?vE=i=B!7%61`rP1;9^AKV^^{!8587UdpdACW92PN+O4wdW}p zxL--{9okEbEg}z4y*9U&B?c{5(7HpK<dSj3$RyiknxD<chy-_)ovGa>W9wKy+zP2* zLHmi&N%RNm71}RL{VC#z%FPnJMBLyePG^#zPr6K*dW*u-()$^ChuV3G=23QNTq)g^ z<QdYNq_r`2rjNLpbVT}zmucr3#LUbPH`AnjH7RYa=@upWFrSCP_e<EL{-D>D!uQ!u zgb(HxvP86z_+{A7jkGNbhvh+*D12XfUE+nXCxh=NK8QXn-6FY!^cHEBi5F%zOJiG^ z{U8U4o9WM`Bm6!3?v~a@;*FH!rRlDe#+AGu(LAZ0r#_+gGbudHb|NX~eoh^Po7k9% zUSdB{xw(_6Kh+`5)a92Y`U7Qw(9tfUeucOdF?EMrTPL;KV(J#Ta+i#Z#MC`<@Lox_ zldo{RHqCpabqDdr;DA)ulBecc8C%5q(R+t@!Tm~V&r_dJc}$X})H}F`$TzcyeMIzM zaeK%ww2w;q8toIJze-;p<&1j21Ro<`5Zx@}!?c44h?^-AGgBvCW;9pbN}ghdxS8qO zWo$k9hVfO>{1Ej3$z>8=p#C73l;~Nu3&A#Ve`J5m)3;}$?-Dn-BKwTOlTtfQxuE`A zN&ijzgy@5DrgU9QTOHG8OKFhTDBdZpEyM%k#K?5FONF1!D8Do9P{ud#cj#k%CR{H2 z7Sn%0^I!CvQ231`FAyh0`z2gS-9mDR_5<!T`-f<TxFOj=y+wFL+A;Anf0pS5lrI`f zrMjB(N8uAv9pW`cH%W`wm@4ryQEtX!h6(=8%<>-7Wj!YRs5GwTbI^OQ1Q&@9M$by` zJLDZIyTrXi%*;jh3t=>wiLPb;;r2@K;|~2AVm2k=CCUZW55$?VkB|o#yg^d(#gvGH znI)f0i5Qu=xV8D&%n~!xx>KeGi9Z@QN`FB6it&P!-tExuW80>saDw<@>VPzFB(88j zBxYtK`-kY)#0`~A>_4WSk?56S`ljUE+tQp*{Wn)h>w4mXMoB{Q#_YRW0%BtZiI-{S zD&(MvHcLNkt0~dmnjSf9#+OO(KGuWuZ)^u@Z%E}B+lyvJq6KU(-1Wo`(N6LW?h5u9 z$-}wlSR(p`H2*=lU}PcfA8OY~V-4ku`mIvDgM33s%*+(WGBZKGnOTb&>LWSY)jA3r zCE$2zW>!dJ3F}AYJ<@xN_7bBfa|c->xJ|;-)I-eut5n&4q<@lN4dsOKJ+l8>WoN>M z9BY-G8TzTrbjTmn&dD!Xn!>A6IYpe{j?1R+m7S>{lIcguW3=v)F2^)eCq5?GDz2Y; zhVUlx1Hnt;{zF_4QXVELOLaH-16P&m<Ge;=pG@zKovA)1=>z1I*(Gzwv)T>B5pBxY zjQ*YSL^3HQ%GN|BaW7J~W`_O|qDQGi=529TQ@0SVlXQS`M{~WnfZrpUFJXs12;7d` zB*$;FlFu+N<o?ML&1KX>RMts4&|&>->v{>7Q@^#IWkacj>w6CGeX5iA?dgio-Y zXrGW0$0`#pqCQ}*-EGrmnvtEkFR;Wcu{FupWp*F0G4eI3JxskpmHK7|H%pN^XquNx z`y8K-gk!mJONTN6`<lt?LvlIsM{$Gn-%tC7$)}|MD6i3MNqUlaBkD+aj(UWd+#|2X z&U8;naGd<Z@nbS^ki13ZOA^pFo34{yoqWVx|D;LW&D5KcUQ>2vY=;!T$$sP5VR`HM zvNN@xNH9r$qJ38SU!&baC6U%E<Pko8LWZ6rPZ8E-^a+lKaG#Wk>p1Sg(RVb_H*#O3 z9>C3G+Yua<!e2NZpl=z+eWaALDeROIWo?S&l}YzXeIM-@B683SJ|rXe#m<av$*pIJ zx%EdJi%j}v?%OO8j^w7P$8b~BUqmIg+q@~sV(JUhH4>0(nOj(*vRa~r>^B_8u*`mz z<`C_UnPL4%4%1(OyCCTjwg=%_NpIyfD!1fTu|%>!T$}fq^Q_Md3}q_w*bYP$Nq)d~ zqwss_E+{(_5-ZccLFW21)z6YY2p^HT{OXZwrc3^rh+H(YpOdkBsds2|{54TQn!lr- zA~_&oi*^VJ-zAwgF)*X_RZK)1X^Nkb?vvDiynaM3`%&!7s-MWoGqE$Zv$E<n{vYi` zt~wn%Q#~PRlX`^LpOM~IVrNp0jiyGQ(?l<_EpQhp;|%3{k-G3A<%-(kawg??V|rha z*Pkh~ZvL++Rr1GNbz0iwmsv%wnc7)7d8W*M@PFi|x$H;s`VrQL?vqmdOqsHwY;Ta! zOT&~KZQ8dbq28IWMSVs>f6Fv~CsBcYL%Sqn_mU@={hUOP@_Tf@E<JM1jC@VPM`G#* zW%=3Mc9!TTcTISmbQkb`6n;;hBl&?u70MMA+5+PSC|`4)+%Rp*8OZ|b3o5H}mSae7 zm2mAaxx&v	iQhNDhmeVO{3X8QITg80wsf7P1|vtd@X0%W#YfkgkzrG24ykP2OWl z;-*+P!fDFdjIcdOi9?2VIYsoY^erpX7Lg<Cb6Z$q<USdEh~pKa$E8lLnRKrdA0zH4 z-7kfm^ox*^zh-Xzg};#J2#$)QEilm+*>3X<_66>|Ob{>QJ}IM5&^JL?m!aok+Wna0 zfV5tTIWBOFBEL*Oxn|nrp9v<VMt+*No|j{XV~$1S^fzT}2lWo=H8MrJY?|xDzUwl* zI_E>^I_f`y0jYe6;}j+i%JE}mj-~wWxO7kP_ju%0nK?)Q5@9atP|iqCO0z}(0R2a0 z@~JY%dVZ$7O>u)Hm(%}1@Dk-~^8SrmO1(m|hx&r{IcZ)_{{Th$?q+baRQHfKs68xm z`87+fnS}f^v*e-~`9f|dOSFe%?t9^I5%EIl0{uv|Pe}L##}mx;Z=wU#0l2l)UqnZS zGT|lauvtz$MzUV!`jwS+<R6;suW|n*KIVnoN|t7l{;JtAnF%|S^gH>?`P4hOK$`R; z&0N1SB<{vtEz#S&$2=--hQ6wKk<UO>qE4f9P!hiPFr$B$_L4IFY}!`ZNfXdUn{-$1 z0hXweQ)YUvG{`wqB@d0ON_97{k(4D|L0y9T&v0h0K7=<>esKM?J4m)tA5ecox>wRJ zqQ$YsOcPsEKO~#J7js@iE*_W4De?@3SLK)IV$O@GGr5@#eM}7fRF2UfHDN>ce=Fv^ zE9N{*0_vbi|3ulNQWbw5M;!LX{ygpo`38R;M?Z+X^5=21vvw}$&*S`goIj8A=W+f# z&Y#B}=3K^~$7Sbo{yff~$NBR(U5oSA<2d){I}$VZy@|gb$M+_C_-=Ev`0H{0dYoO8 z^Vj42^*FmG=dZ_+3;ueXzaHnW$NB4V{(79h9_O#eW%pM4`zpC^?(eJ2?y2<mRdSz! zzpv8YSLyGo^!HVAPo=-FlKT<fmAU&K{e6|XCg<;~<Q^`6U!}jV(%)C<pO5p;$NA^u zcm~6sk@L^T@!Y_DJae%n$Fp(%`8b{#;Mq9;d>nlyuEqK5ah>d1oWCB&GXVbixa=7@ z|9l+JN%4%Fe?HDXA7{_V`RC(!4$_ZvqA^Y$;QpOm;>S7Bv%r3w6F<(0V_XA2&I!*y z`*BYEI46v8;>S5*Oanj8NjBDrALqo6b7Eti_;F7BI46Fb6UNwMOd8|It7OcafO|Up zc$I#<%EKC~(vMfk7)^|cmSK!UKVBtcv274PUM1hVF@BgIuaf8IxYx&zS4sP@hOy=r ziyyDjk5_49R{HTO{dko&W~Cpmk}_y8_CibiyaOC3n0vs_JHYrPs~IbHNc_A5j1N0N zf69rUcYuC2<HGuR2WT&td%({-5NC4__<0BXyaP7(fS-3ja}W4=2N++Txd;5b1Ag8C zKktC%9PslF@Jz*S@$<*={Ur11`1#}f{BbsioS#3A`-KPK=a0+gkh8htjGsTw&mU)V z$ocu>hW-3;e*U;@?l?bx9M7Zr`Qx%V<ox_`%=>tX@$mSr#rS!^`3};LbK=K2@#CCi zbAj6!Cw`ohp==ItKM#29=K=TgfctsCnG4*{1McSm_w#^j4sbsYxaI)&^MG@0?r~}C zi~YO<%RAYe1Ag8C?)f_@HV3$$2b||JZ4Pih54h$4-#_Hsq1%NqPW(70Lw=kSKh6o? NIdFgQ|2NLb{{V`?gXRDL
literal 0 HcmV?d00001
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 399f983983f..11dd143cb55 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -1879,38 +1879,46 @@ static IMFAsyncCallback *create_test_callback(BOOL check_media_event) return &callback->IMFAsyncCallback_iface; }
+#define wait_next_media_event(a, b, c, d, e, f) wait_next_media_event_(__LINE__, a, b, c, d, e, f) +static HRESULT wait_next_media_event_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, + DWORD timeout, MediaEventType *type, GUID *guid, PROPVARIANT *value) +{ + struct test_callback *impl = impl_from_IMFAsyncCallback(callback); + HRESULT hr = S_OK, status; + DWORD ret; + + hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ret = WaitForSingleObject(impl->event, timeout); + ok_(__FILE__, line)(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", ret); + hr = IMFMediaEvent_GetType(impl->media_event, type); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEvent_GetExtendedType(impl->media_event, guid); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEvent_GetValue(impl->media_event, value); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEvent_GetStatus(impl->media_event, &status); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + return status; +} + #define wait_media_event(a, b, c, d, e) wait_media_event_(__LINE__, a, b, c, d, e) static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) { - struct test_callback *impl = impl_from_IMFAsyncCallback(callback); MediaEventType type; - HRESULT hr, status; - DWORD ret; + HRESULT status; GUID guid;
do { - hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ret = WaitForSingleObject(impl->event, timeout); - ok_(__FILE__, line)(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", ret); - hr = IMFMediaEvent_GetType(impl->media_event, &type); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(value); + status = wait_next_media_event_(line, session, callback, timeout, &type, &guid, value); } while (type != expect_type);
- ok_(__FILE__, line)(type == expect_type, "got type %lu\n", type); - - hr = IMFMediaEvent_GetExtendedType(impl->media_event, &guid); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok_(__FILE__, line)(IsEqualGUID(&guid, &GUID_NULL), "got extended type %s\n", debugstr_guid(&guid));
- hr = IMFMediaEvent_GetValue(impl->media_event, value); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEvent_GetStatus(impl->media_event, &status); - ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); - return status; }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/tests/mf.c | 223 +++++++++++++++++++++++++++++++++++--- dlls/mf/tests/resource.rc | 17 +++ 2 files changed, 225 insertions(+), 15 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 11dd143cb55..ccd5d9b083a 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -1879,17 +1879,20 @@ static IMFAsyncCallback *create_test_callback(BOOL check_media_event) return &callback->IMFAsyncCallback_iface; }
-#define wait_next_media_event(a, b, c, d, e, f) wait_next_media_event_(__LINE__, a, b, c, d, e, f) +#define wait_next_media_event(a, b, c, d, e, f) wait_next_media_event_(__LINE__, a, b, c, d, e, f, FALSE) static HRESULT wait_next_media_event_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, - DWORD timeout, MediaEventType *type, GUID *guid, PROPVARIANT *value) + DWORD timeout, MediaEventType *type, GUID *guid, PROPVARIANT *value, BOOL timed_out) { struct test_callback *impl = impl_from_IMFAsyncCallback(callback); HRESULT hr = S_OK, status; DWORD ret;
- hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); + if (!timed_out) + hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); ret = WaitForSingleObject(impl->event, timeout); + if (ret == WAIT_TIMEOUT) + return WAIT_TIMEOUT; ok_(__FILE__, line)(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", ret); hr = IMFMediaEvent_GetType(impl->media_event, type); ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -1903,9 +1906,9 @@ static HRESULT wait_next_media_event_(int line, IMFMediaSession *session, IMFAsy return status; }
-#define wait_media_event(a, b, c, d, e) wait_media_event_(__LINE__, a, b, c, d, e) +#define wait_media_event(a, b, c, d, e) wait_media_event_(__LINE__, a, b, c, d, e, FALSE) static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, - MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) + MediaEventType expect_type, DWORD timeout, PROPVARIANT *value, BOOL timed_out) { MediaEventType type; HRESULT status; @@ -1914,7 +1917,7 @@ static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCal do { PropVariantClear(value); - status = wait_next_media_event_(line, session, callback, timeout, &type, &guid, value); + status = wait_next_media_event_(line, session, callback, timeout, &type, &guid, value, timed_out); } while (type != expect_type);
ok_(__FILE__, line)(IsEqualGUID(&guid, &GUID_NULL), "got extended type %s\n", debugstr_guid(&guid)); @@ -1997,15 +2000,20 @@ static void test_media_session_events(void) IMFMediaType *input_type, *output_type; IMFTopologyNode *src_node, *sink_node; IMFPresentationDescriptor *pd; + MediaEventType event_type; IMFMediaSession *session; + BOOL selected, timed_out; IMFStreamDescriptor *sd; IMFAsyncResult *result; IMFMediaSource *source; + IMFMediaSink *renderer; + IMFStreamSink *stream; IMFTopology *topology; IMFMediaEvent *event; PROPVARIANT propvar; HRESULT hr; ULONG ref; + GUID guid;
stream_sink.handler = &handler.IMFMediaTypeHandler_iface; stream_sink.media_sink = &media_sink.IMFMediaSink_iface; @@ -2371,20 +2379,14 @@ static void test_media_session_events(void) /* sometimes briefly leaking */ IMFMediaSession_Release(session);
- IMFAsyncCallback_Release(callback); - if (handler.current_type) IMFMediaType_Release(handler.current_type); handler.current_type = NULL;
- hr = IMFTopology_Clear(topology); + hr = IMFTopologyNode_DeleteAllItems(src_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_DeleteAllItems(sink_node); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ref = IMFTopologyNode_Release(src_node); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopologyNode_Release(sink_node); - ok(ref == 0, "Release returned %ld\n", ref); - ref = IMFTopology_Release(topology); - ok(ref == 0, "Release returned %ld\n", ref);
ref = IMFMediaSource_Release(source); ok(ref == 0, "Release returned %ld\n", ref); @@ -2396,6 +2398,197 @@ static void test_media_session_events(void) ok(ref == 0, "Release returned %ld\n", ref);
+ /* MFMediaSource playback */ + + source = create_media_source(L"audiodata.wav", L"audio/wav"); + hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); + ok(selected, "got selected %u.\n", !!selected); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_source_node(source, -1, src_node, pd, sd); + + hr = MFCreateAudioRenderer(NULL, &renderer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSink_GetStreamSinkByIndex(renderer, 0, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_sink_node(stream, -1, sink_node); + IMFStreamSink_Release(stream); + IMFMediaSink_Release(renderer); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + 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, MESessionTopologySet, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MEEndOfPresentation, 5000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MESessionEnded, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + 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); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* sometimes briefly leaking */ + IMFMediaSession_Release(session); + IMFMediaSource_Release(source); + IMFPresentationDescriptor_Release(pd); + IMFStreamDescriptor_Release(sd); + + + /* test successive commands */ + + source = create_media_source(L"audiodata.wav", L"audio/wav"); + hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); + ok(selected, "got selected %u.\n", !!selected); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_source_node(source, -1, src_node, pd, sd); + + hr = MFCreateAudioRenderer(NULL, &renderer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSink_GetStreamSinkByIndex(renderer, 0, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_sink_node(stream, -1, sink_node); + IMFStreamSink_Release(stream); + IMFMediaSink_Release(renderer); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Pause(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MESessionPaused, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MESessionStopped, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + /* FIXME: sometimes (rarely) triggers a Wine race condition */ + hr = wait_next_media_event(session, callback, 2000, &event_type, &guid, &propvar); + timed_out = (hr == WAIT_TIMEOUT); + todo_wine_if(timed_out) + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (!timed_out) + { + ok(IsEqualGUID(&guid, &GUID_NULL), "got guid %s\n", debugstr_guid(&guid)); + ok(event_type == MEEndOfPresentation, "got type %lu\n", event_type); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MESessionEnded, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + } + + hr = IMFMediaSession_Pause(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Pause(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = wait_media_event_(__LINE__, session, callback, MESessionPaused, 1000, &propvar, timed_out); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event_(__LINE__, session, callback, MESessionStopped, 1000, &propvar, timed_out); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event_(__LINE__, session, callback, MESessionPaused, 1000, &propvar, timed_out); + ok(hr == MF_E_SESSION_PAUSEWHILESTOPPED, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + 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); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* sometimes briefly leaking */ + IMFMediaSession_Release(session); + IMFMediaSource_Release(source); + IMFPresentationDescriptor_Release(pd); + IMFStreamDescriptor_Release(sd); + + + hr = IMFTopology_Clear(topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ref = IMFTopologyNode_Release(src_node); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopologyNode_Release(sink_node); + ok(ref == 0, "Release returned %ld\n", ref); + ref = IMFTopology_Release(topology); + ok(ref == 0, "Release returned %ld\n", ref); + + /* sometimes briefly leaking */ + IMFAsyncCallback_Release(callback); + hr = MFShutdown(); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); } diff --git a/dlls/mf/tests/resource.rc b/dlls/mf/tests/resource.rc index 25768d21983..e9e13565bf8 100644 --- a/dlls/mf/tests/resource.rc +++ b/dlls/mf/tests/resource.rc @@ -20,9 +20,26 @@
#include "windef.h"
+/* Generated with: + * gst-launch-1.0 audiomixer name=mix ! \ + * audio/x-raw,format=F32LE,rate=22050,channels=2 ! \ + * filesink location=dlls/mf/tests/audiodata.bin \ + * audiotestsrc freq=400 volume=0.2 num-buffers=1 \ + * samplesperbuffer=22050 ! mix. \ + * audiotestsrc freq=600 volume=0.2 num-buffers=1 \ + * samplesperbuffer=22050 timestamp-offset=20000000 ! mix. + */ /* @makedep: audiodata.bin */ audiodata.bin RCDATA audiodata.bin
+/* Generated with: + * gst-launch-1.0 filesrc location=dlls/mf/tests/audiodata.bin num-buffers=10 ! \ + * audio/x-raw,format=F32LE,rate=22050,channels=2,layout=interleaved ! \ + * wavenc ! filesink location=dlls/mf/tests/audiodata.wav + */ +/* @makedep: audiodata.wav */ +audiodata.wav RCDATA audiodata.wav + /* @makedep: audioconvdata.bin */ audioconvdata.bin RCDATA audioconvdata.bin
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mf/tests/mf.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index ccd5d9b083a..75adab4ec52 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -2577,6 +2577,87 @@ static void test_media_session_events(void) IMFStreamDescriptor_Release(sd);
+ /* calling IMFMediaSession_Stop near MEEndOfPresentation preempts MESessionEnded */ + + source = create_media_source(L"audiodata.wav", L"audio/wav"); + hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); + ok(selected, "got selected %u.\n", !!selected); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_source_node(source, -1, src_node, pd, sd); + + hr = MFCreateAudioRenderer(NULL, &renderer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSink_GetStreamSinkByIndex(renderer, 0, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_sink_node(stream, -1, sink_node); + IMFStreamSink_Release(stream); + IMFMediaSink_Release(renderer); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_media_event(session, callback, MEEndOfPresentation, 2000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_next_media_event(session, callback, 500, &event_type, &guid, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(IsEqualGUID(&guid, &GUID_NULL), "got guid %s\n", debugstr_guid(&guid)); + ok(event_type == MESessionTopologyStatus, "got type %lu\n", event_type); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar); + hr = wait_next_media_event(session, callback, 500, &event_type, &guid, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(IsEqualGUID(&guid, &GUID_NULL), "got guid %s\n", debugstr_guid(&guid)); + if (event_type == MESessionCapabilitiesChanged) /* > w7 */ + { + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + hr = wait_next_media_event(session, callback, 500, &event_type, &guid, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(IsEqualGUID(&guid, &GUID_NULL), "got guid %s\n", debugstr_guid(&guid)); + } + todo_wine + ok(event_type == MESessionStopped, "got type %lu\n", event_type); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologiesCleared, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + 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); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* sometimes briefly leaking */ + IMFMediaSession_Release(session); + IMFMediaSource_Release(source); + IMFPresentationDescriptor_Release(pd); + IMFStreamDescriptor_Release(sd); + + hr = IMFTopology_Clear(topology); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ref = IMFTopologyNode_Release(src_node);
From: Rémi Bernon rbernon@codeweavers.com
MR !607 was trying to fix an issue with Life Is Strange Remastered, but although it fixed some race conditions with presentation end, the issue it was trying to fix is still there.
The game calls IMFMediaSession_Stop while the presentation is ending, expects that command to quickly execute, interrupting the presentation end and emitting a MESessionStopped event instead of the MESessionEnded.
Delaying the Stop command and emitting the MESessionEnded event breaks the game assumptions and it crashes. --- dlls/mf/session.c | 12 +++++++++++- dlls/mf/tests/mf.c | 1 - 2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index b2371763150..bee10cad746 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -2524,6 +2524,13 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface,
EnterCriticalSection(&session->cs);
+ if ((session->presentation.flags & SESSION_FLAG_END_OF_PRESENTATION) && op->command != SESSION_CMD_STOP) + { + WARN("session %p command is ending, waiting for it to complete.\n", session); + LeaveCriticalSection(&session->cs); + return S_OK; + } + if (session->presentation.flags & SESSION_FLAG_PENDING_COMMAND) { WARN("session %p command is in progress, waiting for it to complete.\n", session); @@ -2550,6 +2557,9 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface, session_pause(session); break; case SESSION_CMD_STOP: + if (session->presentation.flags & SESSION_FLAG_END_OF_PRESENTATION) + session_set_topo_status(session, S_OK, MF_TOPOSTATUS_ENDED); + session_clear_end_of_presentation(session); session_stop(session); break; case SESSION_CMD_CLOSE: @@ -3521,7 +3531,7 @@ static void session_raise_end_of_presentation(struct media_session *session) { if (session_nodes_is_mask_set(session, MF_TOPOLOGY_MAX, SOURCE_FLAG_END_OF_PRESENTATION)) { - session->presentation.flags |= SESSION_FLAG_END_OF_PRESENTATION | SESSION_FLAG_PENDING_COMMAND; + session->presentation.flags |= SESSION_FLAG_END_OF_PRESENTATION; IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, NULL); } } diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 75adab4ec52..4bf99f6dbd9 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -2630,7 +2630,6 @@ static void test_media_session_events(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(IsEqualGUID(&guid, &GUID_NULL), "got guid %s\n", debugstr_guid(&guid)); } - todo_wine ok(event_type == MESessionStopped, "got type %lu\n", event_type); ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt);
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=127334
Your paranoid android.
=== w7u_2qxl (32 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w7u_adm (32 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w7u_el (32 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc. mf.c:5075: Test failed: WaitForSingleObject returned 0x102 0d14:mf: unhandled exception c0000094 at 0042AA86
=== w8 (32 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w8adm (32 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w864 (32 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w1064v1507 (32 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w1064v1809 (32 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w1064_tsign (32 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w10pro64 (32 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w864 (64 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w1064v1507 (64 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w1064v1809 (64 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w1064_2qxl (64 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w1064_adm (64 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w1064_tsign (64 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w10pro64 (64 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w10pro64_en_AE_u8 (64 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w10pro64_ar (64 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w10pro64_ja (64 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== w10pro64_zh_CN (64 bit report) ===
mf: mf.c:2547: Test failed: Unexpected hr 0xc00d36fc.
=== debian11 (32 bit report) ===
mf: mf: Timeout
=== debian11 (32 bit ar:MA report) ===
mf: mf: Timeout
=== debian11 (32 bit de report) ===
mf: mf: Timeout
=== debian11 (32 bit fr report) ===
mf: mf: Timeout
=== debian11 (32 bit he:IL report) ===
mf: mf: Timeout
=== debian11 (32 bit hi:IN report) ===
mf: mf: Timeout
=== debian11 (32 bit ja:JP report) ===
mf: mf: Timeout
=== debian11 (32 bit zh:CN report) ===
mf: mf: Timeout
=== debian11b (32 bit WoW report) ===
mf: mf: Timeout
=== debian11b (64 bit WoW report) ===
mf: mf: Timeout
v2: Made a simpler fix and added a few tests.
Fwiw I think the behavior around session presentation end is still not completely correct, but this should fix the crash with that game at least.
Extending the tests more shows that other commands such as Pause also should preempt the presentation end, interrupting it, and that it should be resumed whenever Start is called again. Matching the event sequence precisely will require more changes though.
There's also some very rare race condition when commands are called in sequence, where the presentation never ends after some Start/Stop/Pause/Start sequence. I'm not completely sure where it's coming from.
I don't see any failed test but the test returned 258 and the pipeline failed for some reason. I'll have a look.