Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- include/mferror.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+)
diff --git a/include/mferror.h b/include/mferror.h index 6341d832ab..523800d7f0 100644 --- a/include/mferror.h +++ b/include/mferror.h @@ -166,4 +166,46 @@ #define MF_E_CLOCK_NOT_SIMPLE _HRESULT_TYPEDEF_(0xc00d9c43) #define MF_S_CLOCK_STOPPED _HRESULT_TYPEDEF_(0x000d9c44)
+#define MF_E_NO_MORE_DROP_MODES _HRESULT_TYPEDEF_(0xc00da028) +#define MF_E_NO_MORE_QUALITY_LEVELS _HRESULT_TYPEDEF_(0xc00da029) +#define MF_E_DROPTIME_NOT_SUPPORTED _HRESULT_TYPEDEF_(0xc00da02a) +#define MF_E_QUALITYKNOB_WAIT_LONGER _HRESULT_TYPEDEF_(0xc00da02b) +#define MF_E_QM_INVALIDSTATE _HRESULT_TYPEDEF_(0xc00da02c) + +#define MF_E_TRANSCODE_NO_CONTAINERTYPE _HRESULT_TYPEDEF_(0xc00da410) +#define MF_E_TRANSCODE_PROFILE_NO_MATCHING_STREAMS _HRESULT_TYPEDEF_(0xc00da411) +#define MF_E_TRANSCODE_NO_MATCHING_ENCODER _HRESULT_TYPEDEF_(0xc00da412) +#define MF_E_TRANSCODE_INVALID_PROFILE _HRESULT_TYPEDEF_(0xc00da413) + +#define MF_E_ALLOCATOR_NOT_INITIALIZED _HRESULT_TYPEDEF_(0xc00da7f8) +#define MF_E_ALLOCATOR_NOT_COMMITED _HRESULT_TYPEDEF_(0xc00da7f9) +#define MF_E_ALLOCATOR_ALREADY_COMMITED _HRESULT_TYPEDEF_(0xc00da7fa) +#define MF_E_STREAM_ERROR _HRESULT_TYPEDEF_(0xc00da7fb) +#define MF_E_INVALID_STREAM_STATE _HRESULT_TYPEDEF_(0xc00da7fc) +#define MF_E_HW_STREAM_NOT_CONNECTED _HRESULT_TYPEDEF_(0xc00da7fd) + +#define MF_E_NO_CAPTURE_DEVICES_AVAILABLE _HRESULT_TYPEDEF_(0xc00dabe0) +#define MF_E_CAPTURE_SINK_OUTPUT_NOT_SET _HRESULT_TYPEDEF_(0xc00dabe1) +#define MF_E_CAPTURE_SINK_MIRROR_ERROR _HRESULT_TYPEDEF_(0xc00dabe2) +#define MF_E_CAPTURE_SINK_ROTATE_ERROR _HRESULT_TYPEDEF_(0xc00dabe3) +#define MF_E_CAPTURE_ENGINE_INVALID_OP _HRESULT_TYPEDEF_(0xc00dabe4) +#define MF_E_CAPTURE_ENGINE_ALL_EFFECTS_REMOVED _HRESULT_TYPEDEF_(0xc00dabe5) +#define MF_E_CAPTURE_SOURCE_NO_INDEPENDENT_PHOTO_STREAM_PRESENT _HRESULT_TYPEDEF_(0xc00dabe6) +#define MF_E_CAPTURE_SOURCE_NO_VIDEO_STREAM_PRESENT _HRESULT_TYPEDEF_(0xc00dabe7) +#define MF_E_CAPTURE_SOURCE_NO_AUDIO_STREAM_PRESENT _HRESULT_TYPEDEF_(0xc00dabe8) +#define MF_E_CAPTURE_SOURCE_DEVICE_EXTENDEDPROP_OP_IN_PROGRESS _HRESULT_TYPEDEF_(0xc00dabe9) +#define MF_E_CAPTURE_PROPERTY_SET_DURING_PHOTO _HRESULT_TYPEDEF_(0xc00dabea) +#define MF_E_CAPTURE_NO_SAMPLES_IN_QUEUE _HRESULT_TYPEDEF_(0xc00dabeb) +#define MF_E_HW_ACCELERATED_THUMBNAIL_NOT_SUPPORTED _HRESULT_TYPEDEF_(0xc00dabec) +#define MF_E_UNSUPPORTED_CAPTURE_DEVICE_PRESENT _HRESULT_TYPEDEF_(0xc00dabed) + +#define MF_E_TIMELINECONTROLLER_UNSUPPORTED_SOURCE_TYPE _HRESULT_TYPEDEF_(0xc00dafc8) +#define MF_E_TIMELINECONTROLLER_NOT_ALLOWED _HRESULT_TYPEDEF_(0xc00dafc9) +#define MF_E_TIMELINECONTROLLER_CANNOT_ATTACH _HRESULT_TYPEDEF_(0xc00dafca) + +#define MF_E_MEDIA_EXTENSION_APPSERVICE_CONNECTION_FAILED _HRESULT_TYPEDEF_(0xc00db3b0) +#define MF_E_MEDIA_EXTENSION_APPSERVICE_REQUEST_FAILED _HRESULT_TYPEDEF_(0xc00db3b1) +#define MF_E_MEDIA_EXTENSION_PACKAGE_INTEGRITY_CHECK_FAILED _HRESULT_TYPEDEF_(0xc00db3b2) +#define MF_E_MEDIA_EXTENSION_PACKAGE_LICENSE_INVALID _HRESULT_TYPEDEF_(0xc00db3b3) + #endif /* __WINE_MFERROR_H */
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 62 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 5ee4074379..2ef4b01028 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -17,6 +17,7 @@ */
#include <stdarg.h> +#include <math.h>
#define COBJMACROS
@@ -1447,20 +1448,73 @@ static ULONG WINAPI session_rate_support_Release(IMFRateSupport *iface) return IMFMediaSession_Release(&session->IMFMediaSession_iface); }
+static void session_presentation_object_get_rate(IUnknown *object, MFRATE_DIRECTION direction, + BOOL thin, BOOL fastest, float *result) +{ + IMFRateSupport *rate_support; + float rate; + + if (SUCCEEDED(MFGetService(object, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, (void **)&rate_support))) + { + rate = 0.0f; + if (fastest) + { + if (SUCCEEDED(IMFRateSupport_GetFastestRate(rate_support, direction, thin, &rate))) + *result = min(fabsf(rate), *result); + } + else + { + if (SUCCEEDED(IMFRateSupport_GetSlowestRate(rate_support, direction, thin, &rate))) + *result = max(fabsf(rate), *result); + } + + IMFRateSupport_Release(rate_support); + } +} + +static HRESULT session_get_presentation_rate(struct media_session *session, MFRATE_DIRECTION direction, + BOOL thin, BOOL fastest, float *result) +{ + struct media_source *source; + struct media_sink *sink; + + *result = 0.0f; + + EnterCriticalSection(&session->cs); + + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) + { + session_presentation_object_get_rate((IUnknown *)source->source, direction, thin, fastest, result); + } + + LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry) + { + session_presentation_object_get_rate((IUnknown *)sink->sink, direction, thin, fastest, result); + } + + LeaveCriticalSection(&session->cs); + + return S_OK; +} + static HRESULT WINAPI session_rate_support_GetSlowestRate(IMFRateSupport *iface, MFRATE_DIRECTION direction, BOOL thin, float *rate) { - FIXME("%p, %d, %d, %p.\n", iface, direction, thin, rate); + struct media_session *session = impl_session_from_IMFRateSupport(iface);
- return E_NOTIMPL; + TRACE("%p, %d, %d, %p.\n", iface, direction, thin, rate); + + return session_get_presentation_rate(session, direction, thin, FALSE, rate); }
static HRESULT WINAPI session_rate_support_GetFastestRate(IMFRateSupport *iface, MFRATE_DIRECTION direction, BOOL thin, float *rate) { - FIXME("%p, %d, %d, %p.\n", iface, direction, thin, rate); + struct media_session *session = impl_session_from_IMFRateSupport(iface);
- return E_NOTIMPL; + TRACE("%p, %d, %d, %p.\n", iface, direction, thin, rate); + + return session_get_presentation_rate(session, direction, thin, TRUE, rate); }
static HRESULT WINAPI session_rate_support_IsRateSupported(IMFRateSupport *iface, BOOL thin, float rate,
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 2ef4b01028..6da495e3c1 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1416,6 +1416,10 @@ static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IM failed: if (event) IMFMediaEvent_Release(event); + + if (FAILED(hr = IMFMediaEventGenerator_BeginGetEvent(event_source, iface, (IUnknown *)event_source))) + WARN("Failed to re-subscribe, hr %#x.\n", hr); + IMFMediaEventGenerator_Release(event_source);
return hr;
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 243 ++++++++++++++++++++++++---------------------- 1 file changed, 125 insertions(+), 118 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 6da495e3c1..0963bcb06e 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -122,7 +122,8 @@ struct media_session IMFQualityManager *quality_manager; struct { - struct queued_topology current_topology; + IMFTopology *current_topology; + MF_TOPOSTATUS topo_status; struct list sources; struct list sinks; } presentation; @@ -446,7 +447,7 @@ static void session_clear_topologies(struct media_session *session) } }
-static void session_set_topo_status(struct media_session *session, struct queued_topology *topology, HRESULT status, +static void session_set_topo_status(struct media_session *session, HRESULT status, MF_TOPOSTATUS topo_status) { IMFMediaEvent *event; @@ -455,20 +456,20 @@ static void session_set_topo_status(struct media_session *session, struct queued if (topo_status == MF_TOPOSTATUS_INVALID) return;
- if (topo_status > topology->status) + if (topo_status > session->presentation.topo_status) { - param.vt = topology ? VT_UNKNOWN : VT_EMPTY; - param.punkVal = topology ? (IUnknown *)topology->topology : NULL; + param.vt = VT_UNKNOWN; + param.punkVal = (IUnknown *)session->presentation.current_topology;
if (FAILED(MFCreateMediaEvent(MESessionTopologyStatus, &GUID_NULL, status, ¶m, &event))) return;
+ session->presentation.topo_status = topo_status; + IMFMediaEvent_SetUINT32(event, &MF_EVENT_TOPOLOGY_STATUS, topo_status); - topology->status = topo_status; IMFMediaEventQueue_QueueEvent(session->event_queue, event); + IMFMediaEvent_Release(event); } - - IMFMediaEvent_Release(event); }
static HRESULT session_bind_output_nodes(IMFTopology *topology) @@ -533,36 +534,14 @@ static HRESULT session_bind_output_nodes(IMFTopology *topology) return hr; }
-static IMFTopology *session_get_next_topology(struct media_session *session) -{ - struct queued_topology *queued; - - if (!session->presentation.current_topology.topology) - { - struct list *head = list_head(&session->topologies); - if (!head) - return NULL; - - queued = LIST_ENTRY(head, struct queued_topology, entry); - session->presentation.current_topology = *queued; - list_remove(&queued->entry); - heap_free(queued); - } - - return session->presentation.current_topology.topology; -} - static void session_clear_presentation(struct media_session *session) { struct media_source *source, *source2; struct media_stream *stream, *stream2; struct media_sink *sink, *sink2;
- if (session->presentation.current_topology.topology) - { - IMFTopology_Release(session->presentation.current_topology.topology); - session->presentation.current_topology.topology = NULL; - } + IMFTopology_Clear(session->presentation.current_topology); + session->presentation.topo_status = MF_TOPOSTATUS_INVALID;
LIST_FOR_EACH_ENTRY_SAFE(source, source2, &session->presentation.sources, struct media_source, entry) { @@ -594,7 +573,6 @@ static void session_clear_presentation(struct media_session *session) static void session_start(struct media_session *session, const GUID *time_format, const PROPVARIANT *start_position) { IMFTopologyNode *node; - IMFTopology *topology; IMFCollection *nodes; DWORD count, i; HRESULT hr; @@ -604,9 +582,8 @@ static void session_start(struct media_session *session, const GUID *time_format switch (session->state) { case SESSION_STATE_STOPPED: - topology = session_get_next_topology(session);
- if (!topology || FAILED(IMFTopology_GetSourceNodeCollection(topology, &nodes))) + if (FAILED(IMFTopology_GetSourceNodeCollection(session->presentation.current_topology, &nodes))) break;
if (FAILED(IMFCollection_GetElementCount(nodes, &count))) @@ -671,6 +648,111 @@ static void session_start(struct media_session *session, const GUID *time_format LeaveCriticalSection(&session->cs); }
+static void session_raise_topology_set(struct media_session *session, IMFTopology *topology, HRESULT status) +{ + PROPVARIANT param; + + param.vt = topology ? VT_UNKNOWN : VT_EMPTY; + param.punkVal = (IUnknown *)topology; + + IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionTopologySet, &GUID_NULL, status, ¶m); +} + +static void session_set_current_topology(struct media_session *session, IMFTopology *topology) +{ + IMFMediaEvent *event; + HRESULT hr; + + if (FAILED(hr = IMFTopology_CloneFrom(session->presentation.current_topology, topology))) + { + WARN("Failed to clone topology, hr %#x.\n", hr); + return; + } + + /* FIXME: attributes are all zero for now */ + if (SUCCEEDED(MFCreateMediaEvent(MESessionNotifyPresentationTime, &GUID_NULL, S_OK, NULL, &event))) + { + IMFMediaEvent_SetUINT64(event, &MF_EVENT_START_PRESENTATION_TIME, 0); + IMFMediaEvent_SetUINT64(event, &MF_EVENT_PRESENTATION_TIME_OFFSET, 0); + IMFMediaEvent_GetUINT64(event, &MF_EVENT_START_PRESENTATION_TIME_AT_OUTPUT, 0); + + IMFMediaEventQueue_QueueEvent(session->event_queue, event); + IMFMediaEvent_Release(event); + } +} + +static void session_set_topology(struct media_session *session, DWORD flags, IMFTopology *topology) +{ + HRESULT status = S_OK; + + /* Resolve unless claimed to be full. */ + if (!(flags & MFSESSION_SETTOPOLOGY_CLEAR_CURRENT) && topology) + { + if (!(flags & MFSESSION_SETTOPOLOGY_NORESOLUTION)) + { + IMFTopology *resolved_topology = NULL; + + status = session_bind_output_nodes(topology); + + if (SUCCEEDED(status)) + status = IMFTopoLoader_Load(session->topo_loader, topology, &resolved_topology, NULL /* FIXME? */); + + if (SUCCEEDED(status)) + { + topology = resolved_topology; + } + } + } + + EnterCriticalSection(&session->cs); + + if (flags & MFSESSION_SETTOPOLOGY_CLEAR_CURRENT) + { + if ((topology && topology == session->presentation.current_topology) || !topology) + { + /* FIXME: stop current topology, queue next one. */ + session_clear_presentation(session); + } + else + status = S_FALSE; + + topology = NULL; + } + else if (topology && flags & MFSESSION_SETTOPOLOGY_IMMEDIATE) + { + session_clear_topologies(session); + session_clear_presentation(session); + } + + session_raise_topology_set(session, topology, status); + + /* 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); + } + else + { + struct queued_topology *queued_topology; + + if ((queued_topology = heap_alloc_zero(sizeof(*queued_topology)))) + { + queued_topology->topology = topology; + IMFTopology_AddRef(queued_topology->topology); + + list_add_tail(&session->topologies, &queued_topology->entry); + } + } + } + + if (status == S_OK) + session_set_topo_status(session, S_OK, MF_TOPOSTATUS_READY); + + LeaveCriticalSection(&session->cs); +} + static HRESULT WINAPI mfsession_QueryInterface(IMFMediaSession *iface, REFIID riid, void **out) { struct media_session *session = impl_from_IMFMediaSession(iface); @@ -718,6 +800,8 @@ static ULONG WINAPI mfsession_Release(IMFMediaSession *iface) { session_clear_topologies(session); session_clear_presentation(session); + if (session->presentation.current_topology) + IMFTopology_Release(session->presentation.current_topology); if (session->event_queue) IMFMediaEventQueue_Release(session->event_queue); if (session->clock) @@ -1053,21 +1137,10 @@ static HRESULT WINAPI session_commands_callback_GetParameters(IMFAsyncCallback * return E_NOTIMPL; }
-static void session_raise_topology_set(struct media_session *session, IMFTopology *topology, HRESULT status) -{ - PROPVARIANT param; - - param.vt = topology ? VT_UNKNOWN : VT_EMPTY; - param.punkVal = (IUnknown *)topology; - - IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionTopologySet, &GUID_NULL, status, ¶m); -} - static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct session_op *op = impl_op_from_IUnknown(IMFAsyncResult_GetStateNoAddRef(result)); struct media_session *session = impl_from_commands_callback_IMFAsyncCallback(iface); - HRESULT status = S_OK;
switch (op->command) { @@ -1080,76 +1153,8 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface, S_OK, NULL); break; case SESSION_CMD_SET_TOPOLOGY: - { - IMFTopology *topology = op->u.set_topology.topology; - MF_TOPOSTATUS topo_status = MF_TOPOSTATUS_INVALID; - struct queued_topology *queued_topology = NULL; - DWORD flags = op->u.set_topology.flags; - - /* Resolve unless claimed to be full. */ - if (!(flags & MFSESSION_SETTOPOLOGY_CLEAR_CURRENT) && topology) - { - if (!(flags & MFSESSION_SETTOPOLOGY_NORESOLUTION)) - { - IMFTopology *resolved_topology = NULL; - - status = session_bind_output_nodes(topology); - - if (SUCCEEDED(status)) - status = IMFTopoLoader_Load(session->topo_loader, topology, &resolved_topology, NULL /* FIXME? */); - - if (SUCCEEDED(status)) - { - topology = resolved_topology; - topo_status = MF_TOPOSTATUS_READY; - } - } - - if (SUCCEEDED(status)) - { - if (!(queued_topology = heap_alloc_zero(sizeof(*queued_topology)))) - status = E_OUTOFMEMORY; - } - - if (SUCCEEDED(status)) - { - queued_topology->topology = topology; - IMFTopology_AddRef(queued_topology->topology); - } - } - - EnterCriticalSection(&session->cs); - - if (flags & MFSESSION_SETTOPOLOGY_CLEAR_CURRENT) - { - if ((topology && topology == session->presentation.current_topology.topology) || !topology) - { - /* FIXME: stop current topology, queue next one. */ - session_clear_presentation(session); - } - else - status = S_FALSE; - topo_status = MF_TOPOSTATUS_READY; - queued_topology = &session->presentation.current_topology; - } - else if (queued_topology) - { - if (flags & MFSESSION_SETTOPOLOGY_IMMEDIATE) - { - session_clear_topologies(session); - session_clear_presentation(session); - } - - list_add_tail(&session->topologies, &queued_topology->entry); - } - - session_raise_topology_set(session, topology, status); - session_set_topo_status(session, queued_topology, status, topo_status); - - LeaveCriticalSection(&session->cs); - + session_set_topology(session, op->u.set_topology.flags, op->u.set_topology.topology); break; - } case SESSION_CMD_START: { session_start(session, &op->u.start.time_format, &op->u.start.start_position); @@ -1247,7 +1252,7 @@ static BOOL session_set_source_state(struct media_session *session, IMFMediaSour
static HRESULT session_set_sinks_clock(struct media_session *session) { - IMFTopology *topology = session->presentation.current_topology.topology; + IMFTopology *topology = session->presentation.current_topology; IMFTopologyNode *node; IMFCollection *nodes; DWORD count, i; @@ -1363,8 +1368,7 @@ static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IM ret = session_set_source_state(session, (IMFMediaSource *)event_source, source_state); if (ret && event_type == MESourceStarted) { - session_set_topo_status(session, &session->presentation.current_topology, S_OK, - MF_TOPOSTATUS_STARTED_SOURCE); + session_set_topo_status(session, S_OK, MF_TOPOSTATUS_STARTED_SOURCE);
if (session->state == SESSION_STATE_STARTING) { @@ -1609,6 +1613,9 @@ HRESULT WINAPI MFCreateMediaSession(IMFAttributes *config, IMFMediaSession **ses list_init(&object->presentation.sinks); InitializeCriticalSection(&object->cs);
+ if (FAILED(hr = MFCreateTopology(&object->presentation.current_topology))) + goto failed; + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) goto failed;
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 218 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 179 insertions(+), 39 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 0963bcb06e..17bb44eaa7 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -79,12 +79,6 @@ enum session_state SESSION_STATE_SHUT_DOWN, };
-struct media_stream -{ - struct list entry; - IMFMediaStream *stream; -}; - enum source_state { SOURCE_STATE_STOPPED = 0, @@ -96,8 +90,16 @@ struct media_source { struct list entry; IMFMediaSource *source; + IMFPresentationDescriptor *pd; enum source_state state; - struct list streams; +}; + +struct source_node +{ + struct list entry; + IMFMediaStream *stream; + IMFMediaSource *source; + DWORD stream_id; };
struct media_sink @@ -125,6 +127,7 @@ struct media_session IMFTopology *current_topology; MF_TOPOSTATUS topo_status; struct list sources; + struct list source_nodes; struct list sinks; } presentation; struct list topologies; @@ -536,8 +539,8 @@ static HRESULT session_bind_output_nodes(IMFTopology *topology)
static void session_clear_presentation(struct media_session *session) { + struct source_node *src_node, *src_node2; struct media_source *source, *source2; - struct media_stream *stream, *stream2; struct media_sink *sink, *sink2;
IMFTopology_Clear(session->presentation.current_topology); @@ -546,20 +549,22 @@ static void session_clear_presentation(struct media_session *session) LIST_FOR_EACH_ENTRY_SAFE(source, source2, &session->presentation.sources, struct media_source, entry) { list_remove(&source->entry); - - LIST_FOR_EACH_ENTRY_SAFE(stream, stream2, &source->streams, struct media_stream, entry) - { - list_remove(&stream->entry); - if (stream->stream) - IMFMediaStream_Release(stream->stream); - heap_free(stream); - } - if (source->source) IMFMediaSource_Release(source->source); + if (source->pd) + IMFPresentationDescriptor_Release(source->pd); heap_free(source); }
+ LIST_FOR_EACH_ENTRY_SAFE(src_node, src_node2, &session->presentation.source_nodes, struct source_node, entry) + { + list_remove(&src_node->entry); + if (src_node->stream) + IMFMediaStream_Release(src_node->stream); + heap_free(src_node); + } + + LIST_FOR_EACH_ENTRY_SAFE(sink, sink2, &session->presentation.sinks, struct media_sink, entry) { list_remove(&sink->entry); @@ -648,6 +653,130 @@ static void session_start(struct media_session *session, const GUID *time_format LeaveCriticalSection(&session->cs); }
+static struct media_source *session_get_media_source(struct media_session *session, IMFMediaSource *source) +{ + struct media_source *cur; + + LIST_FOR_EACH_ENTRY(cur, &session->presentation.sources, struct media_source, entry) + { + if (source == cur->source) + return cur; + } + + return NULL; +} + +static void session_release_media_source(struct media_source *source) +{ + IMFMediaSource_Release(source->source); + if (source->pd) + IMFPresentationDescriptor_Release(source->pd); + heap_free(source); +} + +static HRESULT session_add_media_source(struct media_session *session, IMFTopologyNode *node, IMFMediaSource *source) +{ + struct media_source *media_source; + HRESULT hr; + + if (session_get_media_source(session, source)) + return S_FALSE; + + if (!(media_source = heap_alloc_zero(sizeof(*media_source)))) + return E_OUTOFMEMORY; + + media_source->source = source; + IMFMediaSource_AddRef(media_source->source); + + hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, &IID_IMFPresentationDescriptor, + (void **)&media_source->pd); + + if (SUCCEEDED(hr)) + list_add_tail(&session->presentation.sources, &media_source->entry); + else + session_release_media_source(media_source); + + 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; @@ -669,6 +798,8 @@ static void session_set_current_topology(struct media_session *session, IMFTopol return; }
+ session_collect_source_nodes(session); + /* FIXME: attributes are all zero for now */ if (SUCCEEDED(MFCreateMediaEvent(MESessionNotifyPresentationTime, &GUID_NULL, S_OK, NULL, &event))) { @@ -1220,17 +1351,36 @@ static HRESULT WINAPI session_events_callback_GetParameters(IMFAsyncCallback *if return E_NOTIMPL; }
-static HRESULT session_add_media_stream(struct media_source *source, IMFMediaStream *stream) +static HRESULT session_add_media_stream(struct media_session *session, IMFMediaSource *source, IMFMediaStream *stream) { - struct media_stream *media_stream; + struct source_node *node; + IMFStreamDescriptor *sd; + DWORD stream_id = 0; + HRESULT hr;
- if (!(media_stream = heap_alloc_zero(sizeof(*media_stream)))) - return E_OUTOFMEMORY; + if (FAILED(hr = IMFMediaStream_GetStreamDescriptor(stream, &sd))) + return hr;
- media_stream->stream = stream; - IMFMediaStream_AddRef(media_stream->stream); + hr = IMFStreamDescriptor_GetStreamIdentifier(sd, &stream_id); + IMFStreamDescriptor_Release(sd); + if (FAILED(hr)) + return hr;
- list_add_tail(&source->streams, &media_stream->entry); + LIST_FOR_EACH_ENTRY(node, &session->presentation.source_nodes, struct source_node, entry) + { + if (node->source == source && node->stream_id == stream_id) + { + if (node->stream) + { + WARN("Source node already has stream set.\n"); + return S_FALSE; + } + + node->stream = stream; + IMFMediaStream_AddRef(node->stream); + break; + } + }
return S_OK; } @@ -1322,7 +1472,6 @@ static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IM enum source_state source_state; IMFMediaEvent *event = NULL; MediaEventType event_type; - struct media_source *cur; IMFMediaSource *source; IMFMediaStream *stream; PROPVARIANT value; @@ -1394,22 +1543,12 @@ static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IM break;
EnterCriticalSection(&session->cs); - - LIST_FOR_EACH_ENTRY(cur, &session->presentation.sources, struct media_source, entry) - { - if (source == cur->source) - { - hr = session_add_media_stream(cur, stream); - - if (SUCCEEDED(hr)) - hr = IMFMediaStream_BeginGetEvent(stream, &session->events_callback, (IUnknown *)stream); - - break; - } - } - + if (SUCCEEDED(hr = session_add_media_stream(session, source, stream))) + hr = IMFMediaStream_BeginGetEvent(stream, &session->events_callback, (IUnknown *)stream); LeaveCriticalSection(&session->cs);
+ IMFMediaSource_Release(source); + break; default: ; @@ -1610,6 +1749,7 @@ 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); InitializeCriticalSection(&object->cs);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 17bb44eaa7..12685583e2 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -79,11 +79,12 @@ enum session_state SESSION_STATE_SHUT_DOWN, };
-enum source_state +enum object_state { - SOURCE_STATE_STOPPED = 0, - SOURCE_STATE_STARTED, - SOURCE_STATE_PAUSED, + OBJ_STATE_STOPPED = 0, + OBJ_STATE_STARTED, + OBJ_STATE_PAUSED, + OBJ_STATE_INVALID, };
struct media_source @@ -91,7 +92,7 @@ struct media_source struct list entry; IMFMediaSource *source; IMFPresentationDescriptor *pd; - enum source_state state; + enum object_state state; };
struct source_node @@ -1385,7 +1386,7 @@ static HRESULT session_add_media_stream(struct media_session *session, IMFMediaS return S_OK; }
-static BOOL session_set_source_state(struct media_session *session, IMFMediaSource *source, enum source_state state) +static BOOL session_set_source_state(struct media_session *session, IMFMediaSource *source, enum object_state state) { struct media_source *cur; BOOL ret = TRUE; @@ -1469,7 +1470,7 @@ static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IM { struct media_session *session = impl_from_events_callback_IMFAsyncCallback(iface); IMFMediaEventGenerator *event_source; - enum source_state source_state; + enum object_state source_state; IMFMediaEvent *event = NULL; MediaEventType event_type; IMFMediaSource *source; @@ -1506,11 +1507,11 @@ static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IM case MESourcePaused: case MESourceStopped: if (event_type == MESourceStarted) - source_state = SOURCE_STATE_STARTED; + source_state = OBJ_STATE_STARTED; else if (event_type == MESourcePaused) - source_state = SOURCE_STATE_PAUSED; + source_state = OBJ_STATE_PAUSED; else - source_state = SOURCE_STATE_STOPPED; + source_state = OBJ_STATE_STOPPED;
EnterCriticalSection(&session->cs);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 12685583e2..288ea60e6f 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -109,6 +109,13 @@ struct media_sink IMFMediaSink *sink; };
+struct output_node +{ + struct list entry; + IMFStreamSink *stream; + enum object_state state; +}; + struct media_session { IMFMediaSession IMFMediaSession_iface; @@ -130,6 +137,7 @@ struct media_session struct list sources; struct list source_nodes; struct list sinks; + struct list output_nodes; } presentation; struct list topologies; enum session_state state; @@ -788,6 +796,89 @@ static void session_raise_topology_set(struct media_session *session, IMFTopolog IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionTopologySet, &GUID_NULL, status, ¶m); }
+static HRESULT session_add_media_sink(struct media_session *session, IMFTopologyNode *node, IMFMediaSink *sink) +{ + struct media_sink *media_sink; + + LIST_FOR_EACH_ENTRY(media_sink, &session->presentation.sinks, struct media_sink, entry) + { + if (sink == media_sink->sink) + return S_FALSE; + } + + if (!(media_sink = heap_alloc_zero(sizeof(*media_sink)))) + return E_OUTOFMEMORY; + + media_sink->sink = sink; + IMFMediaSink_AddRef(media_sink->sink); + + list_add_tail(&session->presentation.sinks, &media_sink->entry); + + return S_OK; +} + +static HRESULT session_collect_output_nodes(struct media_session *session) +{ + IMFTopology *topology = session->presentation.current_topology; + IMFTopologyNode *topo_node; + struct output_node *node; + IMFCollection *nodes; + DWORD count, i; + HRESULT hr; + + if (!list_empty(&session->presentation.output_nodes)) + return S_OK; + + if (FAILED(hr = IMFTopology_GetOutputNodeCollection(topology, &nodes))) + return hr; + + if (FAILED(hr = IMFCollection_GetElementCount(nodes, &count))) + { + IMFCollection_Release(nodes); + return hr; + } + + for (i = 0; i < count; ++i) + { + IMFMediaSink *media_sink = NULL; + + if (FAILED(hr = IMFCollection_GetElement(nodes, i, (IUnknown **)&topo_node))) + break; + + if (!(node = heap_alloc_zero(sizeof(*node)))) + hr = E_OUTOFMEMORY; + + if (SUCCEEDED(hr)) + hr = IMFTopologyNode_GetObject(topo_node, (IUnknown **)&node->stream); + + if (SUCCEEDED(hr)) + hr = IMFStreamSink_GetMediaSink(node->stream, &media_sink); + + if (SUCCEEDED(hr)) + hr = session_add_media_sink(session, topo_node, media_sink); + + if (media_sink) + IMFMediaSink_Release(media_sink); + + if (SUCCEEDED(hr)) + { + list_add_tail(&session->presentation.output_nodes, &node->entry); + } + else if (node) + { + if (node->stream) + IMFStreamSink_Release(node->stream); + heap_free(node); + } + + IMFTopologyNode_Release(topo_node); + } + + IMFCollection_Release(nodes); + + return hr; +} + static void session_set_current_topology(struct media_session *session, IMFTopology *topology) { IMFMediaEvent *event; @@ -800,6 +891,7 @@ static void session_set_current_topology(struct media_session *session, IMFTopol }
session_collect_source_nodes(session); + session_collect_output_nodes(session);
/* FIXME: attributes are all zero for now */ if (SUCCEEDED(MFCreateMediaEvent(MESessionNotifyPresentationTime, &GUID_NULL, S_OK, NULL, &event))) @@ -1752,6 +1844,7 @@ HRESULT WINAPI MFCreateMediaSession(IMFAttributes *config, IMFMediaSession **ses list_init(&object->presentation.sources); list_init(&object->presentation.source_nodes); list_init(&object->presentation.sinks); + list_init(&object->presentation.output_nodes); InitializeCriticalSection(&object->cs);
if (FAILED(hr = MFCreateTopology(&object->presentation.current_topology)))
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 288ea60e6f..0bbf0bac56 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -107,6 +107,7 @@ struct media_sink { struct list entry; IMFMediaSink *sink; + IMFMediaEventGenerator *event_generator; };
struct output_node @@ -573,13 +574,14 @@ static void session_clear_presentation(struct media_session *session) heap_free(src_node); }
- LIST_FOR_EACH_ENTRY_SAFE(sink, sink2, &session->presentation.sinks, struct media_sink, entry) { list_remove(&sink->entry);
if (sink->sink) IMFMediaSink_Release(sink->sink); + if (sink->event_generator) + IMFMediaEventGenerator_Release(sink->event_generator); heap_free(sink); } } @@ -812,6 +814,8 @@ static HRESULT session_add_media_sink(struct media_session *session, IMFTopology media_sink->sink = sink; IMFMediaSink_AddRef(media_sink->sink);
+ IMFMediaSink_QueryInterface(media_sink->sink, &IID_IMFMediaEventGenerator, (void **)&media_sink->event_generator); + list_add_tail(&session->presentation.sinks, &media_sink->entry);
return S_OK;
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 105 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 0bbf0bac56..cbaa2bf23a 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -142,6 +142,7 @@ struct media_session } presentation; struct list topologies; enum session_state state; + DWORD caps; CRITICAL_SECTION cs; };
@@ -798,6 +799,50 @@ static void session_raise_topology_set(struct media_session *session, IMFTopolog IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionTopologySet, &GUID_NULL, status, ¶m); }
+static DWORD session_get_object_rate_caps(IUnknown *object) +{ + IMFRateSupport *rate_support; + DWORD caps = 0; + float rate; + + if (SUCCEEDED(MFGetService(object, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, (void **)&rate_support))) + { + rate = 0.0f; + if (SUCCEEDED(IMFRateSupport_GetFastestRate(rate_support, MFRATE_FORWARD, TRUE, &rate)) && rate != 0.0f) + caps |= MFSESSIONCAP_RATE_FORWARD; + + rate = 0.0f; + if (SUCCEEDED(IMFRateSupport_GetFastestRate(rate_support, MFRATE_REVERSE, TRUE, &rate)) && rate != 0.0f) + caps |= MFSESSIONCAP_RATE_REVERSE; + + IMFRateSupport_Release(rate_support); + } + + return caps; +} + +static void session_set_caps(struct media_session *session, DWORD caps) +{ + DWORD delta = session->caps ^ caps; + IMFMediaEvent *event; + + /* Delta is documented to reflect rate value changes as well, but it's not clear what to compare + them to, since session always queries for current object rates. */ + if (!delta) + return; + + session->caps = caps; + + if (FAILED(MFCreateMediaEvent(MESessionCapabilitiesChanged, &GUID_NULL, S_OK, NULL, &event))) + return; + + IMFMediaEvent_SetUINT32(event, &MF_EVENT_SESSIONCAPS, caps); + IMFMediaEvent_SetUINT32(event, &MF_EVENT_SESSIONCAPS_DELTA, delta); + + IMFMediaEventQueue_QueueEvent(session->event_queue, event); + IMFMediaEvent_Release(event); +} + static HRESULT session_add_media_sink(struct media_session *session, IMFTopologyNode *node, IMFMediaSink *sink) { struct media_sink *media_sink; @@ -885,6 +930,9 @@ static HRESULT session_collect_output_nodes(struct media_session *session)
static void session_set_current_topology(struct media_session *session, IMFTopology *topology) { + struct media_source *source; + DWORD caps, object_flags; + struct media_sink *sink; IMFMediaEvent *event; HRESULT hr;
@@ -907,6 +955,46 @@ static void session_set_current_topology(struct media_session *session, IMFTopol IMFMediaEventQueue_QueueEvent(session->event_queue, event); IMFMediaEvent_Release(event); } + + /* Update session caps. */ + caps = MFSESSIONCAP_START | MFSESSIONCAP_SEEK | MFSESSIONCAP_RATE_FORWARD | MFSESSIONCAP_RATE_REVERSE | + MFSESSIONCAP_DOES_NOT_USE_NETWORK; + + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) + { + if (!caps) + break; + + object_flags = 0; + if (SUCCEEDED(IMFMediaSource_GetCharacteristics(source->source, &object_flags))) + { + if (!(object_flags & MFMEDIASOURCE_DOES_NOT_USE_NETWORK)) + caps &= ~MFSESSIONCAP_DOES_NOT_USE_NETWORK; + if (!(object_flags & MFMEDIASOURCE_CAN_SEEK)) + caps &= ~MFSESSIONCAP_SEEK; + } + + /* Mask unsupported rate caps. */ + + caps &= session_get_object_rate_caps((IUnknown *)source->source) + | ~(MFSESSIONCAP_RATE_FORWARD | MFSESSIONCAP_RATE_REVERSE); + } + + LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry) + { + if (!caps) + break; + + object_flags = 0; + if (SUCCEEDED(IMFMediaSink_GetCharacteristics(sink->sink, &object_flags))) + { + if (!(object_flags & MEDIASINK_RATELESS)) + caps &= session_get_object_rate_caps((IUnknown *)sink->sink) + | ~(MFSESSIONCAP_RATE_FORWARD | MFSESSIONCAP_RATE_REVERSE); + } + } + + session_set_caps(session, caps); }
static void session_set_topology(struct media_session *session, DWORD flags, IMFTopology *topology) @@ -1242,9 +1330,22 @@ static HRESULT WINAPI mfsession_GetClock(IMFMediaSession *iface, IMFClock **cloc
static HRESULT WINAPI mfsession_GetSessionCapabilities(IMFMediaSession *iface, DWORD *caps) { - FIXME("%p, %p.\n", iface, caps); + struct media_session *session = impl_from_IMFMediaSession(iface); + HRESULT hr = S_OK;
- return E_NOTIMPL; + TRACE("%p, %p.\n", iface, caps); + + if (!caps) + return E_POINTER; + + EnterCriticalSection(&session->cs); + if (session->state == SESSION_STATE_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else + *caps = session->caps; + LeaveCriticalSection(&session->cs); + + return hr; }
static HRESULT WINAPI mfsession_GetFullTopology(IMFMediaSession *iface, DWORD flags, TOPOID id, IMFTopology **topology)
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 313 +++++++++++++++++++++++++++------------------- 1 file changed, 186 insertions(+), 127 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index cbaa2bf23a..367f39cd9c 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -73,7 +73,8 @@ struct queued_topology enum session_state { SESSION_STATE_STOPPED = 0, - SESSION_STATE_STARTING, + SESSION_STATE_STARTING_SOURCES, + SESSION_STATE_STARTING_SINKS, SESSION_STATE_RUNNING, SESSION_STATE_CLOSED, SESSION_STATE_SHUT_DOWN, @@ -101,6 +102,7 @@ struct source_node IMFMediaStream *stream; IMFMediaSource *source; DWORD stream_id; + enum object_state state; };
struct media_sink @@ -117,6 +119,12 @@ struct output_node enum object_state state; };
+enum presentation_flags +{ + SESSION_FLAG_SOURCES_SUBSCRIBED = 0x1, + SESSION_FLAG_SINKS_SUBSCRIBED = 0x2, +}; + struct media_session { IMFMediaSession IMFMediaSession_iface; @@ -128,6 +136,7 @@ struct media_session LONG refcount; IMFMediaEventQueue *event_queue; IMFPresentationClock *clock; + IMFPresentationTimeSource *system_time_source; IMFRateControl *clock_rate_control; IMFTopoLoader *topo_loader; IMFQualityManager *quality_manager; @@ -139,6 +148,11 @@ struct media_session struct list source_nodes; struct list sinks; struct list output_nodes; + DWORD flags; + + /* Latest Start() arguments. */ + GUID time_format; + PROPVARIANT start_position; } presentation; struct list topologies; enum session_state state; @@ -589,9 +603,7 @@ static void session_clear_presentation(struct media_session *session)
static void session_start(struct media_session *session, const GUID *time_format, const PROPVARIANT *start_position) { - IMFTopologyNode *node; - IMFCollection *nodes; - DWORD count, i; + struct media_source *source; HRESULT hr;
EnterCriticalSection(&session->cs); @@ -600,60 +612,27 @@ static void session_start(struct media_session *session, const GUID *time_format { case SESSION_STATE_STOPPED:
- if (FAILED(IMFTopology_GetSourceNodeCollection(session->presentation.current_topology, &nodes))) - break; - - if (FAILED(IMFCollection_GetElementCount(nodes, &count))) - break; + session->presentation.time_format = *time_format; + session->presentation.start_position.vt = VT_EMPTY; + PropVariantCopy(&session->presentation.start_position, start_position);
- for (i = 0; i < count; ++i) + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) { - if (SUCCEEDED(hr = IMFCollection_GetElement(nodes, i, (IUnknown **)&node))) + if (!(session->presentation.flags & SESSION_FLAG_SOURCES_SUBSCRIBED)) { - IMFPresentationDescriptor *pd = NULL; - struct media_source *source; - - if (!(source = heap_alloc_zero(sizeof(*source)))) - hr = E_OUTOFMEMORY; - - if (SUCCEEDED(hr)) - hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_SOURCE, &IID_IMFMediaSource, - (void **)&source->source); - - if (SUCCEEDED(hr)) - hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, - &IID_IMFPresentationDescriptor, (void **)&pd); - - /* Subscribe to source events, start it. */ - if (SUCCEEDED(hr)) - hr = IMFMediaSource_BeginGetEvent(source->source, &session->events_callback, - (IUnknown *)source->source); - - if (SUCCEEDED(hr)) - hr = IMFMediaSource_Start(source->source, pd, time_format, start_position); - - if (pd) - IMFPresentationDescriptor_Release(pd); - - if (SUCCEEDED(hr)) - { - list_add_tail(&session->presentation.sources, &source->entry); - } - else if (source) + if (FAILED(hr = IMFMediaSource_BeginGetEvent(source->source, &session->events_callback, + (IUnknown *)source->source))) { - if (source->source) - IMFMediaSource_Release(source->source); - heap_free(source); + WARN("Failed to subscribe to source events, hr %#x.\n", hr); } - - IMFTopologyNode_Release(node); } - }
- session->state = SESSION_STATE_STARTING; - - IMFCollection_Release(nodes); + if (FAILED(hr = IMFMediaSource_Start(source->source, source->pd, &GUID_NULL, start_position))) + WARN("Failed to start media source %p, hr %#x.\n", source->source, hr); + }
+ session->presentation.flags |= SESSION_FLAG_SOURCES_SUBSCRIBED; + session->state = SESSION_STATE_STARTING_SOURCES; break; case SESSION_STATE_RUNNING: FIXME("Seeking is not implemented.\n"); @@ -1122,6 +1101,8 @@ static ULONG WINAPI mfsession_Release(IMFMediaSession *iface) IMFMediaEventQueue_Release(session->event_queue); if (session->clock) IMFPresentationClock_Release(session->clock); + if (session->system_time_source) + IMFPresentationTimeSource_Release(session->system_time_source); if (session->clock_rate_control) IMFRateControl_Release(session->clock_rate_control); if (session->topo_loader) @@ -1485,10 +1466,8 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface, session_set_topology(session, op->u.set_topology.flags, op->u.set_topology.topology); break; case SESSION_CMD_START: - { session_start(session, &op->u.start.time_format, &op->u.start.start_position); break; - } case SESSION_CMD_PAUSE: break; case SESSION_CMD_CLOSE: @@ -1583,98 +1562,191 @@ static HRESULT session_add_media_stream(struct media_session *session, IMFMediaS return S_OK; }
-static BOOL session_set_source_state(struct media_session *session, IMFMediaSource *source, enum object_state state) +static BOOL session_is_source_nodes_state(struct media_session *session, enum object_state state) { - struct media_source *cur; - BOOL ret = TRUE; + struct media_source *source; + struct source_node *node;
- LIST_FOR_EACH_ENTRY(cur, &session->presentation.sources, struct media_source, entry) + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) { - if (source == cur->source) - cur->state = state; - ret &= cur->state == state; + if (source->state != state) + return FALSE; + } + + LIST_FOR_EACH_ENTRY(node, &session->presentation.source_nodes, struct source_node, entry) + { + if (node->state != state) + return FALSE; }
- return ret; + return TRUE; }
-static HRESULT session_set_sinks_clock(struct media_session *session) +static enum object_state session_get_object_state_for_event(MediaEventType event) { - IMFTopology *topology = session->presentation.current_topology; - IMFTopologyNode *node; - IMFCollection *nodes; - DWORD count, i; + switch (event) + { + case MESourceStarted: + case MEStreamStarted: + case MEStreamSinkStarted: + return OBJ_STATE_STARTED; + case MESourcePaused: + case MEStreamPaused: + case MEStreamSinkPaused: + return OBJ_STATE_PAUSED; + case MESourceStopped: + case MEStreamStopped: + case MEStreamSinkStopped: + return OBJ_STATE_STOPPED; + default: + return OBJ_STATE_INVALID; + } +} + +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; HRESULT hr;
- if (!list_empty(&session->presentation.sinks)) - return S_OK; + if (!(session->presentation.flags & SESSION_FLAG_SINKS_SUBSCRIBED)) + { + LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry) + { + if (SUCCEEDED(IMFMediaSink_QueryInterface(sink->sink, &IID_IMFPresentationTimeSource, + (void **)&time_source))) + break; + }
- if (FAILED(hr = IMFTopology_GetOutputNodeCollection(topology, &nodes))) - return hr; + if (time_source) + { + hr = IMFPresentationClock_SetTimeSource(session->clock, time_source); + IMFPresentationTimeSource_Release(time_source); + } + else + hr = IMFPresentationClock_SetTimeSource(session->clock, session->system_time_source);
- if (FAILED(hr = IMFCollection_GetElementCount(nodes, &count))) - { - IMFCollection_Release(nodes); - return hr; + 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) + { + if (FAILED(hr = IMFStreamSink_BeginGetEvent(node->stream, &session->events_callback, + (IUnknown *)node->stream))) + { + WARN("Failed to subscribe to stream sink events, hr %#x.\n", hr); + } + } + + LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry) + { + if (sink->event_generator && FAILED(hr = IMFMediaEventGenerator_BeginGetEvent(sink->event_generator, + &session->events_callback, (IUnknown *)sink->event_generator))) + { + WARN("Failed to subscribe to sink events, hr %#x.\n", hr); + } + + if (FAILED(hr = IMFMediaSink_SetPresentationClock(sink->sink, session->clock))) + WARN("Failed to set presentation clock for the sink, hr %#x.\n", hr); + } + + session->presentation.flags |= SESSION_FLAG_SINKS_SUBSCRIBED; }
- for (i = 0; i < count; ++i) + if (IsEqualGUID(&session->presentation.time_format, &GUID_NULL)) { - IMFStreamSink *stream_sink = NULL; - struct media_sink *sink; - - if (FAILED(hr = IMFCollection_GetElement(nodes, i, (IUnknown **)&node))) - break; + if (session->presentation.start_position.vt == VT_EMPTY) + start_offset = PRESENTATION_CURRENT_POSITION; + else if (session->presentation.start_position.vt == VT_I8) + start_offset = session->presentation.start_position.hVal.QuadPart; + else + FIXME("Unhandled position type %d.\n", session->presentation.start_position.vt); + } + else + FIXME("Unhandled time format %s.\n", debugstr_guid(&session->presentation.time_format));
- if (!(sink = heap_alloc_zero(sizeof(*sink)))) - hr = E_OUTOFMEMORY; + if (FAILED(hr = IMFPresentationClock_Start(session->clock, start_offset))) + WARN("Failed to start session clock, hr %#x.\n", hr);
- if (SUCCEEDED(hr)) - hr = IMFTopologyNode_GetObject(node, (IUnknown **)&stream_sink); + return hr; +}
- if (SUCCEEDED(hr)) - hr = IMFStreamSink_GetMediaSink(stream_sink, &sink->sink);
- if (SUCCEEDED(hr)) - hr = IMFMediaSink_SetPresentationClock(sink->sink, session->clock); +static void session_set_source_object_state(struct media_session *session, IUnknown *object, + MediaEventType event_type) +{ + struct source_node *src_node; + struct media_source *src; + enum object_state state; + BOOL changed = FALSE;
- if (SUCCEEDED(hr)) - hr = IMFStreamSink_BeginGetEvent(stream_sink, &session->events_callback, (IUnknown *)stream_sink); + if ((state = session_get_object_state_for_event(event_type)) == OBJ_STATE_INVALID) + return;
- if (stream_sink) - IMFStreamSink_Release(stream_sink); + switch (event_type) + { + case MESourceStarted: + case MESourcePaused: + case MESourceStopped:
- if (SUCCEEDED(hr)) - { - list_add_tail(&session->presentation.sinks, &sink->entry); - } - else if (sink) - { - if (sink->sink) - IMFMediaSink_Release(sink->sink); - heap_free(sink); - } + LIST_FOR_EACH_ENTRY(src, &session->presentation.sources, struct media_source, entry) + { + if (object == (IUnknown *)src->source) + { + changed = src->state != state; + src->state = state; + break; + } + } + break; + case MEStreamStarted: + case MEStreamPaused: + case MEStreamStopped:
- IMFTopologyNode_Release(node); + LIST_FOR_EACH_ENTRY(src_node, &session->presentation.source_nodes, struct source_node, entry) + { + if (object == (IUnknown *)src_node->stream) + { + changed = src_node->state != state; + src_node->state = state; + break; + } + } + default: + ; }
- IMFCollection_Release(nodes); + if (!changed) + return;
- return hr; + switch (session->state) + { + case SESSION_STATE_STARTING_SOURCES: + if (!session_is_source_nodes_state(session, OBJ_STATE_STARTED)) + break; + + session_set_topo_status(session, S_OK, MF_TOPOSTATUS_STARTED_SOURCE); + if (SUCCEEDED(session_start_clock(session))) + session->state = SESSION_STATE_STARTING_SINKS; + + break; + default: + ; + } }
static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct media_session *session = impl_from_events_callback_IMFAsyncCallback(iface); IMFMediaEventGenerator *event_source; - enum object_state source_state; IMFMediaEvent *event = NULL; MediaEventType event_type; IMFMediaSource *source; IMFMediaStream *stream; PROPVARIANT value; HRESULT hr; - BOOL ret;
if (FAILED(hr = IMFAsyncResult_GetState(result, (IUnknown **)&event_source))) return hr; @@ -1703,30 +1775,14 @@ static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IM case MESourceStarted: case MESourcePaused: case MESourceStopped: - if (event_type == MESourceStarted) - source_state = OBJ_STATE_STARTED; - else if (event_type == MESourcePaused) - source_state = OBJ_STATE_PAUSED; - else - source_state = OBJ_STATE_STOPPED; + case MEStreamStarted: + case MEStreamPaused: + case MEStreamStopped:
EnterCriticalSection(&session->cs); - - ret = session_set_source_state(session, (IMFMediaSource *)event_source, source_state); - if (ret && event_type == MESourceStarted) - { - session_set_topo_status(session, S_OK, MF_TOPOSTATUS_STARTED_SOURCE); - - if (session->state == SESSION_STATE_STARTING) - { - if (SUCCEEDED(session_set_sinks_clock(session))) - { - session->state = SESSION_STATE_RUNNING; - } - } - } - + session_set_source_object_state(session, (IUnknown *)event_source, event_type); LeaveCriticalSection(&session->cs); + break; case MENewStream: stream = (IMFMediaStream *)value.punkVal; @@ -1961,6 +2017,9 @@ HRESULT WINAPI MFCreateMediaSession(IMFAttributes *config, IMFMediaSession **ses if (FAILED(hr = MFCreatePresentationClock(&object->clock))) goto failed;
+ if (FAILED(hr = MFCreateSystemTimeSource(&object->system_time_source))) + goto failed; + if (FAILED(hr = IMFPresentationClock_QueryInterface(object->clock, &IID_IMFRateControl, (void **)&object->clock_rate_control))) {
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 367f39cd9c..70d042cf3d 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1582,6 +1582,19 @@ static BOOL session_is_source_nodes_state(struct media_session *session, enum ob return TRUE; }
+static BOOL session_is_output_nodes_state(struct media_session *session, enum object_state state) +{ + struct output_node *node; + + LIST_FOR_EACH_ENTRY(node, &session->presentation.output_nodes, struct output_node, entry) + { + if (node->state != state) + return FALSE; + } + + return TRUE; +} + static enum object_state session_get_object_state_for_event(MediaEventType event) { switch (event) @@ -1737,6 +1750,68 @@ static void session_set_source_object_state(struct media_session *session, IUnkn } }
+static void session_set_sink_stream_state(struct media_session *session, IMFStreamSink *stream, + MediaEventType event_type) +{ + struct media_source *source; + struct output_node *node; + enum object_state state; + BOOL changed = FALSE; + IMFMediaEvent *event; + DWORD caps, flags; + + 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) + { + if (stream == node->stream) + { + changed = node->state != state; + node->state = state; + break; + } + } + + if (!changed) + return; + + switch (session->state) + { + case SESSION_STATE_STARTING_SINKS: + if (!session_is_output_nodes_state(session, OBJ_STATE_STARTED)) + break; + + session->state = SESSION_STATE_RUNNING; + + caps = session->caps | MFSESSIONCAP_PAUSE; + + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) + { + if (SUCCEEDED(IMFMediaSource_GetCharacteristics(source->source, &flags))) + { + if (!(flags & MFMEDIASOURCE_CAN_PAUSE)) + { + caps &= ~MFSESSIONCAP_PAUSE; + break; + } + } + } + + session_set_caps(session, caps); + + if (SUCCEEDED(MFCreateMediaEvent(MESessionStarted, &GUID_NULL, S_OK, NULL, &event))) + { + IMFMediaEvent_SetUINT64(event, &MF_EVENT_PRESENTATION_TIME_OFFSET, 0); + IMFMediaEventQueue_QueueEvent(session->event_queue, event); + IMFMediaEvent_Release(event); + } + break; + default: + ; + } +} + static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct media_session *session = impl_from_events_callback_IMFAsyncCallback(iface); @@ -1803,6 +1878,15 @@ static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IM
IMFMediaSource_Release(source);
+ break; + case MEStreamSinkStarted: + case MEStreamSinkPaused: + case MEStreamSinkStopped: + + EnterCriticalSection(&session->cs); + session_set_sink_stream_state(session, (IMFStreamSink *)event_source, event_type); + LeaveCriticalSection(&session->cs); + break; default: ;
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 70d042cf3d..b2ddad6c96 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -76,6 +76,9 @@ enum session_state SESSION_STATE_STARTING_SOURCES, SESSION_STATE_STARTING_SINKS, SESSION_STATE_RUNNING, + SESSION_STATE_PAUSING_SINKS, + SESSION_STATE_PAUSING_SOURCES, + SESSION_STATE_PAUSED, SESSION_STATE_CLOSED, SESSION_STATE_SHUT_DOWN, }; @@ -644,6 +647,34 @@ static void session_start(struct media_session *session, const GUID *time_format LeaveCriticalSection(&session->cs); }
+static void session_pause(struct media_session *session) +{ + HRESULT hr; + + EnterCriticalSection(&session->cs); + + switch (session->state) + { + case SESSION_STATE_RUNNING: + + /* Transition in two steps - pause clock, wait for sinks and pause sources. */ + if (SUCCEEDED(hr = IMFPresentationClock_Pause(session->clock))) + session->state = SESSION_STATE_PAUSING_SINKS; + + break; + default: + hr = MF_E_INVALIDREQUEST; + } + + if (FAILED(hr)) + { + session->state = SESSION_STATE_PAUSED; + IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionPaused, &GUID_NULL, hr, NULL); + } + + LeaveCriticalSection(&session->cs); +} + static struct media_source *session_get_media_source(struct media_session *session, IMFMediaSource *source) { struct media_source *cur; @@ -1469,6 +1500,7 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface, session_start(session, &op->u.start.time_format, &op->u.start.start_position); break; case SESSION_CMD_PAUSE: + session_pause(session); break; case SESSION_CMD_CLOSE: EnterCriticalSection(&session->cs); @@ -1744,6 +1776,17 @@ static void session_set_source_object_state(struct media_session *session, IUnkn if (SUCCEEDED(session_start_clock(session))) session->state = SESSION_STATE_STARTING_SINKS;
+ break; + case SESSION_STATE_PAUSING_SOURCES: + if (!session_is_source_nodes_state(session, OBJ_STATE_PAUSED)) + break; + + session->state = SESSION_STATE_PAUSED; + + session_set_caps(session, session->caps & ~MFSESSIONCAP_PAUSE); + + IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionPaused, &GUID_NULL, S_OK, NULL); + break; default: ; @@ -1759,6 +1802,7 @@ static void session_set_sink_stream_state(struct media_session *session, IMFStre BOOL changed = FALSE; IMFMediaEvent *event; DWORD caps, flags; + HRESULT hr;
if ((state = session_get_object_state_for_event(event_type)) == OBJ_STATE_INVALID) return; @@ -1806,6 +1850,25 @@ static void session_set_sink_stream_state(struct media_session *session, IMFStre IMFMediaEventQueue_QueueEvent(session->event_queue, event); IMFMediaEvent_Release(event); } + break; + case SESSION_STATE_PAUSING_SINKS: + if (!session_is_output_nodes_state(session, OBJ_STATE_PAUSED)) + break; + + session->state = SESSION_STATE_PAUSING_SOURCES; + + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) + { + if (FAILED(hr = IMFMediaSource_Pause(source->source))) + break; + } + + if (FAILED(hr)) + { + session->state = SESSION_STATE_PAUSED; + IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionPaused, &GUID_NULL, hr, NULL); + } + break; default: ;
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index b2ddad6c96..485b336570 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -614,6 +614,7 @@ static void session_start(struct media_session *session, const GUID *time_format switch (session->state) { case SESSION_STATE_STOPPED: + case SESSION_STATE_PAUSED:
session->presentation.time_format = *time_format; session->presentation.start_position.vt = VT_EMPTY;
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 485b336570..76310040ee 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -79,6 +79,8 @@ enum session_state SESSION_STATE_PAUSING_SINKS, SESSION_STATE_PAUSING_SOURCES, SESSION_STATE_PAUSED, + SESSION_STATE_STOPPING_SINKS, + SESSION_STATE_STOPPING_SOURCES, SESSION_STATE_CLOSED, SESSION_STATE_SHUT_DOWN, }; @@ -676,6 +678,36 @@ static void session_pause(struct media_session *session) LeaveCriticalSection(&session->cs); }
+static void session_stop(struct media_session *session) +{ + HRESULT hr = S_OK; + + EnterCriticalSection(&session->cs); + + switch (session->state) + { + case SESSION_STATE_RUNNING: + case SESSION_STATE_PAUSED: + + /* Transition in two steps - pause clock, wait for sinks and pause sources. */ + if (SUCCEEDED(hr = IMFPresentationClock_Stop(session->clock))) + session->state = SESSION_STATE_STOPPING_SINKS; + + break; + default: + ; + } + + if (FAILED(hr)) + { + session->state = SESSION_STATE_STOPPED; + IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionStopped, &GUID_NULL, hr, NULL); + } + + LeaveCriticalSection(&session->cs); +} + + static struct media_source *session_get_media_source(struct media_session *session, IMFMediaSource *source) { struct media_source *cur; @@ -1503,6 +1535,9 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface, case SESSION_CMD_PAUSE: session_pause(session); break; + case SESSION_CMD_STOP: + session_stop(session); + break; case SESSION_CMD_CLOSE: EnterCriticalSection(&session->cs); if (session->state != SESSION_STATE_CLOSED) @@ -1724,6 +1759,7 @@ static void session_set_source_object_state(struct media_session *session, IUnkn MediaEventType event_type) { struct source_node *src_node; + struct output_node *out_node; struct media_source *src; enum object_state state; BOOL changed = FALSE; @@ -1788,6 +1824,22 @@ static void session_set_source_object_state(struct media_session *session, IUnkn
IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionPaused, &GUID_NULL, S_OK, NULL);
+ break; + case SESSION_STATE_STOPPING_SOURCES: + if (!session_is_source_nodes_state(session, OBJ_STATE_STOPPED)) + break; + + session->state = SESSION_STATE_STOPPED; + + LIST_FOR_EACH_ENTRY(out_node, &session->presentation.output_nodes, struct output_node, entry) + { + IMFStreamSink_Flush(out_node->stream); + } + + session_set_caps(session, session->caps & ~MFSESSIONCAP_PAUSE); + + IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionStopped, &GUID_NULL, S_OK, NULL); + break; default: ; @@ -1870,6 +1922,25 @@ static void session_set_sink_stream_state(struct media_session *session, IMFStre IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionPaused, &GUID_NULL, hr, NULL); }
+ break; + case SESSION_STATE_STOPPING_SINKS: + if (!session_is_output_nodes_state(session, OBJ_STATE_STOPPED)) + break; + + session->state = SESSION_STATE_STOPPING_SOURCES; + + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) + { + if (FAILED(hr = IMFMediaSource_Stop(source->source))) + break; + } + + if (FAILED(hr)) + { + session->state = SESSION_STATE_STOPPED; + IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionStopped, &GUID_NULL, hr, NULL); + } + break; default: ;