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.
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 | 250 +++++++++++++++++++++++++++++++- 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, 262 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 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
literal 0 HcmV?d00001
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 37c1b0be456..dc31e0c2228 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,244 @@ 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 */ + + hr = IMFMediaSession_GetClock(session, &clock); + ok(hr == S_OK, "Failed to get clock, 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); + + 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 +7085,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 dc31e0c2228..c812344d38e 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 */ @@ -6976,7 +6973,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 */ @@ -7002,9 +6998,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..1255d2ccebd 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 = 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; + 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..94ff7d37a6a 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; + 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;
Nikolay Sivov (@nsivov) commented about dlls/mf/tests/mf.c:
- 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;
- }
Why do we need a new file for this test?
Nikolay Sivov (@nsivov) commented about dlls/mf/tests/mf.c:
- 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);
This one is leaked I think.
Nikolay Sivov (@nsivov) commented about dlls/mf/tests/mf.c:
- /* clock is unaware of thinning */
- hr = IMFMediaSession_GetClock(session, &clock);
- ok(hr == S_OK, "Failed to get clock, 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);
- 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);
We don't really need to test this. Existing clock tests already have cases for MF_E_THINNING_UNSUPPORTED.
Nikolay Sivov (@nsivov) commented about dlls/mf/tests/mf.c:
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");
This is again too complicated and potentially unrelated. Source is responsible for acting on a thinning request, whatever samples it produces after makes no difference to pipeline logic, comparing to a regular playback.
What we need to test is that SetRate(thin=true) propagates to sources, and that MESessionRateChanged is raised accordingly.
Nikolay Sivov (@nsivov) commented about dlls/mf/session.c:
/* Latest SetRate() arguments. */ BOOL thin; float rate;
} presentation;BOOL thin_committed;
We need to figure out how to avoid this additional field.
Nikolay Sivov (@nsivov) commented about dlls/mf/session.c:
static HRESULT WINAPI session_rate_control_GetRate(IMFRateControl *iface, BOOL *thin, float *rate) { struct media_session *session = impl_session_from_IMFRateControl(iface);
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);
}
This should probably be a separate change, going together with SetRate(thin=false).
Nikolay Sivov (@nsivov) commented about dlls/mfsrcsnk/media_source.c:
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;
hr = IMFMediaEventQueue_QueueEventParamVar(source->queue, MEStreamThinMode, &GUID_NULL, S_OK, ¶m);
This one is documented to use VARIANT_TRUE/VARIANT_FALSE.
Nikolay Sivov (@nsivov) commented about dlls/winegstreamer/media_source.c:
- 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;
hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MEStreamThinMode, &GUID_NULL, S_OK, ¶m);
- }
This doesn't look right. Was it tested?
On Fri May 30 23:00:06 2025 +0000, Nikolay Sivov wrote:
This is again too complicated and potentially unrelated. Source is responsible for acting on a thinning request, whatever samples it produces after makes no difference to pipeline logic, comparing to a regular playback. What we need to test is that SetRate(thin=true) propagates to sources, and that MESessionRateChanged is raised accordingly.
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.
On Fri May 30 23:00:06 2025 +0000, Nikolay Sivov wrote:
Why do we need a new file for this test?
The existing test.mp4 doesn't even have multiple keyframes iirc, this file exists to lower the keyframe interval.
On Fri May 30 23:00:06 2025 +0000, Nikolay Sivov wrote:
We need to figure out how to avoid this additional field.
since the presentation clock does not support the thinning parameter, the media session needs to hold it, somehow. perhaps we should query a source for it or something?
does changing the rate of the presentation clock actually change the return value of the media session's GetRate() as well and is this tested? maybe the problem is that the media session should hold both rate and thin on its own.
this is also the reason I added the "clock is unaware of thinning" test in the first place, because an initial version of my patch just forwarded the thin parameter to the clock and made it support that, but that's wrong.