Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mfreadwrite/tests/mfplat.c | 511 ++++++++++++++++++++++++++++++++ 1 file changed, 511 insertions(+)
diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c index b86fb060fd..ba9023b289 100644 --- a/dlls/mfreadwrite/tests/mfplat.c +++ b/dlls/mfreadwrite/tests/mfplat.c @@ -52,6 +52,412 @@ static void init_functions(void) #undef X }
+enum source_state +{ + SOURCE_STOPPED = 0, + SOURCE_RUNNING, +}; + +struct test_media_stream +{ + IMFMediaStream IMFMediaStream_iface; + LONG refcount; + IMFMediaSource *source; + IMFStreamDescriptor *sd; + IMFMediaEventQueue *event_queue; + BOOL is_new; +}; + +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); + heap_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); + ok(callback != NULL && state == (IUnknown *)iface, "Unexpected arguments.\n"); + 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); + ok(!!result && !!event, "Unexpected arguments.\n"); + 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, "Failed to create a sample, hr %#x.\n", hr); + hr = IMFSample_SetSampleTime(sample, 123); + ok(hr == S_OK, "Failed to set sample time, hr %#x.\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, "Failed to create a buffer, hr %#x.\n", hr); + hr = IMFSample_AddBuffer(sample, buffer); + ok(hr == S_OK, "Failed to add a buffer, hr %#x.\n", hr); + IMFMediaBuffer_Release(buffer); + + hr = IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, + (IUnknown *)sample); + ok(hr == S_OK, "Failed to submit event, hr %#x.\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_source +{ + IMFMediaSource IMFMediaSource_iface; + LONG refcount; + IMFMediaEventQueue *event_queue; + IMFPresentationDescriptor *pd; + struct test_media_stream *streams[TEST_SOURCE_NUM_STREAMS]; + enum source_state state; + CRITICAL_SECTION cs; +}; + +static struct test_source *impl_from_IMFMediaSource(IMFMediaSource *iface) +{ + return CONTAINING_RECORD(iface, struct test_source, IMFMediaSource_iface); +} + +static HRESULT WINAPI test_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_source_AddRef(IMFMediaSource *iface) +{ + struct test_source *source = impl_from_IMFMediaSource(iface); + return InterlockedIncrement(&source->refcount); +} + +static ULONG WINAPI test_source_Release(IMFMediaSource *iface) +{ + struct test_source *source = impl_from_IMFMediaSource(iface); + ULONG refcount = InterlockedDecrement(&source->refcount); + + if (!refcount) + { + IMFMediaEventQueue_Release(source->event_queue); + heap_free(source); + } + + return refcount; +} + +static HRESULT WINAPI test_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) +{ + struct test_source *source = impl_from_IMFMediaSource(iface); + return IMFMediaEventQueue_GetEvent(source->event_queue, flags, event); +} + +static HRESULT WINAPI test_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct test_source *source = impl_from_IMFMediaSource(iface); + ok(callback != NULL && state == (IUnknown *)iface, "Unexpected arguments source %p, %p, state %p.\n", iface, callback, state); + return IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state); +} + +static HRESULT WINAPI test_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct test_source *source = impl_from_IMFMediaSource(iface); + return IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event); +} + +static HRESULT WINAPI test_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct test_source *source = impl_from_IMFMediaSource(iface); + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI test_source_GetCharacteristics(IMFMediaSource *iface, DWORD *flags) +{ + *flags = MFMEDIASOURCE_CAN_SEEK; + + return S_OK; +} + +static HRESULT WINAPI test_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **pd) +{ + struct test_source *source = impl_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 < ARRAY_SIZE(source->streams); ++i) + { + MFCreateMediaType(&media_type); + + IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM); + + MFCreateStreamDescriptor(i, 1, &media_type, &sds[i]); + + IMFMediaType_Release(media_type); + } + + MFCreatePresentationDescriptor(ARRAY_SIZE(sds), sds, &source->pd); + for (i = 0; i < ARRAY_SIZE(sds); ++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_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *pd, const GUID *time_format, + const PROPVARIANT *start_position) +{ + struct test_source *source = impl_from_IMFMediaSource(iface); + MediaEventType event_type; + PROPVARIANT var; + int i; + +todo_wine { + 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"); +} + EnterCriticalSection(&source->cs); + + event_type = source->state == SOURCE_RUNNING ? MESourceSeeked : MESourceStarted; + IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, NULL); + + for (i = 0; i < ARRAY_SIZE(source->streams); ++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; + IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, &GUID_NULL, S_OK, &var); + + event_type = source->state == SOURCE_RUNNING ? MEStreamSeeked : MEStreamStarted; + IMFMediaEventQueue_QueueEventParamVar(source->streams[i]->event_queue, event_type, &GUID_NULL, + S_OK, NULL); + } + + source->state = SOURCE_RUNNING; + + LeaveCriticalSection(&source->cs); + + return S_OK; +} + +static HRESULT WINAPI test_source_Stop(IMFMediaSource *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_source_Pause(IMFMediaSource *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_source_Shutdown(IMFMediaSource *iface) +{ + struct test_source *source = impl_from_IMFMediaSource(iface); + + IMFMediaEventQueue_Shutdown(source->event_queue); + + return S_OK; +} + +static const IMFMediaSourceVtbl test_source_vtbl = +{ + test_source_QueryInterface, + test_source_AddRef, + test_source_Release, + test_source_GetEvent, + test_source_BeginGetEvent, + test_source_EndGetEvent, + test_source_QueueEvent, + test_source_GetCharacteristics, + test_source_CreatePresentationDescriptor, + test_source_Start, + test_source_Stop, + test_source_Pause, + test_source_Shutdown, +}; + +static struct test_media_stream *create_test_stream(DWORD stream_index, IMFMediaSource *source) +{ + struct test_media_stream *stream; + IMFPresentationDescriptor *pd; + BOOL selected; + + stream = heap_alloc_zero(sizeof(*stream)); + stream->IMFMediaStream_iface.lpVtbl = &test_media_stream_vtbl; + stream->refcount = 1; + MFCreateEventQueue(&stream->event_queue); + 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_source(void) +{ + struct test_source *source; + int i; + + source = heap_alloc_zero(sizeof(*source)); + source->IMFMediaSource_iface.lpVtbl = &test_source_vtbl; + source->refcount = 1; + MFCreateEventQueue(&source->event_queue); + InitializeCriticalSection(&source->cs); + for (i = 0; i < ARRAY_SIZE(source->streams); ++i) + source->streams[i] = create_test_stream(i, &source->IMFMediaSource_iface); + + return &source->IMFMediaSource_iface; +} + static IMFByteStream *get_resource_stream(const char *name) { IMFByteStream *bytestream; @@ -402,6 +808,110 @@ todo_wine IMFByteStream_Release(stream); }
+static void test_source_reader_from_media_source(void) +{ + struct async_callback *callback; + IMFSourceReader *reader; + IMFMediaSource *source; + HRESULT hr; + DWORD actual_index, stream_flags; + IMFSample *sample; + LONGLONG timestamp; + IMFAttributes *attributes; + int i; + + source = create_test_source(); + ok(!!source, "Failed to create test source.\n"); + + callback = create_async_callback(); + + hr = MFCreateAttributes(&attributes, 1); + ok(hr == S_OK, "Failed to create attributes object, hr %#x.\n", hr); + + hr = IMFAttributes_SetUnknown(attributes, &MF_SOURCE_READER_ASYNC_CALLBACK, + (IUnknown *)&callback->IMFSourceReaderCallback_iface); + ok(hr == S_OK, "Failed to set attribute value, hr %#x.\n", hr); + IMFSourceReaderCallback_Release(&callback->IMFSourceReaderCallback_iface); + + hr = MFCreateSourceReaderFromMediaSource(source, NULL, &reader); + ok(hr == S_OK, "Failed to create source reader, hr %#x.\n", hr); + + /* MF_SOURCE_READER_ANY_STREAM */ + hr = IMFSourceReader_SetStreamSelection(reader, 0, FALSE); + ok(hr == S_OK, "Failed to select a stream, hr %#x.\n", hr); + + hr = IMFSourceReader_SetStreamSelection(reader, 1, TRUE); + ok(hr == S_OK, "Failed to select a stream, hr %#x.\n", hr); + + hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_ANY_STREAM, 0, &actual_index, &stream_flags, + ×tamp, &sample); +todo_wine { + ok(hr == S_OK, "Failed to get a sample, hr %#x.\n", hr); + if (SUCCEEDED(hr)) + { + ok(actual_index == 1, "Unexpected stream index %u\n", actual_index); + ok(!stream_flags, "Unexpected stream flags %#x.\n", stream_flags); + ok(timestamp == 123, "Unexpected timestamp.\n"); + ok(!!sample, "Expected sample object.\n"); + IMFSample_Release(sample); + } +} + hr = IMFSourceReader_SetStreamSelection(reader, 0, TRUE); + ok(hr == S_OK, "Failed to select a stream, hr %#x.\n", hr); + + hr = IMFSourceReader_SetStreamSelection(reader, 2, TRUE); + ok(hr == S_OK, "Failed to select a stream, hr %#x.\n", hr); + + for (i = 0; i < 2 * TEST_SOURCE_NUM_STREAMS; ++i) + { + hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_ANY_STREAM, 0, &actual_index, &stream_flags, + ×tamp, &sample); +todo_wine + ok(hr == S_OK, "Failed to get a sample, hr %#x.\n", hr); + if (SUCCEEDED(hr)) + { + ok(actual_index == (i < TEST_SOURCE_NUM_STREAMS ? i : 0), "%d: Unexpected stream index %u\n", + i, actual_index); + ok(!stream_flags, "Unexpected stream flags %#x.\n", stream_flags); + ok(timestamp == 123, "Unexpected timestamp.\n"); + ok(!!sample, "Expected sample object.\n"); + IMFSample_Release(sample); + } + } + + IMFSourceReader_Release(reader); + IMFMediaSource_Release(source); + + /* Async mode. */ + source = create_test_source(); + ok(!!source, "Failed to create test source.\n"); + + hr = MFCreateSourceReaderFromMediaSource(source, attributes, &reader); + ok(hr == S_OK, "Failed to create source reader, hr %#x.\n", hr); + + hr = IMFSourceReader_SetStreamSelection(reader, 0, TRUE); + ok(hr == S_OK, "Failed to select a stream, hr %#x.\n", hr); + + /* Return values are delivered to callback only. */ + hr = IMFSourceReader_ReadSample(reader, 0, 0, &actual_index, &stream_flags, ×tamp, &sample); +todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + hr = IMFSourceReader_ReadSample(reader, 0, 0, NULL, &stream_flags, ×tamp, &sample); +todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + hr = IMFSourceReader_ReadSample(reader, 0, 0, NULL, NULL, ×tamp, &sample); +todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + hr = IMFSourceReader_ReadSample(reader, 0, 0, NULL, NULL, NULL, &sample); +todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + IMFSourceReader_Release(reader); +} + START_TEST(mfplat) { HRESULT hr; @@ -413,6 +923,7 @@ START_TEST(mfplat)
test_factory(); test_source_reader(); + test_source_reader_from_media_source();
hr = MFShutdown(); ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr);