mf: Implement initial support for starting media sources within a session.

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Nikolay Sivov 2019-10-01 16:35:33 +03:00 committed by Alexandre Julliard
parent 0d41926cc0
commit 350a3ec6a2
1 changed files with 188 additions and 8 deletions

View File

@ -37,6 +37,7 @@ enum session_command
SESSION_CMD_CLEAR_TOPOLOGIES,
SESSION_CMD_CLOSE,
SESSION_CMD_SET_TOPOLOGY,
SESSION_CMD_START,
};
struct session_op
@ -51,6 +52,11 @@ struct session_op
DWORD flags;
IMFTopology *topology;
} set_topology;
struct
{
GUID time_format;
PROPVARIANT start_position;
} start;
} u;
};
@ -63,6 +69,8 @@ struct queued_topology
enum session_state
{
SESSION_STATE_STOPPED = 0,
SESSION_STATE_STARTING,
SESSION_STATE_RUNNING,
SESSION_STATE_CLOSED,
SESSION_STATE_SHUT_DOWN,
};
@ -74,6 +82,7 @@ struct media_session
IMFRateSupport IMFRateSupport_iface;
IMFRateControl IMFRateControl_iface;
IMFAsyncCallback commands_callback;
IMFAsyncCallback events_callback;
LONG refcount;
IMFMediaEventQueue *event_queue;
IMFPresentationClock *clock;
@ -161,6 +170,11 @@ static struct media_session *impl_from_commands_callback_IMFAsyncCallback(IMFAsy
return CONTAINING_RECORD(iface, struct media_session, commands_callback);
}
static struct media_session *impl_from_events_callback_IMFAsyncCallback(IMFAsyncCallback *iface)
{
return CONTAINING_RECORD(iface, struct media_session, events_callback);
}
static struct media_session *impl_from_IMFGetService(IMFGetService *iface)
{
return CONTAINING_RECORD(iface, struct media_session, IMFGetService_iface);
@ -248,10 +262,17 @@ static ULONG WINAPI session_op_Release(IUnknown *iface)
if (!refcount)
{
if (op->command == SESSION_CMD_SET_TOPOLOGY)
switch (op->command)
{
case SESSION_CMD_SET_TOPOLOGY:
if (op->u.set_topology.topology)
IMFTopology_Release(op->u.set_topology.topology);
break;
case SESSION_CMD_START:
PropVariantClear(&op->u.start.start_position);
break;
default:
;
}
heap_free(op);
}
@ -270,7 +291,7 @@ static HRESULT create_session_op(enum session_command command, struct session_op
{
struct session_op *op;
if (!(op = heap_alloc(sizeof(*op))))
if (!(op = heap_alloc_zero(sizeof(*op))))
return E_OUTOFMEMORY;
op->IUnknown_iface.lpVtbl = &session_op_vtbl;
@ -330,7 +351,7 @@ static HRESULT session_bind_output_nodes(IMFTopology *topology)
if (FAILED(hr = IMFTopologyNode_GetNodeType(node, &node_type)) || node_type != MF_TOPOLOGY_OUTPUT_NODE)
{
IMFTopologyNode_Release(node);
break;
continue;
}
if (SUCCEEDED(hr = IMFTopologyNode_GetObject(node, &object)))
@ -370,6 +391,92 @@ 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->current_topology)
{
struct list *head = list_head(&session->topologies);
if (!head)
return NULL;
queued = LIST_ENTRY(head, struct queued_topology, entry);
session->current_topology = queued->topology;
list_remove(&queued->entry);
heap_free(queued);
}
return session->current_topology;
}
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;
EnterCriticalSection(&session->cs);
switch (session->state)
{
case SESSION_STATE_STOPPED:
topology = session_get_next_topology(session);
if (!topology || FAILED(IMFTopology_GetSourceNodeCollection(topology, &nodes)))
break;
if (FAILED(IMFCollection_GetElementCount(nodes, &count)))
break;
for (i = 0; i < count; ++i)
{
if (SUCCEEDED(IMFCollection_GetElement(nodes, i, (IUnknown **)&node)))
{
IMFPresentationDescriptor *pd = NULL;
IMFMediaSource *source = NULL;
hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_SOURCE, &IID_IMFMediaSource,
(void **)&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, &session->events_callback, NULL);
if (SUCCEEDED(hr))
hr = IMFMediaSource_Start(source, pd, time_format, start_position);
if (source)
IMFMediaSource_Release(source);
if (pd)
IMFPresentationDescriptor_Release(pd);
IMFTopologyNode_Release(node);
}
}
session->state = SESSION_STATE_STARTING;
IMFCollection_Release(nodes);
break;
case SESSION_STATE_RUNNING:
FIXME("Seeking is not implemented.\n");
break;
default:
;
}
LeaveCriticalSection(&session->cs);
}
static HRESULT WINAPI mfsession_QueryInterface(IMFMediaSession *iface, REFIID riid, void **out)
{
struct media_session *session = impl_from_IMFMediaSession(iface);
@ -518,11 +625,29 @@ static HRESULT WINAPI mfsession_ClearTopologies(IMFMediaSession *iface)
return hr;
}
static HRESULT WINAPI mfsession_Start(IMFMediaSession *iface, const GUID *format, const PROPVARIANT *start)
static HRESULT WINAPI mfsession_Start(IMFMediaSession *iface, const GUID *format, const PROPVARIANT *start_position)
{
FIXME("(%p)->(%s, %p)\n", iface, debugstr_guid(format), start);
struct media_session *session = impl_from_IMFMediaSession(iface);
struct session_op *op;
HRESULT hr;
return E_NOTIMPL;
TRACE("%p, %s, %p.\n", iface, debugstr_guid(format), start_position);
if (!start_position)
return E_POINTER;
if (FAILED(hr = create_session_op(SESSION_CMD_START, &op)))
return hr;
op->u.start.time_format = format ? *format : GUID_NULL;
hr = PropVariantCopy(&op->u.start.start_position, start_position);
if (SUCCEEDED(hr))
hr = session_submit_command(session, op);
IUnknown_Release(&op->IUnknown_iface);
return hr;
}
static HRESULT WINAPI mfsession_Pause(IMFMediaSession *iface)
@ -765,7 +890,10 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface,
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))
@ -819,6 +947,11 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface,
break;
}
case SESSION_CMD_START:
{
session_start(session, &op->u.start.time_format, &op->u.start.start_position);
break;
}
case SESSION_CMD_CLOSE:
EnterCriticalSection(&session->cs);
if (session->state != SESSION_STATE_CLOSED)
@ -845,6 +978,52 @@ static const IMFAsyncCallbackVtbl session_commands_callback_vtbl =
session_commands_callback_Invoke,
};
static HRESULT WINAPI session_events_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
{
if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
IsEqualIID(riid, &IID_IUnknown))
{
*obj = iface;
IMFAsyncCallback_AddRef(iface);
return S_OK;
}
WARN("Unsupported %s.\n", debugstr_guid(riid));
*obj = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI session_events_callback_AddRef(IMFAsyncCallback *iface)
{
struct media_session *session = impl_from_events_callback_IMFAsyncCallback(iface);
return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
}
static ULONG WINAPI session_events_callback_Release(IMFAsyncCallback *iface)
{
struct media_session *session = impl_from_events_callback_IMFAsyncCallback(iface);
return IMFMediaSession_Release(&session->IMFMediaSession_iface);
}
static HRESULT WINAPI session_events_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
{
return E_NOTIMPL;
}
static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
{
return S_OK;
}
static const IMFAsyncCallbackVtbl session_events_callback_vtbl =
{
session_events_callback_QueryInterface,
session_events_callback_AddRef,
session_events_callback_Release,
session_events_callback_GetParameters,
session_events_callback_Invoke,
};
static HRESULT WINAPI session_rate_support_QueryInterface(IMFRateSupport *iface, REFIID riid, void **obj)
{
struct media_session *session = impl_session_from_IMFRateSupport(iface);
@ -960,6 +1139,7 @@ HRESULT WINAPI MFCreateMediaSession(IMFAttributes *config, IMFMediaSession **ses
object->IMFRateSupport_iface.lpVtbl = &session_rate_support_vtbl;
object->IMFRateControl_iface.lpVtbl = &session_rate_control_vtbl;
object->commands_callback.lpVtbl = &session_commands_callback_vtbl;
object->events_callback.lpVtbl = &session_events_callback_vtbl;
object->refcount = 1;
list_init(&object->topologies);
InitializeCriticalSection(&object->cs);