It's possible that a state object pointer not in the topology node collection gets passed to session_get_node_object(). Instead of returning the last node when the object is not found, we should return a NULL so that the state of the last node is not changed by mistake.
-- v9: mf/tests: Test IMFMediaSession::Start(). mf: Support seeking while a session is running. mf/tests: Add a create_test_topology() helper. mf: Add a session_flush_nodes() helper. mf: Make session_get_node_object() more robust.
From: Zhiyi Zhang zzhang@codeweavers.com
It's possible that a state object pointer not in the topology node collection gets passed to session_get_node_object(). Instead of returning the last node when the object is not found, we should return a NULL so that the state of the last node is not changed by mistake.
Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/mf/session.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index b5b631fb00a..aef0582d3cc 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -2851,10 +2851,10 @@ static struct topo_node *session_get_node_object(struct media_session *session, LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) { if (node->type == node_type && object == node->object.object) - break; + return node; }
- return node; + return NULL; }
static BOOL session_set_node_object_state(struct media_session *session, IUnknown *object,
From: Zhiyi Zhang zzhang@codeweavers.com
Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/mf/session.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index aef0582d3cc..ca5e367ec44 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -912,6 +912,19 @@ static HRESULT session_subscribe_sources(struct media_session *session) return hr; }
+static void session_flush_nodes(struct media_session *session) +{ + struct topo_node *node; + + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->type == MF_TOPOLOGY_OUTPUT_NODE) + IMFStreamSink_Flush(node->object.sink_stream); + else if (node->type == MF_TOPOLOGY_TRANSFORM_NODE) + IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_COMMAND_FLUSH, 0); + } +} + static void session_start(struct media_session *session, const GUID *time_format, const PROPVARIANT *start_position) { struct media_source *source; @@ -2879,7 +2892,6 @@ static void session_set_source_object_state(struct media_session *session, IUnkn struct media_source *src; struct media_sink *sink; enum object_state state; - struct topo_node *node; BOOL changed = FALSE; DWORD i, count; HRESULT hr; @@ -2975,21 +2987,7 @@ static void session_set_source_object_state(struct media_session *session, IUnkn if (!session_is_source_nodes_state(session, OBJ_STATE_STOPPED)) break;
- LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) - { - switch (node->type) - { - case MF_TOPOLOGY_OUTPUT_NODE: - IMFStreamSink_Flush(node->object.sink_stream); - break; - case MF_TOPOLOGY_TRANSFORM_NODE: - IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_COMMAND_FLUSH, 0); - break; - default: - ; - } - } - + session_flush_nodes(session); session_set_caps(session, session->caps & ~MFSESSIONCAP_PAUSE);
if (session->presentation.flags & SESSION_FLAG_FINALIZE_SINKS)
From: Zhiyi Zhang zzhang@codeweavers.com
Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/mf/tests/mf.c | 80 +++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 37 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index aefc1b92b4d..00f29871bc2 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -4923,6 +4923,48 @@ static void test_sample_grabber_is_mediatype_supported(void) IMFSampleGrabberSinkCallback_Release(grabber_callback); }
+/* create a test topology with the specified source and sink */ +static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *sink_activate) +{ + IMFTopologyNode *src_node, *sink_node; + IMFPresentationDescriptor *pd; + IMFTopology *topology = NULL; + IMFStreamDescriptor *sd; + BOOL selected; + HRESULT hr; + + hr = MFCreateTopology(&topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &sink_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &src_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, sink_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, src_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(selected, "got selected %u.\n", !!selected); + init_source_node(source, -1, src_node, pd, sd); + hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)sink_activate); + ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); + hr = IMFTopologyNode_SetUINT32(sink_node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_ALLOW_DECODER); + ok(hr == S_OK, "Failed to set connect method, hr %#lx.\n", hr); + hr = IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFStreamDescriptor_Release(sd); + IMFPresentationDescriptor_Release(pd); + IMFTopologyNode_Release(src_node); + IMFTopologyNode_Release(sink_node); + return topology; +} + static void test_sample_grabber_orientation(GUID subtype) { media_type_desc video_rgb32_desc = @@ -4932,17 +4974,13 @@ static void test_sample_grabber_orientation(GUID subtype) };
struct test_grabber_callback *grabber_callback; - IMFTopologyNode *src_node, *sink_node; - IMFPresentationDescriptor *pd; IMFAsyncCallback *callback; IMFActivate *sink_activate; IMFMediaType *output_type; IMFMediaSession *session; - IMFStreamDescriptor *sd; IMFMediaSource *source; IMFTopology *topology; PROPVARIANT propvar; - BOOL selected; HRESULT hr; DWORD res;
@@ -4966,30 +5004,6 @@ static void test_sample_grabber_orientation(GUID subtype) hr = MFCreateMediaSession(NULL, &session); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &sink_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &src_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = MFCreateTopology(&topology); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, sink_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopology_AddNode(topology, src_node); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFTopologyNode_ConnectOutput(src_node, 0, sink_node, 0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); - ok(selected, "got selected %u.\n", !!selected); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - init_source_node(source, -1, src_node, pd, sd); - IMFTopologyNode_Release(src_node); - IMFPresentationDescriptor_Release(pd); - IMFStreamDescriptor_Release(sd); - hr = MFCreateMediaType(&output_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); init_media_type(output_type, video_rgb32_desc, -1); @@ -4997,15 +5011,7 @@ static void test_sample_grabber_orientation(GUID subtype) ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); IMFMediaType_Release(output_type);
- hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)sink_activate); - ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); - hr = IMFTopologyNode_SetUINT32(sink_node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_ALLOW_DECODER); - ok(hr == S_OK, "Failed to set connect method, hr %#lx.\n", hr); - IMFTopologyNode_Release(sink_node); - - hr = IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, TRUE); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - + topology = create_test_topology(source, sink_activate); hr = IMFMediaSession_SetTopology(session, 0, topology); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); IMFTopology_Release(topology);
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/mf/session.c | 49 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index ca5e367ec44..42459d1cc5c 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -91,6 +91,7 @@ enum session_state { SESSION_STATE_STOPPED = 0, SESSION_STATE_STARTING_SOURCES, + SESSION_STATE_SEEKING_SOURCES, SESSION_STATE_PREROLLING_SINKS, SESSION_STATE_STARTING_SINKS, SESSION_STATE_STARTED, @@ -110,6 +111,7 @@ enum object_state OBJ_STATE_STARTED, OBJ_STATE_PAUSED, OBJ_STATE_PREROLLED, + OBJ_STATE_SEEKING, OBJ_STATE_INVALID, };
@@ -929,6 +931,7 @@ static void session_start(struct media_session *session, const GUID *time_format { struct media_source *source; struct topo_node *topo_node; + MFTIME duration; HRESULT hr; UINT i;
@@ -981,8 +984,42 @@ static void session_start(struct media_session *session, const GUID *time_format session->state = SESSION_STATE_STARTING_SOURCES; break; case SESSION_STATE_STARTED: - FIXME("Seeking is not implemented.\n"); - session_command_complete(session); + session->presentation.time_format = *time_format; + session->presentation.start_position.vt = VT_EMPTY; + PropVariantCopy(&session->presentation.start_position, start_position); + + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) + { + hr = IMFPresentationDescriptor_GetUINT64(source->pd, &MF_PD_DURATION, (UINT64 *)&duration); + if (SUCCEEDED(hr) && IsEqualGUID(time_format, &GUID_NULL) + && start_position->vt == VT_I8 && start_position->hVal.QuadPart > duration) + { + WARN("Start position %s out of range, hr %#lx.\n", wine_dbgstr_longlong(start_position->hVal.QuadPart), hr); + session_command_complete_with_event(session, MESessionStarted, MF_E_INVALID_POSITION, NULL); + return; + } + + if (FAILED(hr = IMFMediaSource_Stop(source->source))) + { + WARN("Failed to stop media source %p, hr %#lx.\n", source->source, hr); + session_command_complete_with_event(session, MESessionStarted, hr, NULL); + return; + } + + if (FAILED(hr = IMFMediaSource_Start(source->source, source->pd, &GUID_NULL, start_position))) + { + WARN("Failed to start media source %p, hr %#lx.\n", source->source, hr); + session_command_complete_with_event(session, MESessionStarted, hr, NULL); + return; + } + } + + session_flush_nodes(session); + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) + { + source->state = OBJ_STATE_SEEKING; + } + session->state = SESSION_STATE_SEEKING_SOURCES; break; default: session_command_complete_with_event(session, MESessionStarted, MF_E_INVALIDREQUEST, NULL); @@ -2976,6 +3013,14 @@ static void session_set_source_object_state(struct media_session *session, IUnkn else if (SUCCEEDED(session_start_clock(session))) session->state = SESSION_STATE_STARTING_SINKS;
+ break; + case SESSION_STATE_SEEKING_SOURCES: + if (!session_is_source_nodes_state(session, OBJ_STATE_STARTED)) + break; + + if (SUCCEEDED(session_start_clock(session))) + session_set_started(session); + break; case SESSION_STATE_PAUSING_SOURCES: if (!session_is_source_nodes_state(session, OBJ_STATE_PAUSED))
From: Zhiyi Zhang zzhang@codeweavers.com
Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/mf/tests/mf.c | 960 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 952 insertions(+), 8 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 00f29871bc2..5f5e3e9f06f 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -2075,6 +2075,7 @@ static IMFMediaSource *create_media_source(const WCHAR *name, const WCHAR *mime) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, NULL, MF_RESOLUTION_MEDIASOURCE, NULL, &obj_type, (IUnknown **)&source); + todo_wine_if(hr == MF_E_UNEXPECTED) /* Gitlab CI Debian runner */ ok(hr == S_OK || broken(hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE), "Unexpected hr %#lx.\n", hr); IMFSourceResolver_Release(resolver); IMFByteStream_Release(stream); @@ -2086,6 +2087,626 @@ static IMFMediaSource *create_media_source(const WCHAR *name, const WCHAR *mime) return source; }
+enum object_state +{ + SOURCE_START, + SOURCE_PAUSE, + SOURCE_STOP, + SOURCE_SHUTDOWN, + SINK_ON_CLOCK_START, + SINK_ON_CLOCK_PAUSE, + SINK_ON_CLOCK_STOP, + SINK_ON_CLOCK_RESTART, + SINK_ON_CLOCK_SETRATE, +}; + +#define MAX_OBJECT_STATE 1024 + +struct object_state_record +{ + enum object_state states[MAX_OBJECT_STATE]; + unsigned int state_count; +}; +static struct object_state_record actual_object_state_record; + +#define add_object_state(a, b) _add_object_state(__LINE__, a, b) +static void _add_object_state(int line, struct object_state_record *record, enum object_state state) +{ + ok_(__FILE__, line)(record->state_count < MAX_OBJECT_STATE, "exceeded state_count maximum %d.\n", MAX_OBJECT_STATE); + if (record->state_count < MAX_OBJECT_STATE) + record->states[record->state_count++] = state; +} + +#define compare_object_states(a, b) _compare_object_states(__LINE__, a, b) +static void _compare_object_states(int line, const struct object_state_record *r1, + const struct object_state_record *r2) +{ + ok_(__FILE__, line)(r1->state_count == r2->state_count, "State count not equal.\n"); + if (r1->state_count == r2->state_count) + ok_(__FILE__, line)(!memcmp(r1->states, r2->states, sizeof(enum object_state) * r1->state_count), "Got different states.\n"); +} + +enum source_state +{ + SOURCE_STOPPED, + SOURCE_RUNNING, + SOURCE_PAUSED, +}; + +struct test_media_stream +{ + IMFMediaStream IMFMediaStream_iface; + IMFMediaEventQueue *event_queue; + IMFStreamDescriptor *sd; + IMFMediaSource *source; + LONGLONG sample_duration; + LONGLONG sample_time; + BOOL is_new; + LONG refcount; +}; + +static struct test_media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface) +{ + return CONTAINING_RECORD(iface, struct test_media_stream, IMFMediaStream_iface); +} + +static HRESULT WINAPI test_media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out) +{ + if (IsEqualIID(riid, &IID_IMFMediaStream) + || IsEqualIID(riid, &IID_IMFMediaEventGenerator) + || IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + } + else + { + *out = NULL; + return E_NOINTERFACE; + } + + IMFMediaStream_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI test_media_stream_AddRef(IMFMediaStream *iface) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + return InterlockedIncrement(&stream->refcount); +} + +static ULONG WINAPI test_media_stream_Release(IMFMediaStream *iface) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + ULONG refcount = InterlockedDecrement(&stream->refcount); + + if (!refcount) + { + IMFMediaEventQueue_Release(stream->event_queue); + free(stream); + } + + return refcount; +} + +static HRESULT WINAPI test_media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + return IMFMediaEventQueue_GetEvent(stream->event_queue, flags, event); +} + +static HRESULT WINAPI test_media_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + return IMFMediaEventQueue_BeginGetEvent(stream->event_queue, callback, state); +} + +static HRESULT WINAPI test_media_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + return IMFMediaEventQueue_EndGetEvent(stream->event_queue, result, event); +} + +static HRESULT WINAPI test_media_stream_QueueEvent(IMFMediaStream *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + return IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI test_media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **source) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + + *source = stream->source; + IMFMediaSource_AddRef(*source); + + return S_OK; +} + +static HRESULT WINAPI test_media_stream_GetStreamDescriptor(IMFMediaStream *iface, IMFStreamDescriptor **sd) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + + *sd = stream->sd; + IMFStreamDescriptor_AddRef(*sd); + + return S_OK; +} + +static HRESULT WINAPI test_media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) +{ + struct test_media_stream *stream = impl_from_IMFMediaStream(iface); + IMFMediaBuffer *buffer; + IMFSample *sample; + HRESULT hr; + + hr = MFCreateSample(&sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (stream->sample_duration) + { + hr = IMFSample_SetSampleDuration(sample, stream->sample_duration); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFSample_SetSampleTime(sample, stream->sample_time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + stream->sample_time += stream->sample_duration; + } + else + { + hr = IMFSample_SetSampleTime(sample, 123); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFSample_SetSampleDuration(sample, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + if (token) + IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token); + + /* Reader expects buffers, empty samples are considered an error. */ + hr = MFCreateMemoryBuffer(8, &buffer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFSample_AddBuffer(sample, buffer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaBuffer_Release(buffer); + + hr = IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, + (IUnknown *)sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFSample_Release(sample); + + return S_OK; +} + +static const IMFMediaStreamVtbl test_media_stream_vtbl = +{ + test_media_stream_QueryInterface, + test_media_stream_AddRef, + test_media_stream_Release, + test_media_stream_GetEvent, + test_media_stream_BeginGetEvent, + test_media_stream_EndGetEvent, + test_media_stream_QueueEvent, + test_media_stream_GetMediaSource, + test_media_stream_GetStreamDescriptor, + test_media_stream_RequestSample, +}; + +#define TEST_SOURCE_NUM_STREAMS 3 + +struct test_seek_source +{ + IMFMediaSource IMFMediaSource_iface; + IMFMediaEventQueue *event_queue; + IMFPresentationDescriptor *pd; + struct test_media_stream *streams[TEST_SOURCE_NUM_STREAMS]; + enum source_state state; + unsigned stream_count; + CRITICAL_SECTION cs; + BOOL seekable; + LONG refcount; +}; + +static struct test_seek_source *impl_test_seek_source_from_IMFMediaSource(IMFMediaSource *iface) +{ + return CONTAINING_RECORD(iface, struct test_seek_source, IMFMediaSource_iface); +} + +static HRESULT WINAPI test_seek_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) +{ + if (IsEqualIID(riid, &IID_IMFMediaSource) + || IsEqualIID(riid, &IID_IMFMediaEventGenerator) + || IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + } + else + { + *out = NULL; + return E_NOINTERFACE; + } + + IMFMediaSource_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI test_seek_source_AddRef(IMFMediaSource *iface) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + return InterlockedIncrement(&source->refcount); +} + +static ULONG WINAPI test_seek_source_Release(IMFMediaSource *iface) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + ULONG refcount = InterlockedDecrement(&source->refcount); + + if (!refcount) + { + IMFMediaEventQueue_Release(source->event_queue); + free(source); + } + + return refcount; +} + +static HRESULT WINAPI test_seek_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + return IMFMediaEventQueue_GetEvent(source->event_queue, flags, event); +} + +static HRESULT WINAPI test_seek_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + return IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state); +} + +static HRESULT WINAPI test_seek_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + return IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event); +} + +static HRESULT WINAPI test_seek_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI test_seek_source_GetCharacteristics(IMFMediaSource *iface, DWORD *flags) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + + if (source->seekable) + *flags = MFMEDIASOURCE_CAN_PAUSE | MFMEDIASOURCE_CAN_SEEK; + else + *flags = MFMEDIASOURCE_CAN_PAUSE; + return S_OK; +} + +static HRESULT WINAPI test_seek_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **pd) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + IMFStreamDescriptor *sds[ARRAY_SIZE(source->streams)]; + IMFMediaType *media_type; + HRESULT hr = S_OK; + int i; + + EnterCriticalSection(&source->cs); + + if (source->pd) + { + *pd = source->pd; + IMFPresentationDescriptor_AddRef(*pd); + } + else + { + for (i = 0; i < source->stream_count; ++i) + { + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_RGB32); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, (UINT64)640 << 32 | 480); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateStreamDescriptor(i, 1, &media_type, &sds[i]); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFMediaType_Release(media_type); + } + + hr = MFCreatePresentationDescriptor(source->stream_count, sds, &source->pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_SetUINT64(source->pd, &MF_PD_DURATION, 10 * 10000000); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationDescriptor_SelectStream(source->pd, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + for (i = 0; i < source->stream_count; ++i) + IMFStreamDescriptor_Release(sds[i]); + + *pd = source->pd; + IMFPresentationDescriptor_AddRef(*pd); + } + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static BOOL is_stream_selected(IMFPresentationDescriptor *pd, DWORD index) +{ + IMFStreamDescriptor *sd; + BOOL selected = FALSE; + + if (SUCCEEDED(IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, index, &selected, &sd))) + IMFStreamDescriptor_Release(sd); + + return selected; +} + +static HRESULT WINAPI test_seek_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *pd, const GUID *time_format, + const PROPVARIANT *start_position) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + MediaEventType event_type; + PROPVARIANT var; + HRESULT hr; + int i; + + add_object_state(&actual_object_state_record, SOURCE_START); + + ok(time_format && IsEqualGUID(time_format, &GUID_NULL), "Unexpected time format %s.\n", + wine_dbgstr_guid(time_format)); + ok(start_position && (start_position->vt == VT_I8 || start_position->vt == VT_EMPTY), + "Unexpected position type.\n"); + + /* This is what makes IMFMediaSession::Start() seeking fail, not the lacking of MFMEDIASOURCE_CAN_SEEK. + * Without this, IMFMediaSession::Start() seeking succeeds even with the missing MFMEDIASOURCE_CAN_SEEK. + * If this is check is not here, the first IMFMediaSession::Start() call to a non-zero position + * succeeds somehow on Windows 10, then all following seeks fails and no MESessionStarted events */ + if (!source->seekable && start_position && start_position->vt == VT_I8 && start_position->hVal.QuadPart) + return E_FAIL; + + EnterCriticalSection(&source->cs); + + event_type = source->state != SOURCE_STOPPED ? MESourceSeeked : MESourceStarted; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + for (i = 0; i < source->stream_count; ++i) + { + if (!is_stream_selected(pd, i)) + continue; + + var.vt = VT_UNKNOWN; + var.punkVal = (IUnknown *)&source->streams[i]->IMFMediaStream_iface; + event_type = source->streams[i]->is_new ? MENewStream : MEUpdatedStream; + source->streams[i]->is_new = FALSE; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + event_type = source->state != SOURCE_STOPPED ? MEStreamSeeked : MEStreamStarted; + hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, + S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + source->state = SOURCE_RUNNING; + + LeaveCriticalSection(&source->cs); + + return S_OK; +} + +static HRESULT WINAPI test_seek_source_Stop(IMFMediaSource *iface) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + MediaEventType event_type; + HRESULT hr; + int i; + + add_object_state(&actual_object_state_record, SOURCE_STOP); + + EnterCriticalSection(&source->cs); + + event_type = MESourceStopped; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + for (i = 0; i < source->stream_count; ++i) + { + if (!is_stream_selected(source->pd, i)) + continue; + + event_type = MEStreamStopped; + hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, + S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + source->state = SOURCE_STOPPED; + + LeaveCriticalSection(&source->cs); + + return S_OK; +} + +static HRESULT WINAPI test_seek_source_Pause(IMFMediaSource *iface) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + MediaEventType event_type; + HRESULT hr; + int i; + + add_object_state(&actual_object_state_record, SOURCE_PAUSE); + + EnterCriticalSection(&source->cs); + + event_type = MESourcePaused; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + for (i = 0; i < source->stream_count; ++i) + { + if (!is_stream_selected(source->pd, i)) + continue; + + event_type = MEStreamPaused; + hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, + S_OK, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + source->state = SOURCE_PAUSED; + LeaveCriticalSection(&source->cs); + + return S_OK; +} + +static HRESULT WINAPI test_seek_source_Shutdown(IMFMediaSource *iface) +{ + struct test_seek_source *source = impl_test_seek_source_from_IMFMediaSource(iface); + HRESULT hr; + + add_object_state(&actual_object_state_record, SOURCE_SHUTDOWN); + + hr = IMFMediaEventQueue_Shutdown(source->event_queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + return S_OK; +} + +static const IMFMediaSourceVtbl test_seek_source_vtbl = +{ + test_seek_source_QueryInterface, + test_seek_source_AddRef, + test_seek_source_Release, + test_seek_source_GetEvent, + test_seek_source_BeginGetEvent, + test_seek_source_EndGetEvent, + test_seek_source_QueueEvent, + test_seek_source_GetCharacteristics, + test_seek_source_CreatePresentationDescriptor, + test_seek_source_Start, + test_seek_source_Stop, + test_seek_source_Pause, + test_seek_source_Shutdown, +}; + +static HRESULT WINAPI test_seek_clock_sink_QueryInterface(IMFClockStateSink *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFClockStateSink) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFClockStateSink_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_seek_clock_sink_AddRef(IMFClockStateSink *iface) +{ + return 2; +} + +static ULONG WINAPI test_seek_clock_sink_Release(IMFClockStateSink *iface) +{ + return 1; +} + +static HRESULT WINAPI test_seek_clock_sink_OnClockStart(IMFClockStateSink *iface, MFTIME system_time, LONGLONG offset) +{ + add_object_state(&actual_object_state_record, SINK_ON_CLOCK_START); + return S_OK; +} + +static HRESULT WINAPI test_seek_clock_sink_OnClockStop(IMFClockStateSink *iface, MFTIME system_time) +{ + add_object_state(&actual_object_state_record, SINK_ON_CLOCK_STOP); + return S_OK; +} + +static HRESULT WINAPI test_seek_clock_sink_OnClockPause(IMFClockStateSink *iface, MFTIME system_time) +{ + add_object_state(&actual_object_state_record, SINK_ON_CLOCK_PAUSE); + return S_OK; +} + +static HRESULT WINAPI test_seek_clock_sink_OnClockRestart(IMFClockStateSink *iface, MFTIME system_time) +{ + add_object_state(&actual_object_state_record, SINK_ON_CLOCK_RESTART); + return S_OK; +} + +static HRESULT WINAPI test_seek_clock_sink_OnClockSetRate(IMFClockStateSink *iface, MFTIME system_time, float rate) +{ + add_object_state(&actual_object_state_record, SINK_ON_CLOCK_SETRATE); + return S_OK; +} + +static const IMFClockStateSinkVtbl test_seek_clock_sink_vtbl = +{ + test_seek_clock_sink_QueryInterface, + test_seek_clock_sink_AddRef, + test_seek_clock_sink_Release, + test_seek_clock_sink_OnClockStart, + test_seek_clock_sink_OnClockStop, + test_seek_clock_sink_OnClockPause, + test_seek_clock_sink_OnClockRestart, + test_seek_clock_sink_OnClockSetRate, +}; + +static struct test_media_stream *create_test_stream(DWORD stream_index, IMFMediaSource *source) +{ + struct test_media_stream *stream; + IMFPresentationDescriptor *pd; + BOOL selected; + HRESULT hr; + + stream = calloc(1, sizeof(*stream)); + stream->IMFMediaStream_iface.lpVtbl = &test_media_stream_vtbl; + stream->refcount = 1; + hr = MFCreateEventQueue(&stream->event_queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + stream->source = source; + IMFMediaSource_AddRef(stream->source); + stream->is_new = TRUE; + + IMFMediaSource_CreatePresentationDescriptor(source, &pd); + IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, stream_index, &selected, &stream->sd); + IMFPresentationDescriptor_Release(pd); + + return stream; +} + +static IMFMediaSource *create_test_seek_source(BOOL seekable) +{ + struct test_seek_source *source; + int i; + + source = calloc(1, sizeof(*source)); + source->IMFMediaSource_iface.lpVtbl = &test_seek_source_vtbl; + source->refcount = 1; + source->stream_count = 1; + source->seekable = seekable; + MFCreateEventQueue(&source->event_queue); + InitializeCriticalSection(&source->cs); + for (i = 0; i < source->stream_count; ++i) + source->streams[i] = create_test_stream(i, &source->IMFMediaSource_iface); + + return &source->IMFMediaSource_iface; +} + static void test_media_session_events(void) { static const media_type_desc audio_float_44100 = @@ -2887,27 +3508,27 @@ static ULONG WINAPI test_grabber_callback_Release(IMFSampleGrabberSinkCallback *
static HRESULT WINAPI test_grabber_callback_OnClockStart(IMFSampleGrabberSinkCallback *iface, MFTIME time, LONGLONG offset) { - return E_NOTIMPL; + return S_OK; }
static HRESULT WINAPI test_grabber_callback_OnClockStop(IMFSampleGrabberSinkCallback *iface, MFTIME time) { - return E_NOTIMPL; + return S_OK; }
static HRESULT WINAPI test_grabber_callback_OnClockPause(IMFSampleGrabberSinkCallback *iface, MFTIME time) { - return E_NOTIMPL; + return S_OK; }
static HRESULT WINAPI test_grabber_callback_OnClockRestart(IMFSampleGrabberSinkCallback *iface, MFTIME time) { - return E_NOTIMPL; + return S_OK; }
static HRESULT WINAPI test_grabber_callback_OnClockSetRate(IMFSampleGrabberSinkCallback *iface, MFTIME time, float rate) { - return E_NOTIMPL; + return S_OK; }
static HRESULT WINAPI test_grabber_callback_OnSetPresentationClock(IMFSampleGrabberSinkCallback *iface, @@ -4923,8 +5544,8 @@ static void test_sample_grabber_is_mediatype_supported(void) IMFSampleGrabberSinkCallback_Release(grabber_callback); }
-/* create a test topology with the specified source and sink */ -static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *sink_activate) +/* create a test topology with the specified source and sink, and return duration if required */ +static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *sink_activate, UINT64 *duration) { IMFTopologyNode *src_node, *sink_node; IMFPresentationDescriptor *pd; @@ -4950,6 +5571,11 @@ static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *si hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, 0, &selected, &sd); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(selected, "got selected %u.\n", !!selected); + if (duration) + { + hr = IMFPresentationDescriptor_GetUINT64(pd, &MF_PD_DURATION, duration); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } init_source_node(source, -1, src_node, pd, sd); hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)sink_activate); ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); @@ -5011,7 +5637,7 @@ static void test_sample_grabber_orientation(GUID subtype) ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); IMFMediaType_Release(output_type);
- topology = create_test_topology(source, sink_activate); + topology = create_test_topology(source, sink_activate, NULL); hr = IMFMediaSession_SetTopology(session, 0, topology); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); IMFTopology_Release(topology); @@ -7208,6 +7834,323 @@ static void test_MFCreateSequencerSegmentOffset(void) PropVariantClear(&propvar); }
+static void test_media_session_Start(void) +{ + static const struct object_state_record expected_object_state_records[] = + { + {{SOURCE_START, SINK_ON_CLOCK_START}, 2}, + {{SOURCE_STOP, SOURCE_START, SINK_ON_CLOCK_START}, 3}, + {{SOURCE_STOP, SOURCE_START, SINK_ON_CLOCK_START}, 3}, + }; + media_type_desc video_rgb32_desc = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), + }; + static const MFTIME allowed_error = 500000; + IMFClockStateSink test_seek_clock_sink = {&test_seek_clock_sink_vtbl}; + struct test_grabber_callback *grabber_callback; + IMFPresentationClock *presentation_clock; + enum source_state initial_state; + IMFActivate *sink_activate; + IMFAsyncCallback *callback; + IMFMediaType *output_type; + IMFMediaSession *session; + IMFMediaSource *source; + IMFTopology *topology; + MFTIME time, old_time; + PROPVARIANT propvar; + IMFClock *clock; + UINT64 duration; + DWORD caps; + HRESULT hr; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); + + if (!(source = create_media_source(L"test.mp4", L"video/mp4"))) + { + todo_wine /* Gitlab CI Debian runner */ + win_skip("MP4 media source is not supported, skipping tests.\n"); + MFShutdown(); + return; + } + + grabber_callback = impl_from_IMFSampleGrabberSinkCallback(create_test_grabber_callback()); + hr = MFCreateMediaType(&output_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(output_type, video_rgb32_desc, -1); + hr = MFCreateSampleGrabberSinkActivate(output_type, &grabber_callback->IMFSampleGrabberSinkCallback_iface, &sink_activate); + ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); + IMFMediaType_Release(output_type); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + topology = create_test_topology(source, sink_activate, &duration); + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopology_Release(topology); + + hr = IMFMediaSession_GetClock(session, &clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFClock_QueryInterface(clock, &IID_IMFPresentationClock, (void **)&presentation_clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFClock_Release(clock); + + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + callback = create_test_callback(TRUE); + hr = wait_media_event(session, callback, MESessionStarted, 5000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Seek to 1s */ + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationClock_GetTime(presentation_clock, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(llabs(time - 10000000) <= allowed_error, "Unexpected time %I64d.\n", time); + + /* Seek to beyond duration */ + propvar.vt = VT_I8; + propvar.hVal.QuadPart = duration + 10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == MF_E_INVALID_POSITION, "Unexpected hr %#lx.\n", hr); + + /* Seek to negative position */ + propvar.vt = VT_I8; + propvar.hVal.QuadPart = -10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationClock_GetTime(presentation_clock, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(llabs(time - (-10000000)) <= allowed_error, "Unexpected time %I64d.\n", time); + + /* Seek backwards to 0s */ + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 0; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationClock_GetTime(presentation_clock, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(llabs(time) <= allowed_error, "Unexpected time %I64d.\n", time); + + /* Seek to 1s while in paused state */ + hr = IMFMediaSession_Pause(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionPaused, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFPresentationClock_GetTime(presentation_clock, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(llabs(time - 10000000) <= allowed_error, "Unexpected time %I64d.\n", time); + old_time = time; + + /* Expected the presentation clock is running */ + Sleep(100); + hr = IMFPresentationClock_GetTime(presentation_clock, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(time > old_time, "Unexpected time %I64d.\n", time); + + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Close(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Media session is shut down */ + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + + IMFPresentationClock_Release(presentation_clock); + IMFMediaSource_Release(source); + IMFAsyncCallback_Release(callback); + /* sometimes briefly leaking */ + IMFMediaSession_Release(session); + IMFActivate_ShutdownObject(sink_activate); + IMFActivate_Release(sink_activate); + IMFSampleGrabberSinkCallback_Release(&grabber_callback->IMFSampleGrabberSinkCallback_iface); + + /* Unseekable media source */ + source = create_test_seek_source(FALSE); + hr = IMFMediaSource_GetCharacteristics(source, &caps); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok((caps & MFMEDIASOURCE_CAN_SEEK) == 0, "Got unexpected caps %#lx.\n", caps); + grabber_callback = impl_from_IMFSampleGrabberSinkCallback(create_test_grabber_callback()); + hr = MFCreateMediaType(&output_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(output_type, video_rgb32_desc, -1); + hr = MFCreateSampleGrabberSinkActivate(output_type, &grabber_callback->IMFSampleGrabberSinkCallback_iface, &sink_activate); + ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); + IMFMediaType_Release(output_type); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + topology = create_test_topology(source, sink_activate, &duration); + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopology_Release(topology); + + hr = IMFMediaSession_GetClock(session, &clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFClock_QueryInterface(clock, &IID_IMFPresentationClock, (void **)&presentation_clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFClock_Release(clock); + + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + callback = create_test_callback(TRUE); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSession_GetSessionCapabilities(session, &caps); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok((caps & MFSESSIONCAP_SEEK) == 0, "Got unexpected caps %#lx\n", caps); + + /* Seek to 1s */ + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 1000, &propvar); + ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationClock_GetTime(presentation_clock, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(llabs(time) <= allowed_error, "Unexpected time %I64d.\n", time); + + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Close(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFPresentationClock_Release(presentation_clock); + IMFAsyncCallback_Release(callback); + IMFMediaSession_Release(session); + IMFMediaSource_Release(source); + IMFActivate_ShutdownObject(sink_activate); + IMFActivate_Release(sink_activate); + IMFSampleGrabberSinkCallback_Release(&grabber_callback->IMFSampleGrabberSinkCallback_iface); + + /* Test object state transitions */ + for (initial_state = SOURCE_STOPPED; initial_state <= SOURCE_PAUSED; initial_state++) + { + winetest_push_context("Test %d", initial_state); + + source = create_test_seek_source(TRUE); + callback = create_test_callback(TRUE); + + grabber_callback = impl_from_IMFSampleGrabberSinkCallback(create_test_grabber_callback()); + hr = MFCreateMediaType(&output_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + init_media_type(output_type, video_rgb32_desc, -1); + hr = MFCreateSampleGrabberSinkActivate(output_type, &grabber_callback->IMFSampleGrabberSinkCallback_iface, &sink_activate); + ok(hr == S_OK, "Failed to create grabber sink, hr %#lx.\n", hr); + IMFMediaType_Release(output_type); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + topology = create_test_topology(source, sink_activate, &duration); + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopology_Release(topology); + + hr = IMFMediaSession_GetClock(session, &clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFClock_QueryInterface(clock, &IID_IMFPresentationClock, (void **)&presentation_clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFPresentationClock_AddClockStateSink(presentation_clock, &test_seek_clock_sink); + ok(hr == S_OK, "Failed to add a sink, hr %#lx.\n", hr); + IMFClock_Release(clock); + + if (initial_state == SOURCE_RUNNING || initial_state == SOURCE_PAUSED) + { + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 5000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + if (initial_state == SOURCE_PAUSED) + { + hr = IMFMediaSession_Pause(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionPaused, 5000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + /* Seek to 1s */ + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionStarted, 5000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine_if(initial_state == SOURCE_PAUSED) + compare_object_states(&actual_object_state_record, &expected_object_state_records[initial_state]); + + hr = IMFMediaSession_Stop(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Close(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFPresentationClock_RemoveClockStateSink(presentation_clock, &test_seek_clock_sink); + IMFPresentationClock_Release(presentation_clock); + IMFAsyncCallback_Release(callback); + IMFMediaSession_Release(session); + IMFMediaSource_Release(source); + IMFActivate_ShutdownObject(sink_activate); + IMFActivate_Release(sink_activate); + IMFSampleGrabberSinkCallback_Release(&grabber_callback->IMFSampleGrabberSinkCallback_iface); + winetest_pop_context(); + } + + hr = MFShutdown(); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} + START_TEST(mf) { init_functions(); @@ -7244,4 +8187,5 @@ START_TEST(mf) test_MFRequireProtectedEnvironment(); test_mpeg4_media_sink(); test_MFCreateSequencerSegmentOffset(); + test_media_session_Start(); }
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=139458
Your paranoid android.
=== debian11b (64 bit WoW report) ===
dinput: device8.c:2238: Test failed: 0x700: got key_state[0] 0
v9: Simplify state transitions to only handle the SESSION_STATE_STARTED case, which only needs setting a new position for sources and the presentation clock. Also added tests to verify the state transitions.
This version still does Stop() and immediately Start() on sources, without waiting for anything.
On Mon Nov 13 14:42:37 2023 +0000, Nikolay Sivov wrote:
This version still does Stop() and immediately Start() on sources, without waiting for anything.
Actually, I don't think the Stop() is necessary. The only reason why I added the Stop() call is that's what the tests show. It would be simpler if I removed the Stop() call and adding it now just complicates things. Do you think it is ok if I do that?