mf: Implement sink prerolling.

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
feature/deterministic
Nikolay Sivov 2020-04-13 19:05:55 +03:00 committed by Alexandre Julliard
parent 930ff8786f
commit 8a299c4bc3
1 changed files with 93 additions and 25 deletions

View File

@ -76,6 +76,7 @@ enum session_state
{ {
SESSION_STATE_STOPPED = 0, SESSION_STATE_STOPPED = 0,
SESSION_STATE_STARTING_SOURCES, SESSION_STATE_STARTING_SOURCES,
SESSION_STATE_PREROLLING_SINKS,
SESSION_STATE_STARTING_SINKS, SESSION_STATE_STARTING_SINKS,
SESSION_STATE_STARTED, SESSION_STATE_STARTED,
SESSION_STATE_PAUSING_SINKS, SESSION_STATE_PAUSING_SINKS,
@ -93,6 +94,7 @@ enum object_state
OBJ_STATE_STOPPED = 0, OBJ_STATE_STOPPED = 0,
OBJ_STATE_STARTED, OBJ_STATE_STARTED,
OBJ_STATE_PAUSED, OBJ_STATE_PAUSED,
OBJ_STATE_PREROLLED,
OBJ_STATE_INVALID, OBJ_STATE_INVALID,
}; };
@ -108,6 +110,7 @@ struct media_sink
{ {
struct list entry; struct list entry;
IMFMediaSink *sink; IMFMediaSink *sink;
IMFMediaSinkPreroll *preroll;
IMFMediaEventGenerator *event_generator; IMFMediaEventGenerator *event_generator;
BOOL finalized; BOOL finalized;
}; };
@ -168,6 +171,7 @@ enum presentation_flags
SESSION_FLAG_SOURCES_SUBSCRIBED = 0x1, SESSION_FLAG_SOURCES_SUBSCRIBED = 0x1,
SESSION_FLAG_PRESENTATION_CLOCK_SET = 0x2, SESSION_FLAG_PRESENTATION_CLOCK_SET = 0x2,
SESSION_FLAG_FINALIZE_SINKS = 0x4, SESSION_FLAG_FINALIZE_SINKS = 0x4,
SESSION_FLAG_NEEDS_PREROLL = 0x8,
}; };
struct media_session struct media_session
@ -722,6 +726,8 @@ static void session_clear_presentation(struct media_session *session)
if (sink->sink) if (sink->sink)
IMFMediaSink_Release(sink->sink); IMFMediaSink_Release(sink->sink);
if (sink->preroll)
IMFMediaSinkPreroll_Release(sink->preroll);
if (sink->event_generator) if (sink->event_generator)
IMFMediaEventGenerator_Release(sink->event_generator); IMFMediaEventGenerator_Release(sink->event_generator);
heap_free(sink); heap_free(sink);
@ -982,6 +988,7 @@ static DWORD session_get_object_rate_caps(IUnknown *object)
static HRESULT session_add_media_sink(struct media_session *session, IMFTopologyNode *node, IMFMediaSink *sink) static HRESULT session_add_media_sink(struct media_session *session, IMFTopologyNode *node, IMFMediaSink *sink)
{ {
struct media_sink *media_sink; struct media_sink *media_sink;
DWORD flags;
LIST_FOR_EACH_ENTRY(media_sink, &session->presentation.sinks, struct media_sink, entry) LIST_FOR_EACH_ENTRY(media_sink, &session->presentation.sinks, struct media_sink, entry)
{ {
@ -997,6 +1004,12 @@ static HRESULT session_add_media_sink(struct media_session *session, IMFTopology
IMFMediaSink_QueryInterface(media_sink->sink, &IID_IMFMediaEventGenerator, (void **)&media_sink->event_generator); IMFMediaSink_QueryInterface(media_sink->sink, &IID_IMFMediaEventGenerator, (void **)&media_sink->event_generator);
if (SUCCEEDED(IMFMediaSink_GetCharacteristics(sink, &flags)) && flags & MEDIASINK_CAN_PREROLL)
{
if (SUCCEEDED(IMFMediaSink_QueryInterface(media_sink->sink, &IID_IMFMediaSinkPreroll, (void **)&media_sink->preroll)))
session->presentation.flags |= SESSION_FLAG_NEEDS_PREROLL;
}
list_add_tail(&session->presentation.sinks, &media_sink->entry); list_add_tail(&session->presentation.sinks, &media_sink->entry);
return S_OK; return S_OK;
@ -1915,6 +1928,8 @@ static enum object_state session_get_object_state_for_event(MediaEventType event
case MEStreamStopped: case MEStreamStopped:
case MEStreamSinkStopped: case MEStreamSinkStopped:
return OBJ_STATE_STOPPED; return OBJ_STATE_STOPPED;
case MEStreamSinkPrerolled:
return OBJ_STATE_PREROLLED;
default: default:
return OBJ_STATE_INVALID; return OBJ_STATE_INVALID;
} }
@ -1931,11 +1946,10 @@ static void session_set_consumed_clock(IUnknown *object, IMFPresentationClock *c
} }
} }
static HRESULT session_start_clock(struct media_session *session) static void session_set_presentation_clock(struct media_session *session)
{ {
IMFPresentationTimeSource *time_source = NULL; IMFPresentationTimeSource *time_source = NULL;
struct media_source *source; struct media_source *source;
LONGLONG start_offset = 0;
struct media_sink *sink; struct media_sink *sink;
struct topo_node *node; struct topo_node *node;
HRESULT hr; HRESULT hr;
@ -2007,6 +2021,12 @@ static HRESULT session_start_clock(struct media_session *session)
session->presentation.flags |= SESSION_FLAG_PRESENTATION_CLOCK_SET; session->presentation.flags |= SESSION_FLAG_PRESENTATION_CLOCK_SET;
} }
}
static HRESULT session_start_clock(struct media_session *session)
{
LONGLONG start_offset = 0;
HRESULT hr;
if (IsEqualGUID(&session->presentation.time_format, &GUID_NULL)) if (IsEqualGUID(&session->presentation.time_format, &GUID_NULL))
{ {
@ -2026,14 +2046,36 @@ static HRESULT session_start_clock(struct media_session *session)
return hr; return hr;
} }
static BOOL session_set_node_object_state(struct media_session *session, IUnknown *object,
MF_TOPOLOGY_TYPE node_type, enum object_state state)
{
struct topo_node *node;
BOOL changed = FALSE;
LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry)
{
if (node->type == node_type && object == node->object.object)
{
changed = node->state != state;
node->state = state;
break;
}
}
return changed;
}
static void session_set_source_object_state(struct media_session *session, IUnknown *object, static void session_set_source_object_state(struct media_session *session, IUnknown *object,
MediaEventType event_type) MediaEventType event_type)
{ {
IMFStreamSink *stream_sink;
struct media_source *src; struct media_source *src;
struct media_sink *sink;
enum object_state state; enum object_state state;
struct topo_node *node; struct topo_node *node;
unsigned int i, count;
BOOL changed = FALSE; BOOL changed = FALSE;
HRESULT hr;
if ((state = session_get_object_state_for_event(event_type)) == OBJ_STATE_INVALID) if ((state = session_get_object_state_for_event(event_type)) == OBJ_STATE_INVALID)
return; return;
@ -2058,15 +2100,7 @@ static void session_set_source_object_state(struct media_session *session, IUnkn
case MEStreamPaused: case MEStreamPaused:
case MEStreamStopped: case MEStreamStopped:
LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) changed = session_set_node_object_state(session, object, MF_TOPOLOGY_SOURCESTREAM_NODE, state);
{
if (node->type == MF_TOPOLOGY_SOURCESTREAM_NODE && object == node->object.object)
{
changed = node->state != state;
node->state = state;
break;
}
}
default: default:
; ;
} }
@ -2081,7 +2115,44 @@ static void session_set_source_object_state(struct media_session *session, IUnkn
break; break;
session_set_topo_status(session, S_OK, MF_TOPOSTATUS_STARTED_SOURCE); session_set_topo_status(session, S_OK, MF_TOPOSTATUS_STARTED_SOURCE);
if (SUCCEEDED(session_start_clock(session)))
session_set_presentation_clock(session);
if (session->presentation.flags & SESSION_FLAG_NEEDS_PREROLL)
{
MFTIME preroll_time = 0;
if (session->presentation.start_position.vt == VT_I8)
preroll_time = session->presentation.start_position.hVal.QuadPart;
/* Mark stream sinks without prerolling support as PREROLLED to keep state test logic generic. */
LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
{
if (sink->preroll)
{
/* FIXME: abort and enter error state on failure. */
if (FAILED(hr = IMFMediaSinkPreroll_NotifyPreroll(sink->preroll, preroll_time)))
WARN("Preroll notification failed, hr %#x.\n", hr);
}
else
{
if (SUCCEEDED(IMFMediaSink_GetStreamSinkCount(sink->sink, &count)))
{
for (i = 0; i < count; ++i)
{
if (SUCCEEDED(IMFMediaSink_GetStreamSinkByIndex(sink->sink, i, &stream_sink)))
{
session_set_node_object_state(session, (IUnknown *)stream_sink, MF_TOPOLOGY_OUTPUT_NODE,
OBJ_STATE_PREROLLED);
IMFStreamSink_Release(stream_sink);
}
}
}
}
}
session->state = SESSION_STATE_PREROLLING_SINKS;
}
else if (SUCCEEDED(session_start_clock(session)))
session->state = SESSION_STATE_STARTING_SINKS; session->state = SESSION_STATE_STARTING_SINKS;
break; break;
@ -2137,31 +2208,27 @@ static void session_set_sink_stream_state(struct media_session *session, IMFStre
MediaEventType event_type) MediaEventType event_type)
{ {
struct media_source *source; struct media_source *source;
struct topo_node *node;
enum object_state state; enum object_state state;
BOOL changed = FALSE;
IMFMediaEvent *event; IMFMediaEvent *event;
DWORD caps, flags; DWORD caps, flags;
BOOL changed;
HRESULT hr; HRESULT hr;
if ((state = session_get_object_state_for_event(event_type)) == OBJ_STATE_INVALID) if ((state = session_get_object_state_for_event(event_type)) == OBJ_STATE_INVALID)
return; return;
LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) if (!(changed = session_set_node_object_state(session, (IUnknown *)stream, MF_TOPOLOGY_OUTPUT_NODE, state)))
{
if (node->type == MF_TOPOLOGY_OUTPUT_NODE && stream == node->object.sink_stream)
{
changed = node->state != state;
node->state = state;
break;
}
}
if (!changed)
return; return;
switch (session->state) switch (session->state)
{ {
case SESSION_STATE_PREROLLING_SINKS:
if (!session_is_output_nodes_state(session, OBJ_STATE_PREROLLED))
break;
if (SUCCEEDED(session_start_clock(session)))
session->state = SESSION_STATE_STARTING_SINKS;
break;
case SESSION_STATE_STARTING_SINKS: case SESSION_STATE_STARTING_SINKS:
if (!session_is_output_nodes_state(session, OBJ_STATE_STARTED)) if (!session_is_output_nodes_state(session, OBJ_STATE_STARTED))
break; break;
@ -2630,6 +2697,7 @@ static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IM
case MEStreamSinkStarted: case MEStreamSinkStarted:
case MEStreamSinkPaused: case MEStreamSinkPaused:
case MEStreamSinkStopped: case MEStreamSinkStopped:
case MEStreamSinkPrerolled:
EnterCriticalSection(&session->cs); EnterCriticalSection(&session->cs);
session_set_sink_stream_state(session, (IMFStreamSink *)event_source, event_type); session_set_sink_stream_state(session, (IMFStreamSink *)event_source, event_type);