From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mf/tests/mf.c | 918 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 850 insertions(+), 68 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index d2686236393..b1605a9ae1e 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -720,6 +720,8 @@ static const struct test_handler test_handler = {.IMFMediaTypeHandler_iface.lpVt struct test_media_sink { IMFMediaSink IMFMediaSink_iface; + IMFPresentationClock *clock; + IMFStreamSink *stream; BOOL shutdown; };
@@ -770,14 +772,34 @@ static HRESULT WINAPI test_media_sink_RemoveStreamSink(IMFMediaSink *iface, DWOR return E_NOTIMPL; }
+DEFINE_EXPECT(test_media_sink_GetStreamSinkCount); +DEFINE_EXPECT(test_media_sink_SetPresentationClock); +DEFINE_EXPECT(test_media_sink_GetPresentationClock); + static HRESULT WINAPI test_media_sink_GetStreamSinkCount(IMFMediaSink *iface, DWORD *count) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + HRESULT hr; + if (expect_test_media_sink_GetStreamSinkCount) + { + *count = 1; + hr = S_OK; + } + else + { + hr = E_NOTIMPL; + } + CHECK_EXPECT(test_media_sink_GetStreamSinkCount); + return hr; }
static HRESULT WINAPI test_media_sink_GetStreamSinkByIndex(IMFMediaSink *iface, DWORD index, IMFStreamSink **sink) { + struct test_media_sink *sink_impl = impl_from_IMFMediaSink(iface); + if (sink_impl->stream) + { + IMFStreamSink_AddRef(*sink = sink_impl->stream); + return S_OK; + } ok(0, "Unexpected call.\n"); return E_NOTIMPL; } @@ -790,13 +812,37 @@ static HRESULT WINAPI test_media_sink_GetStreamSinkById(IMFMediaSink *iface, DWO
static HRESULT WINAPI test_media_sink_SetPresentationClock(IMFMediaSink *iface, IMFPresentationClock *clock) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + struct test_media_sink *sink = impl_from_IMFMediaSink(iface); + HRESULT hr; + + if (expect_test_media_sink_SetPresentationClock) + { + if (sink->clock) IMFPresentationClock_Release(clock); + IMFPresentationClock_AddRef(sink->clock = clock); + hr = S_OK; + } + else + { + hr = E_NOTIMPL; + } + CHECK_EXPECT(test_media_sink_SetPresentationClock); + return hr; }
static HRESULT WINAPI test_media_sink_GetPresentationClock(IMFMediaSink *iface, IMFPresentationClock **clock) { - ok(0, "Unexpected call.\n"); + struct test_media_sink *sink = impl_from_IMFMediaSink(iface); + + CHECK_EXPECT2(test_media_sink_GetPresentationClock); + if (expect_test_media_sink_GetPresentationClock) + { + if (sink->clock) + { + IMFPresentationClock_AddRef(*clock = sink->clock); + return S_OK; + } + return MF_E_NO_CLOCK; + } return E_NOTIMPL; }
@@ -808,6 +854,54 @@ static HRESULT WINAPI test_media_sink_Shutdown(IMFMediaSink *iface) return S_OK; }
+enum object_state +{ + SOURCE_START, + SOURCE_PAUSE, + SOURCE_STOP, + SOURCE_SHUTDOWN, + SOURCE_REQUEST_SAMPLE, + SINK_ON_CLOCK_START, + SINK_ON_CLOCK_PAUSE, + SINK_ON_CLOCK_STOP, + SINK_ON_CLOCK_RESTART, + SINK_ON_CLOCK_SETRATE, + SINK_FLUSH, + SINK_PROCESS_SAMPLE, + MFT_BEGIN, + MFT_START, + MFT_FLUSH, + MFT_PROCESS_INPUT, + MFT_PROCESS_OUTPUT, +}; + +#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"); +} + + static const IMFMediaSinkVtbl test_media_sink_vtbl = { test_media_sink_QueryInterface, @@ -835,6 +929,8 @@ struct test_stream_sink
IMFAttributes *attributes; IUnknown *device_manager; + + IMFMediaEventQueue *event_queue; };
static struct test_stream_sink *impl_from_IMFStreamSink(IMFStreamSink *iface) @@ -889,6 +985,11 @@ static HRESULT WINAPI test_stream_sink_GetEvent(IMFStreamSink *iface, DWORD flag
static HRESULT WINAPI test_stream_sink_BeginGetEvent(IMFStreamSink *iface, IMFAsyncCallback *callback, IUnknown *state) { + struct test_stream_sink *sink = impl_from_IMFStreamSink(iface); + + if (sink->event_queue) + return IMFMediaEventQueue_BeginGetEvent(sink->event_queue, callback, state); + ok(0, "Unexpected call.\n"); return E_NOTIMPL; } @@ -896,6 +997,11 @@ static HRESULT WINAPI test_stream_sink_BeginGetEvent(IMFStreamSink *iface, IMFAs static HRESULT WINAPI test_stream_sink_EndGetEvent(IMFStreamSink *iface, IMFAsyncResult *result, IMFMediaEvent **event) { + struct test_stream_sink *sink = impl_from_IMFStreamSink(iface); + + if (sink->event_queue) + return IMFMediaEventQueue_EndGetEvent(sink->event_queue, result, event); + ok(0, "Unexpected call.\n"); return E_NOTIMPL; } @@ -903,6 +1009,11 @@ static HRESULT WINAPI test_stream_sink_EndGetEvent(IMFStreamSink *iface, IMFAsyn static HRESULT WINAPI test_stream_sink_QueueEvent(IMFStreamSink *iface, MediaEventType event_type, REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) { + struct test_stream_sink *sink = impl_from_IMFStreamSink(iface); + + if (sink->event_queue) + return IMFMediaEventQueue_QueueEventParamVar(sink->event_queue, event_type, ext_type, hr, value); + ok(0, "Unexpected call.\n"); return E_NOTIMPL; } @@ -942,10 +1053,22 @@ static HRESULT WINAPI test_stream_sink_GetMediaTypeHandler(IMFStreamSink *iface, return E_NOTIMPL; }
+DEFINE_EXPECT(test_stream_sink_ProcessSample); +DEFINE_EXPECT(test_stream_sink_Flush); + static HRESULT WINAPI test_stream_sink_ProcessSample(IMFStreamSink *iface, IMFSample *sample) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + HRESULT hr; + + if (expect_test_stream_sink_ProcessSample) + hr = S_OK; + else + hr = E_NOTIMPL; + + CHECK_EXPECT(test_stream_sink_ProcessSample); + add_object_state(&actual_object_state_record, SINK_PROCESS_SAMPLE); + + return hr; }
static HRESULT WINAPI test_stream_sink_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type, @@ -957,8 +1080,17 @@ static HRESULT WINAPI test_stream_sink_PlaceMarker(IMFStreamSink *iface, MFSTREA
static HRESULT WINAPI test_stream_sink_Flush(IMFStreamSink *iface) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + HRESULT hr; + + if (expect_test_stream_sink_Flush) + hr = S_OK; + else + hr = E_NOTIMPL; + + CHECK_EXPECT(test_stream_sink_Flush); + add_object_state(&actual_object_state_record, SINK_FLUSH); + + return hr; }
static const IMFStreamSinkVtbl test_stream_sink_vtbl = @@ -1273,45 +1405,6 @@ 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, @@ -1328,6 +1421,9 @@ struct test_media_stream LONGLONG sample_duration; LONGLONG sample_time; BOOL is_new; + BOOL test_expect; + BOOL delay_sample; + IMFSample *delayed_sample; LONG refcount; };
@@ -1367,6 +1463,8 @@ static ULONG WINAPI test_media_stream_Release(IMFMediaStream *iface)
if (!refcount) { + if (stream->delayed_sample) + IMFSample_Release(stream->delayed_sample); IMFMediaEventQueue_Release(stream->event_queue); free(stream); } @@ -1419,6 +1517,8 @@ static HRESULT WINAPI test_media_stream_GetStreamDescriptor(IMFMediaStream *ifac return S_OK; }
+DEFINE_EXPECT(test_media_stream_RequestSample); + static HRESULT WINAPI test_media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) { struct test_media_stream *stream = impl_from_IMFMediaStream(iface); @@ -1426,6 +1526,20 @@ static HRESULT WINAPI test_media_stream_RequestSample(IMFMediaStream *iface, IUn IMFSample *sample; HRESULT hr;
+ if (stream->test_expect) + { + if (expect_test_media_stream_RequestSample) + hr = S_OK; + else + hr = E_NOTIMPL; + + CHECK_EXPECT(test_media_stream_RequestSample); + add_object_state(&actual_object_state_record, SOURCE_REQUEST_SAMPLE); + + if (FAILED(hr)) + return hr; + } + hr = MFCreateSample(&sample); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (stream->sample_duration) @@ -1457,10 +1571,18 @@ static HRESULT WINAPI test_media_stream_RequestSample(IMFMediaStream *iface, IUn 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); + if (stream->delay_sample) + { + if (stream->delayed_sample) IMFSample_Release(stream->delayed_sample); + stream->delayed_sample = sample; + } + else + { + 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; } @@ -1662,10 +1784,6 @@ static HRESULT WINAPI test_seek_source_Start(IMFMediaSource *iface, IMFPresentat
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)) @@ -1678,12 +1796,16 @@ static HRESULT WINAPI test_seek_source_Start(IMFMediaSource *iface, IMFPresentat 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; + event_type = start_position->vt == VT_I8 ? MEStreamSeeked : MEStreamStarted; hr = IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, - S_OK, NULL); + S_OK, start_position); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); }
+ event_type = start_position->vt == VT_I8 ? MESourceSeeked : MESourceStarted; + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, start_position); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + source->state = SOURCE_RUNNING;
LeaveCriticalSection(&source->cs); @@ -1735,10 +1857,6 @@ static HRESULT WINAPI test_seek_source_Pause(IMFMediaSource *iface)
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)) @@ -1750,6 +1868,10 @@ static HRESULT WINAPI test_seek_source_Pause(IMFMediaSource *iface) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); }
+ 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); + source->state = SOURCE_PAUSED; LeaveCriticalSection(&source->cs);
@@ -3832,10 +3954,10 @@ static void test_sample_grabber_is_mediatype_supported(void) IMFSampleGrabberSinkCallback_Release(grabber_callback); }
-/* 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) +/* create a test topology with the specified source, sink, and option MFT. Return duration if required */ +static IMFTopology *create_test_topology_unk(IMFMediaSource *source, IUnknown *sink, IUnknown *mft, UINT64 *duration) { - IMFTopologyNode *src_node, *sink_node; + IMFTopologyNode *src_node, *sink_node, *mft_node; IMFPresentationDescriptor *pd; IMFTopology *topology = NULL; IMFStreamDescriptor *sd; @@ -3852,8 +3974,24 @@ static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *si 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); + if (mft) + { + hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mft_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopology_AddNode(topology, mft_node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(src_node, 0, mft_node, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_ConnectOutput(mft_node, 0, sink_node, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyNode_SetObject(mft_node, mft); + ok(hr == S_OK, "Failed to set object, hr %#lx.\n", hr); + } + else + { + 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); @@ -3865,7 +4003,7 @@ static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *si 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); + hr = IMFTopologyNode_SetObject(sink_node, sink); 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); @@ -3879,6 +4017,11 @@ static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *si return topology; }
+static IMFTopology *create_test_topology(IMFMediaSource *source, IMFActivate *sink_activate, UINT64 *duration) +{ + return create_test_topology_unk(source, (IUnknown*)sink_activate, NULL, duration); +} + static void test_sample_grabber_orientation(GUID subtype) { media_type_desc video_rgb32_desc = @@ -6941,6 +7084,644 @@ static void test_media_session_Close(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); }
+struct test_transform +{ + IMFTransform IMFTransform_iface; + LONG refcount; + + const MFT_OUTPUT_STREAM_INFO *output_stream_info; + + UINT input_count; + IMFMediaType **input_types; + IMFMediaType *input_type; + + UINT output_count; + IMFMediaType **output_types; + IMFMediaType *output_type; + + IMFSample *output; +}; + +static struct test_transform *test_transform_from_IMFTransform(IMFTransform *iface) +{ + return CONTAINING_RECORD(iface, struct test_transform, IMFTransform_iface); +} + +static HRESULT WINAPI test_transform_QueryInterface(IMFTransform *iface, REFIID iid, void **out) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + + if (IsEqualGUID(iid, &IID_IUnknown) + || IsEqualGUID(iid, &IID_IMFTransform)) + { + IMFTransform_AddRef(&transform->IMFTransform_iface); + *out = &transform->IMFTransform_iface; + return S_OK; + } + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_transform_AddRef(IMFTransform *iface) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + ULONG refcount = InterlockedIncrement(&transform->refcount); + return refcount; +} + +static ULONG WINAPI test_transform_Release(IMFTransform *iface) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + ULONG refcount = InterlockedDecrement(&transform->refcount); + + if (!refcount) + { + if (transform->input_type) + IMFMediaType_Release(transform->input_type); + if (transform->output_type) + IMFMediaType_Release(transform->output_type); + free(transform); + } + + return refcount; +} + +static HRESULT WINAPI test_transform_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, + DWORD *input_maximum, DWORD *output_minimum, DWORD *output_maximum) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) +{ + *inputs = *outputs = 1; + return S_OK; +} + +static HRESULT WINAPI test_transform_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, + DWORD output_size, DWORD *outputs) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) +{ + ok(0, "Unexpected %s call.\n", __func__); + return E_NOTIMPL; +} + +static void test_transform_set_output_stream_info(IMFTransform *iface, const MFT_OUTPUT_STREAM_INFO *info) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + transform->output_stream_info = info; +} + +static HRESULT WINAPI test_transform_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + + ok(!!transform->output_stream_info, "Unexpected %s iface %p call.\n", __func__, iface); + if (!transform->output_stream_info) + return E_NOTIMPL; + + *info = *transform->output_stream_info; + return S_OK; +} + +static HRESULT WINAPI test_transform_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetInputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_DeleteInputStream(IMFTransform *iface, DWORD id) +{ + ok(0, "Unexpected %s call.\n", __func__); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +{ + ok(0, "Unexpected %s call.\n", __func__); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + + if (index >= transform->input_count) + { + *type = NULL; + return MF_E_NO_MORE_TYPES; + } + + *type = transform->input_types[index]; + IMFMediaType_AddRef(*type); + return S_OK; +} + +static HRESULT WINAPI test_transform_GetOutputAvailableType(IMFTransform *iface, DWORD id, + DWORD index, IMFMediaType **type) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + + if (index >= transform->output_count) + { + *type = NULL; + return MF_E_NO_MORE_TYPES; + } + + *type = transform->output_types[index]; + IMFMediaType_AddRef(*type); + return S_OK; +} + +static HRESULT WINAPI test_transform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + if (transform->input_type) + IMFMediaType_Release(transform->input_type); + if ((transform->input_type = type)) + IMFMediaType_AddRef(transform->input_type); + return S_OK; +} + +static HRESULT WINAPI test_transform_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + if (transform->output_type) + IMFMediaType_Release(transform->output_type); + if ((transform->output_type = type)) + IMFMediaType_AddRef(transform->output_type); + return S_OK; +} + +static HRESULT WINAPI test_transform_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + if (!(*type = transform->input_type)) + return MF_E_TRANSFORM_TYPE_NOT_SET; + IMFMediaType_AddRef(*type); + return S_OK; +} + +static HRESULT WINAPI test_transform_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + if (!(*type = transform->output_type)) + return MF_E_TRANSFORM_TYPE_NOT_SET; + IMFMediaType_AddRef(*type); + return S_OK; +} + +static HRESULT WINAPI test_transform_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) +{ + ok(0, "Unexpected %s call.\n", __func__); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_GetOutputStatus(IMFTransform *iface, DWORD *flags) +{ + ok(0, "Unexpected %s call.\n", __func__); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) +{ + ok(0, "Unexpected %s call.\n", __func__); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) +{ + ok(0, "Unexpected %s call.\n", __func__); + return E_NOTIMPL; +} + +DEFINE_EXPECT(test_transform_ProcessMessage_BEGIN_STREAMING); +DEFINE_EXPECT(test_transform_ProcessMessage_START_OF_STREAM); +DEFINE_EXPECT(test_transform_ProcessMessage_FLUSH); + +static HRESULT WINAPI test_transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) +{ + switch (message) + { + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: + CHECK_EXPECT(test_transform_ProcessMessage_BEGIN_STREAMING); + add_object_state(&actual_object_state_record, MFT_BEGIN); + return S_OK; + + case MFT_MESSAGE_NOTIFY_START_OF_STREAM: + todo_wine_if(!expect_test_transform_ProcessMessage_START_OF_STREAM) + CHECK_EXPECT(test_transform_ProcessMessage_START_OF_STREAM); + add_object_state(&actual_object_state_record, MFT_START); + return S_OK; + + case MFT_MESSAGE_COMMAND_FLUSH: + CHECK_EXPECT(test_transform_ProcessMessage_FLUSH); + add_object_state(&actual_object_state_record, MFT_FLUSH); + return S_OK; + + default: + ok(0, "Unexpected %s call %#x.\n", __func__, message); + return E_NOTIMPL; + } +} + +DEFINE_EXPECT(test_transform_ProcessInput); +DEFINE_EXPECT(test_transform_ProcessOutput); + +static HRESULT WINAPI test_transform_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + HRESULT hr; + + if (expect_test_transform_ProcessInput) + { + if (transform->output) + { + hr = MF_E_NOTACCEPTING; + } + else + { + IMFSample_AddRef(transform->output = sample); + hr = S_OK; + } + } + else + { + hr = E_NOTIMPL; + } + + CHECK_EXPECT(test_transform_ProcessInput); + add_object_state(&actual_object_state_record, MFT_PROCESS_INPUT); + + return hr; +} + +static HRESULT WINAPI test_transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, + MFT_OUTPUT_DATA_BUFFER *data, DWORD *status) +{ + struct test_transform *transform = test_transform_from_IMFTransform(iface); + HRESULT hr; + + if (expect_test_transform_ProcessOutput) + { + if (transform->output) + { + *status = 0; + data->pSample = transform->output; + transform->output = NULL; + hr = S_OK; + } + else + { + hr = MF_E_TRANSFORM_NEED_MORE_INPUT; + } + } + else + { + hr = E_NOTIMPL; + } + + CHECK_EXPECT2(test_transform_ProcessOutput); + add_object_state(&actual_object_state_record, MFT_PROCESS_OUTPUT); + + return hr; +} + +static const IMFTransformVtbl test_transform_vtbl = +{ + test_transform_QueryInterface, + test_transform_AddRef, + test_transform_Release, + test_transform_GetStreamLimits, + test_transform_GetStreamCount, + test_transform_GetStreamIDs, + test_transform_GetInputStreamInfo, + test_transform_GetOutputStreamInfo, + test_transform_GetAttributes, + test_transform_GetInputStreamAttributes, + test_transform_GetOutputStreamAttributes, + test_transform_DeleteInputStream, + test_transform_AddInputStreams, + test_transform_GetInputAvailableType, + test_transform_GetOutputAvailableType, + test_transform_SetInputType, + test_transform_SetOutputType, + test_transform_GetInputCurrentType, + test_transform_GetOutputCurrentType, + test_transform_GetInputStatus, + test_transform_GetOutputStatus, + test_transform_SetOutputBounds, + test_transform_ProcessEvent, + test_transform_ProcessMessage, + test_transform_ProcessInput, + test_transform_ProcessOutput, +}; + +static HRESULT WINAPI test_transform_create(UINT input_count, IMFMediaType **input_types, + UINT output_count, IMFMediaType **output_types, BOOL d3d_aware, IMFTransform **out) +{ + struct test_transform *transform; + + if (!(transform = calloc(1, sizeof(*transform)))) + return E_OUTOFMEMORY; + transform->IMFTransform_iface.lpVtbl = &test_transform_vtbl; + transform->refcount = 1; + + transform->input_count = input_count; + transform->input_types = input_types; + transform->input_type = input_types[0]; + IMFMediaType_AddRef(transform->input_type); + transform->output_count = output_count; + transform->output_types = output_types; + transform->output_type = output_types[0]; + IMFMediaType_AddRef(transform->output_type); + + *out = &transform->IMFTransform_iface; + return S_OK; +} + +static void test_media_session_seek(void) +{ + static const struct object_state_record expected_start_state_records = {{SOURCE_START, MFT_START, SINK_ON_CLOCK_START}, 3}; + static const struct object_state_record expected_sample_request_and_delivery_records = {{MFT_PROCESS_OUTPUT, SOURCE_REQUEST_SAMPLE, MFT_PROCESS_INPUT, MFT_PROCESS_OUTPUT, SINK_PROCESS_SAMPLE}, 5}; + static const struct object_state_record expected_sample_request_only_records = {{MFT_PROCESS_OUTPUT, SOURCE_REQUEST_SAMPLE}, 2}; + static const struct object_state_record expected_paused_state_records = {{SINK_ON_CLOCK_PAUSE, SOURCE_PAUSE}, 2}; + static const struct object_state_record expected_seek_start_no_pending_request_records = {{SOURCE_STOP, MFT_FLUSH, SOURCE_START, SINK_FLUSH, SINK_ON_CLOCK_START}, 5}; + static const struct object_state_record expected_seek_start_pending_request_records = {{SOURCE_STOP, MFT_FLUSH, SOURCE_START, MFT_PROCESS_OUTPUT, SOURCE_REQUEST_SAMPLE, SINK_FLUSH, SINK_ON_CLOCK_START}, 7}; + + IMFClockStateSink test_seek_clock_sink = {&test_seek_clock_sink_vtbl}; + struct test_stream_sink stream_sink = test_stream_sink; + struct test_media_sink media_sink = test_media_sink; + MFT_OUTPUT_STREAM_INFO output_stream_info = {0}; + struct test_handler handler = test_handler; + IMFPresentationClock *presentation_clock; + struct test_seek_source *media_source; + struct test_callback *test_callback; + IMFAsyncCallback *callback; + IMFMediaSession *session; + IMFMediaSource *source; + IMFTopology *topology; + PROPVARIANT propvar; + IMFMediaType *type; + IMFTransform *mft; + IMFClock *clock; + UINT32 status; + HRESULT hr; + INT i; + + stream_sink.handler = &handler.IMFMediaTypeHandler_iface; + stream_sink.media_sink = &media_sink.IMFMediaSink_iface; + media_sink.stream = &stream_sink.IMFStreamSink_iface; + MFCreateEventQueue(&stream_sink.event_queue); + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + source = create_test_seek_source(TRUE); + media_source = impl_test_seek_source_from_IMFMediaSource(source); + for (i = 0; i < media_source->stream_count; i++) + media_source->streams[i]->test_expect = TRUE; + + hr = MFCreateMediaType(&type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, &MFVideoFormat_RGB32); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT64(type, &MF_MT_FRAME_SIZE, (UINT64)640 << 32 | 480); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + mft = NULL; + hr = test_transform_create(1, &type, 1, &type, FALSE, &mft); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + test_transform_set_output_stream_info(mft, &output_stream_info); + IMFMediaType_Release(type); + + SET_EXPECT(test_transform_ProcessMessage_BEGIN_STREAMING); + topology = create_test_topology_unk(source, (IUnknown*)&stream_sink.IMFStreamSink_iface, (IUnknown*) mft, NULL); + 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); + + callback = create_test_callback(TRUE); + test_callback = impl_from_IMFAsyncCallback(callback); + PropVariantInit(&propvar); + hr = wait_media_event(session, callback, MESessionTopologyStatus, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEvent_GetUINT32(test_callback->media_event, &MF_EVENT_TOPOLOGY_STATUS, &status); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(status == MF_TOPOSTATUS_READY, "Unexpected status %d.\n", status); + PropVariantClear(&propvar); + CHECK_CALLED(test_transform_ProcessMessage_BEGIN_STREAMING); + + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + SET_EXPECT(test_media_sink_GetPresentationClock); + SET_EXPECT(test_media_sink_SetPresentationClock); + SET_EXPECT(test_media_sink_GetStreamSinkCount); + SET_EXPECT(test_transform_ProcessMessage_START_OF_STREAM); + hr = IMFMediaSession_Start(session, NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = wait_media_event(session, callback, MESessionTopologyStatus, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEvent_GetUINT32(test_callback->media_event, &MF_EVENT_TOPOLOGY_STATUS, &status); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(status == MF_TOPOSTATUS_STARTED_SOURCE, "Unexpected status %d.\n", status); + PropVariantClear(&propvar); + + hr = IMFStreamSink_QueueEvent(&stream_sink.IMFStreamSink_iface, MEStreamSinkStarted, &GUID_NULL, S_OK, &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); + + Sleep(20); + + todo_wine + CHECK_CALLED(test_media_sink_GetPresentationClock); + CHECK_CALLED(test_media_sink_SetPresentationClock); + todo_wine + CHECK_CALLED(test_media_sink_GetStreamSinkCount); + CHECK_CALLED(test_transform_ProcessMessage_START_OF_STREAM); + + compare_object_states(&actual_object_state_record, &expected_start_state_records); + + /* Test a sample request with sample delivery, then pause and then start with a seek */ + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + SET_EXPECT(test_media_stream_RequestSample); + SET_EXPECT(test_transform_ProcessOutput); + SET_EXPECT(test_transform_ProcessInput); + SET_EXPECT(test_stream_sink_ProcessSample); + IMFStreamSink_QueueEvent(&stream_sink.IMFStreamSink_iface, MEStreamSinkRequestSample, &GUID_NULL, S_OK, &propvar); + + Sleep(20); + + CHECK_CALLED(test_media_stream_RequestSample); + CHECK_CALLED(test_transform_ProcessOutput); + CHECK_CALLED(test_transform_ProcessInput); + CHECK_CALLED(test_stream_sink_ProcessSample); + + compare_object_states(&actual_object_state_record, &expected_sample_request_and_delivery_records); + + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + hr = IMFMediaSession_Pause(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + Sleep(20); + + hr = IMFStreamSink_QueueEvent(&stream_sink.IMFStreamSink_iface, MEStreamSinkPaused, &GUID_NULL, S_OK, &propvar); + 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); + + compare_object_states(&actual_object_state_record, &expected_paused_state_records); + + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + SET_EXPECT(test_media_sink_GetPresentationClock); + SET_EXPECT(test_media_sink_GetStreamSinkCount); + SET_EXPECT(test_stream_sink_Flush); + SET_EXPECT(test_transform_ProcessMessage_FLUSH); + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + + Sleep(20); + + hr = IMFStreamSink_QueueEvent(&stream_sink.IMFStreamSink_iface, MEStreamSinkStarted, &GUID_NULL, S_OK, &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); + + todo_wine + CHECK_CALLED(test_media_sink_GetPresentationClock); + todo_wine + CHECK_CALLED(test_media_sink_GetStreamSinkCount); + todo_wine + CHECK_CALLED(test_stream_sink_Flush); + todo_wine + CHECK_CALLED(test_transform_ProcessMessage_FLUSH); + + todo_wine + compare_object_states(&actual_object_state_record, &expected_seek_start_no_pending_request_records); + + /* Test a sample request only (i.e. with no sample delivery), then pause and then start with a seek */ + for (i = 0; i < media_source->stream_count; i++) + media_source->streams[i]->delay_sample = TRUE; + + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + SET_EXPECT(test_media_stream_RequestSample); + SET_EXPECT(test_transform_ProcessOutput); + IMFStreamSink_QueueEvent(&stream_sink.IMFStreamSink_iface, MEStreamSinkRequestSample, &GUID_NULL, S_OK, &propvar); + + Sleep(20); + + CHECK_CALLED(test_media_stream_RequestSample); + CHECK_CALLED(test_transform_ProcessOutput); + + compare_object_states(&actual_object_state_record, &expected_sample_request_only_records); + + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + hr = IMFMediaSession_Pause(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + Sleep(20); + + hr = IMFStreamSink_QueueEvent(&stream_sink.IMFStreamSink_iface, MEStreamSinkPaused, &GUID_NULL, S_OK, &propvar); + 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); + + compare_object_states(&actual_object_state_record, &expected_paused_state_records); + + memset(&actual_object_state_record, 0, sizeof(actual_object_state_record)); + SET_EXPECT(test_media_sink_GetPresentationClock); + SET_EXPECT(test_media_sink_GetStreamSinkCount); + SET_EXPECT(test_stream_sink_Flush); + SET_EXPECT(test_transform_ProcessMessage_FLUSH); + SET_EXPECT(test_transform_ProcessOutput); + SET_EXPECT(test_media_stream_RequestSample); + propvar.vt = VT_I8; + propvar.hVal.QuadPart = 10000000; + hr = IMFMediaSession_Start(session, NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&propvar); + + Sleep(20); + + hr = IMFStreamSink_QueueEvent(&stream_sink.IMFStreamSink_iface, MEStreamSinkStarted, &GUID_NULL, S_OK, &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); + + todo_wine + CHECK_CALLED(test_media_sink_GetPresentationClock); + todo_wine + CHECK_CALLED(test_media_sink_GetStreamSinkCount); + todo_wine + CHECK_CALLED(test_stream_sink_Flush); + todo_wine + CHECK_CALLED(test_transform_ProcessMessage_FLUSH); + todo_wine + CHECK_CALLED(test_transform_ProcessOutput); + todo_wine + CHECK_CALLED(test_media_stream_RequestSample); + + todo_wine + compare_object_states(&actual_object_state_record, &expected_seek_start_pending_request_records); + + IMFPresentationClock_RemoveClockStateSink(presentation_clock, &test_seek_clock_sink); + IMFPresentationClock_Release(presentation_clock); + IMFAsyncCallback_Release(callback); + IMFTransform_Release(mft); + + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFMediaSession_Release(session); + + IMFMediaEventQueue_Release(stream_sink.event_queue); + if (media_sink.clock) IMFPresentationClock_Release(media_sink.clock); + + hr = MFShutdown(); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} + START_TEST(mf) { init_functions(); @@ -6977,4 +7758,5 @@ START_TEST(mf) test_MFEnumDeviceSources(); test_media_session_Close(); test_media_session_source_shutdown(); + test_media_session_seek(); }