[PATCH 0/7] MR10200: mf/tests: Add support for MF_TOPONODE_MARKIN_HERE to transform node.
After a seek, Media Session may drop all samples with a PTS prior to the seek time. It will drop the samples if they are output from a TopologyNode with the MF_TOPONODE_MARKIN_HERE attribute set. If a sample starts before the seek time, but ends after, it will be trimmed. A trimmed sample will have its PTS adjusted to match the seek time, and its duration reduced by the time removed. If the sample is an uncompressed audio sample, it will also trim the samples content. Removing the trimmed data from the beginning of the buffer. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10200
From: Brendan McGrath <bmcgrath@codeweavers.com> Tests if the decoder topology node is created with the MF_TOPONODE_MARKIN_HERE and MF_TOPONODE_MARKOUT_HERE attributes set. --- dlls/mf/tests/topology.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/dlls/mf/tests/topology.c b/dlls/mf/tests/topology.c index d2c1b047c52..28e51a4198b 100644 --- a/dlls/mf/tests/topology.c +++ b/dlls/mf/tests/topology.c @@ -3224,6 +3224,20 @@ todo_wine { ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); ok(value == 1, "Unexpected value.\n"); + value = 0; + hr = IMFTopologyNode_GetUINT32(mft_node, &MF_TOPONODE_MARKIN_HERE, &value); + todo_wine + ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); + todo_wine + ok(value == 1, "Unexpected value.\n"); + + value = 0; + hr = IMFTopologyNode_GetUINT32(mft_node, &MF_TOPONODE_MARKOUT_HERE, &value); + todo_wine + ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); + todo_wine + ok(value == 1, "Unexpected value.\n"); + class_id = GUID_NULL; hr = IMFTopologyNode_GetGUID(mft_node, &MF_TOPONODE_TRANSFORM_OBJECTID, &class_id); ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10200
From: Brendan McGrath <bmcgrath@codeweavers.com> Adds the MF_TOPONODE_MARKIN_HERE and MF_TOPONODE_MARKOUT_HERE attributes to the decoder topology node. --- dlls/mf/tests/topology.c | 4 ---- dlls/mf/topology_loader.c | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dlls/mf/tests/topology.c b/dlls/mf/tests/topology.c index 28e51a4198b..63a8192aca3 100644 --- a/dlls/mf/tests/topology.c +++ b/dlls/mf/tests/topology.c @@ -3226,16 +3226,12 @@ todo_wine { value = 0; hr = IMFTopologyNode_GetUINT32(mft_node, &MF_TOPONODE_MARKIN_HERE, &value); - todo_wine ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); - todo_wine ok(value == 1, "Unexpected value.\n"); value = 0; hr = IMFTopologyNode_GetUINT32(mft_node, &MF_TOPONODE_MARKOUT_HERE, &value); - todo_wine ok(hr == S_OK, "Failed to get attribute, hr %#lx.\n", hr); - todo_wine ok(value == 1, "Unexpected value.\n"); class_id = GUID_NULL; diff --git a/dlls/mf/topology_loader.c b/dlls/mf/topology_loader.c index 73f70e62bc6..012e519af8a 100644 --- a/dlls/mf/topology_loader.c +++ b/dlls/mf/topology_loader.c @@ -313,6 +313,8 @@ static HRESULT topology_branch_connect_indirect(IMFTopology *topology, MF_CONNEC else { IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_DECODER, 1); + IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_MARKIN_HERE, 1); + IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_MARKOUT_HERE, 1); method_mask = MF_CONNECT_ALLOW_CONVERTER; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10200
From: Brendan McGrath <bmcgrath@codeweavers.com> --- dlls/mf/tests/mf.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index df1362381b2..08d871a9aa3 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -1925,16 +1925,34 @@ static HRESULT WINAPI test_media_stream_GetStreamDescriptor(IMFMediaStream *ifac return S_OK; } +static void test_media_stream_send_delayed_samples(struct test_media_stream *stream) +{ + DWORD delayed_samples; + IMFSample *sample; + HRESULT hr; + int i; + + hr = IMFCollection_GetElementCount(stream->delayed_samples, &delayed_samples); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + for (i = 0; i < delayed_samples; i++) + { + hr = IMFCollection_RemoveElement(stream->delayed_samples, 0, (IUnknown **)&sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, + (IUnknown *)sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } +} + DEFINE_EXPECT(test_media_stream_RequestSample); static HRESULT WINAPI test_media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) { struct test_media_stream *stream = impl_from_IMFMediaStream(iface); - IMFSample *sample, *delayed_sample; IMFMediaBuffer *buffer; - DWORD delayed_samples; + IMFSample *sample; HRESULT hr; - INT i; if (stream->test_expect) { @@ -1989,17 +2007,7 @@ static HRESULT WINAPI test_media_stream_RequestSample(IMFMediaStream *iface, IUn } else { - hr = IMFCollection_GetElementCount(stream->delayed_samples, &delayed_samples); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - for (i = 0; i < delayed_samples; i++) - { - hr = IMFCollection_RemoveElement(stream->delayed_samples, 0, (IUnknown **)&delayed_sample); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, - (IUnknown *)delayed_sample); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - } + test_media_stream_send_delayed_samples(stream); hr = IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, (IUnknown *)sample); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10200
From: Brendan McGrath <bmcgrath@codeweavers.com> This is required to manually set the MARKIN and MARKOUT attributes. --- dlls/mf/tests/mf.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 08d871a9aa3..363efe32086 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -10305,18 +10305,21 @@ static void test_media_session_seek(void) static const struct object_state_record expected_seek_start_no_pending_request_records = {{SOURCE_STOP, MFT_FLUSH, SOURCE_START, SINK_FLUSH, SINK_ON_CLOCK_START}, 5}; static const struct object_state_record expected_seek_start_pending_request_records = {{SOURCE_STOP, MFT_FLUSH, SOURCE_START, MFT_PROCESS_OUTPUT, SOURCE_REQUEST_SAMPLE, SINK_FLUSH, SINK_ON_CLOCK_START}, 7}; + IMFTopologyNode *src_node, *mft_node, *sink_node; MFT_OUTPUT_STREAM_INFO output_stream_info = {0}; struct test_callback *test_callback; struct test_media_sink *media_sink; struct test_source *media_source; struct test_handler *handler; IMFAsyncCallback *callback; + IMFCollection *collection; IMFMediaSession *session; IMFMediaSource *source; IMFTopology *topology; PROPVARIANT propvar; IMFMediaType *type; IMFTransform *mft; + DWORD input_idx; UINT32 status; HRESULT hr; INT i; @@ -10348,11 +10351,35 @@ static void test_media_session_seek(void) hr = test_transform_create(1, &type, 1, &type, FALSE, &mft); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); test_transform_set_output_stream_info(mft, &output_stream_info); - IMFMediaType_Release(type); SET_EXPECT(test_transform_ProcessMessage_BEGIN_STREAMING); topology = create_test_topology_unk(source, (IUnknown*)media_sink->stream, (IUnknown*) mft, NULL); - hr = IMFMediaSession_SetTopology(session, 0, topology); + + hr = IMFTopology_GetSourceNodeCollection(topology, &collection); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFCollection_GetElement(collection, 0, (IUnknown**)&src_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFCollection_Release(collection); + + hr = IMFTopologyNode_SetUINT64(src_node, &MF_TOPONODE_MEDIASTART, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_GetOutput(src_node, 0, &mft_node, &input_idx); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopologyNode_Release(src_node); + + hr = IMFTopologyNode_SetUINT32(mft_node, &MF_TOPONODE_MARKIN_HERE, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetUINT32(mft_node, &MF_TOPONODE_MARKOUT_HERE, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_GetOutput(mft_node, 0, &sink_node, &input_idx); + IMFTopologyNode_Release(mft_node); + + hr = IMFTopologyNode_SetInputPrefType(sink_node, 0, type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopologyNode_Release(sink_node); + IMFMediaType_Release(type); + + hr = IMFMediaSession_SetTopology(session, MFSESSION_SETTOPOLOGY_NORESOLUTION, topology); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); IMFTopology_Release(topology); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10200
From: Brendan McGrath <bmcgrath@codeweavers.com> After a seek, Media Session may drop all samples with a PTS prior to the seek time. It will drop the samples if they are output from a TopologyNode with the MF_TOPONODE_MARKIN_HERE attribute set. If a sample starts before the seek time, but ends after, it will be trimmed. A trimmed sample will have its PTS adjusted to match the seek time, and its duration reduced by the time removed. If the sample is an uncompressed audio sample, it will also trim the samples content. Removing the trimmed data from the beginning of the buffer. --- dlls/mf/tests/mf.c | 68 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 363efe32086..9733ef56181 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -1815,6 +1815,7 @@ struct test_media_stream IMFMediaSource *source; LONGLONG sample_duration; LONGLONG sample_time; + LONGLONG prev_key_frame; BOOL is_new; BOOL test_expect; BOOL delay_sample; @@ -2048,6 +2049,7 @@ static struct test_media_stream *create_test_stream(DWORD stream_index, IMFMedia IMFMediaSource_AddRef(stream->source); stream->is_new = TRUE; stream->sample_duration = 333667; + stream->prev_key_frame = -1; hr = MFCreateCollection(&stream->delayed_samples); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); stream->delayed_sample_event = CreateEventW(NULL, FALSE, FALSE, NULL); @@ -2283,7 +2285,10 @@ static HRESULT WINAPI test_source_Start(IMFMediaSource *iface, IMFPresentationDe if (start_position->vt == VT_I8) { - source->streams[i]->sample_time = start_position->hVal.QuadPart; + if (source->streams[i]->prev_key_frame >= 0) + source->streams[i]->sample_time = source->streams[i]->prev_key_frame; + else + source->streams[i]->sample_time = start_position->hVal.QuadPart; event_type = MEStreamSeeked; } else @@ -10204,7 +10209,7 @@ static HRESULT WINAPI test_transform_ProcessInput(IMFTransform *iface, DWORD id, hr = E_NOTIMPL; } - CHECK_EXPECT(test_transform_ProcessInput); + CHECK_EXPECT2(test_transform_ProcessInput); add_object_state(&actual_object_state_record, MFT_PROCESS_INPUT); return hr; @@ -10314,13 +10319,16 @@ static void test_media_session_seek(void) IMFAsyncCallback *callback; IMFCollection *collection; IMFMediaSession *session; + LONGLONG time, duration; IMFMediaSource *source; IMFTopology *topology; PROPVARIANT propvar; IMFMediaType *type; IMFTransform *mft; + IMFSample *sample; DWORD input_idx; UINT32 status; + DWORD count; HRESULT hr; INT i; @@ -10347,6 +10355,8 @@ static void test_media_session_seek(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFMediaType_SetUINT64(type, &MF_MT_FRAME_SIZE, (UINT64)640 << 32 | 480); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT32(type, &MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); mft = NULL; hr = test_transform_create(1, &type, 1, &type, FALSE, &mft); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -10437,7 +10447,6 @@ static void test_media_session_seek(void) CHECK_CALLED(test_transform_ProcessOutput); CHECK_CALLED(test_transform_ProcessInput); CHECK_CALLED(test_stream_sink_ProcessSample); - CLEAR_CALLED(test_stream_sink_ProcessSample); compare_object_states(&actual_object_state_record, &expected_sample_request_and_delivery_records); @@ -10471,7 +10480,7 @@ static void test_media_session_seek(void) SET_EXPECT(test_stream_sink_Flush); SET_EXPECT(test_transform_ProcessMessage_FLUSH); propvar.vt = VT_I8; - propvar.hVal.QuadPart = 10000000; + propvar.hVal.QuadPart = 1000000; hr = IMFMediaSession_Start(session, NULL, &propvar); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); PropVariantClear(&propvar); @@ -10522,12 +10531,21 @@ static void test_media_session_seek(void) SET_EXPECT(test_transform_ProcessMessage_FLUSH); SET_EXPECT(test_transform_ProcessOutput); SET_EXPECT(test_media_stream_RequestSample); + SET_EXPECT(test_transform_ProcessInput); + SET_EXPECT(test_stream_sink_ProcessSample); propvar.vt = VT_I8; propvar.hVal.QuadPart = 10000000; + + /* mimic that nearest keyframe is one and a half frames prior to requested seek position */ + media_source->streams[0]->prev_key_frame = 10000000 - media_source->streams[0]->sample_duration * 3 / 2; + hr = IMFMediaSession_Start(session, NULL, &propvar); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); PropVariantClear(&propvar); + ResetEvent(media_sink->stream->sample_event); + IMFCollection_RemoveAllElements(media_sink->stream->samples); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -10537,13 +10555,49 @@ static void test_media_session_seek(void) CHECK_CALLED(test_media_sink_GetStreamSinkCount); CHECK_CALLED(test_stream_sink_Flush); CHECK_CALLED(test_transform_ProcessMessage_FLUSH); - CHECK_CALLED(test_transform_ProcessOutput); CHECK_CALLED(test_media_stream_RequestSample); CLEAR_CALLED(test_transform_ProcessMessage_FLUSH); flaky compare_object_states(&actual_object_state_record, &expected_seek_start_pending_request_records); + hr = WaitForSingleObject(media_source->streams[0]->delayed_sample_event, 1000); + ok(hr == WAIT_OBJECT_0, "Unexpected hr %#lx.\n", hr); + + media_source->streams[0]->delay_sample = FALSE; + media_source->streams[0]->test_expect = FALSE; + + /* Release the delayed samples */ + test_media_stream_send_delayed_samples(media_source->streams[0]); + + hr = WaitForSingleObject(media_sink->stream->sample_event, 1000); + ok(hr == WAIT_OBJECT_0, "Unexpected hr %#lx.\n", hr); + + /* The first frame should be dropped and the second should be trimmed */ + hr = IMFCollection_GetElementCount(media_sink->stream->samples, &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 1, "Unexpected count %ld\n", count); + + for (i = 0; i < count; i++) + { + winetest_push_context("sample %d", i); + + hr = IMFCollection_GetElement(media_sink->stream->samples, i, (IUnknown**)&sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFSample_GetSampleTime(sample, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSample_GetSampleDuration(sample, &duration); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(time == 10000000, "Unexpected time %I64d.\n", time); + todo_wine + ok(duration == (media_source->streams[0]->sample_duration+1)/2, "Unexpected duration %I64d.\n", duration); + + IMFSample_Release(sample); + winetest_pop_context(); + } + IMFAsyncCallback_Release(callback); IMFTransform_Release(mft); @@ -10554,6 +10608,10 @@ static void test_media_session_seek(void) hr = IMFMediaSource_Shutdown(source); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + CLEAR_CALLED(test_transform_ProcessInput); + CLEAR_CALLED(test_transform_ProcessOutput); + CLEAR_CALLED(test_stream_sink_ProcessSample); + IMFMediaSession_Release(session); IMFMediaSource_Release(source); IMFMediaSink_Release(&media_sink->IMFMediaSink_iface); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10200
From: Brendan McGrath <bmcgrath@codeweavers.com> If a transform node has the MF_TOPONODE_MARKIN_HERE attribute, and outputs a sample that is prior to the current start time, it will be dropped. --- dlls/mf/session.c | 80 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 8 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 28195f914b0..0d6d17ac2f5 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -172,6 +172,7 @@ struct transform_stream struct list samples; unsigned int requests; unsigned int min_buffer_size; + BOOL samples_independent; BOOL draining; }; @@ -179,6 +180,7 @@ enum topo_node_flags { TOPO_NODE_END_OF_STREAM = 0x1, TOPO_NODE_SCRUB_SAMPLE_COMPLETE = 0x2, + TOPO_NODE_MARKIN_HERE = 0x4, }; struct topo_node @@ -1749,9 +1751,9 @@ static HRESULT session_set_transform_stream_info(struct topo_node *node) DWORD *input_map = NULL, *output_map = NULL; DWORD i, input_count, output_count; struct transform_stream *streams; + UINT32 bytes_per_second, value; unsigned int block_alignment; IMFMediaType *media_type; - UINT32 bytes_per_second; GUID major = { 0 }; HRESULT hr; @@ -1796,6 +1798,8 @@ static HRESULT session_set_transform_stream_info(struct topo_node *node) if (SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &bytes_per_second))) streams[i].min_buffer_size = max(streams[i].min_buffer_size, bytes_per_second); } + if (SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, &value)) && value) + streams[i].samples_independent = TRUE; IMFMediaType_Release(media_type); } } @@ -1869,6 +1873,7 @@ static HRESULT session_append_node(struct media_session *session, IMFTopologyNod IMFMediaType *media_type; IMFStreamDescriptor *sd; HRESULT hr = S_OK; + UINT32 value; if (!(topo_node = calloc(1, sizeof(*topo_node)))) return E_OUTOFMEMORY; @@ -1878,6 +1883,8 @@ static HRESULT session_append_node(struct media_session *session, IMFTopologyNod topo_node->node = node; IMFTopologyNode_AddRef(topo_node->node); topo_node->session = session; + if (SUCCEEDED(IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_MARKIN_HERE, &value)) && value) + topo_node->flags |= TOPO_NODE_MARKIN_HERE; switch (topo_node->type) { @@ -3639,6 +3646,58 @@ static void release_output_samples(struct topo_node *node, MFT_OUTPUT_DATA_BUFFE } } +static BOOL transform_node_markin_need_more_input(const struct media_session *session, struct topo_node *node, MFT_OUTPUT_DATA_BUFFER *buffers) +{ + BOOL need_more_input, drop_sample; + LONGLONG time, duration; + HRESULT hr; + UINT i; + + if (!(node->flags & TOPO_NODE_MARKIN_HERE) || session->presentation.start_position.vt != VT_I8) + return FALSE; + + need_more_input = TRUE; + + for (i = 0; i < node->u.transform.output_count; ++i) + { + struct transform_stream *stream = &node->u.transform.outputs[i]; + + drop_sample = FALSE; + + if (buffers[i].pEvents) + need_more_input = FALSE; + + if (buffers[i].dwStatus & MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) + continue; + + if (!stream->samples_independent) + { + need_more_input = FALSE; + continue; + } + + if (FAILED(hr = IMFSample_GetSampleTime(buffers[i].pSample, &time))) + WARN("Failed to get sample time, hr %#lx\n", hr); + else if (FAILED(hr = IMFSample_GetSampleDuration(buffers[i].pSample, &duration))) + WARN("Failed to get sample time, hr %#lx\n", hr); + else if (time + duration <= session->presentation.start_position.hVal.QuadPart) + drop_sample = TRUE; + + if (drop_sample) + { + IMFSample_Release(buffers[i].pSample); + buffers[i].pSample = NULL; + buffers[i].dwStatus |= MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE; + } + else + { + need_more_input = FALSE; + } + } + + return need_more_input; +} + static HRESULT transform_node_pull_samples(const struct media_session *session, struct topo_node *node) { MFT_OUTPUT_DATA_BUFFER *buffers; @@ -3652,17 +3711,22 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, goto done; status = 0; - hr = IMFTransform_ProcessOutput(node->object.transform, 0, node->u.transform.output_count, buffers, &status); - if (hr == MF_E_TRANSFORM_STREAM_CHANGE && SUCCEEDED(hr = transform_node_format_changed(node, buffers))) + + do { - release_output_samples(node, buffers); + hr = IMFTransform_ProcessOutput(node->object.transform, 0, node->u.transform.output_count, buffers, &status); + if (hr == MF_E_TRANSFORM_STREAM_CHANGE && SUCCEEDED(hr = transform_node_format_changed(node, buffers))) + { + release_output_samples(node, buffers); - memset(buffers, 0, node->u.transform.output_count * sizeof(*buffers)); - if (FAILED(hr = allocate_output_samples(session, node, buffers))) - goto done; + memset(buffers, 0, node->u.transform.output_count * sizeof(*buffers)); + if (FAILED(hr = allocate_output_samples(session, node, buffers))) + goto done; - hr = IMFTransform_ProcessOutput(node->object.transform, 0, node->u.transform.output_count, buffers, &status); + hr = IMFTransform_ProcessOutput(node->object.transform, 0, node->u.transform.output_count, buffers, &status); + } } + while (hr == S_OK && transform_node_markin_need_more_input(session, node, buffers)); /* Collect returned samples for all streams. */ for (i = 0; i < node->u.transform.output_count; ++i) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10200
From: Brendan McGrath <bmcgrath@codeweavers.com> If a transform node has the MF_TOPONODE_MARKIN_HERE attribute, and outputs a sample that spans across the current start time, then its sample time will be updated to match that start time and the duration adjusted accordingly. If the sample is an audio sample, its data will also be trimmed accordingly. --- dlls/mf/session.c | 47 +++++++++++++++++++++++++++++++++++++++++++++- dlls/mf/tests/mf.c | 2 -- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 0d6d17ac2f5..df38971af38 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -171,6 +171,9 @@ struct transform_stream { struct list samples; unsigned int requests; + BOOL raw_audio; + unsigned int block_alignment; + unsigned int bytes_per_second; unsigned int min_buffer_size; BOOL samples_independent; BOOL draining; @@ -1749,12 +1752,12 @@ static DWORD transform_node_get_stream_id(struct topo_node *node, BOOL output, u static HRESULT session_set_transform_stream_info(struct topo_node *node) { DWORD *input_map = NULL, *output_map = NULL; + GUID major = { 0 }, subtype = { 0 }; DWORD i, input_count, output_count; struct transform_stream *streams; UINT32 bytes_per_second, value; unsigned int block_alignment; IMFMediaType *media_type; - GUID major = { 0 }; HRESULT hr; hr = IMFTransform_GetStreamCount(node->object.transform, &input_count, &output_count); @@ -1794,9 +1797,16 @@ static HRESULT session_set_transform_stream_info(struct topo_node *node) if (SUCCEEDED(IMFMediaType_GetMajorType(media_type, &major)) && IsEqualGUID(&major, &MFMediaType_Audio) && SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &block_alignment))) { + streams[i].block_alignment = block_alignment; streams[i].min_buffer_size = block_alignment; if (SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &bytes_per_second))) + { + streams[i].bytes_per_second = bytes_per_second; streams[i].min_buffer_size = max(streams[i].min_buffer_size, bytes_per_second); + } + if (SUCCEEDED(IMFMediaType_GetGUID(media_type, &MF_MT_SUBTYPE, &subtype)) && + (IsEqualGUID(&subtype, &MFAudioFormat_Float) || IsEqualGUID(&subtype, &MFAudioFormat_PCM))) + streams[i].raw_audio = TRUE; } if (SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, &value)) && value) streams[i].samples_independent = TRUE; @@ -3683,6 +3693,41 @@ static BOOL transform_node_markin_need_more_input(const struct media_session *se else if (time + duration <= session->presentation.start_position.hVal.QuadPart) drop_sample = TRUE; + if (!drop_sample && time < session->presentation.start_position.hVal.QuadPart) + { + LONGLONG delta = session->presentation.start_position.hVal.QuadPart - time; + duration -= delta; + IMFSample_SetSampleTime(buffers[i].pSample, session->presentation.start_position.hVal.QuadPart); + IMFSample_SetSampleDuration(buffers[i].pSample, duration); + + if (stream->raw_audio && stream->bytes_per_second && stream->block_alignment) + { + IMFMediaBuffer *buffer; + LONGLONG delta_bytes; + DWORD current_length; + BYTE *data; + + delta_bytes = stream->bytes_per_second * delta / MFCLOCK_FREQUENCY_HNS / stream->block_alignment * stream->block_alignment; + + if (FAILED(hr = IMFSample_GetTotalLength(buffers[i].pSample, ¤t_length))) + WARN("Failed to get total length of sample, hr %#lx\n", hr); + else if (delta_bytes >= current_length) + { + drop_sample = TRUE; + } + else if (SUCCEEDED(IMFSample_ConvertToContiguousBuffer(buffers[i].pSample, &buffer))) + { + if (SUCCEEDED(IMFMediaBuffer_Lock(buffer, &data, NULL, NULL))) + { + memmove(data, data + delta_bytes, current_length - delta_bytes); + IMFMediaBuffer_Unlock(buffer); + IMFMediaBuffer_SetCurrentLength(buffer, current_length - delta_bytes); + } + IMFMediaBuffer_Release(buffer); + } + } + } + if (drop_sample) { IMFSample_Release(buffers[i].pSample); diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 9733ef56181..f7e27b4c441 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -10589,9 +10589,7 @@ static void test_media_session_seek(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSample_GetSampleDuration(sample, &duration); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(time == 10000000, "Unexpected time %I64d.\n", time); - todo_wine ok(duration == (media_source->streams[0]->sample_duration+1)/2, "Unexpected duration %I64d.\n", duration); IMFSample_Release(sample); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10200
participants (2)
-
Brendan McGrath -
Brendan McGrath (@redmcg)