Needed by Underworld Island (Steam ID: 2150830) to skip videos properly.
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/mfmediaengine/tests/mfmediaengine.c | 395 +++++++++++++++++++++++ 1 file changed, 395 insertions(+)
diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index 7ddc1fa0679..36e109d5ad0 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -1970,6 +1970,400 @@ static void test_GetDuration(void) IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); }
+struct unseekable_stream +{ + IMFByteStream IMFByteStream_iface; + IMFByteStream *original_stream; + LONG refcount; +}; + +static struct unseekable_stream *impl_unseekable_stream_from_IMFByteStream(IMFByteStream *iface) +{ + return CONTAINING_RECORD(iface, struct unseekable_stream, IMFByteStream_iface); +} + +static HRESULT WINAPI unseekable_stream_QueryInterface(IMFByteStream *iface, + REFIID riid, void **out) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IMFByteStream)) + { + IMFByteStream_AddRef(iface); + *out = iface; + return S_OK; + } + + return IMFByteStream_QueryInterface(stream->original_stream, riid, out); +} + +static ULONG WINAPI unseekable_stream_AddRef(IMFByteStream *iface) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + return InterlockedIncrement(&stream->refcount); +} + +static ULONG WINAPI unseekable_stream_Release(IMFByteStream *iface) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + ULONG refcount = InterlockedDecrement(&stream->refcount); + + if (!refcount) + { + IMFByteStream_Release(stream->original_stream); + free(stream); + } + + return refcount; +} + +static HRESULT WINAPI unseekable_stream_GetCapabilities(IMFByteStream *iface, + DWORD *capabilities) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + HRESULT hr; + + hr = IMFByteStream_GetCapabilities(stream->original_stream, capabilities); + if (SUCCEEDED(hr)) + *capabilities &= ~(MFBYTESTREAM_IS_SEEKABLE | MFBYTESTREAM_HAS_SLOW_SEEK); + + return hr; +} + +static HRESULT WINAPI unseekable_stream_GetLength(IMFByteStream *iface, QWORD *length) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + return IMFByteStream_GetLength(stream->original_stream, length); +} + +static HRESULT WINAPI unseekable_stream_SetLength(IMFByteStream *iface, QWORD length) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + return IMFByteStream_SetLength(stream->original_stream, length); +} + +static HRESULT WINAPI unseekable_stream_GetCurrentPosition(IMFByteStream *iface, + QWORD *position) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + return IMFByteStream_GetCurrentPosition(stream->original_stream, position); +} + +static HRESULT WINAPI unseekable_stream_SetCurrentPosition(IMFByteStream *iface, + QWORD position) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + return IMFByteStream_SetCurrentPosition(stream->original_stream, position); +} + +static HRESULT WINAPI unseekable_stream_IsEndOfStream(IMFByteStream *iface, BOOL *eos) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + return IMFByteStream_IsEndOfStream(stream->original_stream, eos); +} + +static HRESULT WINAPI unseekable_stream_Read(IMFByteStream *iface, BYTE *data, + ULONG count, ULONG *byte_read) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + return IMFByteStream_Read(stream->original_stream, data, count, byte_read); +} + +static HRESULT WINAPI unseekable_stream_BeginRead(IMFByteStream *iface, BYTE *data, + ULONG size, IMFAsyncCallback *callback, IUnknown *state) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + return IMFByteStream_BeginRead(stream->original_stream, data, size, callback, state); +} + +static HRESULT WINAPI unseekable_stream_EndRead(IMFByteStream *iface, + IMFAsyncResult *result, ULONG *byte_read) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + return IMFByteStream_EndRead(stream->original_stream, result, byte_read); +} + +static HRESULT WINAPI unseekable_stream_Write(IMFByteStream *iface, const BYTE *data, + ULONG count, ULONG *written) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + return IMFByteStream_Write(stream->original_stream, data, count, written); +} + +static HRESULT WINAPI unseekable_stream_BeginWrite(IMFByteStream *iface, + const BYTE *data, ULONG size, IMFAsyncCallback *callback, IUnknown *state) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + return IMFByteStream_BeginWrite(stream->original_stream, data, size, callback, state); +} + +static HRESULT WINAPI unseekable_stream_EndWrite(IMFByteStream *iface, + IMFAsyncResult *result, ULONG *written) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + return IMFByteStream_EndWrite(stream->original_stream, result, written); +} + +static HRESULT WINAPI unseekable_stream_Seek(IMFByteStream *iface, + MFBYTESTREAM_SEEK_ORIGIN seek, LONGLONG offset, DWORD flags, QWORD *current) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + return IMFByteStream_Seek(stream->original_stream, seek, offset, flags, current); +} + +static HRESULT WINAPI unseekable_stream_Flush(IMFByteStream *iface) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + return IMFByteStream_Flush(stream->original_stream); +} + +static HRESULT WINAPI unseekable_stream_Close(IMFByteStream *iface) +{ + struct unseekable_stream *stream = impl_unseekable_stream_from_IMFByteStream(iface); + + return IMFByteStream_Close(stream->original_stream); +} + +static const IMFByteStreamVtbl unseekable_stream_vtbl = +{ + unseekable_stream_QueryInterface, + unseekable_stream_AddRef, + unseekable_stream_Release, + unseekable_stream_GetCapabilities, + unseekable_stream_GetLength, + unseekable_stream_SetLength, + unseekable_stream_GetCurrentPosition, + unseekable_stream_SetCurrentPosition, + unseekable_stream_IsEndOfStream, + unseekable_stream_Read, + unseekable_stream_BeginRead, + unseekable_stream_EndRead, + unseekable_stream_Write, + unseekable_stream_BeginWrite, + unseekable_stream_EndWrite, + unseekable_stream_Seek, + unseekable_stream_Flush, + unseekable_stream_Close, +}; + +static IMFByteStream *create_unseekable_stream(IMFByteStream *stream) +{ + struct unseekable_stream *object; + + object = calloc(1, sizeof(*object)); + object->IMFByteStream_iface.lpVtbl = &unseekable_stream_vtbl; + object->original_stream = stream; + IMFByteStream_AddRef(stream); + object->refcount = 1; + + return &object->IMFByteStream_iface; +} + +struct test_seek_notify +{ + IMFMediaEngineNotify IMFMediaEngineNotify_iface; + HANDLE playing_event; + HRESULT expected_error; + HRESULT error; + LONG refcount; +}; + +static struct test_seek_notify *impl_from_test_seek_notify(IMFMediaEngineNotify *iface) +{ + return CONTAINING_RECORD(iface, struct test_seek_notify, IMFMediaEngineNotify_iface); +} + +static HRESULT WINAPI test_seek_notify_QueryInterface(IMFMediaEngineNotify *iface, REFIID riid, + void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IMFMediaEngineNotify)) + { + *obj = iface; + IMFMediaEngineNotify_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_seek_notify_AddRef(IMFMediaEngineNotify *iface) +{ + struct test_seek_notify *notify = impl_from_test_seek_notify(iface); + + return InterlockedIncrement(¬ify->refcount); +} + +static ULONG WINAPI test_seek_notify_Release(IMFMediaEngineNotify *iface) +{ + struct test_seek_notify *notify = impl_from_test_seek_notify(iface); + ULONG refcount = InterlockedDecrement(¬ify->refcount); + + if (!refcount) + { + CloseHandle(notify->playing_event); + free(notify); + } + + return refcount; +} + +static HRESULT WINAPI test_seek_notify_EventNotify(IMFMediaEngineNotify *iface, DWORD event, + DWORD_PTR param1, DWORD param2) +{ + struct test_seek_notify *notify = impl_from_test_seek_notify(iface); + + switch (event) + { + case MF_MEDIA_ENGINE_EVENT_PLAYING: + SetEvent(notify->playing_event); + break; + case MF_MEDIA_ENGINE_EVENT_ERROR: + ok(param2 == notify->expected_error, "Unexpected error %#lx\n", param2); + notify->error = param2; + break; + } + + return S_OK; +} + +static IMFMediaEngineNotifyVtbl test_seek_notify_vtbl = +{ + test_seek_notify_QueryInterface, + test_seek_notify_AddRef, + test_seek_notify_Release, + test_seek_notify_EventNotify, +}; + +static struct test_seek_notify *create_seek_notify(void) +{ + struct test_seek_notify *object; + + object = calloc(1, sizeof(*object)); + object->IMFMediaEngineNotify_iface.lpVtbl = &test_seek_notify_vtbl; + object->playing_event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!object->playing_event, "Failed to create an event, error %lu.\n", GetLastError()); + object->refcount = 1; + return object; +} + +static void test_GetSeekable(void) +{ + IMFByteStream *stream, *unseekable_stream = NULL; + struct test_seek_notify *notify; + IMFMediaEngineEx *media_engine; + IMFMediaTimeRange *time_range; + double start, end, duration; + DWORD count; + HRESULT hr; + DWORD res; + BSTR url; + + notify = create_seek_notify(); + media_engine = create_media_engine_ex(¬ify->IMFMediaEngineNotify_iface, NULL, DXGI_FORMAT_B8G8R8X8_UNORM); + IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); + + stream = load_resource(L"i420-64x64.avi", L"video/avi"); + url = SysAllocString(L"i420-64x64.avi"); + hr = IMFMediaEngineEx_SetSourceFromByteStream(media_engine, stream, url); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Media engine is not ready */ + hr = IMFMediaEngineEx_GetSeekable(media_engine, &time_range); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + count = IMFMediaTimeRange_GetLength(time_range); + ok(!count, "Unexpected count %lu.\n", count); + IMFMediaTimeRange_Release(time_range); + } + + hr = IMFMediaEngineEx_Play(media_engine); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + res = WaitForSingleObject(notify->playing_event, 5000); + ok(!res, "Unexpected res %#lx.\n", res); + if (FAILED(notify->error)) + { + win_skip("Media engine reported error %#lx, skipping tests.\n", notify->error); + goto done; + } + + /* Media engine is ready */ + hr = IMFMediaEngineEx_GetSeekable(media_engine, &time_range); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + count = IMFMediaTimeRange_GetLength(time_range); + ok(count == 1, "Unexpected count %lu.\n", count); + hr = IMFMediaTimeRange_GetStart(time_range, 0, &start); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(start == 0, "Unexpected start %lf.\n", start); + hr = IMFMediaTimeRange_GetEnd(time_range, 0, &end); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + duration = IMFMediaEngineEx_GetDuration(media_engine); + ok(end == duration, "Unexpected end %lf.\n", end); + IMFMediaTimeRange_Release(time_range); + } + + /* Media engine is shut down */ + IMFMediaEngineEx_Shutdown(media_engine); + + hr = IMFMediaEngineEx_GetSeekable(media_engine, &time_range); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + + IMFMediaEngineEx_Release(media_engine); + + /* Unseekable bytestreams */ + notify = create_seek_notify(); + media_engine = create_media_engine_ex(¬ify->IMFMediaEngineNotify_iface, NULL, DXGI_FORMAT_B8G8R8X8_UNORM); + IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); + unseekable_stream = create_unseekable_stream(stream); + hr = IMFMediaEngineEx_SetSourceFromByteStream(media_engine, unseekable_stream, url); + todo_wine_if(hr == MF_E_BYTESTREAM_NOT_SEEKABLE) + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (FAILED(hr)) + goto done; + + hr = IMFMediaEngineEx_Play(media_engine); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + notify->expected_error = MF_E_INVALIDREQUEST; + res = WaitForSingleObject(notify->playing_event, 5000); + ok(res == S_OK, "Unexpected res %#lx.\n", res); + + hr = IMFMediaEngineEx_GetSeekable(media_engine, &time_range); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + count = IMFMediaTimeRange_GetLength(time_range); + ok(!count, "Unexpected count %lu.\n", count); + IMFMediaTimeRange_Release(time_range); + } + +done: + IMFMediaEngineEx_Shutdown(media_engine); + IMFMediaEngineEx_Release(media_engine); + if (unseekable_stream) IMFByteStream_Release(unseekable_stream); + SysFreeString(url); + IMFByteStream_Release(stream); +} + START_TEST(mfmediaengine) { HRESULT hr; @@ -2003,6 +2397,7 @@ START_TEST(mfmediaengine) test_TransferVideoFrame(); test_effect(); test_GetDuration(); + test_GetSeekable();
IMFMediaEngineClassFactory_Release(factory);
From: Zhiyi Zhang zzhang@codeweavers.com
Needed by Underworld Island (Steam ID: 2150830) to skip videos properly. --- dlls/mfmediaengine/main.c | 20 ++++++++++++-- dlls/mfmediaengine/tests/mfmediaengine.c | 34 +++++++++--------------- 2 files changed, 31 insertions(+), 23 deletions(-)
diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index 2ef1cb8413a..e69e70fccf6 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -1899,14 +1899,30 @@ static HRESULT WINAPI media_engine_GetPlayed(IMFMediaEngineEx *iface, IMFMediaTi static HRESULT WINAPI media_engine_GetSeekable(IMFMediaEngineEx *iface, IMFMediaTimeRange **seekable) { struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr = E_NOTIMPL; + DWORD flags; + HRESULT hr;
- FIXME("(%p, %p): stub.\n", iface, seekable); + TRACE("%p, %p.\n", iface, seekable);
EnterCriticalSection(&engine->cs);
if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) + { hr = MF_E_SHUTDOWN; + LeaveCriticalSection(&engine->cs); + return hr; + } + + hr = create_time_range(seekable); + if (SUCCEEDED(hr) && !isnan(engine->duration) && engine->presentation.source) + { + hr = IMFMediaSource_GetCharacteristics(engine->presentation.source, &flags); + if (SUCCEEDED(hr) && (flags & MFBYTESTREAM_IS_SEEKABLE)) + hr = IMFMediaTimeRange_AddRange(*seekable, 0.0, engine->duration); + + if (FAILED(hr)) + IMFMediaTimeRange_Release(*seekable); + }
LeaveCriticalSection(&engine->cs);
diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index 36e109d5ad0..e72d99db0de 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -2284,14 +2284,10 @@ static void test_GetSeekable(void)
/* Media engine is not ready */ hr = IMFMediaEngineEx_GetSeekable(media_engine, &time_range); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - count = IMFMediaTimeRange_GetLength(time_range); - ok(!count, "Unexpected count %lu.\n", count); - IMFMediaTimeRange_Release(time_range); - } + count = IMFMediaTimeRange_GetLength(time_range); + ok(!count, "Unexpected count %lu.\n", count); + IMFMediaTimeRange_Release(time_range);
hr = IMFMediaEngineEx_Play(media_engine); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2305,21 +2301,17 @@ static void test_GetSeekable(void)
/* Media engine is ready */ hr = IMFMediaEngineEx_GetSeekable(media_engine, &time_range); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - count = IMFMediaTimeRange_GetLength(time_range); - ok(count == 1, "Unexpected count %lu.\n", count); - hr = IMFMediaTimeRange_GetStart(time_range, 0, &start); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(start == 0, "Unexpected start %lf.\n", start); - hr = IMFMediaTimeRange_GetEnd(time_range, 0, &end); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - duration = IMFMediaEngineEx_GetDuration(media_engine); - ok(end == duration, "Unexpected end %lf.\n", end); - IMFMediaTimeRange_Release(time_range); - } + count = IMFMediaTimeRange_GetLength(time_range); + ok(count == 1, "Unexpected count %lu.\n", count); + hr = IMFMediaTimeRange_GetStart(time_range, 0, &start); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(start == 0, "Unexpected start %lf.\n", start); + hr = IMFMediaTimeRange_GetEnd(time_range, 0, &end); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + duration = IMFMediaEngineEx_GetDuration(media_engine); + ok(end == duration, "Unexpected end %lf.\n", end); + IMFMediaTimeRange_Release(time_range);
/* Media engine is shut down */ IMFMediaEngineEx_Shutdown(media_engine);