Supersedes https://gitlab.winehq.org/wine/wine/-/merge_requests/8505
-- v2: mfsrcsnk: Implement thinning. mfsrcsnk: Peek for stream token presence without removing it. mfsrcsnk: Factor out a media_source_request_stream_sample helper. mfsrcsnk: Process SetRate asynchronously. mfsrcsnk: Introduce a DEFINE_MF_ASYNC_PARAMS macro. winedmo: Fall back to dts for sample time if pts is not present. mfsrcsnk/tests: Add tests for thinning.
From: Charlotte Pabst cpabst@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
From: Charlotte Pabst cpabst@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 );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mfsrcsnk/media_source.c | 100 +++++++++++++++++------------------ 1 file changed, 49 insertions(+), 51 deletions(-)
diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index 9ea4e5ecef1..3f94d262114 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,66 +180,21 @@ 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) -{ - 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; -} - -static ULONG WINAPI async_start_params_AddRef(IUnknown *iface) -{ - struct async_start_params *params = async_start_params_from_IUnknown(iface); - return InterlockedIncrement(¶ms->refcount); -} - -static ULONG WINAPI async_start_params_Release(IUnknown *iface) +static void async_start_params_destroy(struct async_start_params *params) { - struct async_start_params *params = async_start_params_from_IUnknown(iface); - ULONG refcount = InterlockedDecrement(¶ms->refcount); - - if (!refcount) - { - IMFPresentationDescriptor_Release(params->descriptor); - PropVariantClear(¶ms->position); - free(params); - } - - return refcount; + IMFPresentationDescriptor_Release(params->descriptor); + PropVariantClear(¶ms->position); + free(params); }
-static const IUnknownVtbl async_start_params_vtbl = -{ - async_start_params_QueryInterface, - async_start_params_AddRef, - async_start_params_Release, -}; +DEFINE_MF_ASYNC_PARAMS(async_start_params);
static HRESULT async_start_params_create(IMFPresentationDescriptor *descriptor, const GUID *time_format, const PROPVARIANT *position, IUnknown **out) { struct async_start_params *params;
- if (!(params = calloc(1, sizeof(*params)))) - return E_OUTOFMEMORY; - - params->IUnknown_iface.lpVtbl = &async_start_params_vtbl; - params->refcount = 1; - + if (!(params = async_start_params_alloc())) return E_OUTOFMEMORY; params->descriptor = descriptor; IMFPresentationDescriptor_AddRef(descriptor); params->format = *time_format;
From: Charlotte Pabst cpabst@codeweavers.com
--- dlls/mfsrcsnk/media_source.c | 58 +++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 4 deletions(-)
diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index 3f94d262114..d31be5c3734 100644 --- a/dlls/mfsrcsnk/media_source.c +++ b/dlls/mfsrcsnk/media_source.c @@ -204,6 +204,33 @@ static HRESULT async_start_params_create(IMFPresentationDescriptor *descriptor, return S_OK; }
+struct async_set_rate_params +{ + IUnknown IUnknown_iface; + LONG refcount; + float rate; + BOOL thin; +}; + +static void async_set_rate_params_destroy(struct async_set_rate_params *params) +{ + free(params); +} + +DEFINE_MF_ASYNC_PARAMS(async_set_rate_params); + +static HRESULT async_set_rate_params_create(float rate, BOOL thin, IUnknown **out) +{ + struct async_set_rate_params *params; + + if (!(params = async_set_rate_params_alloc())) return E_OUTOFMEMORY; + params->rate = rate; + params->thin = thin; + + *out = ¶ms->IUnknown_iface; + return S_OK; +} + struct media_stream { IMFMediaStream IMFMediaStream_iface; @@ -231,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; @@ -1036,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); @@ -1051,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) @@ -1724,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)))
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?
Well, perhaps but I don't see why it should be treated differently from a missed read, which might need to try reading again to satisfy a given stream. Also, all the media source commands (including reads [^1]) are executed on the global shared callback queue, and they should not stall the queue for an unbounded period of time.
It might be better to separate the addition of the DEFINE_MF_ASYNC_PARAMS helper from the "Process SetRate asynchronously" commit.
Sure, split it to a separate change.
[^1]: Actual stream reads should probably use the IO queues but it's probably a trickier design change.
From: Rémi Bernon rbernon@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;
From: Rémi Bernon rbernon@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)
From: Charlotte Pabst cpabst@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;