mf: Add initial async implementation of SetTopology().

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Nikolay Sivov 2019-09-27 17:00:45 +03:00 committed by Alexandre Julliard
parent e578ee3b0a
commit d2bd44992e
2 changed files with 222 additions and 31 deletions

View File

@ -36,6 +36,7 @@ enum session_command
{
SESSION_CMD_CLEAR_TOPOLOGIES,
SESSION_CMD_CLOSE,
SESSION_CMD_SET_TOPOLOGY,
};
struct session_op
@ -43,6 +44,14 @@ struct session_op
IUnknown IUnknown_iface;
LONG refcount;
enum session_command command;
union
{
struct
{
DWORD flags;
IMFTopology *topology;
} set_topology;
} u;
};
struct queued_topology
@ -71,6 +80,7 @@ struct media_session
IMFRateControl *clock_rate_control;
IMFTopoLoader *topo_loader;
IMFQualityManager *quality_manager;
IMFTopology *current_topology;
struct list topologies;
enum session_state state;
CRITICAL_SECTION cs;
@ -238,6 +248,11 @@ static ULONG WINAPI session_op_Release(IUnknown *iface)
if (!refcount)
{
if (op->command == SESSION_CMD_SET_TOPOLOGY)
{
if (op->u.set_topology.topology)
IMFTopology_Release(op->u.set_topology.topology);
}
heap_free(op);
}
@ -251,7 +266,7 @@ static IUnknownVtbl session_op_vtbl =
session_op_Release,
};
static HRESULT create_session_op(enum session_command command, IUnknown **ret)
static HRESULT create_session_op(enum session_command command, struct session_op **ret)
{
struct session_op *op;
@ -262,11 +277,25 @@ static HRESULT create_session_op(enum session_command command, IUnknown **ret)
op->refcount = 1;
op->command = command;
*ret = &op->IUnknown_iface;
*ret = op;
return S_OK;
}
static HRESULT session_submit_command(struct media_session *session, struct session_op *op)
{
HRESULT hr;
EnterCriticalSection(&session->cs);
if (session->state == SESSION_STATE_SHUT_DOWN)
hr = MF_E_SHUTDOWN;
else
hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &session->commands_callback, &op->IUnknown_iface);
LeaveCriticalSection(&session->cs);
return hr;
}
static void session_clear_topologies(struct media_session *session)
{
struct queued_topology *ptr, *next;
@ -279,6 +308,68 @@ static void session_clear_topologies(struct media_session *session)
}
}
static HRESULT session_bind_output_nodes(IMFTopology *topology)
{
MF_TOPOLOGY_TYPE node_type;
IMFStreamSink *stream_sink;
IMFMediaSink *media_sink;
WORD node_count = 0, i;
IMFTopologyNode *node;
IMFActivate *activate;
UINT32 stream_id;
IUnknown *object;
HRESULT hr;
hr = IMFTopology_GetNodeCount(topology, &node_count);
for (i = 0; i < node_count; ++i)
{
if (FAILED(hr = IMFTopology_GetNode(topology, i, &node)))
break;
if (FAILED(hr = IMFTopologyNode_GetNodeType(node, &node_type)) || node_type != MF_TOPOLOGY_OUTPUT_NODE)
{
IMFTopologyNode_Release(node);
break;
}
if (SUCCEEDED(hr = IMFTopologyNode_GetObject(node, &object)))
{
stream_sink = NULL;
if (FAILED(IUnknown_QueryInterface(object, &IID_IMFStreamSink, (void **)&stream_sink)))
{
if (SUCCEEDED(hr = IUnknown_QueryInterface(object, &IID_IMFActivate, (void **)&activate)))
{
if (SUCCEEDED(hr = IMFActivate_ActivateObject(activate, &IID_IMFMediaSink, (void **)&media_sink)))
{
if (FAILED(IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_STREAMID, &stream_id)))
stream_id = 0;
stream_sink = NULL;
if (FAILED(IMFMediaSink_GetStreamSinkById(media_sink, stream_id, &stream_sink)))
hr = IMFMediaSink_AddStreamSink(media_sink, stream_id, NULL, &stream_sink);
if (stream_sink)
hr = IMFTopologyNode_SetObject(node, (IUnknown *)stream_sink);
IMFMediaSink_Release(media_sink);
}
IMFActivate_Release(activate);
}
}
if (stream_sink)
IMFStreamSink_Release(stream_sink);
IUnknown_Release(object);
}
IMFTopologyNode_Release(node);
}
return hr;
}
static HRESULT WINAPI mfsession_QueryInterface(IMFMediaSession *iface, REFIID riid, void **out)
{
struct media_session *session = impl_from_IMFMediaSession(iface);
@ -325,6 +416,8 @@ static ULONG WINAPI mfsession_Release(IMFMediaSession *iface)
if (!refcount)
{
session_clear_topologies(session);
if (session->current_topology)
IMFTopology_Release(session->current_topology);
if (session->event_queue)
IMFMediaEventQueue_Release(session->event_queue);
if (session->clock)
@ -382,33 +475,28 @@ static HRESULT WINAPI mfsession_QueueEvent(IMFMediaSession *iface, MediaEventTyp
static HRESULT WINAPI mfsession_SetTopology(IMFMediaSession *iface, DWORD flags, IMFTopology *topology)
{
struct media_session *session = impl_from_IMFMediaSession(iface);
struct queued_topology *queued_topology;
FIXME("%p, %#x, %p.\n", iface, flags, topology);
if (!(queued_topology = heap_alloc(sizeof(*queued_topology))))
return E_OUTOFMEMORY;
queued_topology->topology = topology;
IMFTopology_AddRef(queued_topology->topology);
EnterCriticalSection(&session->cs);
list_add_tail(&session->topologies, &queued_topology->entry);
LeaveCriticalSection(&session->cs);
return S_OK;
}
static HRESULT session_submit_command(struct media_session *session, IUnknown *op)
{
struct session_op *op;
WORD node_count = 0;
HRESULT hr;
EnterCriticalSection(&session->cs);
if (session->state == SESSION_STATE_SHUT_DOWN)
hr = MF_E_SHUTDOWN;
else
hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &session->commands_callback, op);
LeaveCriticalSection(&session->cs);
TRACE("%p, %#x, %p.\n", iface, flags, topology);
if (topology)
{
if (FAILED(IMFTopology_GetNodeCount(topology, &node_count)) || node_count == 0)
return E_INVALIDARG;
}
if (FAILED(hr = create_session_op(SESSION_CMD_SET_TOPOLOGY, &op)))
return hr;
op->u.set_topology.flags = flags;
op->u.set_topology.topology = topology;
if (op->u.set_topology.topology)
IMFTopology_AddRef(op->u.set_topology.topology);
hr = session_submit_command(session, op);
IUnknown_Release(&op->IUnknown_iface);
return hr;
}
@ -416,7 +504,7 @@ static HRESULT session_submit_command(struct media_session *session, IUnknown *o
static HRESULT WINAPI mfsession_ClearTopologies(IMFMediaSession *iface)
{
struct media_session *session = impl_from_IMFMediaSession(iface);
IUnknown *op;
struct session_op *op;
HRESULT hr;
TRACE("%p.\n", iface);
@ -425,7 +513,7 @@ static HRESULT WINAPI mfsession_ClearTopologies(IMFMediaSession *iface)
return hr;
hr = session_submit_command(session, op);
IUnknown_Release(op);
IUnknown_Release(&op->IUnknown_iface);
return hr;
}
@ -454,7 +542,7 @@ static HRESULT WINAPI mfsession_Stop(IMFMediaSession *iface)
static HRESULT WINAPI mfsession_Close(IMFMediaSession *iface)
{
struct media_session *session = impl_from_IMFMediaSession(iface);
IUnknown *op;
struct session_op *op;
HRESULT hr;
TRACE("(%p)\n", iface);
@ -463,7 +551,7 @@ static HRESULT WINAPI mfsession_Close(IMFMediaSession *iface)
return hr;
hr = session_submit_command(session, op);
IUnknown_Release(op);
IUnknown_Release(&op->IUnknown_iface);
return hr;
}
@ -627,6 +715,7 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface,
{
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)
{
@ -638,6 +727,98 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface,
IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionTopologiesCleared, &GUID_NULL,
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;
DWORD flags = op->u.set_topology.flags;
PROPVARIANT param;
if (flags & MFSESSION_SETTOPOLOGY_CLEAR_CURRENT)
{
EnterCriticalSection(&session->cs);
if ((topology && topology == session->current_topology) || !topology)
{
/* FIXME: stop current topology, queue next one. */
if (session->current_topology)
{
IMFTopology_Release(session->current_topology);
session->current_topology = NULL;
}
}
else
status = S_FALSE;
topo_status = MF_TOPOSTATUS_READY;
LeaveCriticalSection(&session->cs);
}
else if (topology)
{
/* Resolve unless claimed to be full. */
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;
}
if (SUCCEEDED(status))
{
if (!(queued_topology = heap_alloc_zero(sizeof(*queued_topology))))
status = E_OUTOFMEMORY;
else
{
EnterCriticalSection(&session->cs);
if (flags & MFSESSION_SETTOPOLOGY_IMMEDIATE)
{
session_clear_topologies(session);
if (session->current_topology)
{
IMFTopology_Release(session->current_topology);
session->current_topology = NULL;
}
}
queued_topology->topology = topology;
IMFTopology_AddRef(queued_topology->topology);
list_add_tail(&session->topologies, &queued_topology->entry);
LeaveCriticalSection(&session->cs);
}
}
}
if (topology)
{
param.vt = VT_UNKNOWN;
param.punkVal = (IUnknown *)topology;
}
else
param.vt = VT_EMPTY;
IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionTopologySet, &GUID_NULL,
status, &param);
if (topo_status != MF_TOPOSTATUS_INVALID)
{
IMFMediaEvent *event;
if (SUCCEEDED(MFCreateMediaEvent(MESessionTopologyStatus, &GUID_NULL, status, &param, &event)))
{
IMFMediaEvent_SetUINT32(event, &MF_EVENT_TOPOLOGY_STATUS, topo_status);
IMFMediaEventQueue_QueueEvent(session->event_queue, event);
IMFMediaEvent_Release(event);
}
}
break;
}
case SESSION_CMD_CLOSE:
EnterCriticalSection(&session->cs);
if (session->state != SESSION_STATE_CLOSED)

View File

@ -370,6 +370,16 @@ enum _MFT_ENUM_FLAG
MFT_ENUM_FLAG_UNTRUSTED_STOREMFT = 0x00000400,
};
typedef enum
{
MF_TOPOSTATUS_INVALID = 0,
MF_TOPOSTATUS_READY = 100,
MF_TOPOSTATUS_STARTED_SOURCE = 200,
MF_TOPOSTATUS_DYNAMIC_CHANGED = 210,
MF_TOPOSTATUS_SINK_SWITCHED = 300,
MF_TOPOSTATUS_ENDED = 400,
} MF_TOPOSTATUS;
HRESULT WINAPI MFAddPeriodicCallback(MFPERIODICCALLBACK callback, IUnknown *context, DWORD *key);
HRESULT WINAPI MFAllocateWorkQueue(DWORD *queue);
HRESULT WINAPI MFAllocateWorkQueueEx(MFASYNC_WORKQUEUE_TYPE queue_type, DWORD *queue);