From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/mfmediaengine/main.c | 81 ++++++++++++++++++++++-- dlls/mfmediaengine/tests/mfmediaengine.c | 4 -- dlls/mfmediaengine/video_frame_sink.c | 24 +++++-- 3 files changed, 94 insertions(+), 15 deletions(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index 61c435240a5..c6848327d09 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -99,6 +99,7 @@ enum media_engine_flags FLAGS_ENGINE_SOURCE_PENDING = 0x10000, FLAGS_ENGINE_PLAY_PENDING = 0x20000, FLAGS_ENGINE_SEEKING = 0x40000, + FLAGS_ENGINE_SCRUBBING = 0x80000, }; struct vec3 @@ -921,13 +922,32 @@ static HRESULT WINAPI media_engine_callback_GetParameters(IMFAsyncCallback *ifac return E_NOTIMPL; } +static HRESULT media_engine_set_rate(struct media_engine *engine, BOOL thin, double rate) +{ + IMFRateControl *rate_control; + HRESULT hr; + + if (FAILED(hr = MFGetService((IUnknown *)engine->session, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateControl, (void **)&rate_control))) + return hr; + + if (FAILED(hr = IMFRateControl_SetRate(rate_control, thin, rate))) + WARN("Failed to set rate, hr %#lx.\n", hr); + + IMFRateControl_Release(rate_control); + + return hr; +} + static HRESULT media_engine_set_current_time(struct media_engine *engine, double seektime); +static void media_engine_start_playback(struct media_engine *engine); static HRESULT WINAPI media_engine_session_events_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct media_engine *engine = impl_from_session_events_IMFAsyncCallback(iface); IMFMediaEvent *event = NULL; MediaEventType event_type; + BOOL playing_event; + PROPVARIANT rate; HRESULT hr; if (FAILED(hr = IMFMediaSession_EndGetEvent(engine->session, result, &event))) @@ -988,6 +1008,9 @@ static HRESULT WINAPI media_engine_session_events_Invoke(IMFAsyncCallback *iface IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_LOADEDDATA, 0, 0); IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_CANPLAY, 0, 0); + if (engine->flags & FLAGS_ENGINE_SCRUBBING) + media_engine_set_rate(engine, FALSE, 0.0); + LeaveCriticalSection(&engine->cs); PropVariantClear(&value); @@ -1004,8 +1027,11 @@ static HRESULT WINAPI media_engine_session_events_Invoke(IMFAsyncCallback *iface if (isfinite(engine->next_seek)) media_engine_set_current_time(engine, engine->next_seek); } + + playing_event = !(engine->flags & FLAGS_ENGINE_SCRUBBING); LeaveCriticalSection(&engine->cs); - IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PLAYING, 0, 0); + if (playing_event) + IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PLAYING, 0, 0); break; case MESessionEnded: @@ -1021,6 +1047,45 @@ static HRESULT WINAPI media_engine_session_events_Invoke(IMFAsyncCallback *iface case MEEndOfPresentationSegment: video_frame_sink_notify_end_of_presentation_segment(engine->presentation.frame_sink); break; + + case MESessionRateChanged: + + EnterCriticalSection(&engine->cs); + if (engine->flags & FLAGS_ENGINE_SCRUBBING && + IMFMediaEvent_GetValue(event, &rate) == S_OK && + rate.vt == VT_R4) + { + if (rate.fltVal == 0.0) + { + /* Start playback with rate at 0.0 */ + media_engine_start_playback(engine); + } + else + { + /* Scrubbing is complete */ + media_engine_set_flag(engine, FLAGS_ENGINE_SCRUBBING, FALSE); + if (engine->flags & FLAGS_ENGINE_PLAY_PENDING) + media_engine_start_playback(engine); + } + } + LeaveCriticalSection(&engine->cs); + break; + + case MESessionScrubSampleComplete: + + EnterCriticalSection(&engine->cs); + if (engine->flags & FLAGS_ENGINE_SCRUBBING && FAILED(hr = IMFMediaSession_Pause(engine->session))) + WARN("Failed to pause media session %#lx.\n", hr); + LeaveCriticalSection(&engine->cs); + break; + + case MESessionPaused: + + EnterCriticalSection(&engine->cs); + if (engine->flags & FLAGS_ENGINE_SCRUBBING) + media_engine_set_rate(engine, FALSE, engine->default_playback_rate); + LeaveCriticalSection(&engine->cs); + break; } failed: @@ -1484,6 +1549,11 @@ static HRESULT WINAPI media_engine_load_handler_Invoke(IMFAsyncCallback *iface, engine->network_state = MF_MEDIA_ENGINE_NETWORK_IDLE; if (start_playback) media_engine_start_playback(engine); + else + { + /* start scrubbing playback */ + media_engine_set_flag(engine, FLAGS_ENGINE_SCRUBBING, TRUE); + } } else { @@ -2174,7 +2244,7 @@ static HRESULT WINAPI media_engine_Play(IMFMediaEngineEx *iface) media_engine_set_flag(engine, FLAGS_ENGINE_PAUSED | FLAGS_ENGINE_IS_ENDED, FALSE); IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PLAY, 0, 0); - if (!(engine->flags & FLAGS_ENGINE_SOURCE_PENDING)) + if (!(engine->flags & (FLAGS_ENGINE_SOURCE_PENDING | FLAGS_ENGINE_SCRUBBING))) media_engine_start_playback(engine); else media_engine_set_flag(engine, FLAGS_ENGINE_PLAY_PENDING, TRUE); @@ -2885,8 +2955,11 @@ static HRESULT WINAPI media_engine_OnVideoStreamTick(IMFMediaEngineEx *iface, LO else { MFTIME clocktime; - IMFPresentationClock_GetTime(engine->clock, &clocktime); - hr = video_frame_sink_get_pts(engine->presentation.frame_sink, clocktime, pts); + *pts = MINLONGLONG; + if (SUCCEEDED(IMFPresentationClock_GetTime(engine->clock, &clocktime))) + hr = video_frame_sink_get_pts(engine->presentation.frame_sink, clocktime, pts); + else + hr = S_FALSE; } LeaveCriticalSection(&engine->cs); diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index c90f4c4d4dc..980f44bcd04 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -1507,7 +1507,6 @@ static void test_TransferVideoFrame(void) /* now that byte stream is set, we will recieve a frame */ res = WaitForSingleObject(notify->frame_ready_event, 5000); - todo_wine ok(!res, "Unexpected res %#lx.\n", res); if (FAILED(notify->error)) @@ -1533,9 +1532,7 @@ static void test_TransferVideoFrame(void) /* confirm we have a frame available before calling play */ pts = 0; hr = IMFMediaEngineEx_OnVideoStreamTick(media_engine, &pts); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(pts == 0, "Unexpected timestamp.\n"); /* confirm we can transfer a frame before calling play */ @@ -1544,7 +1541,6 @@ static void test_TransferVideoFrame(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); res = compare_rgb32(texture, &dst_rect, rb_texture, L"rgb32frame.bmp"); - todo_wine ok(res == 0, "Unexpected %lu%% diff\n", res); hr = IMFMediaEngineEx_Play(media_engine); diff --git a/dlls/mfmediaengine/video_frame_sink.c b/dlls/mfmediaengine/video_frame_sink.c index f710466fed4..fe1d13c64b2 100644 --- a/dlls/mfmediaengine/video_frame_sink.c +++ b/dlls/mfmediaengine/video_frame_sink.c @@ -391,6 +391,7 @@ static void video_frame_sink_notify(struct video_frame_sink *sink, unsigned int static HRESULT WINAPI video_frame_sink_stream_ProcessSample(IMFStreamSink *iface, IMFSample *sample) { struct video_frame_sink *sink = impl_from_IMFStreamSink(iface); + BOOL first_frame = FALSE; LONGLONG sampletime; HRESULT hr = S_OK; @@ -417,11 +418,19 @@ static HRESULT WINAPI video_frame_sink_stream_ProcessSample(IMFStreamSink *iface { video_frame_sink_notify(sink, MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY); video_frame_sink_set_flag(sink, FLAGS_FIRST_FRAME, TRUE); + first_frame = TRUE; } /* else TODO: send MEQualityNotify event */ IMFSample_AddRef(sample); - video_frame_sink_sample_queue_push(sink, sample, FALSE); + if (first_frame && sink->rate == 0.0) + { + video_frame_sink_sample_queue_set_presentation(sink, sample); + IMFStreamSink_QueueEvent(&sink->IMFStreamSink_iface, MEStreamSinkScrubSampleComplete, + &GUID_NULL, S_OK, NULL); + } + else + video_frame_sink_sample_queue_push(sink, sample, FALSE); if (sink->queue.used != ARRAY_SIZE(sink->queue.samples)) video_frame_sink_stream_request_sample(sink); @@ -1018,10 +1027,6 @@ static HRESULT video_frame_sink_set_state(struct video_frame_sink *sink, enum si if (state != sink->state || state != SINK_STATE_PAUSED) { - if (sink->rate == 0.0f && state == SINK_STATE_RUNNING) - IMFStreamSink_QueueEvent(&sink->IMFStreamSink_iface, MEStreamSinkScrubSampleComplete, - &GUID_NULL, S_OK, NULL); - IMFStreamSink_QueueEvent(&sink->IMFStreamSink_iface, events[state], &GUID_NULL, S_OK, NULL); } sink->state = state; @@ -1268,6 +1273,7 @@ static HRESULT sample_get_pts(IMFSample *sample, MFTIME clocktime, LONGLONG *pts HRESULT video_frame_sink_get_pts(struct video_frame_sink *sink, MFTIME clocktime, LONGLONG *pts) { HRESULT hr = S_FALSE; + LONGLONG sample_pts; *pts = MINLONGLONG; if (sink) @@ -1292,8 +1298,12 @@ HRESULT video_frame_sink_get_pts(struct video_frame_sink *sink, MFTIME clocktime if (transfer_sample) video_frame_sink_stream_request_sample(sink); - else if (!sink->queue.sample_presented) - hr = sample_get_pts(sink->queue.presentation_sample, clocktime, pts); + else if (sink->queue.presentation_sample && !sink->queue.sample_presented) + { + hr = IMFSample_GetSampleTime(sink->queue.presentation_sample, &sample_pts); + if (hr == S_OK) + *pts = sample_pts; + } LeaveCriticalSection(&sink->cs); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10342