Marked as WIP since it depends on !8175 for the test to run reliably (and currently includes its commits).
Tests should be run both with HKCU\Software\Wine\MediaFoundation\DisableGstByteStreamHandler enabled and disabled.
-- v3: mfsrcsnk: Support thinning. winedmo: Support thinning. winegstreamer: Support thinning in media source. winegstreamer: Support thinning in wg_parser. mf: Support thinning in media session. mf/tests: Add tests for thinning.
From: Charlotte Pabst cpabst@codeweavers.com
Before this, stopping and restarting the media session could cause MFT streams to get stuck in the "already pending" state, which would lead to no more samples getting delivered. --- dlls/mf/session.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 483ea6f904f..a9213d2cf18 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1014,8 +1014,13 @@ static void session_flush_nodes(struct media_session *session) { if (node->type == MF_TOPOLOGY_OUTPUT_NODE) IMFStreamSink_Flush(node->object.sink_stream); - else if (node->type == MF_TOPOLOGY_TRANSFORM_NODE) + else if (node->type == MF_TOPOLOGY_TRANSFORM_NODE) { + UINT i; + for (i = 0; i < node->u.transform.output_count; i++) + node->u.transform.outputs[i].requests = 0; + IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_COMMAND_FLUSH, 0); + } } }
From: Charlotte Pabst cpabst@codeweavers.com
This reverts commit 18c0dd7390b354d2e505e54cd3f3f9b08226b265.
The actual bug that broke sample delivery was in mf session code.
This commit is faulty since the Flush() method - which is called at session Stop, but runs after sample_grabber_set_state - already releases pending items, while processing markers immediately. --- dlls/mf/samplegrabber.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/dlls/mf/samplegrabber.c b/dlls/mf/samplegrabber.c index 016c68f98b8..de599139736 100644 --- a/dlls/mf/samplegrabber.c +++ b/dlls/mf/samplegrabber.c @@ -1191,7 +1191,6 @@ static HRESULT sample_grabber_set_state(struct sample_grabber *grabber, enum sin sample_grabber_cancel_timer(grabber); release_samples(grabber); grabber->sample_count = MAX_SAMPLE_QUEUE_LENGTH; - sample_grabber_release_pending_items(grabber); }
if (state == SINK_STATE_RUNNING && grabber->state != SINK_STATE_RUNNING)
From: Charlotte Pabst cpabst@codeweavers.com
--- dlls/mf/tests/longtest.mp4 | Bin 0 -> 22658 bytes dlls/mf/tests/mf.c | 244 +++++++++++++++++++++++++++++++- dlls/mf/tests/resource.rc | 13 ++ dlls/mf/tests/rgb32-3frames.bmp | Bin 0 -> 49314 bytes dlls/mf/tests/thinning_test.mp4 | Bin 0 -> 7199 bytes 5 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 dlls/mf/tests/longtest.mp4 create mode 100644 dlls/mf/tests/rgb32-3frames.bmp create mode 100644 dlls/mf/tests/thinning_test.mp4
diff --git a/dlls/mf/tests/longtest.mp4 b/dlls/mf/tests/longtest.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..9015490d8f415313a9a7c0479238af6c8caad52d GIT binary patch literal 22658 zcmZQzU{FXasVvAXFfn3aU|;~zxdkSMnZ^0JnZ@}aF^;sN)Kmrri#@q1i6smS3{0E? z3=9lX=_w2%Yi_!z9f)dXW)5m#VEod+P-@`bC32d9fq^yS^_xvP3=B*!*jWGne{#q5 z<=$sEJ+@ENxuI}V`Q7^pBQp~PU4`WQqErP#Fw;ZN$jn6FH^9|h*F?cF%vm8hKP5F; zL07>!zo0TFHLXO!$iTo@*T}#Kq%otUq`*pFzr4I$uPieqH9sdYPcOeHT_0qwUPei7 zj)Ja2enClQeqOPaLULkKVzRBFLQ!g(t+7H%YEn*qa<;9Zm4TIkLSkNGPGxbbtwDvc zl|hA}p|L`4s%=JUg+g&@Qf{iPxk5p4rL7@|j4w*DHPkaOurkmyFi^<NtVm6X2bpN7 zkeeD`l$e*EYHMhwkepGJpPLw;n`&#QP*RkdlapC&Yp7sRVUdztVr!_7T$pQXppcT9 zm{OIWmuhQdsAFiTkd|0n5?@f9oml`<0WqMkAU;1WtvI#BR@X?OB%>%bF{RknNFgUb zKRYoaH8CX~CT6HmoRgWHiV!eR$SVTdk(`;ESORiKW?o5ZQBGoVYKpCaLQ+m?QDS9$ za(-?>Vu`JRLUMjyaY<2PW?pJad}dxrQ6fl9T2W$dYO$@cLQ;G|Wl>^oW{RzmLQ;HU zN@4-X7fJC+nTf?9OEQa6!Tu^w%}mcINwPIm$S+9Ei%-ulur*MC2o~5HDP*TsX6BXH z8d+LGnen-qdA3HD3dPB(d8x^zCAKC83SiqoL0XhroMCIAP?Q`G_by1jDA_hSsYoF= zsiY_s<gKL4k|K}?jSNgI6hN$!{2WjWDHMV{XKSEmW}r}50I~>7Csx>+St=A36qlqH z*qSJmC6&eJCRTv-*@8^~2_%)K6=znZ+8P=eSt#Ts=EP?drP$`>=cOv7W#;6hg2FYk zAigNEBs1UEP|w6bA+gZb&<d0)7#bKD7@1Q$S{S7MzxnTHzwJqd)Ry9l_1~}0-l!;j zJ?!$=U%S=6q#XK?ao}CjM4g>?w)(w|ws?5;a`(Ywv%|%>bTrEBo$t9%`MouLa`*I> z{&s==Uv6Fxx~VWZ`d>$Y(~tF1>+D#wzf0}N^%ilip1}Dvs{ZJ&Z>QwmoV7Z0WV-sZ zGl~C}mn-V#xc{D*zP`uSdauOtN_F)oCEpL9O?_8g_vy##)w47o-Y9zed?DY$Iz6Sy z&-cm;3p!h>O75Ppz-p1t-3|ARE5B*zaO*Crz8!S>mOy@Id-kf;9=wLF>(%m9^QR=} zU6$#d$it)_-88Ll-P9xfTypFFl^a%RCfhD_`4G1L=Xy51yROCu!R3Pi0|NuM<1CdN z$MyeiEnDmW77SouVBmC|=Ty-w&rsqB7vf-?mssMM&uEA&lAPse&vYCt0y0a`aaM-s z3df9@J{+e1K_()!b34v!a~0GP|Icu~5v(3$4F}`Ar6rF3j8~CGmS;J}GggB|pk}V| zT;Z4lHxuDXZpV42Tt#&x{v*4SlX2e5632Rm^9^7lK-O?F&U>AO5Q3We0mW2gm(J(I zV(NTdu&D^gb284?$1rua35u!6Zk->5&D27$smN}fUyNyL1&XOCZk+}-6=4-8<NU2) zQ&HTy9c(H@2oeIb_n?@HFo4@}{w=7f2&*_5=l=woisIJan5I5JF%@9|x8poHsHq66 zI2q^JVwgJ59>dgGDk!ER4B&R0=L9tsVHGDht)i!aWK2^*sTGm35C(u#E0&bC6zo<= zAVO2ta<Hk8K!JqotkLv3nqEiK>uC8pTE32!ucPgO(e}V-dtkIZFxnm%Z4ZpL2S(ci zqwRsw_P}U+V6;6j+8!8f4~(`4XwV)Ia-6jaGFB|&Bk}*=wWYHWgT<VT^G<??Yat`J z#7~bh3Z`~6F|h5Y#*`7${xA2w%!?Ei3H!fP;lg2WBem6&U*C4Sw(fG#trPQ}2+QBP z^;ds;&YA}mTi)<bisgvjdhtL1_LaubDgys>|Mc=ltqm7_>2>&zk@mc=?I&vvx?l1! zSiJGIlt$f2PMIl!tAkrbgYW;--@y~i(q7ng%3w+1TV8=T6BH!u8y7M*Y|Hk$^8P!^ zOSiYa6ZK?1t&~2fzA?D{_@vhi+bT<(A4-+7Df9PA{(Pcby|ii9-!*I6cVC_GwnBUH ziRi_Qnnr5+74J6cvTWZx88P5Z>;w{gP#zNa(BX3&gYt+eBt-0i2jvC9(>(}5PVf{` zJ@Tv)C*wR?4$4EE4;|_!q7XxND_N6B$kP&oViE}v_kxaSlSn9T#WRTn2}DR5m``vL z2@!<&Cy|g%g-#+NyOqpIBotF|q*ufw5{g^fpi@%_!O`@}$vB_Rlb*<FU^Kmgr|3u1 zD<T9?%B;aKNk5uiAw@2<jzw)GB8pOQdjK*?j}RO!Uq{PV_@WX>GJ>=+M%x3>MNp7H zf#$8z_A6>99HlN8ZNHATUq{;mcoywJijvXt6&eDNMW&EI87*HSAu!tC8trePEs92| zV@J!^(ejnFMf{`X>*)9ZN*bWS_yBDtk+?wnjwJrS`tM{vm*dUMC8w|Qe>i^R!IbGY zs#bT(9ADG%^3cw&{nM977RPR4=dhQQ=xcSjs@mG<;x%JiP2}OE%_a4Bq<<wxc6dBt z|My^y#?heJk<1l-2e*~ZI`>;fFvh#CobR#u`S805&J#*5Bv*=O$gi2Q_0xRO%Vuh- z+%K(!IF6=#o&NE-|ILf8H<e!AnzBlwFJ7BVSi`7`&9~!`>%Lr%j=saoj9urH91kh) zJzBAj|8=CO!LHv$Mms+5JGJrc-cD{t#%~^r$_!nkMQ0pnn!8i~Ol*nFh7)$buF88( zKDxO0t>I~(<_s}|=v_S>4`*v!YBm=QPw_XuJ^j}d>paHG7KgtHe7nP=YysYz18N~~ zJI+$fajgG$t=FxQfkA#=Zhn3l0|P@&ZdpbOXrJQ~uWi8~%)n4;z`&^`!NAA>LLey! zW`t015{(ZU8G_4UGME?`<ldGPC1x`)FvOH(gY9Go*$2W5FsnfJqU%Mo2h~vw3}Aaf z1PC)QsHCQp6f-a|$fV{JgZ5^EOk)K5hk^YPM6VoAZc1h%XrE_p3fLVCAUA+8%<rK` zK-w5|Gg5Mjz$^v^2GF)j7#l=~fwozCB<7{$q=MAQZqLokOJiVQ5Gl(A8;a~~l@y4a zOiEEINDW9oNS>#(C`SR}dD-&flB66428Q#+CB@(ffrvBgPb^CYZ3Tnr0nrHTz~I2( z!N35<AW@L1AR0H8`2U}Qfx#%TEZLbcg+b*1Lwp-o8R7d@8AOswiXZ_BaxsJ0N(Kf7 z;pF_BqP*ms3I;YXWME(rC`c?WfI1!I7>VK%aD0Fq#K^$F6v4p2{1OyD#U;g{cmjzt zL)0^Zw#SA;<&r@X5OGkff%JiB&XVGi;wq>dC~ZeDFtC6eBnom40|SE)6iYKOFo49+ z`NB|jF!@nF0|O`wM#BS=UO-_1!lU6a8XlnX1C$m(n9}qh11WbHouK6s$V?cf)a+6H zqiL0zX>~L`jqLOUE32#_WtGs0WOy5n3DiDgWB|3L!EJWoUPv2_{St%*x6Bx!Z9GN> zhEaJ)xQvDmBpgQ52P7Q8eW20&F<L&1mY?8yWVC#QlnbNfBP1L~O8LmW8`ZzCd@$N> zf`kh!JVx^cBs@k+z8FpakoX==|B!GP&Hs>a7|s8Xa2PEgAmK1tK0v|&y?j7vS201y zt3--RlJmhT!F>(}(6o4DX-WxbE;ukZ6-<Mq6=CzfxfwY{xha{T`CX7W9IIqPCYNPa hmgIvB6iG=0YX(htGJuZUWnf^ig&J&1ioqNpg8_`BAt3+&
literal 0 HcmV?d00001
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 37c1b0be456..72903b48b66 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -2625,6 +2625,9 @@ struct test_grabber_callback IMFCollection *samples; HANDLE ready_event; HANDLE done_event; + HANDLE new_ready_event; + + BOOL enable_timestamps; };
static struct test_grabber_callback *impl_from_IMFSampleGrabberSinkCallback(IMFSampleGrabberSinkCallback *iface) @@ -2661,6 +2664,8 @@ static ULONG WINAPI test_grabber_callback_Release(IMFSampleGrabberSinkCallback * if (!refcount) { IMFCollection_Release(grabber->samples); + if (grabber->new_ready_event && grabber->new_ready_event != grabber->ready_event) + CloseHandle(grabber->new_ready_event); if (grabber->ready_event) CloseHandle(grabber->ready_event); if (grabber->done_event) @@ -2673,6 +2678,10 @@ static ULONG WINAPI test_grabber_callback_Release(IMFSampleGrabberSinkCallback *
static HRESULT WINAPI test_grabber_callback_OnClockStart(IMFSampleGrabberSinkCallback *iface, MFTIME time, LONGLONG offset) { + struct test_grabber_callback *grabber = impl_from_IMFSampleGrabberSinkCallback(iface); + IMFCollection_RemoveAllElements(grabber->samples); + if (grabber->new_ready_event) + grabber->ready_event = grabber->new_ready_event; return S_OK; }
@@ -2717,7 +2726,7 @@ static HRESULT WINAPI test_grabber_callback_OnProcessSample(IMFSampleGrabberSink hr = IMFSample_SetSampleFlags(sample, sample_flags); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); /* FIXME: sample time is inconsistent across windows versions, ignore it */ - hr = IMFSample_SetSampleTime(sample, 0); + hr = IMFSample_SetSampleTime(sample, grabber->enable_timestamps ? sample_time : 0); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSample_SetSampleDuration(sample, sample_duration); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -6802,6 +6811,238 @@ static void test_media_session_Close(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); }
+static void grab_samples_from_start(IMFMediaSession *session, IMFAsyncCallback *callback, struct test_grabber_callback *grabber_callback, UINT n) +{ + DWORD res; + PROPVARIANT propvar; + HRESULT hr; + IMFMediaStream *stream = NULL; + + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStopped, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 0; + 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); + + for (UINT i = 0; i < n; i++) { + res = WaitForSingleObject(grabber_callback->new_ready_event, 5000); + ok(!res, "WaitForSingleObject returned %#lx\n", res); + + if (res) + break; + + if (i+1 == n) + grabber_callback->ready_event = NULL; + + SetEvent(grabber_callback->done_event); + } +} + +static void test_media_session_thinning(void) +{ + media_type_desc video_rgb32_desc = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), + }; + const struct buffer_desc buffer_desc = + { + .length = 64 * 64 * 4, + .compare = compare_rgb32, .compare_rect = {.right = 64, .bottom = 64}, + .dump = dump_rgb32, .size = {.cx = 64, .cy = 64}, + }; + struct sample_desc thin_sample_desc[3] = + { + { .sample_duration = 333667, .buffer_count = 1, .buffers = &buffer_desc, .total_length = 64*64*4, .sample_time = 333666.666 * 0, }, + { .sample_duration = 333667, .buffer_count = 1, .buffers = &buffer_desc, .total_length = 64*64*4, .sample_time = 333666.666 * 5, }, + { .sample_duration = 333667, .buffer_count = 1, .buffers = &buffer_desc, .total_length = 64*64*4, .sample_time = 333666.666 * 10, }, + }; + struct sample_desc nonthin_sample_desc = + { .sample_duration = 333667, .buffer_count = 1, .buffers = &buffer_desc, .total_length = 64*64*4, .sample_time = 0, .repeat_count = 2, }; + struct test_grabber_callback *grabber_callback; + IMFRateControl *rate_control, *clock_rate_control; + IMFActivate *sink_activate; + IMFAsyncCallback *callback; + IMFMediaType *output_type; + IMFMediaSession *session; + IMFMediaSource *source; + IMFTopology *topology; + IMFClock *clock; + IMFSample *first_sample; + LONGLONG first_sample_time = 0; + PROPVARIANT propvar; + float rate; + BOOL thin; + 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"thinning_test.mp4", L"video/mp4"))) + { + todo_wine /* Gitlab CI Debian runner */ + win_skip("MP4 media source is not supported, skipping tests.\n"); + MFShutdown(); + return; + } + + callback = create_test_callback(TRUE); + + grabber_callback = impl_from_IMFSampleGrabberSinkCallback(create_test_grabber_callback()); + grabber_callback->done_event = CreateEventW(NULL, FALSE, FALSE, NULL); + grabber_callback->enable_timestamps = TRUE; + ok(!!grabber_callback->done_event, "CreateEventW failed, error %lu\n", GetLastError()); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateMediaType(&output_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(output_type, video_rgb32_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); + + topology = create_test_topology(source, sink_activate, NULL); + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopology_Release(topology); + + 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, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* get rate control interfaces */ + + hr = MFGetService((IUnknown *)session, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateControl, (void **)&rate_control); + ok(hr == S_OK, "Failed to get rate control interface, hr %#lx.\n", hr); + + hr = IMFMediaSession_GetClock(session, &clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFClock_QueryInterface(clock, &IID_IMFRateControl, (void **)&clock_rate_control); + ok(hr == S_OK, "Failed to get rate control, hr %#lx.\n", hr); + IMFClock_Release(clock); + + /* try faster rate */ + + hr = IMFRateControl_SetRate(rate_control, FALSE, 1.5f); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = wait_media_event(session, callback, MESessionRateChanged, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + rate = 0.0f; + thin = TRUE; + hr = IMFRateControl_GetRate(rate_control, &thin, &rate); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!thin, "Unexpected thin.\n"); + ok(rate == 1.5f, "Unexpected rate %f.\n", rate); + + /* try thinning */ + + hr = IMFRateControl_SetRate(rate_control, TRUE, 5.0f); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionRateChanged, 1000, &propvar); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + rate = 0.0f; + thin = FALSE; + hr = IMFRateControl_GetRate(rate_control, &thin, &rate); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(thin, "Unexpected !thin.\n"); + todo_wine + ok(rate == 5.0f, "Unexpected rate %f.\n", rate); + + /* clock is unaware of thinning */ + + rate = 0.0f; + thin = TRUE; + hr = IMFRateControl_GetRate(clock_rate_control, &thin, &rate); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!thin, "Unexpected thin.\n"); + todo_wine + ok(rate == 5.0f, "Unexpected rate %f.\n", rate); + + /* get first sample time */ + + grabber_callback->new_ready_event = grabber_callback->new_ready_event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!grabber_callback->new_ready_event, "CreateEventW failed, error %lu\n", GetLastError()); + + grab_samples_from_start(session, callback, grabber_callback, 1); + + hr = IMFCollection_GetElement(grabber_callback->samples, 0, (IUnknown **) &first_sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (hr == S_OK) { + hr = IMFSample_GetSampleTime(first_sample, &first_sample_time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFSample_Release(first_sample); + + nonthin_sample_desc.sample_time += first_sample_time; + for (UINT i = 0; i < 3; i++) + thin_sample_desc[i].sample_time += first_sample_time; + } + + /* test thinned */ + + thin_sample_desc[1].todo_time = TRUE; + thin_sample_desc[2].todo_time = TRUE; + + grab_samples_from_start(session, callback, grabber_callback, 3); + check_mf_sample_collection(grabber_callback->samples, thin_sample_desc, L"rgb32-3frames.bmp"); + + /* test non-thinned */ + + hr = IMFRateControl_SetRate(rate_control, FALSE, 1.0f); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + grab_samples_from_start(session, callback, grabber_callback, 3); + check_mf_sample_collection(grabber_callback->samples, &nonthin_sample_desc, L"rgb32-3frames.bmp"); + + /* shut down */ + + CloseHandle(grabber_callback->new_ready_event); + grabber_callback->new_ready_event = NULL; + + hr = IMFMediaSession_ClearTopologies(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); + 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); + hr = IMFActivate_ShutdownObject(sink_activate); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFRateControl_Release(rate_control); + IMFRateControl_Release(clock_rate_control); + IMFMediaSource_Release(source); + IMFAsyncCallback_Release(callback); + IMFMediaSession_Release(session); + IMFActivate_ShutdownObject(sink_activate); + IMFActivate_Release(sink_activate); + IMFSampleGrabberSinkCallback_Release(&grabber_callback->IMFSampleGrabberSinkCallback_iface); + + hr = MFShutdown(); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} + START_TEST(mf) { init_functions(); @@ -6838,4 +7079,5 @@ START_TEST(mf) test_MFEnumDeviceSources(); test_media_session_Close(); test_media_session_source_shutdown(); + test_media_session_thinning(); } diff --git a/dlls/mf/tests/resource.rc b/dlls/mf/tests/resource.rc index 873d373e079..8f093bd1af5 100644 --- a/dlls/mf/tests/resource.rc +++ b/dlls/mf/tests/resource.rc @@ -141,6 +141,10 @@ abgr32frame-crop.bmp RCDATA abgr32frame-crop.bmp /* @makedep: rgb32frame-grabber.bmp */ rgb32frame-grabber.bmp RCDATA rgb32frame-grabber.bmp
+/* Generated from running the tests on Windows */ +/* @makedep: rgb32-3frames.bmp */ +rgb32-3frames.bmp RCDATA rgb32-3frames.bmp + /* Generated from running the tests on Windows */ /* @makedep: rgb32frame-extra-width.bmp */ rgb32frame-extra-width.bmp RCDATA rgb32frame-extra-width.bmp @@ -177,3 +181,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=64,height=64,framerate=30000/1001 ! \ + * videoflip method=clockwise ! videoconvert ! \ + * x264enc key-int-max=5 ! mp4mux ! filesink location=dlls/mf/tests/thinning_test.mp4 + */ +/* @makedep: thinning_test.mp4 */ +thinning_test.mp4 RCDATA thinning_test.mp4 diff --git a/dlls/mf/tests/rgb32-3frames.bmp b/dlls/mf/tests/rgb32-3frames.bmp new file mode 100644 index 0000000000000000000000000000000000000000..cc29ad6e0faf5b02b99cf136832632be77ab6204 GIT binary patch literal 49314 zcmZ?rHFID912YB&1`P%V1`rp785k76;$Q&?3r_y~_m5!|kA}f$8W>FjqiJB|rGd-; z|1*qa{P6#OhLMbC|NqZ0l5z0YPYfd&yMF(|Fp@FD|Gx|)8T0=8!!VNZNRInazyD=W z{r_Y{<B^^Q_WZA57|Gb_aU;V>#{d5_FpOk;^S30!NX8>M?nnL3!0_=u<A}y1Jq_&q z&&e>7@v8sa3?msQe`a79$ynwu1H(wh+P@eWMlxpj&%iK>N5fz=4UEh*P`I&>VI*Vq zr)mr%8Q+k)$uN>}pI;xtNXCB|{xOW=(J&ZI10ypHTw?prFp}{@=Kl;M8PDSV&oGj4 zFvBN?k&Im!zA%hr%)s!MVI*T7hCd7=8IR<+ANBiR1{KyPBN~tNG_YH?hG8UQM}|g* zk&OQ_GBAu}e1k!fVI<>`9QUJsAK7_-H2z29e>4q@>@+ZXYQX5O=aIMTc{I*P<9sv? zke>!d@`(W;zmMb-13-Qs$tMPY{63OT3;_9kB%c@n^7}~NH9L||3;_9kq{shA_y6dr z0VDg=l#zU50Lbqn`NRN_-$(kc*^zu=0LbqnJ^n|!|3^;^7}=+$jN}soKz<*|CkBB0 zK9Wxi0Qr3+pBMo0`$#@90Oa@4-Qpv2xA<thkH$MF4UFUy13-Qs$tMPY{63OT3;_9k zB%c@n^84s+@sYV(d^Fxi;~kU+M)HXPAis~~69YhgAIT>Mfc!p^PYeM0eI%b40P_1t z-!(guPYeM0eWb_#NcaEfsR1MV)Rd8YVgShRBl*Mtkl#o8uE~*nVgShRBR&2{{XepI z4UfkEX#9_+fsvaAMo$eG-91eH?%~m}8x6bBG%)hg0BGm@NXDR@^CKC9cFvDv4B9zA zk}+uK{7A+leb+Q-=ln><BR&2{y8lN{4H!K&0JM94q+-y{`H_rA`mSlv&iRpyM|%8^ zbpMZ@8Zdfl0BHC8NX4L?^CKC9cFvDv4B9zAk}+uK{7A;2ox`JebO-rp8W@>r0JL*{ zBxBId`H_r4JLg9-2JM_5$r!YAcodKBARkQwBQp(vcFvDv4B9zAk}+uK{7A;2o%16Z zgLcl3WIWP$O@nsMk7PX3<A0?4fArLV(NhCJyXQwL2JM_5$#|sim;~*dAIW&6$N#AR TNA8Z{(YPOt`_VKoveN(nPbC;h
literal 0 HcmV?d00001
diff --git a/dlls/mf/tests/thinning_test.mp4 b/dlls/mf/tests/thinning_test.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..7e9f7206b5686a2aa79bbe2808dee42c76016d3e GIT binary patch literal 7199 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 E0NSC6RsaA1
literal 0 HcmV?d00001
From: Charlotte Pabst cpabst@codeweavers.com
--- dlls/mf/session.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index a9213d2cf18..823388c3065 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -260,6 +260,8 @@ struct media_session /* Latest SetRate() arguments. */ BOOL thin; float rate; + + BOOL thin_committed; } presentation; struct list topologies; struct list commands; @@ -1540,7 +1542,7 @@ static void session_set_rate(struct media_session *session, BOOL thin, float rat if (SUCCEEDED(hr)) hr = IMFRateControl_GetRate(session->clock_rate_control, NULL, &clock_rate);
- if (SUCCEEDED(hr) && (rate != clock_rate) && SUCCEEDED(hr = session_subscribe_sources(session))) + if (SUCCEEDED(hr) && (rate != clock_rate || thin != session->presentation.thin_committed) && SUCCEEDED(hr = session_subscribe_sources(session))) { LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) { @@ -1553,6 +1555,7 @@ static void session_set_rate(struct media_session *session, BOOL thin, float rat { session->presentation.flags |= SESSION_FLAG_PENDING_RATE_CHANGE; session->presentation.rate = rate; + session->presentation.thin = thin; return; } } @@ -1577,7 +1580,8 @@ static void session_complete_rate_change(struct media_session *session) session->presentation.flags &= ~SESSION_FLAG_PENDING_RATE_CHANGE; session_set_presentation_clock(session);
- hr = IMFRateControl_SetRate(session->clock_rate_control, session->presentation.thin, + session->presentation.thin_committed = session->presentation.thin; + hr = IMFRateControl_SetRate(session->clock_rate_control, FALSE, session->presentation.rate);
param.vt = VT_R4; @@ -4742,7 +4746,10 @@ static HRESULT WINAPI session_rate_control_GetRate(IMFRateControl *iface, BOOL *
TRACE("%p, %p, %p.\n", iface, thin, rate);
- return IMFRateControl_GetRate(session->clock_rate_control, thin, rate); + if (thin) + *thin = session->presentation.thin_committed; + + return IMFRateControl_GetRate(session->clock_rate_control, NULL, rate); }
static const IMFRateControlVtbl session_rate_control_vtbl =
From: Charlotte Pabst cpabst@codeweavers.com
--- dlls/winegstreamer/gst_private.h | 2 ++ dlls/winegstreamer/main.c | 12 +++++++ dlls/winegstreamer/unixlib.h | 8 +++++ dlls/winegstreamer/wg_parser.c | 54 ++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 60e0c3af8bc..2d5986fdb1b 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -81,6 +81,8 @@ void wg_parser_disconnect(wg_parser_t parser); bool wg_parser_get_next_read_offset(wg_parser_t parser, uint64_t *offset, uint32_t *size); void wg_parser_push_data(wg_parser_t parser, const void *data, uint32_t size);
+void wg_parser_set_thinning(wg_parser_t parser, BOOL thinning); + uint32_t wg_parser_get_stream_count(wg_parser_t parser); wg_parser_stream_t wg_parser_get_stream(wg_parser_t parser, uint32_t index);
diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index d18db8b21e8..c68cf0c04f1 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -240,6 +240,18 @@ void wg_parser_push_data(wg_parser_t parser, const void *data, uint32_t size) WINE_UNIX_CALL(unix_wg_parser_push_data, ¶ms); }
+void wg_parser_set_thinning(wg_parser_t parser, BOOL thinning) +{ + struct wg_parser_set_thinning_params params = + { + .parser = parser, + .thinning = thinning, + }; + TRACE("parser %#I64x, thinning %d.\n", parser, thinning); + + WINE_UNIX_CALL(unix_wg_parser_set_thinning, ¶ms); +} + uint32_t wg_parser_get_stream_count(wg_parser_t parser) { struct wg_parser_get_stream_count_params params = diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 83e38849fa9..1df26360aca 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -245,6 +245,12 @@ struct wg_parser_push_data_params UINT32 size; };
+struct wg_parser_set_thinning_params +{ + wg_parser_t parser; + BOOL thinning; +}; + struct wg_parser_get_stream_count_params { wg_parser_t parser; @@ -429,6 +435,8 @@ enum unix_funcs unix_wg_parser_get_next_read_offset, unix_wg_parser_push_data,
+ unix_wg_parser_set_thinning, + unix_wg_parser_get_stream_count, unix_wg_parser_get_stream,
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index e806298fb57..dd1812d2d0e 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -80,6 +80,7 @@ struct wg_parser bool output_compressed; bool no_more_pads, has_duration, error; bool err_on, warn_on; + bool thinning;
pthread_cond_t read_cond, read_done_cond; struct @@ -226,6 +227,19 @@ static NTSTATUS wg_parser_push_data(void *args) return S_OK; }
+static NTSTATUS wg_parser_set_thinning(void *args) +{ + const struct wg_parser_set_thinning_params *params = args; + struct wg_parser *parser = get_parser(params->parser); + BOOL thinning = params->thinning; + + pthread_mutex_lock(&parser->mutex); + parser->thinning = thinning; + pthread_mutex_unlock(&parser->mutex); + + return S_OK; +} + static NTSTATUS wg_parser_stream_get_current_format(void *args) { const struct wg_parser_stream_get_current_format_params *params = args; @@ -600,10 +614,46 @@ static void no_more_pads_cb(GstElement *element, gpointer user) pthread_cond_signal(&parser->init_cond); }
+static GstPadProbeReturn decoder_pad_probe(GstPad *pad, GstPadProbeInfo *info, gpointer user) +{ + struct wg_parser *parser = user; + GstBuffer *buffer; + bool thinning; + + pthread_mutex_lock(&parser->mutex); + thinning = parser->thinning; + pthread_mutex_unlock(&parser->mutex); + + if (thinning && (buffer = gst_pad_probe_info_get_buffer(info))) + { + if (GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT)) + { + GST_DEBUG("Stream is thinned; discarding delta frame."); + return GST_PAD_PROBE_DROP; + } + else + { + GST_DEBUG("Stream is thinned; found key frame."); + } + } + + return GST_PAD_PROBE_OK; +} + static void deep_element_added_cb(GstBin *self, GstBin *sub_bin, GstElement *element, gpointer user) { + struct wg_parser *parser = user; + if (element) + { + GstElementFactory *fact = GST_ELEMENT_GET_CLASS(element)->elementfactory; + const char *klass = gst_element_factory_get_klass(fact); + GstPad *sink; + if (strstr(klass, GST_ELEMENT_FACTORY_KLASS_DECODER) && (sink = gst_element_get_static_pad(element, "sink"))) + gst_pad_add_probe(sink, GST_PAD_PROBE_TYPE_BUFFER, decoder_pad_probe, parser, NULL); + set_max_threads(element); + } }
static gboolean sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) @@ -1911,6 +1961,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_parser_get_next_read_offset), X(wg_parser_push_data),
+ X(wg_parser_set_thinning), + X(wg_parser_get_stream_count), X(wg_parser_get_stream),
@@ -2308,6 +2360,8 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X(wg_parser_get_next_read_offset), X64(wg_parser_push_data),
+ X(wg_parser_set_thinning), + X(wg_parser_get_stream_count), X(wg_parser_get_stream),
From: Charlotte Pabst cpabst@codeweavers.com
--- dlls/mf/tests/mf.c | 7 ------- dlls/winegstreamer/media_source.c | 28 +++++++++++++++++++++------- 2 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 72903b48b66..2a22d775d68 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -6951,16 +6951,13 @@ static void test_media_session_thinning(void) hr = IMFRateControl_SetRate(rate_control, TRUE, 5.0f); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = wait_media_event(session, callback, MESessionRateChanged, 1000, &propvar); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
rate = 0.0f; thin = FALSE; hr = IMFRateControl_GetRate(rate_control, &thin, &rate); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(thin, "Unexpected !thin.\n"); - todo_wine ok(rate == 5.0f, "Unexpected rate %f.\n", rate);
/* clock is unaware of thinning */ @@ -6970,7 +6967,6 @@ static void test_media_session_thinning(void) hr = IMFRateControl_GetRate(clock_rate_control, &thin, &rate); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(!thin, "Unexpected thin.\n"); - todo_wine ok(rate == 5.0f, "Unexpected rate %f.\n", rate);
/* get first sample time */ @@ -6996,9 +6992,6 @@ static void test_media_session_thinning(void)
/* test thinned */
- thin_sample_desc[1].todo_time = TRUE; - thin_sample_desc[2].todo_time = TRUE; - grab_samples_from_start(session, callback, grabber_callback, 3); check_mf_sample_collection(grabber_callback->samples, thin_sample_desc, L"rgb32-3frames.bmp");
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index ab842b3e438..7cf3ad7c5a3 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -202,6 +202,7 @@ struct media_source SOURCE_SHUTDOWN, } state; float rate; + BOOL thin;
HANDLE read_thread; bool read_thread_shutdown; @@ -1279,23 +1280,36 @@ static HRESULT WINAPI media_source_rate_control_SetRate(IMFRateControl *iface, B { struct media_source *source = impl_from_IMFRateControl(iface); HRESULT hr; + BOOL old_thin;
FIXME("%p, %d, %f.\n", iface, thin, rate);
if (rate < 0.0f) return MF_E_REVERSE_UNSUPPORTED;
- if (thin) - return MF_E_THINNING_UNSUPPORTED; - if (FAILED(hr = IMFRateSupport_IsRateSupported(&source->IMFRateSupport_iface, thin, rate, NULL))) return hr;
EnterCriticalSection(&source->cs); + old_thin = source->thin; source->rate = rate; + source->thin = thin; LeaveCriticalSection(&source->cs);
- return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourceRateChanged, &GUID_NULL, S_OK, NULL); + wg_parser_set_thinning(source->wg_parser, thin); + + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourceRateChanged, &GUID_NULL, S_OK, NULL))) + return hr; + + if (old_thin != thin) + { + PROPVARIANT param; + param.vt = VT_BOOL; + param.boolVal = thin ? VARIANT_TRUE : VARIANT_FALSE; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MEStreamThinMode, &GUID_NULL, S_OK, ¶m); + } + + return hr; }
static HRESULT WINAPI media_source_rate_control_GetRate(IMFRateControl *iface, BOOL *thin, float *rate) @@ -1304,11 +1318,10 @@ static HRESULT WINAPI media_source_rate_control_GetRate(IMFRateControl *iface, B
TRACE("%p, %p, %p.\n", iface, thin, rate);
- if (thin) - *thin = FALSE; - EnterCriticalSection(&source->cs); *rate = source->rate; + if (thin) + *thin = source->thin; LeaveCriticalSection(&source->cs);
return S_OK; @@ -1734,6 +1747,7 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc IMFByteStream_AddRef(context->stream); object->file_size = context->file_size; object->rate = 1.0f; + object->thin = FALSE; InitializeCriticalSectionEx(&object->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO); object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs");
From: Charlotte Pabst cpabst@codeweavers.com
--- dlls/mfsrcsnk/media_source.c | 2 +- dlls/winedmo/main.c | 6 +++--- dlls/winedmo/unix_demuxer.c | 18 +++++++++++++++--- dlls/winedmo/unixlib.h | 1 + include/wine/winedmo.h | 2 +- 5 files changed, 21 insertions(+), 8 deletions(-)
diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index d7f20e511c8..9203fd33374 100644 --- a/dlls/mfsrcsnk/media_source.c +++ b/dlls/mfsrcsnk/media_source.c @@ -588,7 +588,7 @@ static HRESULT demuxer_read_sample(struct winedmo_demuxer demuxer, UINT *index,
if (FAILED(hr = create_media_buffer_sample(buffer_size, &sample, &output.pBuffer))) return hr; - if ((status = winedmo_demuxer_read(demuxer, index, &output, &buffer_size))) + if ((status = winedmo_demuxer_read(demuxer, index, &output, &buffer_size, FALSE))) { if (status == STATUS_BUFFER_TOO_SMALL) hr = E_PENDING; else if (status == STATUS_END_OF_FILE) hr = MF_E_END_OF_STREAM; diff --git a/dlls/winedmo/main.c b/dlls/winedmo/main.c index 1e91d8e2e51..6a462555f71 100644 --- a/dlls/winedmo/main.c +++ b/dlls/winedmo/main.c @@ -222,12 +222,12 @@ NTSTATUS CDECL winedmo_demuxer_destroy( struct winedmo_demuxer *demuxer ) return status; }
-NTSTATUS CDECL winedmo_demuxer_read( struct winedmo_demuxer demuxer, UINT *stream, DMO_OUTPUT_DATA_BUFFER *buffer, UINT *buffer_size ) +NTSTATUS CDECL winedmo_demuxer_read( struct winedmo_demuxer demuxer, UINT *stream, DMO_OUTPUT_DATA_BUFFER *buffer, UINT *buffer_size, BOOL thin ) { - struct demuxer_read_params params = {.demuxer = demuxer}; + struct demuxer_read_params params = {.demuxer = demuxer, .thin = thin}; NTSTATUS status;
- TRACE( "demuxer %#I64x, stream %p, buffer %p, buffer_size %p\n", demuxer.handle, stream, buffer, buffer_size ); + TRACE( "demuxer %#I64x, stream %p, buffer %p, buffer_size %p, thin %d\n", demuxer.handle, stream, buffer, buffer_size, thin );
buffer_lock( buffer, ¶ms.sample ); status = UNIX_CALL( demuxer_read, ¶ms ); diff --git a/dlls/winedmo/unix_demuxer.c b/dlls/winedmo/unix_demuxer.c index 7a1a262e6c9..ac41b2b8227 100644 --- a/dlls/winedmo/unix_demuxer.c +++ b/dlls/winedmo/unix_demuxer.c @@ -220,7 +220,7 @@ NTSTATUS demuxer_destroy( void *arg ) return STATUS_SUCCESS; }
-static NTSTATUS demuxer_filter_packet( struct demuxer *demuxer, AVPacket **packet ) +static NTSTATUS demuxer_filter_packet( struct demuxer *demuxer, AVPacket **packet, BOOL thin ) { struct stream *stream; int i, ret; @@ -233,7 +233,19 @@ static NTSTATUS demuxer_filter_packet( struct demuxer *demuxer, AVPacket **packe if (!(stream = demuxer->last_stream)) ret = 0; else { - if (!(ret = av_bsf_receive_packet( stream->filter, *packet ))) return STATUS_SUCCESS; + if (!(ret = av_bsf_receive_packet( stream->filter, *packet ))) { + if (thin) + { + if ((*packet)->flags & AV_PKT_FLAG_KEY) TRACE("Thinning: Found key frame.\n"); + else + { + TRACE("Thinning: Skipping delta frame.\n"); + av_packet_free( packet ); + continue; + } + } + return STATUS_SUCCESS; + } if (ret == AVERROR_EOF) stream->eos = TRUE; if (!ret || ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) ret = 0; else WARN( "Failed to read packet from filter, error %s.\n", debugstr_averr( ret ) ); @@ -280,7 +292,7 @@ NTSTATUS demuxer_read( void *arg )
TRACE( "demuxer %p, capacity %#x\n", demuxer, capacity );
- if ((status = demuxer_filter_packet( demuxer, &packet ))) return status; + if ((status = demuxer_filter_packet( demuxer, &packet, params->thin ))) return status;
params->sample.size = packet->size; if ((capacity < packet->size)) diff --git a/dlls/winedmo/unixlib.h b/dlls/winedmo/unixlib.h index cf37bd5342a..eda808e2cd0 100644 --- a/dlls/winedmo/unixlib.h +++ b/dlls/winedmo/unixlib.h @@ -121,6 +121,7 @@ struct demuxer_read_params struct winedmo_demuxer demuxer; UINT32 stream; struct sample sample; + BOOL thin; };
struct demuxer_seek_params diff --git a/include/wine/winedmo.h b/include/wine/winedmo.h index c068dfa1de8..ab592777eab 100644 --- a/include/wine/winedmo.h +++ b/include/wine/winedmo.h @@ -46,7 +46,7 @@ NTSTATUS CDECL winedmo_demuxer_check( const char *mime_type ); NTSTATUS CDECL winedmo_demuxer_create( const WCHAR *url, struct winedmo_stream *stream, UINT64 stream_size, INT64 *duration, UINT *stream_count, WCHAR *mime_type, struct winedmo_demuxer *demuxer ); NTSTATUS CDECL winedmo_demuxer_destroy( struct winedmo_demuxer *demuxer ); -NTSTATUS CDECL winedmo_demuxer_read( struct winedmo_demuxer demuxer, UINT *stream, DMO_OUTPUT_DATA_BUFFER *buffer, UINT *buffer_size ); +NTSTATUS CDECL winedmo_demuxer_read( struct winedmo_demuxer demuxer, UINT *stream, DMO_OUTPUT_DATA_BUFFER *buffer, UINT *buffer_size, BOOL thin ); NTSTATUS CDECL winedmo_demuxer_seek( struct winedmo_demuxer demuxer, INT64 timestamp ); NTSTATUS CDECL winedmo_demuxer_stream_lang( struct winedmo_demuxer demuxer, UINT stream, WCHAR *buffer, UINT len ); NTSTATUS CDECL winedmo_demuxer_stream_name( struct winedmo_demuxer demuxer, UINT stream, WCHAR *buffer, UINT len );
From: Charlotte Pabst cpabst@codeweavers.com
--- dlls/mfsrcsnk/media_source.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-)
diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index 9203fd33374..3d8b9e5bb73 100644 --- a/dlls/mfsrcsnk/media_source.c +++ b/dlls/mfsrcsnk/media_source.c @@ -240,6 +240,7 @@ struct media_source IMFByteStream *stream; WCHAR *url; float rate; + BOOL thin;
struct winedmo_demuxer winedmo_demuxer; struct winedmo_stream winedmo_stream; @@ -575,7 +576,7 @@ static HRESULT create_media_buffer_sample(UINT buffer_size, IMFSample **sample, return hr; }
-static HRESULT demuxer_read_sample(struct winedmo_demuxer demuxer, UINT *index, IMFSample **out) +static HRESULT demuxer_read_sample(struct winedmo_demuxer demuxer, UINT *index, IMFSample **out, BOOL thin) { UINT buffer_size = 0x1000; IMFSample *sample; @@ -588,7 +589,7 @@ static HRESULT demuxer_read_sample(struct winedmo_demuxer demuxer, UINT *index,
if (FAILED(hr = create_media_buffer_sample(buffer_size, &sample, &output.pBuffer))) return hr; - if ((status = winedmo_demuxer_read(demuxer, index, &output, &buffer_size, FALSE))) + if ((status = winedmo_demuxer_read(demuxer, index, &output, &buffer_size, thin))) { if (status == STATUS_BUFFER_TOO_SMALL) hr = E_PENDING; else if (status == STATUS_END_OF_FILE) hr = MF_E_END_OF_STREAM; @@ -635,7 +636,7 @@ static HRESULT media_source_read(struct media_source *source) if (source->state != SOURCE_RUNNING) return S_OK;
- if (FAILED(hr = demuxer_read_sample(source->winedmo_demuxer, &index, &sample)) && hr != MF_E_END_OF_STREAM) + if (FAILED(hr = demuxer_read_sample(source->winedmo_demuxer, &index, &sample, source->thin)) && hr != MF_E_END_OF_STREAM) { WARN("Failed to read stream %u data, hr %#lx\n", index, hr); return hr; @@ -1042,22 +1043,34 @@ static HRESULT WINAPI media_source_IMFRateControl_SetRate(IMFRateControl *iface, { struct media_source *source = media_source_from_IMFRateControl(iface); HRESULT hr; + BOOL old_thin;
FIXME("source %p, thin %d, rate %f, stub!\n", source, thin, rate);
if (rate < 0.0f) return MF_E_REVERSE_UNSUPPORTED; - if (thin) - return MF_E_THINNING_UNSUPPORTED;
if (FAILED(hr = IMFRateSupport_IsRateSupported(&source->IMFRateSupport_iface, thin, rate, NULL))) return hr;
EnterCriticalSection(&source->cs); + old_thin = source->thin; source->rate = rate; + source->thin = thin; LeaveCriticalSection(&source->cs);
- return IMFMediaEventQueue_QueueEventParamVar(source->queue, MESourceRateChanged, &GUID_NULL, S_OK, NULL); + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(source->queue, MESourceRateChanged, &GUID_NULL, S_OK, NULL))) + return hr; + + if (old_thin != thin) + { + PROPVARIANT param; + param.vt = VT_BOOL; + param.boolVal = thin ? VARIANT_TRUE : VARIANT_FALSE; + hr = IMFMediaEventQueue_QueueEventParamVar(source->queue, MEStreamThinMode, &GUID_NULL, S_OK, ¶m); + } + + return hr; }
static HRESULT WINAPI media_source_IMFRateControl_GetRate(IMFRateControl *iface, BOOL *thin, float *rate) @@ -1066,11 +1079,10 @@ static HRESULT WINAPI media_source_IMFRateControl_GetRate(IMFRateControl *iface,
TRACE("source %p, thin %p, rate %p\n", source, thin, rate);
- if (thin) - *thin = FALSE; - EnterCriticalSection(&source->cs); *rate = source->rate; + if (thin) + *thin = source->thin; LeaveCriticalSection(&source->cs);
return S_OK;
On Sat May 31 15:11:24 2025 +0000, Charlotte Pabst wrote:
so, one commit that removes the responsibility for `thin` from the clock, and another one that makes the media session handle it?
Yes, we have tests already that show that clock rejects thin=true argument as invalid.
On Sat May 31 15:10:28 2025 +0000, Charlotte Pabst wrote:
what exactly isn't right? setting `old_thin = thin`? I've fixed that now.
I haven't checked that, but events are wrong here - MEStreamThinMode is raised by streams. Next question would be if we should send it for every stream, or only for streams where it makes sense.
On Sat May 31 14:15:02 2025 +0000, Charlotte Pabst wrote:
I don't see how it's unrelated to test that thinning actually does what it promises, i.e. deliver only keyframes (here done by checking the timestamps because the MFSampleExtension_CleanPoint attribute isn't actually reliable in winegstreamer yet - GST_BUFFER_FLAG_DELTA_UNIT gets stripped by the decoder). And there are already existing tests in this file (i.e. test_sample_grabber_orientation) that depend on source behavior.
Thinning at media session level doesn't promise anything, it's up to sources to do something with it, or ignore it. Source might even request thinning on their own, along with a different rate value.