From: Charlotte Pabst cpabst@codeweavers.com
--- dlls/mfsrcsnk/tests/Makefile.in | 3 +- dlls/mfsrcsnk/tests/mfsrcsnk.c | 528 ++++++++++++++++++++++++++ dlls/mfsrcsnk/tests/resource.rc | 31 ++ dlls/mfsrcsnk/tests/test_thinning.avi | Bin 0 -> 7716 bytes 4 files changed, 561 insertions(+), 1 deletion(-) create mode 100644 dlls/mfsrcsnk/tests/resource.rc create mode 100644 dlls/mfsrcsnk/tests/test_thinning.avi
diff --git a/dlls/mfsrcsnk/tests/Makefile.in b/dlls/mfsrcsnk/tests/Makefile.in index 55a84256266..89889d6f723 100644 --- a/dlls/mfsrcsnk/tests/Makefile.in +++ b/dlls/mfsrcsnk/tests/Makefile.in @@ -2,4 +2,5 @@ TESTDLL = mfsrcsnk.dll IMPORTS = ole32 mfsrcsnk mfplat mf uuid mfuuid
SOURCES = \ - mfsrcsnk.c + mfsrcsnk.c \ + resource.rc diff --git a/dlls/mfsrcsnk/tests/mfsrcsnk.c b/dlls/mfsrcsnk/tests/mfsrcsnk.c index 1aba4094c62..e78fe8adbb8 100644 --- a/dlls/mfsrcsnk/tests/mfsrcsnk.c +++ b/dlls/mfsrcsnk/tests/mfsrcsnk.c @@ -26,9 +26,31 @@ #include "mfapi.h" #include "mfidl.h" #include "mferror.h" +#include "wine/mfinternal.h"
#include "wine/test.h"
+static const char *debugstr_time(LONGLONG time) +{ + ULONGLONG abstime = time >= 0 ? time : -time; + unsigned int i = 0, j = 0; + char buffer[23], rev[23]; + + while (abstime || i <= 8) + { + buffer[i++] = '0' + (abstime % 10); + abstime /= 10; + if (i == 7) buffer[i++] = '.'; + } + if (time < 0) buffer[i++] = '-'; + + while (i--) rev[j++] = buffer[i]; + while (rev[j-1] == '0' && rev[j-2] != '.') --j; + rev[j] = 0; + + return wine_dbg_sprintf("%s", rev); +} + #define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported) { @@ -204,6 +226,511 @@ static void test_wave_sink(void) IMFByteStream_Release(bytestream); }
+struct source_create_callback +{ + IMFAsyncCallback iface; + LONG refcount; + + IMFByteStreamHandler *handler; + HRESULT hr; + MF_OBJECT_TYPE type; + IUnknown *object; + HANDLE event; +}; + +struct source_create_callback *source_create_callback_from_iface(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct source_create_callback, iface); +} + +static HRESULT WINAPI source_create_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI source_create_callback_AddRef(IMFAsyncCallback *iface) +{ + struct source_create_callback *callback = source_create_callback_from_iface(iface); + return InterlockedIncrement(&callback->refcount); +} + +static ULONG WINAPI source_create_callback_Release(IMFAsyncCallback *iface) +{ + struct source_create_callback *callback = source_create_callback_from_iface(iface); + ULONG refcount = InterlockedDecrement(&callback->refcount); + if (refcount == 0) + { + if (callback->object) + IUnknown_Release(callback->object); + IMFByteStreamHandler_Release(callback->handler); + CloseHandle(callback->event); + free(callback); + } + return refcount; +} + +static HRESULT WINAPI source_create_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI source_create_callback_stream_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct source_create_callback *callback = source_create_callback_from_iface(iface); + callback->hr = IMFByteStreamHandler_EndCreateObject(callback->handler, result, &callback->type, + &callback->object); + SetEvent(callback->event); + return callback->hr; +} + +static const IMFAsyncCallbackVtbl source_create_callback_vtbl = +{ + &source_create_callback_QueryInterface, + &source_create_callback_AddRef, + &source_create_callback_Release, + &source_create_callback_GetParameters, + &source_create_callback_stream_Invoke, +}; + +static HRESULT create_source(const GUID *guid_handler, IMFByteStream *stream, IMFMediaSource **source) +{ + HRESULT hr; + IMFByteStreamHandler *handler; + struct source_create_callback *callback; + + if (!(callback = calloc(1, sizeof *callback))) + return E_OUTOFMEMORY; + hr = CoCreateInstance(guid_handler, NULL, CLSCTX_INPROC_SERVER, &IID_IMFByteStreamHandler, (void **)&handler); + if (FAILED(hr)) + { + free(callback); + return hr; + } + callback->iface.lpVtbl = &source_create_callback_vtbl; + callback->refcount = 1; + callback->handler = handler; + callback->object = NULL; + callback->type = MF_OBJECT_INVALID; + callback->hr = E_PENDING; + callback->event = CreateEventW(NULL, FALSE, FALSE, NULL); + + hr = IMFByteStreamHandler_BeginCreateObject(callback->handler, stream, NULL, + MF_RESOLUTION_MEDIASOURCE, NULL, NULL, &callback->iface, NULL); + if (FAILED(hr)) + goto done; + + WaitForSingleObject(callback->event, INFINITE); + if (FAILED(hr = callback->hr)) + goto done; + if (callback->type != MF_OBJECT_MEDIASOURCE) + { + hr = E_UNEXPECTED; + goto done; + } + + hr = S_OK; + *source = (IMFMediaSource *)callback->object; + callback->object = NULL; + +done: + IMFAsyncCallback_Release(&callback->iface); + return hr; +} + +static IMFByteStream *create_byte_stream(const BYTE *data, ULONG data_len) +{ + IMFByteStream *stream; + HRESULT hr; + + hr = MFCreateTempFile(MF_ACCESSMODE_READWRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFByteStream_Write(stream, data, data_len, &data_len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFByteStream_SetCurrentPosition(stream, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + return stream; +} + +static IMFByteStream *create_resource_byte_stream(const WCHAR *name) +{ + const BYTE *resource_data; + ULONG resource_len; + HRSRC resource; + + resource = FindResourceW(NULL, name, (const WCHAR *)RT_RCDATA); + ok(resource != 0, "FindResourceW %s failed, error %lu\n", debugstr_w(name), GetLastError()); + resource_data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); + resource_len = SizeofResource(GetModuleHandleW(NULL), resource); + + return create_byte_stream(resource_data, resource_len); +} + +struct test_callback +{ + IMFAsyncCallback IMFAsyncCallback_iface; + LONG refcount; + + HANDLE event; + IMFMediaEvent *media_event; + BOOL check_media_event; +}; + +static struct test_callback *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct test_callback, IMFAsyncCallback_iface); +} + +static HRESULT WINAPI testcallback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI testcallback_AddRef(IMFAsyncCallback *iface) +{ + struct test_callback *callback = impl_from_IMFAsyncCallback(iface); + return InterlockedIncrement(&callback->refcount); +} + +static ULONG WINAPI testcallback_Release(IMFAsyncCallback *iface) +{ + struct test_callback *callback = impl_from_IMFAsyncCallback(iface); + ULONG refcount = InterlockedDecrement(&callback->refcount); + + if (!refcount) + { + if (callback->media_event) + IMFMediaEvent_Release(callback->media_event); + CloseHandle(callback->event); + free(callback); + } + + return refcount; +} + +static HRESULT WINAPI testcallback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + ok(flags != NULL && queue != NULL, "Unexpected arguments.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testcallback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct test_callback *callback = CONTAINING_RECORD(iface, struct test_callback, IMFAsyncCallback_iface); + IUnknown *object; + HRESULT hr; + + ok(result != NULL, "Unexpected result object.\n"); + + if (callback->media_event) + IMFMediaEvent_Release(callback->media_event); + + if (callback->check_media_event) + { + hr = IMFAsyncResult_GetObject(result, &object); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMFAsyncResult_GetState(result, &object); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + callback->media_event = (void *)0xdeadbeef; + hr = IMFMediaEventGenerator_EndGetEvent((IMFMediaEventGenerator *)object, + result, &callback->media_event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IUnknown_Release(object); + } + + SetEvent(callback->event); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl testcallbackvtbl = +{ + testcallback_QueryInterface, + testcallback_AddRef, + testcallback_Release, + testcallback_GetParameters, + testcallback_Invoke, +}; + +static IMFAsyncCallback *create_test_callback(BOOL check_media_event) +{ + struct test_callback *callback; + + if (!(callback = calloc(1, sizeof(*callback)))) + return NULL; + + callback->refcount = 1; + callback->check_media_event = check_media_event; + callback->IMFAsyncCallback_iface.lpVtbl = &testcallbackvtbl; + callback->event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!callback->event, "CreateEventW failed, error %lu\n", GetLastError()); + + return &callback->IMFAsyncCallback_iface; +} + +#define next_media_event(a, b, c, d) next_media_event_(__LINE__, (IMFMediaEventGenerator *)a, b, c, d) +static HRESULT next_media_event_(int line, IMFMediaEventGenerator *source, IMFAsyncCallback *callback, DWORD timeout, + IMFMediaEvent **event) +{ + struct test_callback *impl = impl_from_IMFAsyncCallback(callback); + HRESULT hr; + DWORD ret; + + hr = IMFMediaEventGenerator_BeginGetEvent(source, &impl->IMFAsyncCallback_iface, (IUnknown *)source); + ok_(__FILE__, line)(hr == S_OK || hr == MF_S_MULTIPLE_BEGIN, "Unexpected hr %#lx.\n", hr); + ret = WaitForSingleObject(impl->event, timeout); + *event = impl->media_event; + impl->media_event = NULL; + + return ret; +} + +#define wait_media_event(a, b, c, d, e) wait_media_event_(__LINE__, (IMFMediaEventGenerator *)a, b, c, d, e) +static HRESULT wait_media_event_(int line, IMFMediaEventGenerator *source, IMFAsyncCallback *callback, + MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) +{ + IMFMediaEvent *event = NULL; + MediaEventType type; + HRESULT hr, status; + DWORD ret; + GUID guid; + + do + { + if (event) IMFMediaEvent_Release(event); + ret = next_media_event(source, callback, timeout, &event); + if (ret) return MF_E_NO_EVENTS_AVAILABLE; + hr = IMFMediaEvent_GetType(event, &type); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(__FILE__, line)(type == expect_type, "got %#lx.\n", type); + } while (type != expect_type); + + hr = IMFMediaEvent_GetExtendedType(event, &guid); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(__FILE__, line)(IsEqualGUID(&guid, &GUID_NULL), "got extended type %s\n", debugstr_guid(&guid)); + + hr = IMFMediaEvent_GetValue(event, value); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEvent_GetStatus(event, &status); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFMediaEvent_Release(event); + return status; +} + +static void test_sample_times_at_rate(IMFMediaSource *source, FLOAT rate, BOOL thin) +{ + static LONGLONG expect_times[] = + { + 0, 333666, + 1668333, 333666, + 2002000, 333666, + 2335666, 333666, + 2669333, 333666, + }; + static LONGLONG expect_times_thin[ARRAY_SIZE(expect_times)] = + { + 0, 333666, + 1668333, 333666, + 3336666, 333666, + 5005000, 333666, + 6673333, 333666, + }; + IMFAsyncCallback *callback; + IMFRateControl *rate_control; + IMFPresentationDescriptor *pd; + IMFMediaStream *stream; + IMFMediaEvent *event; + PROPVARIANT value; + LONGLONG time; + HRESULT hr; + DWORD ret; + + winetest_push_context("%f/%u", rate, thin); + + hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + value.vt = VT_EMPTY; + hr = IMFMediaSource_Start(source, pd, &GUID_NULL, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFPresentationDescriptor_Release(pd); + + callback = create_test_callback(TRUE); + if (!winetest_platform_is_wine) + { + hr = wait_media_event(source, callback, thin ? MENewStream : MEUpdatedStream, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + else + { + ret = next_media_event(source, callback, 100, &event); + ok(ret == 0, "Unexpected ret %#lx.\n", ret); + hr = IMFMediaEvent_GetType(event, &ret); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine_if(!thin) + ok(ret == (thin ? MENewStream : MEUpdatedStream), "Unexpected type %#lx.\n", ret); + hr = IMFMediaEvent_GetValue(event, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaEvent_Release(event); + } + ok(value.vt == VT_UNKNOWN, "got vt %u\n", value.vt); + stream = (IMFMediaStream *)value.punkVal; + IMFMediaStream_AddRef(stream); + PropVariantClear(&value); + + hr = wait_media_event(stream, callback, MEStreamStarted, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(value.vt == VT_I8, "got vt %u\n", value.vt); + hr = wait_media_event(source, callback, MESourceStarted, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(value.vt == VT_I8, "got vt %u\n", value.vt); + + winetest_push_context("sample 0"); + + hr = IMFMediaStream_RequestSample(stream, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(stream, callback, MEMediaSample, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(value.vt == VT_UNKNOWN, "got vt %u\n", value.vt); + hr = IMFSample_GetSampleTime((IMFSample *)value.punkVal, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(time == expect_times[0], "Unexpected time %s.\n", debugstr_time(time)); + hr = IMFSample_GetSampleDuration((IMFSample *)value.punkVal, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(time == expect_times[1], "Unexpected time %s.\n", debugstr_time(time)); + hr = IMFSample_GetSampleFlags((IMFSample *)value.punkVal, &ret); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(ret == 0, "Unexpected flags %#lx.\n", ret); + PropVariantClear(&value); + + winetest_pop_context(); + + hr = MFGetService((IUnknown *)source, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateControl, (void **)&rate_control); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFRateControl_SetRate(rate_control, thin, rate); + todo_wine_if(thin && hr == MF_E_THINNING_UNSUPPORTED) + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFRateControl_Release(rate_control); + + hr = wait_media_event(source, callback, MESourceRateChanged, 100, &value); + todo_wine_if(thin) + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(value.vt == VT_R4, "got vt %u\n", value.vt); + ret = next_media_event(source, callback, 100, &event); + ok(ret == WAIT_TIMEOUT, "Unexpected ret %#lx.\n", ret); + + hr = IMFMediaStream_RequestSample(stream, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (!winetest_platform_is_wine) + { + hr = wait_media_event(stream, callback, MEStreamThinMode, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(value.vt == VT_INT, "got vt %u\n", value.vt); + ok(value.iVal == thin, "Unexpected thin %d\n", value.iVal); + } + + winetest_push_context("sample 1"); + + hr = wait_media_event(stream, callback, MEMediaSample, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(value.vt == VT_UNKNOWN, "got vt %u\n", value.vt); + hr = IMFSample_GetSampleTime((IMFSample *)value.punkVal, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(time == expect_times[2], "Unexpected time %s.\n", debugstr_time(time)); + hr = IMFSample_GetSampleDuration((IMFSample *)value.punkVal, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(time == expect_times[3], "Unexpected time %s.\n", debugstr_time(time)); + hr = IMFSample_GetSampleFlags((IMFSample *)value.punkVal, &ret); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(ret == 0, "Unexpected flags %#lx.\n", ret); + PropVariantClear(&value); + + winetest_pop_context(); + + for (int i = 2; i < ARRAY_SIZE(expect_times) / 2; i++) + { + winetest_push_context("sample %u", i); + + hr = IMFMediaStream_RequestSample(stream, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(stream, callback, MEMediaSample, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(value.vt == VT_UNKNOWN, "got vt %u\n", value.vt); + hr = IMFSample_GetSampleTime((IMFSample *)value.punkVal, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(time == (thin ? expect_times_thin[2 * i] : expect_times[2 * i]), "Unexpected time %s.\n", debugstr_time(time)); + hr = IMFSample_GetSampleDuration((IMFSample *)value.punkVal, &time); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(time == (thin ? expect_times_thin[2 * i + 1] : expect_times[2 * i + 1]), "Unexpected time %s.\n", debugstr_time(time)); + hr = IMFSample_GetSampleFlags((IMFSample *)value.punkVal, &ret); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(ret == 0, "Unexpected flags %#lx.\n", ret); + PropVariantClear(&value); + + winetest_pop_context(); + } + + hr = IMFMediaSource_Stop(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(source, callback, MESourceStopped, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(value.vt == VT_EMPTY, "got vt %u\n", value.vt); + hr = wait_media_event(stream, callback, MEStreamStopped, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(value.vt == VT_EMPTY, "got vt %u\n", value.vt); + + IMFMediaStream_Release(stream); + IMFAsyncCallback_Release(callback); + + winetest_pop_context(); +} + +static void test_thinning(void) +{ + IMFMediaSource *source; + IMFByteStream *stream; + HRESULT hr; + + stream = create_resource_byte_stream(L"test_thinning.avi"); + hr = create_source(&CLSID_AVIByteStreamPlugin, stream, &source); + IMFByteStream_Release(stream); + + if (FAILED(hr)) + { + win_skip("Failed to create MPEG4 source: %#lx.\n", hr); + return; + } + + test_sample_times_at_rate(source, 2.0, TRUE); + test_sample_times_at_rate(source, 3.0, FALSE); + + hr = IMFMediaSource_Shutdown(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaSource_Release(source); +} + START_TEST(mfsrcsnk) { HRESULT hr; @@ -212,6 +739,7 @@ START_TEST(mfsrcsnk) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
test_wave_sink(); + test_thinning();
hr = MFShutdown(); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); diff --git a/dlls/mfsrcsnk/tests/resource.rc b/dlls/mfsrcsnk/tests/resource.rc new file mode 100644 index 00000000000..7b3a6532f0a --- /dev/null +++ b/dlls/mfsrcsnk/tests/resource.rc @@ -0,0 +1,31 @@ +/* + * Resources for mfsrcsnk test suite. + * + * Copyright 2025 Charlotte Pabst 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" + +/* Generated with: + gst-launch-1.0 videotestsrc num-buffers=60 pattern=smpte100 ! \ + video/x-raw,format=I420,width=64,height=64,framerate=30000/1001 ! \ + videoflip method=clockwise ! videoconvert ! \ + x264enc key-int-max=5 ! qtmux ! filesink location=tmp.mp4 && \ + ffmpeg -i tmp.mp4 dlls/mfsrcsnk/tests/test_thinning.avi + */ +/* @makedep: test_thinning.avi */ +test_thinning.avi RCDATA test_thinning.avi diff --git a/dlls/mfsrcsnk/tests/test_thinning.avi b/dlls/mfsrcsnk/tests/test_thinning.avi new file mode 100644 index 0000000000000000000000000000000000000000..acc35cce915e86f5fd394f24a397bcf33be034c9 GIT binary patch literal 7716 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 b5)2HW5)+mmK1rgQ$0vnqo(3A<3Cafm!3?dO
literal 0 HcmV?d00001