diff --git a/dlls/wmvcore/tests/Makefile.in b/dlls/wmvcore/tests/Makefile.in index 8054ddd0a2e..856e02d45d5 100644 --- a/dlls/wmvcore/tests/Makefile.in +++ b/dlls/wmvcore/tests/Makefile.in @@ -3,3 +3,6 @@ IMPORTS = ole32 wmvcore C_SRCS = \ wmvcore.c + +RC_SRCS = \ + rsrc.rc diff --git a/dlls/wmvcore/tests/rsrc.rc b/dlls/wmvcore/tests/rsrc.rc new file mode 100644 index 00000000000..cd642ceb1d0 --- /dev/null +++ b/dlls/wmvcore/tests/rsrc.rc @@ -0,0 +1,25 @@ +/* + * Resource file for wmvcore tests + * + * Copyright 2021 Zebediah Figura for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "windef.h" + +/* ffmpeg -f lavfi -i smptebars -f lavfi -i "sine=frequency=700" -t 2.0 -f asf -vcodec wmv1 -vf scale=64x48 -acodec wmav1 test.wmv */ +/* @makedep: test.wmv */ +test.wmv RCDATA "test.wmv" diff --git a/dlls/wmvcore/tests/test.wmv b/dlls/wmvcore/tests/test.wmv new file mode 100644 index 00000000000..fa953771cd3 Binary files /dev/null and b/dlls/wmvcore/tests/test.wmv differ diff --git a/dlls/wmvcore/tests/wmvcore.c b/dlls/wmvcore/tests/wmvcore.c index 79bcfef0d52..dd1e3027d57 100644 --- a/dlls/wmvcore/tests/wmvcore.c +++ b/dlls/wmvcore/tests/wmvcore.c @@ -1,6 +1,7 @@ /* * Copyright 2017 Alistair Leslie-Hughes * Copyright 2019 Vijay Kiran Kamuju + * Copyright 2021 Zebediah Figura for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -16,15 +17,43 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ + +#include #define COBJMACROS #define WIN32_LEAN_AND_MEAN #include "initguid.h" #include "wmsdk.h" +#include "wmsecure.h" #include "wine/test.h" HRESULT WINAPI WMCreateWriterPriv(IWMWriter **writer); +static WCHAR *load_resource(const WCHAR *name) +{ + static WCHAR pathW[MAX_PATH]; + DWORD written; + HANDLE file; + HRSRC res; + void *ptr; + + GetTempPathW(ARRAY_SIZE(pathW), pathW); + wcscat(pathW, name); + + file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok(file != INVALID_HANDLE_VALUE, "Failed to create file %s, error %u.\n", + wine_dbgstr_w(pathW), GetLastError()); + + res = FindResourceW(NULL, name, (LPCWSTR)RT_RCDATA); + ok(!!res, "Failed to load resource, error %u.\n", GetLastError()); + ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res)); + WriteFile(file, ptr, SizeofResource( GetModuleHandleA(NULL), res), &written, NULL); + ok(written == SizeofResource(GetModuleHandleA(NULL), res), "Failed to write resource.\n"); + CloseHandle(file); + + return pathW; +} + #define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) static HRESULT check_interface_(unsigned int line, void *iface, REFIID riid, BOOL supported) { @@ -225,6 +254,368 @@ static void test_iscontentprotected(void) ok(drm == FALSE, "got %0dx\n", drm); } +struct teststream +{ + IStream IStream_iface; + LONG refcount; + HANDLE file; +}; + +static struct teststream *impl_from_IStream(IStream *iface) +{ + return CONTAINING_RECORD(iface, struct teststream, IStream_iface); +} + +static HRESULT WINAPI stream_QueryInterface(IStream *iface, REFIID iid, void **out) +{ + if (winetest_debug > 1) + trace("%04x: IStream::QueryInterface(%s)\n", GetCurrentThreadId(), debugstr_guid(iid)); + + if (!IsEqualGUID(iid, &IID_IWMGetSecureChannel) && !IsEqualGUID(iid, &IID_IWMIStreamProps)) + ok(0, "Unexpected IID %s.\n", debugstr_guid(iid)); + + return E_NOINTERFACE; +} + +static ULONG WINAPI stream_AddRef(IStream *iface) +{ + struct teststream *stream = impl_from_IStream(iface); + + return InterlockedIncrement(&stream->refcount); +} + +static ULONG WINAPI stream_Release(IStream *iface) +{ + struct teststream *stream = impl_from_IStream(iface); + + return InterlockedDecrement(&stream->refcount); +} + +static HRESULT WINAPI stream_Read(IStream *iface, void *data, ULONG size, ULONG *ret_size) +{ + struct teststream *stream = impl_from_IStream(iface); + + if (winetest_debug > 2) + trace("%04x: IStream::Read(size %u)\n", GetCurrentThreadId(), size); + + ok(size > 0, "Got zero size.\n"); + ok(!!ret_size, "Got NULL ret_size pointer.\n"); + if (!ReadFile(stream->file, data, size, ret_size, NULL)) + return HRESULT_FROM_WIN32(GetLastError()); + return S_OK; +} + +static HRESULT WINAPI stream_Write(IStream *iface, const void *data, ULONG size, ULONG *ret_size) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_Seek(IStream *iface, LARGE_INTEGER offset, DWORD method, ULARGE_INTEGER *ret_offset) +{ + struct teststream *stream = impl_from_IStream(iface); + LARGE_INTEGER size; + + if (winetest_debug > 2) + trace("%04x: IStream::Seek(offset %I64u, method %#x)\n", GetCurrentThreadId(), offset.QuadPart, method); + + GetFileSizeEx(stream->file, &size); + ok(offset.QuadPart < size.QuadPart, "Expected offset less than size %I64u, got %I64u.\n", + size.QuadPart, offset.QuadPart); + + ok(method == STREAM_SEEK_SET, "Got method %#x.\n", method); + ok(!ret_offset, "Got unexpected ret_offset pointer %p\n", ret_offset); + + if (!SetFilePointerEx(stream->file, offset, &offset, method)) + return HRESULT_FROM_WIN32(GetLastError()); + return S_OK; +} + +static HRESULT WINAPI stream_SetSize(IStream *iface, ULARGE_INTEGER size) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_CopyTo(IStream *iface, IStream *dest, ULARGE_INTEGER size, + ULARGE_INTEGER *read_size, ULARGE_INTEGER *write_size) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_Commit(IStream *iface, DWORD flags) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_Revert(IStream *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_LockRegion(IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_UnlockRegion(IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_Stat(IStream *iface, STATSTG *stat, DWORD flags) +{ + struct teststream *stream = impl_from_IStream(iface); + LARGE_INTEGER size; + + if (winetest_debug > 1) + trace("%04x: IStream::Stat(flags %#x)\n", GetCurrentThreadId(), flags); + + ok(flags == STATFLAG_NONAME, "Got flags %#x.\n", flags); + + stat->type = 0xdeadbeef; + GetFileSizeEx(stream->file, &size); + stat->cbSize.QuadPart = size.QuadPart; + stat->grfMode = 0; + stat->grfLocksSupported = TRUE; + + return S_OK; +} + +static HRESULT WINAPI stream_Clone(IStream *iface, IStream **out) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static const IStreamVtbl stream_vtbl = +{ + stream_QueryInterface, + stream_AddRef, + stream_Release, + stream_Read, + stream_Write, + stream_Seek, + stream_SetSize, + stream_CopyTo, + stream_Commit, + stream_Revert, + stream_LockRegion, + stream_UnlockRegion, + stream_Stat, + stream_Clone, +}; + +static void test_sync_reader_streaming(void) +{ + DWORD size, flags, output_number, expect_output_number; + const WCHAR *filename = load_resource(L"test.wmv"); + WORD stream_numbers[2], stream_number; + IWMStreamConfig *config, *config2; + bool eos[2] = {0}, first = true; + struct teststream stream = {0}; + ULONG i, j, count, ref; + IWMSyncReader *reader; + IWMProfile *profile; + QWORD pts, duration; + INSSBuffer *sample; + HANDLE file; + HRESULT hr; + BYTE *data; + BOOL ret; + + file = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0); + ok(file != INVALID_HANDLE_VALUE, "Failed to open %s, error %u.\n", debugstr_w(file), GetLastError()); + + stream.IStream_iface.lpVtbl = &stream_vtbl; + stream.refcount = 1; + stream.file = file; + + hr = WMCreateSyncReader(NULL, 0, &reader); + ok(hr == S_OK, "Got hr %#x.\n", hr); + IWMSyncReader_QueryInterface(reader, &IID_IWMProfile, (void **)&profile); + + hr = IWMSyncReader_OpenStream(reader, &stream.IStream_iface); + ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine ok(stream.refcount > 1, "Got refcount %d.\n", stream.refcount); + + hr = IWMProfile_GetStreamCount(profile, NULL); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + + count = 0xdeadbeef; + hr = IWMProfile_GetStreamCount(profile, &count); + ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine ok(count == 2, "Got count %u.\n", count); + + count = 0xdeadbeef; + hr = IWMSyncReader_GetOutputCount(reader, &count); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine ok(count == 2, "Got count %u.\n", count); + + for (i = 0; i < 2; ++i) + { + hr = IWMProfile_GetStream(profile, i, &config); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + if (hr == S_OK) + { + hr = IWMProfile_GetStream(profile, i, &config2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(config2 != config, "Expected different objects.\n"); + ref = IWMStreamConfig_Release(config2); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + stream_numbers[i] = 0xdead; + hr = IWMStreamConfig_GetStreamNumber(config, &stream_numbers[i]); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(stream_numbers[i] == i + 1, "Got stream number %u.\n", stream_numbers[i]); + + ref = IWMStreamConfig_Release(config); + ok(!ref, "Got outstanding refcount %d.\n", ref); + } + + hr = IWMSyncReader_SetReadStreamSamples(reader, stream_numbers[i], FALSE); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + } + + hr = IWMProfile_GetStream(profile, 2, &config); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + + while (!eos[0] || !eos[1]) + { + for (j = 0; j < 2; ++j) + { + stream_number = pts = duration = flags = output_number = 0xdeadbeef; + hr = IWMSyncReader_GetNextSample(reader, stream_numbers[j], &sample, + &pts, &duration, &flags, &output_number, &stream_number); + if (first) + todo_wine ok(hr == S_OK, "Expected at least one valid sample; got hr %#x.\n", hr); + else if (eos[j]) + ok(hr == NS_E_NO_MORE_SAMPLES, "Got hr %#x.\n", hr); + else + ok(hr == S_OK || hr == NS_E_NO_MORE_SAMPLES, "Got hr %#x.\n", hr); + + if (hr == S_OK) + { + hr = INSSBuffer_GetBufferAndLength(sample, &data, &size); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + ref = INSSBuffer_Release(sample); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + hr = IWMSyncReader_GetOutputNumberForStream(reader, stream_number, &expect_output_number); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(output_number == expect_output_number, "Expected output number %u, got %u.\n", + expect_output_number, output_number); + } + else + { + ok(pts == 0xdeadbeef, "Got PTS %I64u.\n", pts); + ok(duration == 0xdeadbeef, "Got duration %I64u.\n", duration); + ok(flags == 0xdeadbeef, "Got flags %#x.\n", flags); + ok(output_number == 0xdeadbeef, "Got output number %u.\n", output_number); + eos[j] = true; + } + + todo_wine ok(stream_number == stream_numbers[j], "Expected stream number %u, got %u.\n", + stream_numbers[j], stream_number); + } + first = false; + } + + hr = IWMSyncReader_GetNextSample(reader, stream_numbers[0], &sample, + &pts, &duration, &flags, NULL, NULL); + todo_wine ok(hr == NS_E_NO_MORE_SAMPLES, "Got hr %#x.\n", hr); + + hr = IWMSyncReader_GetNextSample(reader, stream_numbers[1], &sample, + &pts, &duration, &flags, NULL, NULL); + todo_wine ok(hr == NS_E_NO_MORE_SAMPLES, "Got hr %#x.\n", hr); + + hr = IWMSyncReader_SetRange(reader, 0, 0); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IWMSyncReader_GetNextSample(reader, stream_numbers[0], &sample, &pts, &duration, &flags, NULL, NULL); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + if (hr == S_OK) + INSSBuffer_Release(sample); + + hr = IWMSyncReader_GetNextSample(reader, 0, &sample, &pts, &duration, &flags, NULL, NULL); + todo_wine ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + + hr = IWMSyncReader_GetNextSample(reader, 0, &sample, &pts, &duration, &flags, &output_number, NULL); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + if (hr == S_OK) + INSSBuffer_Release(sample); + + hr = IWMSyncReader_GetNextSample(reader, 0, &sample, &pts, &duration, &flags, NULL, &stream_number); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + if (hr == S_OK) + INSSBuffer_Release(sample); + + for (;;) + { + stream_number = pts = duration = flags = output_number = 0xdeadbeef; + hr = IWMSyncReader_GetNextSample(reader, 0, &sample, + &pts, &duration, &flags, &output_number, &stream_number); + todo_wine ok(hr == S_OK || hr == NS_E_NO_MORE_SAMPLES, "Got hr %#x.\n", hr); + + if (hr == S_OK) + { + hr = INSSBuffer_GetBufferAndLength(sample, &data, &size); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ref = INSSBuffer_Release(sample); + ok(!ref, "Got outstanding refcount %d.\n", ref); + } + else + { + ok(pts == 0xdeadbeef, "Got PTS %I64u.\n", pts); + ok(duration == 0xdeadbeef, "Got duration %I64u.\n", duration); + ok(flags == 0xdeadbeef, "Got flags %#x.\n", flags); + ok(output_number == 0xdeadbeef, "Got output number %u.\n", output_number); + ok(stream_number == 0xbeef, "Got stream number %u.\n", stream_number); + break; + } + } + + hr = IWMSyncReader_GetNextSample(reader, 0, &sample, + &pts, &duration, &flags, NULL, &stream_number); + todo_wine ok(hr == NS_E_NO_MORE_SAMPLES, "Got hr %#x.\n", hr); + + hr = IWMSyncReader_GetNextSample(reader, stream_numbers[0], &sample, + &pts, &duration, &flags, NULL, NULL); + todo_wine ok(hr == NS_E_NO_MORE_SAMPLES, "Got hr %#x.\n", hr); + + hr = IWMSyncReader_GetNextSample(reader, stream_numbers[1], &sample, + &pts, &duration, &flags, NULL, NULL); + todo_wine ok(hr == NS_E_NO_MORE_SAMPLES, "Got hr %#x.\n", hr); + + hr = IWMSyncReader_Close(reader); + todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IWMSyncReader_Close(reader); + todo_wine ok(hr == NS_E_INVALID_REQUEST, "Got hr %#x.\n", hr); + + ok(stream.refcount == 1, "Got outstanding refcount %d.\n", stream.refcount); + + SetFilePointer(stream.file, 0, NULL, FILE_BEGIN); + hr = IWMSyncReader_OpenStream(reader, &stream.IStream_iface); + ok(hr == S_OK, "Got hr %#x.\n", hr); + todo_wine ok(stream.refcount > 1, "Got refcount %d.\n", stream.refcount); + + IWMProfile_Release(profile); + ref = IWMSyncReader_Release(reader); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + ok(stream.refcount == 1, "Got outstanding refcount %d.\n", stream.refcount); + CloseHandle(stream.file); + ret = DeleteFileW(filename); + ok(ret, "Failed to delete %s, error %u.\n", debugstr_w(filename), GetLastError()); +} + START_TEST(wmvcore) { HRESULT hr; @@ -241,6 +632,7 @@ START_TEST(wmvcore) test_WMCreateWriterPriv(); test_urlextension(); test_iscontentprotected(); + test_sync_reader_streaming(); CoUninitialize(); }