mf/session: Implement support for sinks that provide sample allocators.

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Nikolay Sivov 2020-11-12 13:36:38 +03:00 committed by Alexandre Julliard
parent 9008b2c9b7
commit e272e1a1c3
1 changed files with 146 additions and 27 deletions

View File

@ -45,6 +45,7 @@ enum session_command
/* Internally used commands. */
SESSION_CMD_END,
SESSION_CMD_QM_NOTIFY_TOPOLOGY,
SESSION_CMD_SA_READY,
};
struct session_op
@ -68,6 +69,10 @@ struct session_op
{
IMFTopology *topology;
} notify_topology;
struct
{
TOPOID node_id;
} sa_ready;
} u;
struct list entry;
};
@ -148,6 +153,7 @@ enum topo_node_flags
struct topo_node
{
struct list entry;
struct media_session *session;
MF_TOPOLOGY_TYPE type;
TOPOID node_id;
IMFTopologyNode *node;
@ -171,7 +177,9 @@ struct topo_node
struct
{
unsigned int requests;
IMFVideoSampleAllocatorNotify notify_cb;
IMFVideoSampleAllocator *allocator;
IMFVideoSampleAllocatorCallback *allocator_cb;
} sink;
struct
{
@ -400,6 +408,11 @@ static struct quality_manager *impl_from_IMFQualityManager(IMFQualityManager *if
return CONTAINING_RECORD(iface, struct quality_manager, IMFQualityManager_iface);
}
static struct topo_node *impl_node_from_IMFVideoSampleAllocatorNotify(IMFVideoSampleAllocatorNotify *iface)
{
return CONTAINING_RECORD(iface, struct topo_node, u.sink.notify_cb);
}
/* IMFLocalMFTRegistration */
static HRESULT WINAPI local_mft_registration_QueryInterface(IMFLocalMFTRegistration *iface, REFIID riid, void **obj)
{
@ -746,6 +759,11 @@ static void release_topo_node(struct topo_node *node)
case MF_TOPOLOGY_OUTPUT_NODE:
if (node->u.sink.allocator)
IMFVideoSampleAllocator_Release(node->u.sink.allocator);
if (node->u.sink.allocator_cb)
{
IMFVideoSampleAllocatorCallback_SetCallback(node->u.sink.allocator_cb, NULL);
IMFVideoSampleAllocatorCallback_Release(node->u.sink.allocator_cb);
}
break;
default:
;
@ -804,6 +822,19 @@ static void session_clear_presentation(struct media_session *session)
}
}
static struct topo_node *session_get_node_by_id(const 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 void session_start(struct media_session *session, const GUID *time_format, const PROPVARIANT *start_position)
{
struct media_source *source;
@ -1204,6 +1235,56 @@ static HRESULT session_get_stream_sink_type(IMFStreamSink *sink, IMFMediaType **
return hr;
}
static HRESULT WINAPI node_sample_allocator_cb_QueryInterface(IMFVideoSampleAllocatorNotify *iface,
REFIID riid, void **obj)
{
if (IsEqualIID(riid, &IID_IMFVideoSampleAllocatorNotify) ||
IsEqualIID(riid, &IID_IUnknown))
{
*obj = iface;
IMFVideoSampleAllocatorNotify_AddRef(iface);
return S_OK;
}
*obj = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI node_sample_allocator_cb_AddRef(IMFVideoSampleAllocatorNotify *iface)
{
return 2;
}
static ULONG WINAPI node_sample_allocator_cb_Release(IMFVideoSampleAllocatorNotify *iface)
{
return 1;
}
static HRESULT session_request_sample_from_node(struct media_session *session, IMFTopologyNode *node, DWORD output);
static HRESULT WINAPI node_sample_allocator_cb_NotifyRelease(IMFVideoSampleAllocatorNotify *iface)
{
struct topo_node *topo_node = impl_node_from_IMFVideoSampleAllocatorNotify(iface);
struct session_op *op;
if (SUCCEEDED(create_session_op(SESSION_CMD_SA_READY, &op)))
{
op->u.sa_ready.node_id = topo_node->node_id;
MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &topo_node->session->commands_callback, &op->IUnknown_iface);
IUnknown_Release(&op->IUnknown_iface);
}
return S_OK;
}
static const IMFVideoSampleAllocatorNotifyVtbl node_sample_allocator_cb_vtbl =
{
node_sample_allocator_cb_QueryInterface,
node_sample_allocator_cb_AddRef,
node_sample_allocator_cb_Release,
node_sample_allocator_cb_NotifyRelease,
};
static HRESULT session_append_node(struct media_session *session, IMFTopologyNode *node)
{
struct topo_node *topo_node;
@ -1220,10 +1301,13 @@ static HRESULT session_append_node(struct media_session *session, IMFTopologyNod
IMFTopologyNode_GetTopoNodeID(node, &topo_node->node_id);
topo_node->node = node;
IMFTopologyNode_AddRef(topo_node->node);
topo_node->session = session;
switch (topo_node->type)
{
case MF_TOPOLOGY_OUTPUT_NODE:
topo_node->u.sink.notify_cb.lpVtbl = &node_sample_allocator_cb_vtbl;
if (FAILED(hr = IMFTopologyNode_GetObject(node, &object)))
{
WARN("Node %s does not have associated object.\n", wine_dbgstr_longlong(topo_node->node_id));
@ -1245,6 +1329,10 @@ static HRESULT session_append_node(struct media_session *session, IMFTopologyNod
&IID_IMFVideoSampleAllocator, (void **)&topo_node->u.sink.allocator)))
{
IMFVideoSampleAllocator_InitializeSampleAllocator(topo_node->u.sink.allocator, 2, media_type);
IMFVideoSampleAllocator_QueryInterface(topo_node->u.sink.allocator,
&IID_IMFVideoSampleAllocatorCallback, (void **)&topo_node->u.sink.allocator_cb);
IMFVideoSampleAllocatorCallback_SetCallback(topo_node->u.sink.allocator_cb,
&topo_node->u.sink.notify_cb);
}
IMFMediaType_Release(media_type);
}
@ -1955,6 +2043,9 @@ 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);
struct topo_node *topo_node;
IMFTopologyNode *upstream_node;
unsigned int upstream_output;
EnterCriticalSection(&session->cs);
@ -1985,6 +2076,18 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface,
case SESSION_CMD_QM_NOTIFY_TOPOLOGY:
IMFQualityManager_NotifyTopology(session->quality_manager, op->u.notify_topology.topology);
break;
case SESSION_CMD_SA_READY:
topo_node = session_get_node_by_id(session, op->u.sa_ready.node_id);
if (topo_node->u.sink.requests)
{
if (SUCCEEDED(IMFTopologyNode_GetInput(topo_node->node, 0, &upstream_node, &upstream_output)))
{
session_request_sample_from_node(session, upstream_node, upstream_output);
IMFTopologyNode_Release(upstream_node);
}
}
break;
default:
;
}
@ -2463,19 +2566,6 @@ 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 DWORD transform_node_get_stream_id(struct topo_node *node, BOOL output, DWORD index)
{
unsigned int *map = output ? node->u.transform.output_map : node->u.transform.input_map;
@ -2496,6 +2586,47 @@ static struct sample *transform_create_sample(IMFSample *sample)
return sample_entry;
}
static HRESULT transform_get_external_output_sample(const struct media_session *session, struct topo_node *transform,
unsigned int output_index, const MFT_OUTPUT_STREAM_INFO *stream_info, IMFSample **sample)
{
IMFTopologyNode *downstream_node;
unsigned int downstream_input;
IMFMediaBuffer *buffer = NULL;
struct topo_node *topo_node;
TOPOID node_id;
HRESULT hr;
if (FAILED(IMFTopologyNode_GetOutput(transform->node, output_index, &downstream_node, &downstream_input)))
{
WARN("Failed to get connected node for output %u.\n", output_index);
return MF_E_UNEXPECTED;
}
IMFTopologyNode_GetTopoNodeID(downstream_node, &node_id);
IMFTopologyNode_Release(downstream_node);
topo_node = session_get_node_by_id(session, node_id);
if (topo_node->type == MF_TOPOLOGY_OUTPUT_NODE && topo_node->u.sink.allocator)
{
hr = IMFVideoSampleAllocator_AllocateSample(topo_node->u.sink.allocator, sample);
}
else
{
hr = MFCreateAlignedMemoryBuffer(stream_info->cbSize, stream_info->cbAlignment, &buffer);
if (SUCCEEDED(hr))
hr = MFCreateSample(sample);
if (SUCCEEDED(hr))
hr = IMFSample_AddBuffer(*sample, buffer);
if (buffer)
IMFMediaBuffer_Release(buffer);
}
return hr;
}
static HRESULT transform_node_pull_samples(const struct media_session *session, struct topo_node *node)
{
MFT_OUTPUT_STREAM_INFO stream_info;
@ -2503,7 +2634,7 @@ static HRESULT transform_node_pull_samples(const struct media_session *session,
struct sample *queued_sample;
DWORD status = 0;
unsigned int i;
HRESULT hr;
HRESULT hr = E_UNEXPECTED;
if (!(buffers = heap_calloc(node->u.transform.output_count, sizeof(*buffers))))
return E_OUTOFMEMORY;
@ -2521,19 +2652,7 @@ static HRESULT transform_node_pull_samples(const struct media_session *session,
if (!(stream_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES))
{
IMFMediaBuffer *buffer = NULL;
hr = MFCreateAlignedMemoryBuffer(stream_info.cbSize, stream_info.cbAlignment, &buffer);
if (SUCCEEDED(hr))
hr = MFCreateSample(&buffers[i].pSample);
if (SUCCEEDED(hr))
hr = IMFSample_AddBuffer(buffers[i].pSample, buffer);
if (buffer)
IMFMediaBuffer_Release(buffer);
if (FAILED(hr))
if (FAILED(hr = transform_get_external_output_sample(session, node, i, &stream_info, &buffers[i].pSample)))
break;
}
}