Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mfplat/buffer.c | 62 ++++++++++++++++++++++++++++++++++ dlls/mfplat/mfplat.spec | 1 + dlls/mfplat/tests/mfplat.c | 68 ++++++++++++++++++++++++++++++++++++++ include/mfapi.h | 2 ++ 4 files changed, 133 insertions(+)
diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index 590288e344..b1d353a59b 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -448,6 +448,68 @@ HRESULT WINAPI MFCreate2DMediaBuffer(DWORD width, DWORD height, DWORD fourcc, BO return create_2d_buffer(width, height, fourcc, buffer); }
+static unsigned int buffer_get_aligned_length(unsigned int length, unsigned int alignment) +{ + length = (length + alignment) / alignment; + length *= alignment; + + return length; +} + +HRESULT WINAPI MFCreateMediaBufferFromMediaType(IMFMediaType *media_type, LONGLONG duration, DWORD min_length, + DWORD alignment, IMFMediaBuffer **buffer) +{ + UINT32 length = 0, block_alignment; + LONGLONG avg_length; + HRESULT hr; + GUID major; + + TRACE("%p, %s, %u, %u, %p.\n", media_type, wine_dbgstr_longlong(duration), min_length, alignment, buffer); + + if (!media_type) + return E_INVALIDARG; + + if (FAILED(hr = IMFMediaType_GetMajorType(media_type, &major))) + return hr; + + if (IsEqualGUID(&major, &MFMediaType_Audio)) + { + block_alignment = 0; + if (FAILED(IMFMediaType_GetUINT32(media_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &block_alignment))) + WARN("Block alignment was not specified.\n"); + + if (block_alignment) + { + avg_length = 0; + + if (duration) + { + length = 0; + if (SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &length))) + { + /* 100 ns -> 1 s */ + avg_length = length * duration / (10 * 1000 * 1000); + } + } + + alignment = max(16, alignment); + + length = buffer_get_aligned_length(avg_length + 1, alignment); + length = buffer_get_aligned_length(length, block_alignment); + } + else + length = 0; + + length = max(length, min_length); + + return create_1d_buffer(length, MF_1_BYTE_ALIGNMENT, buffer); + } + else + FIXME("Major type %s is not supported.\n", debugstr_guid(&major)); + + return E_NOTIMPL; +} + static HRESULT WINAPI sample_QueryInterface(IMFSample *iface, REFIID riid, void **out) { TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out); diff --git a/dlls/mfplat/mfplat.spec b/dlls/mfplat/mfplat.spec index 2fb6388a31..9f08d1aff2 100644 --- a/dlls/mfplat/mfplat.spec +++ b/dlls/mfplat/mfplat.spec @@ -52,6 +52,7 @@ @ stdcall MFCreateMFByteStreamOnStreamEx(ptr ptr) @ stdcall MFCreateMFByteStreamWrapper(ptr ptr) @ stub MFCreateMFVideoFormatFromMFMediaType +@ stdcall MFCreateMediaBufferFromMediaType(ptr int64 long long ptr) @ stub MFCreateMediaBufferWrapper @ stdcall MFCreateMediaEvent(long ptr long ptr ptr) @ stdcall MFCreateMediaType(ptr) diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 31194db77f..7134c62a79 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -95,6 +95,8 @@ static HRESULT (WINAPI *pMFGetPlaneSize)(DWORD format, DWORD width, DWORD height static HRESULT (WINAPI *pMFGetStrideForBitmapInfoHeader)(DWORD format, DWORD width, LONG *stride); static HRESULT (WINAPI *pMFCreate2DMediaBuffer)(DWORD width, DWORD height, DWORD fourcc, BOOL bottom_up, IMFMediaBuffer **buffer); +static HRESULT (WINAPI *pMFCreateMediaBufferFromMediaType)(IMFMediaType *media_type, LONGLONG duration, DWORD min_length, + DWORD min_alignment, IMFMediaBuffer **buffer);
static const WCHAR fileschemeW[] = L"file://";
@@ -665,6 +667,7 @@ static void init_functions(void) X(MFCreate2DMediaBuffer); X(MFCreateDXGIDeviceManager); X(MFCreateSourceResolver); + X(MFCreateMediaBufferFromMediaType); X(MFCreateMFByteStreamOnStream); X(MFCreateTransformActivate); X(MFGetPlaneSize); @@ -4587,6 +4590,70 @@ static void test_MFCreate2DMediaBuffer(void) IMFMediaBuffer_Release(buffer); }
+static void test_MFCreateMediaBufferFromMediaType(void) +{ + static struct audio_buffer_test + { + unsigned int duration; + unsigned int min_length; + unsigned int min_alignment; + unsigned int block_alignment; + unsigned int bytes_per_second; + unsigned int buffer_length; + } audio_tests[] = + { + { 0, 0, 0, 4, 0, 20 }, + { 0, 16, 0, 4, 0, 20 }, + { 0, 0, 32, 4, 0, 36 }, + { 0, 64, 32, 4, 0, 64 }, + { 1, 0, 0, 4, 16, 36 }, + { 2, 0, 0, 4, 16, 52 }, + }; + IMFMediaBuffer *buffer; + UINT32 length; + HRESULT hr; + IMFMediaType *media_type; + unsigned int i; + + if (!pMFCreateMediaBufferFromMediaType) + { + win_skip("MFCreateMediaBufferFromMediaType() is not available.\n"); + return; + } + + hr = pMFCreateMediaBufferFromMediaType(NULL, 0, 0, 0, &buffer); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Failed to create media type, hr %#x.\n", hr); + + hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); + ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr); + + for (i = 0; i < ARRAY_SIZE(audio_tests); ++i) + { + const struct audio_buffer_test *ptr = &audio_tests[i]; + + hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, ptr->block_alignment); + ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr); + + hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, ptr->bytes_per_second); + ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr); + + hr = pMFCreateMediaBufferFromMediaType(media_type, ptr->duration * 10000000, ptr->min_length, + ptr->min_alignment, &buffer); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + hr = IMFMediaBuffer_GetMaxLength(buffer, &length); + ok(hr == S_OK, "Failed to get length, hr %#x.\n", hr); + ok(ptr->buffer_length == length, "%d: unexpected buffer length %u, expected %u.\n", i, length, ptr->buffer_length); + + IMFMediaBuffer_Release(buffer); + } + + IMFMediaType_Release(media_type); +} + START_TEST(mfplat) { char **argv; @@ -4640,6 +4707,7 @@ START_TEST(mfplat) test_queue_com(); test_MFGetStrideForBitmapInfoHeader(); test_MFCreate2DMediaBuffer(); + test_MFCreateMediaBufferFromMediaType();
CoUninitialize(); } diff --git a/include/mfapi.h b/include/mfapi.h index 9f4db44d1e..9c22b6bfd8 100644 --- a/include/mfapi.h +++ b/include/mfapi.h @@ -411,6 +411,8 @@ HRESULT WINAPI MFCreateDXGIDeviceManager(UINT *token, IMFDXGIDeviceManager **man HRESULT WINAPI MFCreateEventQueue(IMFMediaEventQueue **queue); HRESULT WINAPI MFCreateFile(MF_FILE_ACCESSMODE accessmode, MF_FILE_OPENMODE openmode, MF_FILE_FLAGS flags, LPCWSTR url, IMFByteStream **bytestream); +HRESULT WINAPI MFCreateMediaBufferFromMediaType(IMFMediaType *media_type, LONGLONG duration, DWORD min_length, + DWORD min_alignment, IMFMediaBuffer **buffer); HRESULT WINAPI MFCreateMediaEvent(MediaEventType type, REFGUID extended_type, HRESULT status, const PROPVARIANT *value, IMFMediaEvent **event); HRESULT WINAPI MFCreateMediaType(IMFMediaType **type);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 325 ++++++++++++++++++++++------------------------ 1 file changed, 155 insertions(+), 170 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index cc014f548a..9c3288da34 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -102,15 +102,6 @@ struct media_source enum object_state state; };
-struct source_node -{ - struct list entry; - IMFMediaStream *stream; - IMFMediaSource *source; - DWORD stream_id; - enum object_state state; -}; - struct media_sink { struct list entry; @@ -119,11 +110,28 @@ struct media_sink BOOL finalized; };
-struct output_node +struct topo_node { struct list entry; - IMFStreamSink *stream; + MF_TOPOLOGY_TYPE type; + TOPOID node_id; enum object_state state; + union + { + IMFMediaStream *source_stream; + IMFStreamSink *sink_stream; + IMFTransform *transform; + IUnknown *object; + } object; + + union + { + struct + { + IMFMediaSource *source; + DWORD stream_id; + } source; + } u; };
enum presentation_flags @@ -154,9 +162,8 @@ struct media_session IMFTopology *current_topology; MF_TOPOSTATUS topo_status; struct list sources; - struct list source_nodes; struct list sinks; - struct list output_nodes; + struct list nodes; DWORD flags;
/* Latest Start() arguments. */ @@ -603,11 +610,28 @@ static void session_set_caps(struct media_session *session, DWORD caps) IMFMediaEvent_Release(event); }
+static void release_topo_node(struct topo_node *node) +{ + switch (node->type) + { + case MF_TOPOLOGY_SOURCESTREAM_NODE: + if (node->u.source.source) + IMFMediaSource_Release(node->u.source.source); + break; + default: + ; + } + + if (node->object.object) + IUnknown_Release(node->object.object); + heap_free(node); +} + static void session_clear_presentation(struct media_session *session) { - struct source_node *src_node, *src_node2; struct media_source *source, *source2; struct media_sink *sink, *sink2; + struct topo_node *node, *node2;
IMFTopology_Clear(session->presentation.current_topology); session->presentation.topo_status = MF_TOPOSTATUS_INVALID; @@ -622,12 +646,10 @@ static void session_clear_presentation(struct media_session *session) heap_free(source); }
- LIST_FOR_EACH_ENTRY_SAFE(src_node, src_node2, &session->presentation.source_nodes, struct source_node, entry) + LIST_FOR_EACH_ENTRY_SAFE(node, node2, &session->presentation.nodes, struct topo_node, entry) { - list_remove(&src_node->entry); - if (src_node->stream) - IMFMediaStream_Release(src_node->stream); - heap_free(src_node); + list_remove(&node->entry); + release_topo_node(node); }
LIST_FOR_EACH_ENTRY_SAFE(sink, sink2, &session->presentation.sinks, struct media_sink, entry) @@ -861,84 +883,6 @@ static HRESULT session_add_media_source(struct media_session *session, IMFTopolo return hr; }
-static HRESULT session_add_source_node(struct media_session *session, IMFTopologyNode *node, IMFMediaSource *source) -{ - struct source_node *source_node; - IMFStreamDescriptor *sd; - DWORD stream_id; - HRESULT hr; - - if (FAILED(hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_STREAM_DESCRIPTOR, &IID_IMFStreamDescriptor, - (void **)&sd))) - { - WARN("Missing MF_TOPONODE_STREAM_DESCRIPTOR, hr %#x.\n", hr); - return hr; - } - - hr = IMFStreamDescriptor_GetStreamIdentifier(sd, &stream_id); - IMFStreamDescriptor_Release(sd); - if (FAILED(hr)) - return hr; - - if (!(source_node = heap_alloc_zero(sizeof(*source_node)))) - return E_OUTOFMEMORY; - - source_node->source = source; - IMFMediaSource_AddRef(source_node->source); - - list_add_tail(&session->presentation.source_nodes, &source_node->entry); - - return hr; -} - -static void session_collect_source_nodes(struct media_session *session) -{ - IMFTopology *topology; - IMFTopologyNode *node; - IMFCollection *nodes; - DWORD count, i; - HRESULT hr; - - if (!list_empty(&session->presentation.source_nodes)) - return; - - topology = session->presentation.current_topology; - - if (!topology || FAILED(IMFTopology_GetSourceNodeCollection(topology, &nodes))) - return; - - if (FAILED(IMFCollection_GetElementCount(nodes, &count))) - return; - - /* Create list of distinct sources, and whole list of source nodes. */ - for (i = 0; i < count; ++i) - { - if (SUCCEEDED(hr = IMFCollection_GetElement(nodes, i, (IUnknown **)&node))) - { - IMFMediaSource *source; - - if (FAILED(IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_SOURCE, &IID_IMFMediaSource, (void **)&source))) - { - WARN("Missing MF_TOPONODE_SOURCE, hr %#x.\n", hr); - IMFTopologyNode_Release(node); - continue; - } - - hr = session_add_media_source(session, node, source); - IMFMediaSource_Release(source); - - if (SUCCEEDED(hr)) - { - if (FAILED(hr = session_add_source_node(session, node, source))) - WARN("Failed to add source node, hr %#x.\n", hr); - } - - IMFMediaSource_Release(source); - IMFTopologyNode_Release(node); - } - } -} - static void session_raise_topology_set(struct media_session *session, IMFTopology *topology, HRESULT status) { PROPVARIANT param; @@ -994,66 +938,105 @@ static HRESULT session_add_media_sink(struct media_session *session, IMFTopology return S_OK; }
-static HRESULT session_collect_output_nodes(struct media_session *session) +static HRESULT session_append_node(struct media_session *session, IMFTopologyNode *node) { - IMFTopology *topology = session->presentation.current_topology; - IMFTopologyNode *topo_node; - struct output_node *node; - IMFCollection *nodes; - DWORD count, i; - HRESULT hr; + struct topo_node *topo_node; + IMFMediaSink *media_sink; + IMFStreamDescriptor *sd; + HRESULT hr = S_OK; + IUnknown *object;
- if (!list_empty(&session->presentation.output_nodes)) - return S_OK; + if (!(topo_node = heap_alloc_zero(sizeof(*topo_node)))) + return E_OUTOFMEMORY;
- if (FAILED(hr = IMFTopology_GetOutputNodeCollection(topology, &nodes))) - return hr; + IMFTopologyNode_GetNodeType(node, &topo_node->type); + IMFTopologyNode_GetTopoNodeID(node, &topo_node->node_id);
- if (FAILED(hr = IMFCollection_GetElementCount(nodes, &count))) + switch (topo_node->type) { - IMFCollection_Release(nodes); - return hr; - } + case MF_TOPOLOGY_OUTPUT_NODE: + if (FAILED(hr = IMFTopologyNode_GetObject(node, &object))) + { + WARN("Node %s does not have associated object.\n", wine_dbgstr_longlong(topo_node->node_id)); + break; + } + hr = IUnknown_QueryInterface(object, &IID_IMFStreamSink, (void **)&topo_node->object.object); + IUnknown_Release(object); + if (FAILED(hr)) + break;
- for (i = 0; i < count; ++i) - { - IMFMediaSink *media_sink = NULL; + if (FAILED(hr = IMFStreamSink_GetMediaSink(topo_node->object.sink_stream, &media_sink))) + break; + + hr = session_add_media_sink(session, node, media_sink); + IMFMediaSink_Release(media_sink);
- if (FAILED(hr = IMFCollection_GetElement(nodes, i, (IUnknown **)&topo_node))) break; + case MF_TOPOLOGY_SOURCESTREAM_NODE: + if (FAILED(IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_SOURCE, &IID_IMFMediaSource, + (void **)&topo_node->u.source.source))) + { + WARN("Missing MF_TOPONODE_SOURCE, hr %#x.\n", hr); + break; + }
- if (!(node = heap_alloc_zero(sizeof(*node)))) - hr = E_OUTOFMEMORY; + if (FAILED(hr = session_add_media_source(session, node, topo_node->u.source.source))) + break;
- if (SUCCEEDED(hr)) - hr = IMFTopologyNode_GetObject(topo_node, (IUnknown **)&node->stream); + if (FAILED(hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_STREAM_DESCRIPTOR, + &IID_IMFStreamDescriptor, (void **)&sd))) + { + WARN("Missing MF_TOPONODE_STREAM_DESCRIPTOR, hr %#x.\n", hr); + break; + }
- if (SUCCEEDED(hr)) - hr = IMFStreamSink_GetMediaSink(node->stream, &media_sink); + hr = IMFStreamDescriptor_GetStreamIdentifier(sd, &topo_node->u.source.stream_id); + IMFStreamDescriptor_Release(sd);
- if (SUCCEEDED(hr)) - hr = session_add_media_sink(session, topo_node, media_sink); + break; + case MF_TOPOLOGY_TRANSFORM_NODE: + case MF_TOPOLOGY_TEE_NODE: + FIXME("Unsupported node type %d.\n", topo_node->type);
- if (media_sink) - IMFMediaSink_Release(media_sink); + break; + default: + ; + }
- if (SUCCEEDED(hr)) - { - list_add_tail(&session->presentation.output_nodes, &node->entry); - } - else if (node) + if (SUCCEEDED(hr)) + list_add_tail(&session->presentation.nodes, &topo_node->entry); + else + release_topo_node(topo_node); + + return hr; +} + +static HRESULT session_collect_nodes(struct media_session *session) +{ + IMFTopology *topology = session->presentation.current_topology; + IMFTopologyNode *node; + WORD i, count = 0; + + if (!list_empty(&session->presentation.nodes)) + return S_OK; + + IMFTopology_GetNodeCount(topology, &count); + + for (i = 0; i < count; ++i) + { + if (FAILED(IMFTopology_GetNode(topology, i, &node))) { - if (node->stream) - IMFStreamSink_Release(node->stream); - heap_free(node); + WARN("Failed to get node %u.\n", i); + continue; }
- IMFTopologyNode_Release(topo_node); - } + if (FAILED(session_append_node(session, node))) + WARN("Failed to add node %u.\n", i);
- IMFCollection_Release(nodes); + IMFTopologyNode_Release(node); + }
- return hr; + return S_OK; }
static void session_set_current_topology(struct media_session *session, IMFTopology *topology) @@ -1070,8 +1053,7 @@ static void session_set_current_topology(struct media_session *session, IMFTopol return; }
- session_collect_source_nodes(session); - session_collect_output_nodes(session); + session_collect_nodes(session);
/* FIXME: attributes are all zero for now */ if (SUCCEEDED(MFCreateMediaEvent(MESessionNotifyPresentationTime, &GUID_NULL, S_OK, NULL, &event))) @@ -1714,7 +1696,7 @@ static HRESULT WINAPI session_events_callback_GetParameters(IMFAsyncCallback *if
static HRESULT session_add_media_stream(struct media_session *session, IMFMediaSource *source, IMFMediaStream *stream) { - struct source_node *node; + struct topo_node *node; IMFStreamDescriptor *sd; DWORD stream_id = 0; HRESULT hr; @@ -1727,18 +1709,19 @@ static HRESULT session_add_media_stream(struct media_session *session, IMFMediaS if (FAILED(hr)) return hr;
- LIST_FOR_EACH_ENTRY(node, &session->presentation.source_nodes, struct source_node, entry) + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) { - if (node->source == source && node->stream_id == stream_id) + if (node->type == MF_TOPOLOGY_SOURCESTREAM_NODE && node->u.source.source == source + && node->u.source.stream_id == stream_id) { - if (node->stream) + if (node->object.source_stream) { - WARN("Source node already has stream set.\n"); + WARN("Node already has stream set.\n"); return S_FALSE; }
- node->stream = stream; - IMFMediaStream_AddRef(node->stream); + node->object.source_stream = stream; + IMFMediaStream_AddRef(node->object.source_stream); break; } } @@ -1749,7 +1732,7 @@ static HRESULT session_add_media_stream(struct media_session *session, IMFMediaS static BOOL session_is_source_nodes_state(struct media_session *session, enum object_state state) { struct media_source *source; - struct source_node *node; + struct topo_node *node;
LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) { @@ -1757,9 +1740,9 @@ static BOOL session_is_source_nodes_state(struct media_session *session, enum ob return FALSE; }
- LIST_FOR_EACH_ENTRY(node, &session->presentation.source_nodes, struct source_node, entry) + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) { - if (node->state != state) + if (node->type == MF_TOPOLOGY_SOURCESTREAM_NODE && node->state != state) return FALSE; }
@@ -1768,11 +1751,11 @@ static BOOL session_is_source_nodes_state(struct media_session *session, enum ob
static BOOL session_is_output_nodes_state(struct media_session *session, enum object_state state) { - struct output_node *node; + struct topo_node *node;
- LIST_FOR_EACH_ENTRY(node, &session->presentation.output_nodes, struct output_node, entry) + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) { - if (node->state != state) + if (node->type == MF_TOPOLOGY_OUTPUT_NODE && node->state != state) return FALSE; }
@@ -1804,8 +1787,8 @@ static HRESULT session_start_clock(struct media_session *session) { IMFPresentationTimeSource *time_source = NULL; LONGLONG start_offset = 0; - struct output_node *node; struct media_sink *sink; + struct topo_node *node; HRESULT hr;
if (!(session->presentation.flags & SESSION_FLAG_SINKS_SUBSCRIBED)) @@ -1828,10 +1811,13 @@ static HRESULT session_start_clock(struct media_session *session) if (FAILED(hr)) WARN("Failed to set time source, hr %#x.\n", hr);
- LIST_FOR_EACH_ENTRY(node, &session->presentation.output_nodes, struct output_node, entry) + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) { - if (FAILED(hr = IMFStreamSink_BeginGetEvent(node->stream, &session->events_callback, - (IUnknown *)node->stream))) + if (node->type != MF_TOPOLOGY_OUTPUT_NODE) + continue; + + if (FAILED(hr = IMFStreamSink_BeginGetEvent(node->object.sink_stream, &session->events_callback, + node->object.object))) { WARN("Failed to subscribe to stream sink events, hr %#x.\n", hr); } @@ -1874,10 +1860,9 @@ static HRESULT session_start_clock(struct media_session *session) static void session_set_source_object_state(struct media_session *session, IUnknown *object, MediaEventType event_type) { - struct source_node *src_node; - struct output_node *out_node; struct media_source *src; enum object_state state; + struct topo_node *node; BOOL changed = FALSE;
if ((state = session_get_object_state_for_event(event_type)) == OBJ_STATE_INVALID) @@ -1903,12 +1888,12 @@ static void session_set_source_object_state(struct media_session *session, IUnkn case MEStreamPaused: case MEStreamStopped:
- LIST_FOR_EACH_ENTRY(src_node, &session->presentation.source_nodes, struct source_node, entry) + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) { - if (object == (IUnknown *)src_node->stream) + if (node->type == MF_TOPOLOGY_SOURCESTREAM_NODE && object == node->object.object) { - changed = src_node->state != state; - src_node->state = state; + changed = node->state != state; + node->state = state; break; } } @@ -1945,9 +1930,10 @@ static void session_set_source_object_state(struct media_session *session, IUnkn if (!session_is_source_nodes_state(session, OBJ_STATE_STOPPED)) break;
- LIST_FOR_EACH_ENTRY(out_node, &session->presentation.output_nodes, struct output_node, entry) + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) { - IMFStreamSink_Flush(out_node->stream); + if (node->type == MF_TOPOLOGY_OUTPUT_NODE) + IMFStreamSink_Flush(node->object.sink_stream); }
session_set_caps(session, session->caps & ~MFSESSIONCAP_PAUSE); @@ -1972,7 +1958,7 @@ static void session_set_sink_stream_state(struct media_session *session, IMFStre MediaEventType event_type) { struct media_source *source; - struct output_node *node; + struct topo_node *node; enum object_state state; BOOL changed = FALSE; IMFMediaEvent *event; @@ -1982,9 +1968,9 @@ static void session_set_sink_stream_state(struct media_session *session, IMFStre if ((state = session_get_object_state_for_event(event_type)) == OBJ_STATE_INVALID) return;
- LIST_FOR_EACH_ENTRY(node, &session->presentation.output_nodes, struct output_node, entry) + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) { - if (stream == node->stream) + if (node->type == MF_TOPOLOGY_OUTPUT_NODE && stream == node->object.sink_stream) { changed = node->state != state; node->state = state; @@ -2438,9 +2424,8 @@ HRESULT WINAPI MFCreateMediaSession(IMFAttributes *config, IMFMediaSession **ses object->refcount = 1; list_init(&object->topologies); list_init(&object->presentation.sources); - list_init(&object->presentation.source_nodes); list_init(&object->presentation.sinks); - list_init(&object->presentation.output_nodes); + list_init(&object->presentation.nodes); InitializeCriticalSection(&object->cs);
if (FAILED(hr = MFCreateTopology(&object->presentation.current_topology)))
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/tests/mf.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index d799446676..dfbd928f60 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -150,6 +150,17 @@ static void test_topology(void)
IMFTopology_Release(topology2);
+ /* No attributes by default. */ + for (node_type = MF_TOPOLOGY_OUTPUT_NODE; node_type < MF_TOPOLOGY_TEE_NODE; ++node_type) + { + hr = MFCreateTopologyNode(node_type, &node); + ok(hr == S_OK, "Failed to create a node for type %d, hr %#x.\n", node_type, hr); + hr = IMFTopologyNode_GetCount(node, &count); + ok(hr == S_OK, "Failed to get attribute count, hr %#x.\n", hr); + ok(!count, "Unexpected attribute count %u.\n", count); + IMFTopologyNode_Release(node); + } + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, NULL); ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 162 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 1 deletion(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 9c3288da34..1d5cc4f8d5 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -115,6 +115,7 @@ struct topo_node struct list entry; MF_TOPOLOGY_TYPE type; TOPOID node_id; + IMFTopologyNode *node; enum object_state state; union { @@ -129,8 +130,12 @@ struct topo_node struct { IMFMediaSource *source; - DWORD stream_id; + unsigned int stream_id; } source; + struct + { + unsigned int requests; + } sink; } u; };
@@ -624,6 +629,8 @@ static void release_topo_node(struct topo_node *node)
if (node->object.object) IUnknown_Release(node->object.object); + if (node->node) + IMFTopologyNode_Release(node->node); heap_free(node); }
@@ -951,6 +958,8 @@ static HRESULT session_append_node(struct media_session *session, IMFTopologyNod
IMFTopologyNode_GetNodeType(node, &topo_node->type); IMFTopologyNode_GetTopoNodeID(node, &topo_node->node_id); + topo_node->node = node; + IMFTopologyNode_AddRef(topo_node->node);
switch (topo_node->type) { @@ -2055,6 +2064,145 @@ static void session_set_sink_stream_state(struct media_session *session, IMFStre } }
+static struct topo_node *session_get_node_by_id(struct media_session *session, TOPOID id) +{ + struct topo_node *node; + + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->node_id == id) + return node; + } + + return NULL; +} + +static HRESULT session_request_sample_from_node(struct media_session *session, IMFTopologyNode *upstream_node, + DWORD upsteam_output) +{ + MF_TOPOLOGY_TYPE upstream_type; + struct topo_node *node; + TOPOID upstream_id; + HRESULT hr; + + IMFTopologyNode_GetNodeType(upstream_node, &upstream_type); + IMFTopologyNode_GetTopoNodeID(upstream_node, &upstream_id); + + node = session_get_node_by_id(session, upstream_id); + + switch (upstream_type) + { + case MF_TOPOLOGY_SOURCESTREAM_NODE: + if (FAILED(hr = IMFMediaStream_RequestSample(node->object.source_stream, NULL))) + WARN("Sample request failed, hr %#x.\n", hr); + break; + case MF_TOPOLOGY_TRANSFORM_NODE: + case MF_TOPOLOGY_TEE_NODE: + FIXME("Unhandled upstream node type %d.\n", upstream_type); + default: + hr = E_UNEXPECTED; + } + + return hr; +} + +static void session_request_sample(struct media_session *session, IMFStreamSink *sink_stream) +{ + struct topo_node *sink_node = NULL, *node; + IMFTopologyNode *upstream_node; + DWORD upstream_output; + HRESULT hr; + + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->type == MF_TOPOLOGY_OUTPUT_NODE && node->object.sink_stream == sink_stream) + { + sink_node = node; + break; + } + } + + if (!sink_node) + return; + + if (FAILED(hr = IMFTopologyNode_GetInput(sink_node->node, 0, &upstream_node, &upstream_output))) + { + WARN("Failed to get upstream node connection, hr %#x.\n", hr); + return; + } + + if (SUCCEEDED(session_request_sample_from_node(session, upstream_node, upstream_output))) + sink_node->u.sink.requests++; + IMFTopologyNode_Release(upstream_node); +} + +static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *downstream_node, + DWORD downstream_input, IMFSample *sample) +{ + MF_TOPOLOGY_TYPE downstream_type; + struct topo_node *node; + TOPOID downstream_id; + HRESULT hr; + + IMFTopologyNode_GetNodeType(downstream_node, &downstream_type); + IMFTopologyNode_GetTopoNodeID(downstream_node, &downstream_id); + + node = session_get_node_by_id(session, downstream_id); + + switch (downstream_type) + { + case MF_TOPOLOGY_OUTPUT_NODE: + if (node->u.sink.requests) + { + if (FAILED(hr = IMFStreamSink_ProcessSample(node->object.sink_stream, sample))) + WARN("Sample delivery failed, hr %#x.\n", hr); + node->u.sink.requests--; + } + break; + case MF_TOPOLOGY_TRANSFORM_NODE: + case MF_TOPOLOGY_TEE_NODE: + FIXME("Unhandled downstream node type %d.\n", downstream_type); + break; + default: + ; + } +} + +static void session_deliver_sample(struct media_session *session, IMFMediaStream *stream, const PROPVARIANT *value) +{ + struct topo_node *source_node = NULL, *node; + IMFTopologyNode *downstream_node; + DWORD downstream_input; + HRESULT hr; + + if (value->vt != VT_UNKNOWN || !value->punkVal) + { + WARN("Unexpected value type %d.\n", value->vt); + return; + } + + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->type == MF_TOPOLOGY_SOURCESTREAM_NODE && node->object.source_stream == stream) + { + source_node = node; + break; + } + } + + if (!source_node) + return; + + if (FAILED(hr = IMFTopologyNode_GetOutput(source_node->node, 0, &downstream_node, &downstream_input))) + { + WARN("Failed to get downstream node connection, hr %#x.\n", hr); + return; + } + + session_deliver_sample_to_node(session, downstream_node, downstream_input, (IMFSample *)value->punkVal); + IMFTopologyNode_Release(downstream_node); +} + static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct media_session *session = impl_from_events_callback_IMFAsyncCallback(iface); @@ -2131,6 +2279,18 @@ static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IM LeaveCriticalSection(&session->cs);
break; + case MEStreamSinkRequestSample: + + EnterCriticalSection(&session->cs); + session_request_sample(session, (IMFStreamSink *)event_source); + LeaveCriticalSection(&session->cs); + break; + + case MEMediaSample: + + EnterCriticalSection(&session->cs); + session_deliver_sample(session, (IMFMediaStream *)event_source, &value); + LeaveCriticalSection(&session->cs); default: ; }
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 65 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 19 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 1d5cc4f8d5..e1e7753a1c 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1004,6 +1004,16 @@ static HRESULT session_append_node(struct media_session *session, IMFTopologyNod
break; case MF_TOPOLOGY_TRANSFORM_NODE: + if (SUCCEEDED(hr = IMFTopologyNode_GetObject(node, &object))) + { + hr = IUnknown_QueryInterface(object, &IID_IMFTransform, (void **)&topo_node->object.transform); + IUnknown_Release(object); + } + + if (FAILED(hr)) + WARN("Failed to get IMFTransform for MFT node, hr %#x.\n", hr); + + break; case MF_TOPOLOGY_TEE_NODE: FIXME("Unsupported node type %d.\n", topo_node->type);
@@ -1025,45 +1035,61 @@ static HRESULT session_collect_nodes(struct media_session *session) IMFTopology *topology = session->presentation.current_topology; IMFTopologyNode *node; WORD i, count = 0; + HRESULT hr;
if (!list_empty(&session->presentation.nodes)) return S_OK;
- IMFTopology_GetNodeCount(topology, &count); + if (FAILED(hr = IMFTopology_GetNodeCount(topology, &count))) + return hr;
for (i = 0; i < count; ++i) { - if (FAILED(IMFTopology_GetNode(topology, i, &node))) + if (FAILED(hr = IMFTopology_GetNode(topology, i, &node))) { WARN("Failed to get node %u.\n", i); - continue; + break; }
- if (FAILED(session_append_node(session, node))) - WARN("Failed to add node %u.\n", i); - + hr = session_append_node(session, node); IMFTopologyNode_Release(node); + if (FAILED(hr)) + { + WARN("Failed to add node %u.\n", i); + break; + } }
- return S_OK; + return hr; }
-static void session_set_current_topology(struct media_session *session, IMFTopology *topology) +static HRESULT session_set_current_topology(struct media_session *session, IMFTopology *topology) { struct media_source *source; DWORD caps, object_flags; struct media_sink *sink; + struct topo_node *node; IMFMediaEvent *event; HRESULT hr;
if (FAILED(hr = IMFTopology_CloneFrom(session->presentation.current_topology, topology))) { WARN("Failed to clone topology, hr %#x.\n", hr); - return; + return hr; }
session_collect_nodes(session);
+ LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->type == MF_TOPOLOGY_TRANSFORM_NODE) + { + if (FAILED(hr = IMFTransform_ProcessMessage(node->object.transform, + MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0))) + return hr; + } + } + /* FIXME: attributes are all zero for now */ if (SUCCEEDED(MFCreateMediaEvent(MESessionNotifyPresentationTime, &GUID_NULL, S_OK, NULL, &event))) { @@ -1114,11 +1140,13 @@ static void session_set_current_topology(struct media_session *session, IMFTopol }
session_set_caps(session, caps); + + return S_OK; }
static void session_set_topology(struct media_session *session, DWORD flags, IMFTopology *topology) { - HRESULT status = S_OK; + HRESULT hr = S_OK;
/* Resolve unless claimed to be full. */ if (!(flags & MFSESSION_SETTOPOLOGY_CLEAR_CURRENT) && topology) @@ -1127,12 +1155,12 @@ static void session_set_topology(struct media_session *session, DWORD flags, IMF { IMFTopology *resolved_topology = NULL;
- status = session_bind_output_nodes(topology); + hr = session_bind_output_nodes(topology);
- if (SUCCEEDED(status)) - status = IMFTopoLoader_Load(session->topo_loader, topology, &resolved_topology, NULL /* FIXME? */); + if (SUCCEEDED(hr)) + hr = IMFTopoLoader_Load(session->topo_loader, topology, &resolved_topology, NULL /* FIXME? */);
- if (SUCCEEDED(status)) + if (SUCCEEDED(hr)) { topology = resolved_topology; } @@ -1149,7 +1177,7 @@ static void session_set_topology(struct media_session *session, DWORD flags, IMF session_clear_presentation(session); } else - status = S_FALSE; + hr = S_FALSE;
topology = NULL; } @@ -1159,14 +1187,14 @@ static void session_set_topology(struct media_session *session, DWORD flags, IMF session_clear_presentation(session); }
- session_raise_topology_set(session, topology, status); + session_raise_topology_set(session, topology, hr);
/* With no current topology set it right away, otherwise queue. */ if (topology) { if (session->presentation.topo_status == MF_TOPOSTATUS_INVALID) { - session_set_current_topology(session, topology); + hr = session_set_current_topology(session, topology); } else { @@ -1182,8 +1210,7 @@ static void session_set_topology(struct media_session *session, DWORD flags, IMF } }
- if (status == S_OK) - session_set_topo_status(session, S_OK, MF_TOPOSTATUS_READY); + session_set_topo_status(session, hr, MF_TOPOSTATUS_READY);
LeaveCriticalSection(&session->cs); }
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index e1e7753a1c..64988579f4 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1827,6 +1827,12 @@ static HRESULT session_start_clock(struct media_session *session) struct topo_node *node; HRESULT hr;
+ LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->type == MF_TOPOLOGY_TRANSFORM_NODE) + IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0); + } + if (!(session->presentation.flags & SESSION_FLAG_SINKS_SUBSCRIBED)) { LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry) @@ -1968,8 +1974,17 @@ static void session_set_source_object_state(struct media_session *session, IUnkn
LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) { - if (node->type == MF_TOPOLOGY_OUTPUT_NODE) - IMFStreamSink_Flush(node->object.sink_stream); + switch (node->type) + { + case MF_TOPOLOGY_OUTPUT_NODE: + IMFStreamSink_Flush(node->object.sink_stream); + break; + case MF_TOPOLOGY_TRANSFORM_NODE: + IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_COMMAND_FLUSH, 0); + break; + default: + ; + } }
session_set_caps(session, session->caps & ~MFSESSIONCAP_PAUSE);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 64988579f4..62b2b4f0ec 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -136,6 +136,14 @@ struct topo_node { unsigned int requests; } sink; + struct + { + unsigned int *input_map; + unsigned int input_count; + + unsigned int *output_map; + unsigned int output_count; + } transform; } u; };
@@ -623,6 +631,10 @@ static void release_topo_node(struct topo_node *node) if (node->u.source.source) IMFMediaSource_Release(node->u.source.source); break; + case MF_TOPOLOGY_TRANSFORM_NODE: + heap_free(node->u.transform.input_map); + heap_free(node->u.transform.output_map); + break; default: ; } @@ -945,6 +957,40 @@ static HRESULT session_add_media_sink(struct media_session *session, IMFTopology return S_OK; }
+static HRESULT session_set_transform_stream_info(struct topo_node *node) +{ + unsigned int *input_map = NULL, *output_map = NULL; + unsigned int input_count, output_count; + HRESULT hr; + + hr = IMFTransform_GetStreamCount(node->object.transform, &input_count, &output_count); + if (SUCCEEDED(hr) && (input_count > 1 || output_count > 1)) + { + unsigned int *input_map, *output_map; + + input_map = heap_calloc(input_count, sizeof(*input_map)); + output_map = heap_calloc(output_count, sizeof(*output_map)); + if (FAILED(IMFTransform_GetStreamIDs(node->object.transform, input_count, input_map, + output_count, output_map))) + { + /* Assume sequential identifiers. */ + heap_free(input_map); + heap_free(output_map); + input_map = output_map = NULL; + } + } + + if (SUCCEEDED(hr)) + { + node->u.transform.input_count = input_count; + node->u.transform.output_count = output_count; + node->u.transform.input_map = input_map; + node->u.transform.output_map = output_map; + } + + return hr; +} + static HRESULT session_append_node(struct media_session *session, IMFTopologyNode *node) { struct topo_node *topo_node; @@ -1010,7 +1056,9 @@ static HRESULT session_append_node(struct media_session *session, IMFTopologyNod IUnknown_Release(object); }
- if (FAILED(hr)) + if (SUCCEEDED(hr)) + hr = session_set_transform_stream_info(topo_node); + else WARN("Failed to get IMFTransform for MFT node, hr %#x.\n", hr);
break;
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 225 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 180 insertions(+), 45 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 62b2b4f0ec..503948000e 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -110,6 +110,17 @@ struct media_sink BOOL finalized; };
+struct sample +{ + struct list entry; + IMFSample *sample; +}; + +struct transform_stream +{ + struct list samples; +}; + struct topo_node { struct list entry; @@ -141,6 +152,7 @@ struct topo_node unsigned int *input_map; unsigned int input_count;
+ struct transform_stream *outputs; unsigned int *output_map; unsigned int output_count; } transform; @@ -623,8 +635,23 @@ static void session_set_caps(struct media_session *session, DWORD caps) IMFMediaEvent_Release(event); }
+static void transform_stream_drop_samples(struct transform_stream *stream) +{ + struct sample *sample, *sample2; + + LIST_FOR_EACH_ENTRY_SAFE(sample, sample2, &stream->samples, struct sample, entry) + { + list_remove(&sample->entry); + if (sample->sample) + IMFSample_Release(sample->sample); + heap_free(sample); + } +} + static void release_topo_node(struct topo_node *node) { + unsigned int i; + switch (node->type) { case MF_TOPOLOGY_SOURCESTREAM_NODE: @@ -632,6 +659,9 @@ static void release_topo_node(struct topo_node *node) IMFMediaSource_Release(node->u.source.source); break; case MF_TOPOLOGY_TRANSFORM_NODE: + for (i = 0; i < node->u.transform.output_count; ++i) + transform_stream_drop_samples(&node->u.transform.outputs[i]); + heap_free(node->u.transform.outputs); heap_free(node->u.transform.input_map); heap_free(node->u.transform.output_map); break; @@ -960,14 +990,13 @@ static HRESULT session_add_media_sink(struct media_session *session, IMFTopology static HRESULT session_set_transform_stream_info(struct topo_node *node) { unsigned int *input_map = NULL, *output_map = NULL; - unsigned int input_count, output_count; + unsigned int i, input_count, output_count; + struct transform_stream *streams; HRESULT hr;
hr = IMFTransform_GetStreamCount(node->object.transform, &input_count, &output_count); if (SUCCEEDED(hr) && (input_count > 1 || output_count > 1)) { - unsigned int *input_map, *output_map; - input_map = heap_calloc(input_count, sizeof(*input_map)); output_map = heap_calloc(output_count, sizeof(*output_map)); if (FAILED(IMFTransform_GetStreamIDs(node->object.transform, input_count, input_map, @@ -982,6 +1011,11 @@ static HRESULT session_set_transform_stream_info(struct topo_node *node)
if (SUCCEEDED(hr)) { + streams = heap_calloc(output_count, sizeof(*streams)); + for (i = 0; i < output_count; ++i) + list_init(&streams[i].samples); + node->u.transform.outputs = streams; + node->u.transform.input_count = input_count; node->u.transform.output_count = output_count; node->u.transform.input_map = input_map; @@ -2167,63 +2201,81 @@ static struct topo_node *session_get_node_by_id(struct media_session *session, T return NULL; }
-static HRESULT session_request_sample_from_node(struct media_session *session, IMFTopologyNode *upstream_node, - DWORD upsteam_output) +static DWORD transform_node_get_stream_id(struct topo_node *node, BOOL output, DWORD index) { - MF_TOPOLOGY_TYPE upstream_type; - struct topo_node *node; - TOPOID upstream_id; - HRESULT hr; + unsigned int *map = output ? node->u.transform.output_map : node->u.transform.input_map; + return map ? map[index] : index; +}
- IMFTopologyNode_GetNodeType(upstream_node, &upstream_type); - IMFTopologyNode_GetTopoNodeID(upstream_node, &upstream_id); +static HRESULT transform_node_get_sample(struct topo_node *node, DWORD output, IMFSample **sample) +{ + struct list *list = &node->u.transform.outputs[output].samples; + MFT_OUTPUT_STREAM_INFO stream_info; + MFT_OUTPUT_DATA_BUFFER *buffers; + struct sample *queued_sample; + DWORD status = 0; + unsigned int i; + HRESULT hr;
- node = session_get_node_by_id(session, upstream_id); + *sample = NULL;
- switch (upstream_type) + if (!list_empty(list)) { - case MF_TOPOLOGY_SOURCESTREAM_NODE: - if (FAILED(hr = IMFMediaStream_RequestSample(node->object.source_stream, NULL))) - WARN("Sample request failed, hr %#x.\n", hr); - break; - case MF_TOPOLOGY_TRANSFORM_NODE: - case MF_TOPOLOGY_TEE_NODE: - FIXME("Unhandled upstream node type %d.\n", upstream_type); - default: - hr = E_UNEXPECTED; + queued_sample = LIST_ENTRY(list_head(list), struct sample, entry); + list_remove(&queued_sample->entry); + *sample = queued_sample->sample; + heap_free(queued_sample); + return S_OK; }
- return hr; -} + if (!(buffers = heap_calloc(node->u.transform.output_count, sizeof(*buffers)))) + return E_OUTOFMEMORY;
-static void session_request_sample(struct media_session *session, IMFStreamSink *sink_stream) -{ - struct topo_node *sink_node = NULL, *node; - IMFTopologyNode *upstream_node; - DWORD upstream_output; - HRESULT hr; + for (i = 0; i < node->u.transform.output_count; ++i) + { + buffers[i].dwStreamID = transform_node_get_stream_id(node, TRUE, i); + buffers[i].pSample = NULL; + buffers[i].dwStatus = 0; + buffers[i].pEvents = NULL;
- LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + /* Buffer requirements. */ + memset(&stream_info, 0, sizeof(stream_info)); + if (FAILED(hr = IMFTransform_GetOutputStreamInfo(node->object.transform, buffers[i].dwStreamID, &stream_info))) + goto exit; + + if (!(stream_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES)) + FIXME("Allocate client sample for stream %u.\n", buffers[i].dwStreamID); + } + + hr = IMFTransform_ProcessOutput(node->object.transform, 0, node->u.transform.output_count, buffers, &status); + if (hr == S_OK) { - if (node->type == MF_TOPOLOGY_OUTPUT_NODE && node->object.sink_stream == sink_stream) + /* Collect returned samples for all streams. */ + for (i = 0; i < node->u.transform.output_count; ++i) { - sink_node = node; - break; - } - } + if (buffers[i].pEvents) + IMFCollection_Release(buffers[i].pEvents);
- if (!sink_node) - return; + if (!buffers[i].pSample) + continue;
- if (FAILED(hr = IMFTopologyNode_GetInput(sink_node->node, 0, &upstream_node, &upstream_output))) - { - WARN("Failed to get upstream node connection, hr %#x.\n", hr); - return; + if (i == output) + { + *sample = buffers[i].pSample; + } + else + { + queued_sample = heap_alloc(sizeof(*queued_sample)); + queued_sample->sample = buffers[i].pSample; + list_add_tail(&node->u.transform.outputs[i].samples, &queued_sample->entry); + } + } }
- if (SUCCEEDED(session_request_sample_from_node(session, upstream_node, upstream_output))) - sink_node->u.sink.requests++; - IMFTopologyNode_Release(upstream_node); +exit: + heap_free(buffers); + + return hr; }
static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *downstream_node, @@ -2258,6 +2310,89 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop } }
+static HRESULT session_request_sample_from_node(struct media_session *session, IMFTopologyNode *node, DWORD output) +{ + IMFTopologyNode *downstream_node, *upstream_node; + MF_TOPOLOGY_TYPE node_type; + IMFSample *sample = NULL; + struct topo_node *topo_node; + DWORD downstream_input, upstream_output; + TOPOID node_id; + HRESULT hr; + + IMFTopologyNode_GetNodeType(node, &node_type); + IMFTopologyNode_GetTopoNodeID(node, &node_id); + + topo_node = session_get_node_by_id(session, node_id); + + switch (node_type) + { + case MF_TOPOLOGY_SOURCESTREAM_NODE: + if (FAILED(hr = IMFMediaStream_RequestSample(topo_node->object.source_stream, NULL))) + WARN("Sample request failed, hr %#x.\n", hr); + break; + case MF_TOPOLOGY_TRANSFORM_NODE: + + hr = transform_node_get_sample(topo_node, output, &sample); + if (sample) + { + if (SUCCEEDED(hr = IMFTopologyNode_GetOutput(node, output, &downstream_node, &downstream_input))) + { + session_deliver_sample_to_node(session, downstream_node, downstream_input, sample); + IMFTopologyNode_Release(downstream_node); + } + IMFSample_Release(sample); + } + + /* Forward request to upstream node. */ + if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT || (hr == S_OK && !sample)) + { + if (SUCCEEDED(hr = IMFTopologyNode_GetInput(node, 0 /* FIXME */, &upstream_node, &upstream_output))) + { + hr = session_request_sample_from_node(session, upstream_node, upstream_output); + IMFTopologyNode_Release(upstream_node); + } + } + break; + case MF_TOPOLOGY_TEE_NODE: + FIXME("Unhandled upstream node type %d.\n", node_type); + default: + hr = E_UNEXPECTED; + } + + return hr; +} + +static void session_request_sample(struct media_session *session, IMFStreamSink *sink_stream) +{ + struct topo_node *sink_node = NULL, *node; + IMFTopologyNode *upstream_node; + DWORD upstream_output; + HRESULT hr; + + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->type == MF_TOPOLOGY_OUTPUT_NODE && node->object.sink_stream == sink_stream) + { + sink_node = node; + break; + } + } + + if (!sink_node) + return; + + if (FAILED(hr = IMFTopologyNode_GetInput(sink_node->node, 0, &upstream_node, &upstream_output))) + { + WARN("Failed to get upstream node connection, hr %#x.\n", hr); + return; + } + + if (SUCCEEDED(session_request_sample_from_node(session, upstream_node, upstream_output))) + sink_node->u.sink.requests++; + IMFTopologyNode_Release(upstream_node); +} + static void session_deliver_sample(struct media_session *session, IMFMediaStream *stream, const PROPVARIANT *value) { struct topo_node *source_node = NULL, *node;
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 107 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 20 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 503948000e..e98be7c95f 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -119,6 +119,7 @@ struct sample struct transform_stream { struct list samples; + unsigned int requests; };
struct topo_node @@ -149,6 +150,7 @@ struct topo_node } sink; struct { + struct transform_stream *inputs; unsigned int *input_map; unsigned int input_count;
@@ -659,8 +661,11 @@ static void release_topo_node(struct topo_node *node) IMFMediaSource_Release(node->u.source.source); break; case MF_TOPOLOGY_TRANSFORM_NODE: + for (i = 0; i < node->u.transform.input_count; ++i) + transform_stream_drop_samples(&node->u.transform.inputs[i]); for (i = 0; i < node->u.transform.output_count; ++i) transform_stream_drop_samples(&node->u.transform.outputs[i]); + heap_free(node->u.transform.inputs); heap_free(node->u.transform.outputs); heap_free(node->u.transform.input_map); heap_free(node->u.transform.output_map); @@ -1011,6 +1016,11 @@ static HRESULT session_set_transform_stream_info(struct topo_node *node)
if (SUCCEEDED(hr)) { + streams = heap_calloc(input_count, sizeof(*streams)); + for (i = 0; i < input_count; ++i) + list_init(&streams[i].samples); + node->u.transform.inputs = streams; + streams = heap_calloc(output_count, sizeof(*streams)); for (i = 0; i < output_count; ++i) list_init(&streams[i].samples); @@ -2237,14 +2247,31 @@ static HRESULT transform_node_get_sample(struct topo_node *node, DWORD output, I buffers[i].pSample = NULL; buffers[i].dwStatus = 0; buffers[i].pEvents = NULL; + }
- /* Buffer requirements. */ - memset(&stream_info, 0, sizeof(stream_info)); - if (FAILED(hr = IMFTransform_GetOutputStreamInfo(node->object.transform, buffers[i].dwStreamID, &stream_info))) - goto exit; + memset(&stream_info, 0, sizeof(stream_info)); + if (FAILED(hr = IMFTransform_GetOutputStreamInfo(node->object.transform, buffers[output].dwStreamID, &stream_info))) + goto exit; + + if (!(stream_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES)) + { + IMFMediaBuffer *buffer = NULL; + + if (!stream_info.cbSize) + FIXME("Unhandled buffer size %u.\n", stream_info.cbSize); + + hr = MFCreateAlignedMemoryBuffer(stream_info.cbSize, stream_info.cbAlignment, &buffer); + if (SUCCEEDED(hr)) + hr = MFCreateSample(&buffers[output].pSample); + + if (SUCCEEDED(hr)) + hr = IMFSample_AddBuffer(buffers[output].pSample, buffer);
- if (!(stream_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES)) - FIXME("Allocate client sample for stream %u.\n", buffers[i].dwStreamID); + if (buffer) + IMFMediaBuffer_Release(buffer); + + if (FAILED(hr)) + goto exit; }
hr = IMFTransform_ProcessOutput(node->object.transform, 0, node->u.transform.output_count, buffers, &status); @@ -2278,32 +2305,71 @@ exit: return hr; }
-static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *downstream_node, - DWORD downstream_input, IMFSample *sample) +static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, DWORD input, + IMFSample *sample) { - MF_TOPOLOGY_TYPE downstream_type; - struct topo_node *node; - TOPOID downstream_id; + DWORD stream_id, downstream_input; + IMFTopologyNode *downstream_node; + struct topo_node *topo_node; + MF_TOPOLOGY_TYPE node_type; + TOPOID node_id; + unsigned int i; HRESULT hr;
- IMFTopologyNode_GetNodeType(downstream_node, &downstream_type); - IMFTopologyNode_GetTopoNodeID(downstream_node, &downstream_id); + IMFTopologyNode_GetNodeType(node, &node_type); + IMFTopologyNode_GetTopoNodeID(node, &node_id);
- node = session_get_node_by_id(session, downstream_id); + topo_node = session_get_node_by_id(session, node_id);
- switch (downstream_type) + switch (node_type) { case MF_TOPOLOGY_OUTPUT_NODE: - if (node->u.sink.requests) + if (topo_node->u.sink.requests) { - if (FAILED(hr = IMFStreamSink_ProcessSample(node->object.sink_stream, sample))) + if (FAILED(hr = IMFStreamSink_ProcessSample(topo_node->object.sink_stream, sample))) WARN("Sample delivery failed, hr %#x.\n", hr); - node->u.sink.requests--; + topo_node->u.sink.requests--; } break; case MF_TOPOLOGY_TRANSFORM_NODE: + + stream_id = transform_node_get_stream_id(topo_node, FALSE, input); + + hr = IMFTransform_ProcessInput(topo_node->object.transform, stream_id, sample, 0); + if (hr == MF_E_NOTACCEPTING) + { + struct sample *sample_entry = heap_alloc(sizeof(*sample_entry)); + sample_entry->sample = sample; + list_add_tail(&topo_node->u.transform.inputs[input].samples, &sample_entry->entry); + } + else if (hr == S_OK) + { + /* Check if we need new output is available, push it down. */ + for (i = 0; i < topo_node->u.transform.output_count; ++i) + { + if (!topo_node->u.transform.outputs[i].requests) + continue; + + sample = NULL; + transform_node_get_sample(topo_node, i, &sample); + if (sample) + { + if (SUCCEEDED(hr = IMFTopologyNode_GetOutput(node, i, &downstream_node, &downstream_input))) + { + session_deliver_sample_to_node(session, downstream_node, downstream_input, sample); + topo_node->u.transform.outputs[i].requests--; + IMFTopologyNode_Release(downstream_node); + } + IMFSample_Release(sample); + } + } + } + else + WARN("Transform failed to process input sample, hr %#x.\n", hr); + + break; case MF_TOPOLOGY_TEE_NODE: - FIXME("Unhandled downstream node type %d.\n", downstream_type); + FIXME("Unhandled downstream node type %d.\n", node_type); break; default: ; @@ -2349,7 +2415,8 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I { if (SUCCEEDED(hr = IMFTopologyNode_GetInput(node, 0 /* FIXME */, &upstream_node, &upstream_output))) { - hr = session_request_sample_from_node(session, upstream_node, upstream_output); + if (SUCCEEDED(hr = session_request_sample_from_node(session, upstream_node, upstream_output))) + topo_node->u.transform.outputs[output].requests++; IMFTopologyNode_Release(upstream_node); } }
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=67061
Your paranoid android.
=== w8 (32 bit report) ===
mfplat: mfplat.c:4645: Test failed: Unexpected hr 0xc00d36b4.
=== w8adm (32 bit report) ===
mfplat: mfplat.c:4645: Test failed: Unexpected hr 0xc00d36b4.
=== w864 (32 bit report) ===
mfplat: mfplat.c:4645: Test failed: Unexpected hr 0xc00d36b4. 0c40:mfplat: unhandled exception c0000005 at 004188DA
=== w1064v1809_2scr (32 bit report) ===
mfplat: mfplat.c:2688: Test failed: Unexpected return value 0x102. mfplat.c:2039: Test failed: Failed to get event, hr 0xc00d3e85. mfplat.c:2042: Test failed: Failed to get event, hr 0xc00d3e85. mfplat.c:2045: Test failed: Failed to finalize GetEvent, hr 0xc00d3e85. mfplat.c:2048: Test failed: Unexpected result, hr 0xc00d3e85. 1970:mfplat: unhandled exception c0000005 at 00401AFB
=== w864 (64 bit report) ===
mfplat: mfplat.c:4645: Test failed: Unexpected hr 0xc00d36b4. 0c24:mfplat: unhandled exception c0000005 at 0000000000414CA0