Commit 28764bd0 authored by Anton Baskanov's avatar Anton Baskanov Committed by Alexandre Julliard

amstream: Implement MediaStreamFilter::WaitUntil.

parent 8492657a
......@@ -21,6 +21,7 @@
#define COBJMACROS
#include "amstream_private.h"
#include "wine/debug.h"
#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(amstream);
......@@ -175,6 +176,16 @@ struct filter
IAMMediaStream *seekable_stream;
FILTER_STATE state;
REFERENCE_TIME start_time;
struct list free_events;
struct list used_events;
};
struct event
{
struct list entry;
HANDLE event;
DWORD_PTR cookie;
BOOL interrupted;
};
static inline struct filter *impl_from_IMediaStreamFilter(IMediaStreamFilter *iface)
......@@ -225,6 +236,15 @@ static ULONG WINAPI filter_Release(IMediaStreamFilter *iface)
if (!refcount)
{
struct list *entry;
while ((entry = list_head(&filter->free_events)))
{
struct event *event = LIST_ENTRY(entry, struct event, entry);
list_remove(entry);
CloseHandle(event->event);
free(event);
}
for (i = 0; i < filter->nb_streams; ++i)
{
IAMMediaStream_JoinFilter(filter->streams[i], NULL);
......@@ -261,6 +281,7 @@ static void set_state(struct filter *filter, FILTER_STATE state)
static HRESULT WINAPI filter_Stop(IMediaStreamFilter *iface)
{
struct filter *filter = impl_from_IMediaStreamFilter(iface);
struct event *event;
TRACE("iface %p.\n", iface);
......@@ -268,6 +289,16 @@ static HRESULT WINAPI filter_Stop(IMediaStreamFilter *iface)
set_state(filter, State_Stopped);
LIST_FOR_EACH_ENTRY(event, &filter->used_events, struct event, entry)
{
if (!event->interrupted)
{
event->interrupted = TRUE;
IReferenceClock_Unadvise(filter->clock, event->cookie);
SetEvent(event->event);
}
}
LeaveCriticalSection(&filter->cs);
return S_OK;
......@@ -664,11 +695,59 @@ static HRESULT WINAPI filter_GetCurrentStreamTime(IMediaStreamFilter *iface, REF
return S_OK;
}
static HRESULT WINAPI filter_WaitUntil(IMediaStreamFilter *iface, REFERENCE_TIME WaitStreamTime)
static HRESULT WINAPI filter_WaitUntil(IMediaStreamFilter *iface, REFERENCE_TIME time)
{
FIXME("(%p)->(%s): Stub!\n", iface, wine_dbgstr_longlong(WaitStreamTime));
struct filter *filter = impl_from_IMediaStreamFilter(iface);
struct event *event;
struct list *entry;
HRESULT hr;
return E_NOTIMPL;
TRACE("filter %p, time %s.\n", iface, wine_dbgstr_longlong(time));
EnterCriticalSection(&filter->cs);
if (!filter->clock)
{
LeaveCriticalSection(&filter->cs);
return E_FAIL;
}
if ((entry = list_head(&filter->free_events)))
{
list_remove(entry);
event = LIST_ENTRY(entry, struct event, entry);
}
else
{
event = calloc(1, sizeof(struct event));
event->event = CreateEventW(NULL, FALSE, FALSE, NULL);
entry = &event->entry;
}
hr = IReferenceClock_AdviseTime(filter->clock, time, filter->start_time, (HEVENT)event->event, &event->cookie);
if (FAILED(hr))
{
list_add_tail(&filter->free_events, entry);
LeaveCriticalSection(&filter->cs);
return hr;
}
event->interrupted = FALSE;
list_add_tail(&filter->used_events, entry);
LeaveCriticalSection(&filter->cs);
WaitForSingleObject(event->event, INFINITE);
EnterCriticalSection(&filter->cs);
hr = event->interrupted ? S_FALSE : S_OK;
list_remove(entry);
list_add_tail(&filter->free_events, entry);
LeaveCriticalSection(&filter->cs);
return hr;
}
static HRESULT WINAPI filter_Flush(IMediaStreamFilter *iface, BOOL bCancelEOS)
......@@ -948,6 +1027,8 @@ HRESULT filter_create(IUnknown *outer, void **out)
object->IMediaStreamFilter_iface.lpVtbl = &filter_vtbl;
object->IMediaSeeking_iface.lpVtbl = &filter_seeking_vtbl;
object->refcount = 1;
list_init(&object->free_events);
list_init(&object->used_events);
InitializeCriticalSection(&object->cs);
object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MediaStreamFilter.cs");
......
......@@ -3064,11 +3064,21 @@ out_unknown:
IUnknown_Release(unknown);
}
struct advise_time_cookie
{
LONGLONG base;
LONGLONG offset;
HANDLE event;
HANDLE advise_time_called_event;
BOOL unadvise_called;
};
struct testclock
{
IReferenceClock IReferenceClock_iface;
LONG refcount;
LONGLONG time;
struct advise_time_cookie *advise_time_cookie;
HRESULT get_time_hr;
};
......@@ -3112,7 +3122,19 @@ static HRESULT WINAPI testclock_GetTime(IReferenceClock *iface, REFERENCE_TIME *
static HRESULT WINAPI testclock_AdviseTime(IReferenceClock *iface, REFERENCE_TIME base, REFERENCE_TIME offset, HEVENT event, DWORD_PTR *cookie)
{
struct testclock *clock = impl_from_IReferenceClock(iface);
if (clock->advise_time_cookie)
{
clock->advise_time_cookie->base = base;
clock->advise_time_cookie->offset = offset;
clock->advise_time_cookie->event = (HANDLE)event;
SetEvent(clock->advise_time_cookie->advise_time_called_event);
}
else
{
SetEvent((HANDLE)event);
}
*cookie = (DWORD_PTR)clock->advise_time_cookie;
return S_OK;
}
......@@ -3124,8 +3146,9 @@ static HRESULT WINAPI testclock_AdvisePeriodic(IReferenceClock *iface, REFERENCE
static HRESULT WINAPI testclock_Unadvise(IReferenceClock *iface, DWORD_PTR cookie)
{
ok(0, "Unexpected call.\n");
return E_NOTIMPL;
if (cookie)
((struct advise_time_cookie *)cookie)->unadvise_called = TRUE;
return S_OK;
}
static IReferenceClockVtbl testclock_vtbl =
......@@ -6685,6 +6708,155 @@ static void test_mediastreamfilter_reference_time_to_stream_time(void)
ok(!ref, "Got outstanding refcount %d.\n", ref);
}
struct mediastreamfilter_wait_until_params
{
IMediaStreamFilter *filter;
REFERENCE_TIME time;
HRESULT expected_hr;
};
static DWORD CALLBACK mediastreamfilter_wait_until(void *p)
{
struct mediastreamfilter_wait_until_params *params = (struct mediastreamfilter_wait_until_params *)p;
HRESULT hr;
hr = IMediaStreamFilter_WaitUntil(params->filter, params->time);
ok(hr == params->expected_hr, "Got hr %#x.\n", hr);
return 0;
}
static void test_mediastreamfilter_wait_until(void)
{
struct mediastreamfilter_wait_until_params params1;
struct mediastreamfilter_wait_until_params params2;
struct advise_time_cookie cookie1 = { 0 };
struct advise_time_cookie cookie2 = { 0 };
IMediaStreamFilter *filter;
struct testclock clock;
HANDLE thread1;
HANDLE thread2;
HRESULT hr;
ULONG ref;
hr = CoCreateInstance(&CLSID_MediaStreamFilter, NULL, CLSCTX_INPROC_SERVER,
&IID_IMediaStreamFilter, (void **)&filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
testclock_init(&clock);
cookie1.advise_time_called_event = CreateEventW(NULL, FALSE, FALSE, NULL);
cookie2.advise_time_called_event = CreateEventW(NULL, FALSE, FALSE, NULL);
hr = IMediaStreamFilter_Run(filter, 12345678);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaStreamFilter_WaitUntil(filter, 23456789);
ok(hr == E_FAIL, "Got hr %#x.\n", hr);
hr = IMediaStreamFilter_Stop(filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IMediaStreamFilter_SetSyncSource(filter, &clock.IReferenceClock_iface);
ok(hr == S_OK, "Got hr %#x.\n", hr);
clock.advise_time_cookie = &cookie1;
params1.filter = filter;
params1.time = 23456789;
params1.expected_hr = S_OK;
thread1 = CreateThread(NULL, 0, mediastreamfilter_wait_until, &params1, 0, NULL);
ok(!WaitForSingleObject(cookie1.advise_time_called_event, 2000), "Expected AdviseTime to be called.\n");
ok(WaitForSingleObject(thread1, 100) == WAIT_TIMEOUT, "WaitUntil returned prematurely.\n");
ok(cookie1.base == 23456789, "Got base %s.\n", wine_dbgstr_longlong(cookie1.base));
ok(cookie1.offset == 12345678, "Got offset %s.\n", wine_dbgstr_longlong(cookie1.offset));
ok(!!cookie1.event, "Expected non-NULL event.\n");
SetEvent(cookie1.event);
ok(!WaitForSingleObject(thread1, 2000), "Wait timed out.\n");
CloseHandle(thread1);
ok(!cookie1.unadvise_called, "Unexpected Unadvise call.\n");
hr = IMediaStreamFilter_Run(filter, 12345678);
ok(hr == S_OK, "Got hr %#x.\n", hr);
clock.time = 30000000;
clock.advise_time_cookie = &cookie1;
params1.filter = filter;
params1.time = 23456789;
params1.expected_hr = S_OK;
thread1 = CreateThread(NULL, 0, mediastreamfilter_wait_until, &params1, 0, NULL);
ok(!WaitForSingleObject(cookie1.advise_time_called_event, 2000), "Expected AdviseTime to be called.\n");
ok(WaitForSingleObject(thread1, 100) == WAIT_TIMEOUT, "WaitUntil returned prematurely.\n");
ok(cookie1.base == 23456789, "Got base %s.\n", wine_dbgstr_longlong(cookie1.base));
ok(cookie1.offset == 12345678, "Got offset %s.\n", wine_dbgstr_longlong(cookie1.offset));
ok(!!cookie1.event, "Expected non-NULL event.\n");
clock.advise_time_cookie = &cookie2;
params2.filter = filter;
params2.time = 11111111;
params2.expected_hr = S_OK;
thread2 = CreateThread(NULL, 0, mediastreamfilter_wait_until, &params2, 0, NULL);
ok(!WaitForSingleObject(cookie2.advise_time_called_event, 2000), "Expected AdviseTime to be called.\n");
ok(WaitForSingleObject(thread2, 100) == WAIT_TIMEOUT, "WaitUntil returned prematurely.\n");
ok(cookie2.base == 11111111, "Got base %s.\n", wine_dbgstr_longlong(cookie2.base));
ok(cookie2.offset == 12345678, "Got offset %s.\n", wine_dbgstr_longlong(cookie2.offset));
ok(!!cookie2.event, "Expected non-NULL event.\n");
SetEvent(cookie1.event);
ok(!WaitForSingleObject(thread1, 2000), "Wait timed out.\n");
CloseHandle(thread1);
ok(WaitForSingleObject(thread2, 100) == WAIT_TIMEOUT, "WaitUntil returned prematurely.\n");
SetEvent(cookie2.event);
ok(!WaitForSingleObject(thread2, 2000), "Wait timed out.\n");
CloseHandle(thread2);
clock.advise_time_cookie = &cookie1;
params1.filter = filter;
params1.time = 23456789;
params1.expected_hr = S_FALSE;
thread1 = CreateThread(NULL, 0, mediastreamfilter_wait_until, &params1, 0, NULL);
ok(!WaitForSingleObject(cookie1.advise_time_called_event, 2000), "Expected AdviseTime to be called.\n");
ok(WaitForSingleObject(thread1, 100) == WAIT_TIMEOUT, "WaitUntil returned prematurely.\n");
clock.advise_time_cookie = &cookie2;
params2.filter = filter;
params2.time = 23456789;
params2.expected_hr = S_FALSE;
thread2 = CreateThread(NULL, 0, mediastreamfilter_wait_until, &params2, 0, NULL);
ok(!WaitForSingleObject(cookie2.advise_time_called_event, 2000), "Expected AdviseTime to be called.\n");
ok(WaitForSingleObject(thread2, 100) == WAIT_TIMEOUT, "WaitUntil returned prematurely.\n");
hr = IMediaStreamFilter_Stop(filter);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(cookie1.unadvise_called, "Expected Unadvise to be called.\n");
ok(cookie2.unadvise_called, "Expected Unadvise to be called.\n");
ok(!WaitForSingleObject(thread1, 2000), "Wait timed out.\n");
CloseHandle(thread1);
ok(!WaitForSingleObject(thread2, 2000), "Wait timed out.\n");
CloseHandle(thread2);
CloseHandle(cookie1.advise_time_called_event);
CloseHandle(cookie2.advise_time_called_event);
ref = IMediaStreamFilter_Release(filter);
ok(!ref, "Got outstanding refcount %d.\n", ref);
}
static void test_ddrawstream_getsetdirectdraw(void)
{
IAMMultiMediaStream *mmstream = create_ammultimediastream();
......@@ -8330,6 +8502,7 @@ START_TEST(amstream)
test_mediastreamfilter_get_stop_position();
test_mediastreamfilter_get_current_stream_time();
test_mediastreamfilter_reference_time_to_stream_time();
test_mediastreamfilter_wait_until();
CoUninitialize();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment