[PATCH 0/6] MR9693: mfsrcsnk: Implement thinning.
From: Charlotte Pabst <cpabst(a)codeweavers.com> --- dlls/mfsrcsnk/tests/Makefile.in | 3 +- dlls/mfsrcsnk/tests/mfsrcsnk.c | 621 ++++++++++++++++++++++++++ dlls/mfsrcsnk/tests/resource.rc | 31 ++ dlls/mfsrcsnk/tests/test_thinning.avi | Bin 0 -> 7716 bytes 4 files changed, 654 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..03d206a5f55 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,604 @@ 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"); + ok(!callback->media_event, "Event already present.\n"); + + 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 DWORD 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 || hr == MF_E_MULTIPLE_SUBSCRIBERS, "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_(line, 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, *source_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); + source_callback = create_test_callback(TRUE); + if (!winetest_platform_is_wine) + { + hr = wait_media_event(source, source_callback, thin ? MENewStream : MEUpdatedStream, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + else + { + ret = next_media_event(source, 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, 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); + + hr = wait_media_event(source, 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, 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(); + } + + /* change rate after sample requests */ + hr = IMFMediaStream_RequestSample(stream, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaStream_RequestSample(stream, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaStream_RequestSample(stream, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaStream_RequestSample(stream, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaStream_RequestSample(stream, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaStream_RequestSample(stream, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFRateControl_SetRate(rate_control, !thin, rate+0.5); + todo_wine_if(!thin && hr == MF_E_THINNING_UNSUPPORTED) + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* rate change event is available immediately */ + hr = wait_media_event(source, source_callback, MESourceRateChanged, 100, &value); + todo_wine_if(!thin && hr == MF_E_NO_EVENTS_AVAILABLE) + 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); + PropVariantClear(&value); + hr = wait_media_event(stream, callback, MEMediaSample, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&value); + hr = wait_media_event(stream, callback, MEMediaSample, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&value); + hr = wait_media_event(stream, callback, MEMediaSample, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&value); + hr = wait_media_event(stream, callback, MEMediaSample, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&value); + hr = wait_media_event(stream, callback, MEMediaSample, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&value); + + /* thin mode only changes when a new sample is requested */ + hr = wait_media_event(stream, callback, MEStreamThinMode, 1000, &value); + ok(MF_E_NO_EVENTS_AVAILABLE == hr, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaStream_RequestSample(stream, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (!thin) + { + /* next sample is already a keyframe. thin mode only changes after some samples get skipped */ + hr = wait_media_event(stream, callback, MEMediaSample, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&value); + 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, 1000, &value); + 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); + PropVariantClear(&value); + + /* switch back to previous mode */ + hr = IMFRateControl_SetRate(rate_control, thin, rate+0.5); + todo_wine_if(thin && hr == MF_E_THINNING_UNSUPPORTED) + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(source, source_callback, MESourceRateChanged, 100, &value); + todo_wine_if(thin && hr == MF_E_NO_EVENTS_AVAILABLE) + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + 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, 1000, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + if (thin && !winetest_platform_is_wine) + { + hr = wait_media_event(stream, callback, MEEndOfStream, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(source, source_callback, MEEndOfPresentation, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + else + { + hr = wait_media_event(stream, callback, MEMediaSample, 100, &value); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + PropVariantClear(&value); + } + + hr = IMFMediaSource_Stop(source); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(source, 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); + IMFAsyncCallback_Release(source_callback); + IMFRateControl_Release(rate_control); + + 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 +832,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 -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9693
From: Charlotte Pabst <cpabst(a)codeweavers.com> --- dlls/winedmo/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/winedmo/main.c b/dlls/winedmo/main.c index 77411352445..671e632b16d 100644 --- a/dlls/winedmo/main.c +++ b/dlls/winedmo/main.c @@ -146,6 +146,7 @@ static void buffer_unlock( DMO_OUTPUT_DATA_BUFFER *buffer, struct sample *sample { if (sample->dts != INT64_MIN) IMFSample_SetUINT64( object, &MFSampleExtension_DecodeTimestamp, sample->dts ); if (sample->pts != INT64_MIN) IMFSample_SetSampleTime( object, sample->pts ); + else if (sample->dts != INT64_MIN) IMFSample_SetSampleTime( object, sample->dts ); if (sample->duration != INT64_MIN) IMFSample_SetSampleDuration( object, sample->duration ); if (sample->flags & SAMPLE_FLAG_SYNC_POINT) IMFSample_SetUINT32( object, &MFSampleExtension_CleanPoint, 1 ); IMFSample_Release( object ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9693
From: Charlotte Pabst <cpabst(a)codeweavers.com> --- dlls/mfsrcsnk/media_source.c | 152 +++++++++++++++++++++++------------ 1 file changed, 100 insertions(+), 52 deletions(-) diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index 9ea4e5ecef1..d31be5c3734 100644 --- a/dlls/mfsrcsnk/media_source.c +++ b/dlls/mfsrcsnk/media_source.c @@ -26,6 +26,49 @@ WINE_DEFAULT_DEBUG_CHANNEL(mfplat); +#define DEFINE_MF_ASYNC_PARAMS(type) \ + static struct type *type ## _from_IUnknown(IUnknown *iface) \ + { \ + return CONTAINING_RECORD(iface, struct type, IUnknown_iface); \ + } \ + static HRESULT WINAPI type ## _QueryInterface(IUnknown *iface, REFIID iid, void **out) \ + { \ + if (IsEqualIID(iid, &IID_IUnknown)) \ + { \ + IUnknown_AddRef(iface); \ + *out = iface; \ + return S_OK; \ + } \ + *out = NULL; \ + return E_NOINTERFACE; \ + } \ + static ULONG WINAPI type ## _AddRef(IUnknown *iface) \ + { \ + struct type *object = type ## _from_IUnknown(iface); \ + return InterlockedIncrement(&object->refcount); \ + } \ + static ULONG WINAPI type ## _Release(IUnknown *iface) \ + { \ + struct type *object = type ## _from_IUnknown(iface); \ + ULONG ref = InterlockedDecrement(&object->refcount); \ + if (!ref) type ## _ ## destroy(object); \ + return ref; \ + } \ + static const IUnknownVtbl type ## _vtbl = \ + { \ + type ## _QueryInterface, \ + type ## _AddRef, \ + type ## _Release, \ + }; \ + static struct type *type ## _alloc(void) \ + { \ + struct type *object; \ + if (!(object = calloc(1, sizeof(*object)))) return NULL; \ + object->IUnknown_iface.lpVtbl = &type ## _vtbl; \ + object->refcount = 1; \ + return object; \ + } + #define DEFINE_MF_ASYNC_CALLBACK_(type, name, impl_from, pfx, mem, expr) \ static struct type *impl_from(IMFAsyncCallback *iface) \ { \ @@ -137,70 +180,52 @@ struct async_start_params PROPVARIANT position; }; -static struct async_start_params *async_start_params_from_IUnknown(IUnknown *iface) -{ - return CONTAINING_RECORD(iface, struct async_start_params, IUnknown_iface); -} - -static HRESULT WINAPI async_start_params_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +static void async_start_params_destroy(struct async_start_params *params) { - struct async_start_params *params = async_start_params_from_IUnknown(iface); - - if (IsEqualIID(riid, &IID_IUnknown)) - { - IUnknown_AddRef(¶ms->IUnknown_iface); - *obj = ¶ms->IUnknown_iface; - return S_OK; - } - - WARN("Unsupported interface %s\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; + IMFPresentationDescriptor_Release(params->descriptor); + PropVariantClear(¶ms->position); + free(params); } -static ULONG WINAPI async_start_params_AddRef(IUnknown *iface) -{ - struct async_start_params *params = async_start_params_from_IUnknown(iface); - return InterlockedIncrement(¶ms->refcount); -} +DEFINE_MF_ASYNC_PARAMS(async_start_params); -static ULONG WINAPI async_start_params_Release(IUnknown *iface) +static HRESULT async_start_params_create(IMFPresentationDescriptor *descriptor, const GUID *time_format, + const PROPVARIANT *position, IUnknown **out) { - struct async_start_params *params = async_start_params_from_IUnknown(iface); - ULONG refcount = InterlockedDecrement(¶ms->refcount); + struct async_start_params *params; - if (!refcount) - { - IMFPresentationDescriptor_Release(params->descriptor); - PropVariantClear(¶ms->position); - free(params); - } + if (!(params = async_start_params_alloc())) return E_OUTOFMEMORY; + params->descriptor = descriptor; + IMFPresentationDescriptor_AddRef(descriptor); + params->format = *time_format; + PropVariantCopy(¶ms->position, position); - return refcount; + *out = ¶ms->IUnknown_iface; + return S_OK; } -static const IUnknownVtbl async_start_params_vtbl = +struct async_set_rate_params { - async_start_params_QueryInterface, - async_start_params_AddRef, - async_start_params_Release, + IUnknown IUnknown_iface; + LONG refcount; + float rate; + BOOL thin; }; -static HRESULT async_start_params_create(IMFPresentationDescriptor *descriptor, const GUID *time_format, - const PROPVARIANT *position, IUnknown **out) +static void async_set_rate_params_destroy(struct async_set_rate_params *params) { - struct async_start_params *params; + free(params); +} - if (!(params = calloc(1, sizeof(*params)))) - return E_OUTOFMEMORY; +DEFINE_MF_ASYNC_PARAMS(async_set_rate_params); - params->IUnknown_iface.lpVtbl = &async_start_params_vtbl; - params->refcount = 1; +static HRESULT async_set_rate_params_create(float rate, BOOL thin, IUnknown **out) +{ + struct async_set_rate_params *params; - params->descriptor = descriptor; - IMFPresentationDescriptor_AddRef(descriptor); - params->format = *time_format; - PropVariantCopy(¶ms->position, position); + if (!(params = async_set_rate_params_alloc())) return E_OUTOFMEMORY; + params->rate = rate; + params->thin = thin; *out = ¶ms->IUnknown_iface; return S_OK; @@ -233,6 +258,7 @@ struct media_source IMFAsyncCallback async_stop_iface; IMFAsyncCallback async_pause_iface; IMFAsyncCallback async_read_iface; + IMFAsyncCallback async_set_rate_iface; LONG refcount; CRITICAL_SECTION cs; @@ -1038,9 +1064,28 @@ static ULONG WINAPI media_source_IMFRateControl_Release(IMFRateControl *iface) return IMFMediaSource_Release(&source->IMFMediaSource_iface); } +static HRESULT media_source_async_set_rate(struct media_source *source, IMFAsyncResult *result) +{ + struct async_set_rate_params *params; + IUnknown *state; + + if (!(state = IMFAsyncResult_GetStateNoAddRef(result))) return E_INVALIDARG; + params = async_set_rate_params_from_IUnknown(state); + + EnterCriticalSection(&source->cs); + source->rate = params->rate; + LeaveCriticalSection(&source->cs); + + queue_media_event_value(source->queue, MESourceRateChanged, NULL); + return S_OK; +} + +DEFINE_MF_ASYNC_CALLBACK(media_source, async_set_rate, IMFMediaSource_iface) + static HRESULT WINAPI media_source_IMFRateControl_SetRate(IMFRateControl *iface, BOOL thin, float rate) { struct media_source *source = media_source_from_IMFRateControl(iface); + IUnknown *params; HRESULT hr; FIXME("source %p, thin %d, rate %f, stub!\n", source, thin, rate); @@ -1053,11 +1098,13 @@ static HRESULT WINAPI media_source_IMFRateControl_SetRate(IMFRateControl *iface, if (FAILED(hr = IMFRateSupport_IsRateSupported(&source->IMFRateSupport_iface, thin, rate, NULL))) return hr; - EnterCriticalSection(&source->cs); - source->rate = rate; - LeaveCriticalSection(&source->cs); + if (FAILED(hr = async_set_rate_params_create(rate, thin, ¶ms))) + return hr; - return IMFMediaEventQueue_QueueEventParamVar(source->queue, MESourceRateChanged, &GUID_NULL, S_OK, NULL); + hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &source->async_set_rate_iface, params); + IUnknown_Release(params); + + return hr; } static HRESULT WINAPI media_source_IMFRateControl_GetRate(IMFRateControl *iface, BOOL *thin, float *rate) @@ -1726,6 +1773,7 @@ static HRESULT media_source_create(const WCHAR *url, IMFByteStream *stream, IMFM source->async_stop_iface.lpVtbl = &media_source_async_stop_vtbl; source->async_pause_iface.lpVtbl = &media_source_async_pause_vtbl; source->async_read_iface.lpVtbl = &media_source_async_read_vtbl; + source->async_set_rate_iface.lpVtbl = &media_source_async_set_rate_vtbl; source->refcount = 1; if (FAILED(hr = MFCreateEventQueue(&source->queue))) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9693
From: Rémi Bernon <rbernon(a)codeweavers.com> --- dlls/mfsrcsnk/media_source.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index d31be5c3734..895b80419c0 100644 --- a/dlls/mfsrcsnk/media_source.c +++ b/dlls/mfsrcsnk/media_source.c @@ -826,25 +826,34 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM return hr; } -static HRESULT media_stream_async_request(struct media_stream *stream, IMFAsyncResult *result) +static HRESULT media_source_request_stream_sample(struct media_source *source, struct media_stream *stream, IUnknown *token) { - struct media_source *source = media_source_from_IMFMediaSource(stream->source); - IUnknown *token = IMFAsyncResult_GetStateNoAddRef(result); IMFSample *sample; HRESULT hr = S_OK; - EnterCriticalSection(&source->cs); - if (source->state == SOURCE_SHUTDOWN) - hr = MF_E_SHUTDOWN; - else if (source->state == SOURCE_RUNNING && SUCCEEDED(hr = object_queue_pop(&stream->samples, (IUnknown **)&sample))) + return MF_E_SHUTDOWN; + + if (source->state == SOURCE_RUNNING && SUCCEEDED(hr = object_queue_pop(&stream->samples, (IUnknown **)&sample))) { media_stream_send_sample(stream, sample, token); IMFSample_Release(sample); + return S_OK; } - else if (SUCCEEDED(hr = object_queue_push(&stream->tokens, token)) && source->state == SOURCE_RUNNING) + + if (SUCCEEDED(hr = object_queue_push(&stream->tokens, token)) && source->state == SOURCE_RUNNING) queue_media_source_read(source); + return hr; +} +static HRESULT media_stream_async_request(struct media_stream *stream, IMFAsyncResult *result) +{ + struct media_source *source = media_source_from_IMFMediaSource(stream->source); + IUnknown *token = IMFAsyncResult_GetStateNoAddRef(result); + HRESULT hr = S_OK; + + EnterCriticalSection(&source->cs); + hr = media_source_request_stream_sample(source, stream, token); LeaveCriticalSection(&source->cs); return hr; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9693
From: Rémi Bernon <rbernon(a)codeweavers.com> --- dlls/mfsrcsnk/media_source.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index 895b80419c0..e4ce376abd8 100644 --- a/dlls/mfsrcsnk/media_source.c +++ b/dlls/mfsrcsnk/media_source.c @@ -324,17 +324,17 @@ static HRESULT media_source_send_sample(struct media_source *source, UINT index, if (!(stream = media_stream_from_index(source, index)) || !stream->active) return S_FALSE; - if (SUCCEEDED(hr = object_queue_pop(&stream->tokens, &token))) + if (list_empty(&stream->tokens)) { - media_stream_send_sample(stream, sample, token); - if (token) IUnknown_Release(token); - return S_OK; + if (FAILED(hr = object_queue_push(&stream->samples, (IUnknown *)sample))) + return hr; + return S_FALSE; } - if (FAILED(hr = object_queue_push(&stream->samples, (IUnknown *)sample))) - return hr; - - return S_FALSE; + object_queue_pop(&stream->tokens, &token); + media_stream_send_sample(stream, sample, token); + if (token) IUnknown_Release(token); + return S_OK; } static void queue_media_event_object(IMFMediaEventQueue *queue, MediaEventType type, IUnknown *object) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9693
This merge request was approved by Rémi Bernon. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9693
From: Charlotte Pabst <cpabst(a)codeweavers.com> --- dlls/mfsrcsnk/media_source.c | 95 +++++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 13 deletions(-) diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index e4ce376abd8..16bf0c09204 100644 --- a/dlls/mfsrcsnk/media_source.c +++ b/dlls/mfsrcsnk/media_source.c @@ -235,6 +235,7 @@ struct media_stream { IMFMediaStream IMFMediaStream_iface; IMFAsyncCallback async_request_iface; + IMFAsyncCallback async_request_thin_iface; LONG refcount; IMFMediaSource *source; @@ -245,6 +246,8 @@ struct media_stream BOOL active; BOOL eos; + BOOL thin; + BOOL pending_thin; }; struct media_source @@ -266,6 +269,7 @@ struct media_source IMFByteStream *stream; WCHAR *url; float rate; + BOOL thin; struct winedmo_demuxer winedmo_demuxer; struct winedmo_stream winedmo_stream; @@ -292,13 +296,34 @@ static struct media_source *media_source_from_IMFMediaSource(IMFMediaSource *ifa return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface); } +static void queue_media_event_value(IMFMediaEventQueue *queue, MediaEventType type, const PROPVARIANT *value) +{ + HRESULT hr; + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(queue, type, &GUID_NULL, S_OK, value))) + ERR("Failed to queue event of type %#lx to queue %p, hr %#lx\n", type, queue, hr); +} + +static void media_stream_set_thin(struct media_stream *stream, BOOL thin) +{ + if (stream->thin != thin) + { + PROPVARIANT param = {.vt = VT_INT, .iVal = thin}; + stream->thin = thin; + queue_media_event_value(stream->queue, MEStreamThinMode, ¶m); + } +} + static void media_stream_send_sample(struct media_stream *stream, IMFSample *sample, IUnknown *token) { HRESULT hr = S_OK; if (!token || SUCCEEDED(hr = IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token))) + { + /* thinning stays enabled until a sample is sent, always a keyframe as it was enabled */ + if (!stream->pending_thin) media_stream_set_thin(stream, FALSE); hr = IMFMediaEventQueue_QueueEventParamUnk(stream->queue, MEMediaSample, &GUID_NULL, S_OK, (IUnknown *)sample); + } if (FAILED(hr)) ERR("Failed to send stream %p sample, hr %#lx\n", stream, hr); } @@ -315,6 +340,17 @@ static struct media_stream *media_stream_from_index(struct media_source *source, return NULL; } +static BOOL media_stream_accepts_sample(struct media_stream *stream, IMFSample *sample, BOOL thin) +{ + BOOL thinning = thin || stream->thin; + UINT32 keyframe; + + if (thinning && SUCCEEDED(IMFSample_GetUINT32(sample, &MFSampleExtension_CleanPoint, &keyframe))) + return keyframe; + + return !thinning; +} + static HRESULT media_source_send_sample(struct media_source *source, UINT index, IMFSample *sample) { struct media_stream *stream; @@ -331,6 +367,13 @@ static HRESULT media_source_send_sample(struct media_source *source, UINT index, return S_FALSE; } + if (!media_stream_accepts_sample(stream, sample, stream->pending_thin)) + { + /* thinning stays disabled until a sample is skipped */ + if (stream->pending_thin) media_stream_set_thin(stream, TRUE); + return S_FALSE; + } + object_queue_pop(&stream->tokens, &token); media_stream_send_sample(stream, sample, token); if (token) IUnknown_Release(token); @@ -344,13 +387,6 @@ static void queue_media_event_object(IMFMediaEventQueue *queue, MediaEventType t ERR("Failed to queue event of type %#lx to queue %p, hr %#lx\n", type, queue, hr); } -static void queue_media_event_value(IMFMediaEventQueue *queue, MediaEventType type, const PROPVARIANT *value) -{ - HRESULT hr; - if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(queue, type, &GUID_NULL, S_OK, value))) - ERR("Failed to queue event of type %#lx to queue %p, hr %#lx\n", type, queue, hr); -} - static void queue_media_source_read(struct media_source *source) { HRESULT hr; @@ -826,6 +862,21 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM return hr; } +static HRESULT media_stream_pop_sample(struct media_stream *stream, IMFSample **pSample) +{ + HRESULT hr; + + while (SUCCEEDED(hr = object_queue_pop(&stream->samples, (IUnknown **)pSample)) + && !media_stream_accepts_sample(stream, *pSample, stream->pending_thin)) + { + /* thinning stays disabled until a sample is skipped */ + if (stream->pending_thin) media_stream_set_thin(stream, TRUE); + IMFSample_Release(*pSample); + } + + return hr; +} + static HRESULT media_source_request_stream_sample(struct media_source *source, struct media_stream *stream, IUnknown *token) { IMFSample *sample; @@ -834,7 +885,7 @@ static HRESULT media_source_request_stream_sample(struct media_source *source, s if (source->state == SOURCE_SHUTDOWN) return MF_E_SHUTDOWN; - if (source->state == SOURCE_RUNNING && SUCCEEDED(hr = object_queue_pop(&stream->samples, (IUnknown **)&sample))) + if (source->state == SOURCE_RUNNING && SUCCEEDED(hr = media_stream_pop_sample(stream, &sample))) { media_stream_send_sample(stream, sample, token); IMFSample_Release(sample); @@ -853,6 +904,7 @@ static HRESULT media_stream_async_request(struct media_stream *stream, IMFAsyncR HRESULT hr = S_OK; EnterCriticalSection(&source->cs); + stream->pending_thin = FALSE; hr = media_source_request_stream_sample(source, stream, token); LeaveCriticalSection(&source->cs); @@ -861,6 +913,22 @@ static HRESULT media_stream_async_request(struct media_stream *stream, IMFAsyncR DEFINE_MF_ASYNC_CALLBACK(media_stream, async_request, IMFMediaStream_iface) +static HRESULT media_stream_async_request_thin(struct media_stream *stream, IMFAsyncResult *result) +{ + struct media_source *source = media_source_from_IMFMediaSource(stream->source); + IUnknown *token = IMFAsyncResult_GetStateNoAddRef(result); + HRESULT hr = S_OK; + + EnterCriticalSection(&source->cs); + stream->pending_thin = TRUE; + hr = media_source_request_stream_sample(source, stream, token); + LeaveCriticalSection(&source->cs); + + return hr; +} + +DEFINE_MF_ASYNC_CALLBACK(media_stream, async_request_thin, IMFMediaStream_iface) + static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) { struct media_stream *stream = media_stream_from_IMFMediaStream(iface); @@ -877,6 +945,8 @@ static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown hr = MF_E_MEDIA_SOURCE_WRONGSTATE; else if (stream->eos) hr = MF_E_END_OF_STREAM; + else if (source->thin) + hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &stream->async_request_thin_iface, token); else hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &stream->async_request_iface, token); @@ -911,6 +981,7 @@ static HRESULT media_stream_create(IMFMediaSource *source, IMFStreamDescriptor * object->IMFMediaStream_iface.lpVtbl = &media_stream_vtbl; object->async_request_iface.lpVtbl = &media_stream_async_request_vtbl; + object->async_request_thin_iface.lpVtbl = &media_stream_async_request_thin_vtbl; object->refcount = 1; if (FAILED(hr = MFCreateEventQueue(&object->queue))) @@ -1083,6 +1154,7 @@ static HRESULT media_source_async_set_rate(struct media_source *source, IMFAsync EnterCriticalSection(&source->cs); source->rate = params->rate; + source->thin = params->thin; LeaveCriticalSection(&source->cs); queue_media_event_value(source->queue, MESourceRateChanged, NULL); @@ -1101,8 +1173,6 @@ static HRESULT WINAPI media_source_IMFRateControl_SetRate(IMFRateControl *iface, if (rate < 0.0f) return MF_E_REVERSE_UNSUPPORTED; - if (thin) - return MF_E_THINNING_UNSUPPORTED; if (FAILED(hr = IMFRateSupport_IsRateSupported(&source->IMFRateSupport_iface, thin, rate, NULL))) return hr; @@ -1122,11 +1192,10 @@ static HRESULT WINAPI media_source_IMFRateControl_GetRate(IMFRateControl *iface, TRACE("source %p, thin %p, rate %p\n", source, thin, rate); - if (thin) - *thin = FALSE; - EnterCriticalSection(&source->cs); *rate = source->rate; + if (thin) + *thin = source->thin; LeaveCriticalSection(&source->cs); return S_OK; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9693
This merge request was approved by Charlotte Pabst. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9693
Tested, works. Two minor concerns: - Maybe queueing a new read after a sample is filtered isn't ideal, thinning is meant to reduce latency, if there's a high load on the queue this could delay things significantly, I believe? - It might be better to separate the addition of the DEFINE_MF_ASYNC_PARAMS helper from the "Process SetRate asynchronously" commit. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9693#note_124898
participants (3)
-
Charlotte Pabst -
Charlotte Pabst (@CharlottePabst) -
Rémi Bernon