Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/multimedia.c | 4 ++ dlls/amstream/tests/amstream.c | 84 ++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+)
diff --git a/dlls/amstream/multimedia.c b/dlls/amstream/multimedia.c index 82ef4822344..967baacb747 100644 --- a/dlls/amstream/multimedia.c +++ b/dlls/amstream/multimedia.c @@ -156,7 +156,11 @@ static HRESULT WINAPI multimedia_stream_SetState(IAMMultiMediaStream *iface, STR { hr = IMediaControl_Run(This->media_control); if (SUCCEEDED(hr)) + { + FILTER_STATE state; + IMediaControl_GetState(This->media_control, INFINITE, (OAFilterState *)&state); hr = S_OK; + } } else if (new_state == STREAMSTATE_STOP) hr = IMediaControl_Stop(This->media_control); diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index b436840e407..cb2b3dd76db 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -924,11 +924,13 @@ struct testfilter LONGLONG current_position; LONGLONG stop_position; const AM_MEDIA_TYPE *preferred_mt; + CONDITION_VARIABLE wait_state_cv; HRESULT get_duration_hr; HRESULT get_stop_position_hr; HRESULT set_positions_hr; HRESULT init_stream_hr; HRESULT cleanup_stream_hr; + HRESULT wait_state_hr; };
static inline struct testfilter *impl_from_BaseFilter(struct strmbase_filter *iface) @@ -971,12 +973,37 @@ static HRESULT testfilter_cleanup_stream(struct strmbase_filter *iface) return filter->cleanup_stream_hr; }
+static HRESULT testfilter_wait_state(struct strmbase_filter *iface, DWORD timeout) +{ + struct testfilter *filter = impl_from_BaseFilter(iface); + DWORD start_time = GetTickCount(); + DWORD elapsed = 0; + HRESULT hr; + + EnterCriticalSection(&filter->filter.csFilter); + + while (filter->wait_state_hr == VFW_S_STATE_INTERMEDIATE && elapsed < timeout) + { + DWORD sleep_time = timeout - elapsed; + if (!SleepConditionVariableCS(&filter->wait_state_cv, &filter->filter.csFilter, sleep_time)) + break; + elapsed = GetTickCount() - start_time; + } + + hr = filter->wait_state_hr; + + LeaveCriticalSection(&filter->filter.csFilter); + + return hr; +} + static const struct strmbase_filter_ops testfilter_ops = { .filter_get_pin = testfilter_get_pin, .filter_destroy = testfilter_destroy, .filter_init_stream = testfilter_init_stream, .filter_cleanup_stream = testfilter_cleanup_stream, + .filter_wait_state = testfilter_wait_state, };
static inline struct testfilter *impl_from_base_pin(struct strmbase_pin *iface) @@ -1060,6 +1087,7 @@ static void testfilter_init(struct testfilter *filter) strmbase_filter_init(&filter->filter, NULL, &clsid, &testfilter_ops); strmbase_source_init(&filter->source, &filter->filter, L"", &testsource_ops); filter->stop_position = 0x8000000000000000ULL; + InitializeConditionVariable(&filter->wait_state_cv); }
static inline struct testfilter *impl_from_IMediaSeeking(IMediaSeeking *iface) @@ -2319,11 +2347,25 @@ static void test_initialize(void) IUnknown_Release(graph_inner_unk); }
+static IAMMultiMediaStream *mmstream_mmstream; +static STREAM_STATE mmstream_state; + +static DWORD CALLBACK mmstream_set_state(void *param) +{ + HRESULT hr; + + hr = IAMMultiMediaStream_SetState(mmstream_mmstream, mmstream_state); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + return 0; +} + static void test_set_state(void) { IAMMultiMediaStream *mmstream = create_ammultimediastream(); struct testfilter source; IGraphBuilder *graph; + HANDLE thread; HRESULT hr; ULONG ref;
@@ -2353,6 +2395,41 @@ static void test_set_state(void) hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); ok(hr == S_OK, "Got hr %#x.\n", hr);
+ source.wait_state_hr = E_FAIL; + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + source.wait_state_hr = S_OK; + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + source.wait_state_hr = VFW_S_STATE_INTERMEDIATE; + + mmstream_mmstream = mmstream; + mmstream_state = STREAMSTATE_RUN; + thread = CreateThread(NULL, 0, mmstream_set_state, NULL, 0, NULL); + + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "SetState returned prematurely.\n"); + + EnterCriticalSection(&source.filter.csFilter); + source.wait_state_hr = S_OK; + WakeAllConditionVariable(&source.wait_state_cv); + LeaveCriticalSection(&source.filter.csFilter); + + ok(!WaitForSingleObject(thread, 2000), "Wait timed out.\n"); + CloseHandle(thread); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + source.init_stream_hr = E_FAIL; + source.wait_state_hr = VFW_S_STATE_INTERMEDIATE; + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == E_FAIL, "Got hr %#x.\n", hr); + source.init_stream_hr = S_OK; + source.wait_state_hr = S_OK; + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); ok(hr == S_OK, "Got hr %#x.\n", hr); source.cleanup_stream_hr = E_FAIL; @@ -2369,6 +2446,13 @@ static void test_set_state(void) ok(hr == S_FALSE, "Got hr %#x.\n", hr); source.cleanup_stream_hr = S_OK;
+ hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + source.wait_state_hr = VFW_S_STATE_INTERMEDIATE; + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + source.wait_state_hr = S_OK; + ref = IAMMultiMediaStream_Release(mmstream); ok(!ref, "Got outstanding refcount %d.\n", ref); ref = IGraphBuilder_Release(graph);
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/multimedia.c | 11 +++++++++++ dlls/amstream/tests/amstream.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+)
diff --git a/dlls/amstream/multimedia.c b/dlls/amstream/multimedia.c index 967baacb747..f927cd106a0 100644 --- a/dlls/amstream/multimedia.c +++ b/dlls/amstream/multimedia.c @@ -472,6 +472,17 @@ static HRESULT WINAPI multimedia_stream_OpenFile(IAMMultiMediaStream *iface, } }
+ if (SUCCEEDED(ret) && (flags & AMMSF_NOCLOCK)) + { + IMediaFilter *media_filter; + + if (SUCCEEDED(ret = IGraphBuilder_QueryInterface(This->graph, &IID_IMediaFilter, (void **)&media_filter))) + { + ret = IMediaFilter_SetSyncSource(media_filter, NULL); + IMediaFilter_Release(media_filter); + } + } + IMediaStreamFilter_SupportSeeking(This->filter, This->type == STREAMTYPE_READ);
if (SUCCEEDED(ret) && (flags & AMMSF_RUN)) diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index cb2b3dd76db..dbde4f6e20e 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -418,6 +418,8 @@ static void test_openfile(const WCHAR *test_avi_path) IAMMultiMediaStream *mmstream = create_ammultimediastream(); IMediaControl *media_control; IMediaStreamFilter *filter; + IMediaFilter *media_filter; + IReferenceClock *clock; IGraphBuilder *graph; OAFilterState state; HRESULT hr; @@ -512,6 +514,34 @@ static void test_openfile(const WCHAR *test_avi_path) IMediaControl_Release(media_control); ref = IGraphBuilder_Release(graph); ok(!ref, "Got outstanding refcount %d.\n", ref); + + mmstream = create_ammultimediastream(); + hr = IAMMultiMediaStream_AddMediaStream(mmstream, NULL, &MSPID_PrimaryAudio, 0, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_GetFilterGraph(mmstream, &graph); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(!!graph, "Expected non-NULL graph.\n"); + hr = IGraphBuilder_QueryInterface(graph, &IID_IMediaFilter, (void **)&media_filter); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_OpenFile(mmstream, test_avi_path, AMMSF_NOCLOCK); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + clock = (IReferenceClock *)0xdeadbeef; + IMediaFilter_GetSyncSource(media_filter, &clock); + ok(!clock, "Got clock %p.\n", clock); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + ref = IAMMultiMediaStream_Release(mmstream); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IMediaFilter_Release(media_filter); + ref = IGraphBuilder_Release(graph); + ok(!ref, "Got outstanding refcount %d.\n", ref); }
static void test_mmstream_get_duration(const WCHAR *test_avi_path)
On 10/22/20 2:06 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov baskanov@gmail.com
dlls/amstream/multimedia.c | 11 +++++++++++ dlls/amstream/tests/amstream.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+)
diff --git a/dlls/amstream/multimedia.c b/dlls/amstream/multimedia.c index 967baacb747..f927cd106a0 100644 --- a/dlls/amstream/multimedia.c +++ b/dlls/amstream/multimedia.c @@ -472,6 +472,17 @@ static HRESULT WINAPI multimedia_stream_OpenFile(IAMMultiMediaStream *iface, } }
if (SUCCEEDED(ret) && (flags & AMMSF_NOCLOCK))
{
IMediaFilter *media_filter;
if (SUCCEEDED(ret = IGraphBuilder_QueryInterface(This->graph, &IID_IMediaFilter, (void **)&media_filter)))
{
ret = IMediaFilter_SetSyncSource(media_filter, NULL);
IMediaFilter_Release(media_filter);
}
}
IMediaStreamFilter_SupportSeeking(This->filter, This->type == STREAMTYPE_READ);
if (SUCCEEDED(ret) && (flags & AMMSF_RUN))
diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index cb2b3dd76db..dbde4f6e20e 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -418,6 +418,8 @@ static void test_openfile(const WCHAR *test_avi_path) IAMMultiMediaStream *mmstream = create_ammultimediastream(); IMediaControl *media_control; IMediaStreamFilter *filter;
- IMediaFilter *media_filter;
- IReferenceClock *clock; IGraphBuilder *graph; OAFilterState state; HRESULT hr;
@@ -512,6 +514,34 @@ static void test_openfile(const WCHAR *test_avi_path) IMediaControl_Release(media_control); ref = IGraphBuilder_Release(graph); ok(!ref, "Got outstanding refcount %d.\n", ref);
- mmstream = create_ammultimediastream();
- hr = IAMMultiMediaStream_AddMediaStream(mmstream, NULL, &MSPID_PrimaryAudio, 0, NULL);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IAMMultiMediaStream_GetFilterGraph(mmstream, &graph);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- ok(!!graph, "Expected non-NULL graph.\n");
- hr = IGraphBuilder_QueryInterface(graph, &IID_IMediaFilter, (void **)&media_filter);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IAMMultiMediaStream_OpenFile(mmstream, test_avi_path, AMMSF_NOCLOCK);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- clock = (IReferenceClock *)0xdeadbeef;
- IMediaFilter_GetSyncSource(media_filter, &clock);
- ok(!clock, "Got clock %p.\n", clock);
- hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- ref = IAMMultiMediaStream_Release(mmstream);
- ok(!ref, "Got outstanding refcount %d.\n", ref);
- IMediaFilter_Release(media_filter);
- ref = IGraphBuilder_Release(graph);
- ok(!ref, "Got outstanding refcount %d.\n", ref);
}
static void test_mmstream_get_duration(const WCHAR *test_avi_path)
Can you please also check that the clock is otherwise non-NULL?
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/filter.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-)
diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index b63a0301d64..daeb1fafe71 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -795,15 +795,16 @@ static HRESULT WINAPI filter_seeking_GetDuration(IMediaSeeking *iface, LONGLONG
EnterCriticalSection(&filter->cs);
- if (!(seeking = get_seeking(filter->seekable_stream))) - { - LeaveCriticalSection(&filter->cs); + seeking = get_seeking(filter->seekable_stream); + + LeaveCriticalSection(&filter->cs); + + if (!seeking) return E_NOTIMPL; - } + hr = IMediaSeeking_GetDuration(seeking, duration); IMediaSeeking_Release(seeking);
- LeaveCriticalSection(&filter->cs); return hr; }
@@ -817,15 +818,16 @@ static HRESULT WINAPI filter_seeking_GetStopPosition(IMediaSeeking *iface, LONGL
EnterCriticalSection(&filter->cs);
- if (!(seeking = get_seeking(filter->seekable_stream))) - { - LeaveCriticalSection(&filter->cs); + seeking = get_seeking(filter->seekable_stream); + + LeaveCriticalSection(&filter->cs); + + if (!seeking) return E_NOTIMPL; - } + hr = IMediaSeeking_GetStopPosition(seeking, stop); IMediaSeeking_Release(seeking);
- LeaveCriticalSection(&filter->cs); return hr; }
@@ -860,18 +862,15 @@ static HRESULT WINAPI filter_seeking_SetPositions(IMediaSeeking *iface, LONGLONG
seeking = get_seeking(filter->seekable_stream);
+ LeaveCriticalSection(&filter->cs); + if (!seeking) - { - LeaveCriticalSection(&filter->cs); return E_NOTIMPL; - }
hr = IMediaSeeking_SetPositions(seeking, current_ptr, current_flags, stop_ptr, stop_flags);
IMediaSeeking_Release(seeking);
- LeaveCriticalSection(&filter->cs); - return hr; }
On 10/22/20 2:06 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov baskanov@gmail.com
dlls/amstream/filter.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-)
diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index b63a0301d64..daeb1fafe71 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -795,15 +795,16 @@ static HRESULT WINAPI filter_seeking_GetDuration(IMediaSeeking *iface, LONGLONG
EnterCriticalSection(&filter->cs);
- if (!(seeking = get_seeking(filter->seekable_stream)))
- {
LeaveCriticalSection(&filter->cs);
- seeking = get_seeking(filter->seekable_stream);
- LeaveCriticalSection(&filter->cs);
- if (!seeking) return E_NOTIMPL;
- }
- hr = IMediaSeeking_GetDuration(seeking, duration); IMediaSeeking_Release(seeking);
- LeaveCriticalSection(&filter->cs); return hr;
}
@@ -817,15 +818,16 @@ static HRESULT WINAPI filter_seeking_GetStopPosition(IMediaSeeking *iface, LONGL
EnterCriticalSection(&filter->cs);
- if (!(seeking = get_seeking(filter->seekable_stream)))
- {
LeaveCriticalSection(&filter->cs);
- seeking = get_seeking(filter->seekable_stream);
- LeaveCriticalSection(&filter->cs);
- if (!seeking) return E_NOTIMPL;
- }
- hr = IMediaSeeking_GetStopPosition(seeking, stop); IMediaSeeking_Release(seeking);
- LeaveCriticalSection(&filter->cs); return hr;
}
@@ -860,18 +862,15 @@ static HRESULT WINAPI filter_seeking_SetPositions(IMediaSeeking *iface, LONGLONG
seeking = get_seeking(filter->seekable_stream);
- LeaveCriticalSection(&filter->cs);
- if (!seeking)
{
LeaveCriticalSection(&filter->cs); return E_NOTIMPL;
}
hr = IMediaSeeking_SetPositions(seeking, current_ptr, current_flags, stop_ptr, stop_flags);
IMediaSeeking_Release(seeking);
LeaveCriticalSection(&filter->cs);
return hr;
}
I think this is safe, but the motivation isn't clear to me. Can you please provide a brief explanation?
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/filter.c | 95 +++++++++++++++++- dlls/amstream/tests/amstream.c | 177 ++++++++++++++++++++++++++++++++- 2 files changed, 266 insertions(+), 6 deletions(-)
diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index daeb1fafe71..763ecd434aa 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -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 list *entry;
TRACE("iface %p.\n", iface);
@@ -268,6 +289,17 @@ static HRESULT WINAPI filter_Stop(IMediaStreamFilter *iface)
set_state(filter, State_Stopped);
+ while ((entry = list_head(&filter->used_events))) + { + struct event *event = LIST_ENTRY(entry, struct event, entry); + + list_remove(entry); + + event->interrupted = TRUE; + IReferenceClock_Unadvise(filter->clock, event->cookie); + SetEvent(event->event); + } + LeaveCriticalSection(&filter->cs);
return S_OK; @@ -664,11 +696,66 @@ 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); + + if (event->interrupted) + { + hr = S_FALSE; + } + else + { + list_remove(entry); + hr = S_OK; + } + + list_add_tail(&filter->free_events, entry); + + LeaveCriticalSection(&filter->cs); + + return hr; }
static HRESULT WINAPI filter_Flush(IMediaStreamFilter *iface, BOOL bCancelEOS) @@ -948,6 +1035,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");
diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index dbde4f6e20e..d89f1ecb109 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -3052,11 +3052,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; };
@@ -3100,7 +3110,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) { - SetEvent((HANDLE)event); + 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; }
@@ -3112,8 +3134,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 = @@ -6673,6 +6696,153 @@ 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, ¶ms1, 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.advise_time_cookie = &cookie1; + + params1.filter = filter; + params1.time = 23456789; + params1.expected_hr = S_OK; + thread1 = CreateThread(NULL, 0, mediastreamfilter_wait_until, ¶ms1, 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, ¶ms2, 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, ¶ms1, 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, ¶ms2, 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(); @@ -8318,6 +8488,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(); }
On 10/22/20 2:06 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov baskanov@gmail.com
dlls/amstream/filter.c | 95 +++++++++++++++++- dlls/amstream/tests/amstream.c | 177 ++++++++++++++++++++++++++++++++- 2 files changed, 266 insertions(+), 6 deletions(-)
diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index daeb1fafe71..763ecd434aa 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -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 list *entry;
TRACE("iface %p.\n", iface);
@@ -268,6 +289,17 @@ static HRESULT WINAPI filter_Stop(IMediaStreamFilter *iface)
set_state(filter, State_Stopped);
- while ((entry = list_head(&filter->used_events)))
- {
struct event *event = LIST_ENTRY(entry, struct event, entry);
list_remove(entry);
It strikes me as a little bit simpler to get rid of this list_remove(), use a simple LIST_FOR_EACH_ENTRY in this function, and then always list_remove in WaitUntil().
event->interrupted = TRUE;
IReferenceClock_Unadvise(filter->clock, event->cookie);
SetEvent(event->event);
}
LeaveCriticalSection(&filter->cs);
return S_OK;
@@ -664,11 +696,66 @@ 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);
- if (event->interrupted)
- {
hr = S_FALSE;
- }
- else
- {
list_remove(entry);
hr = S_OK;
- }
- list_add_tail(&filter->free_events, entry);
- LeaveCriticalSection(&filter->cs);
- return hr;
}
static HRESULT WINAPI filter_Flush(IMediaStreamFilter *iface, BOOL bCancelEOS) @@ -948,6 +1035,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");
diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index dbde4f6e20e..d89f1ecb109 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -3052,11 +3052,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;
};
@@ -3100,7 +3110,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) {
- SetEvent((HANDLE)event);
- 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;
}
@@ -3112,8 +3134,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 = @@ -6673,6 +6696,153 @@ 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, ¶ms1, 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.advise_time_cookie = &cookie1;
- params1.filter = filter;
- params1.time = 23456789;
- params1.expected_hr = S_OK;
- thread1 = CreateThread(NULL, 0, mediastreamfilter_wait_until, ¶ms1, 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, ¶ms2, 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, ¶ms1, 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, ¶ms2, 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);
Can you please also check whether flushing unblocks waits? The documentation implies it does, but it's also been wrong before...
- 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(); @@ -8318,6 +8488,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();
}
On Friday, 23 October 2020 23:30:55 +07 you wrote:
On 10/22/20 2:06 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov baskanov@gmail.com
dlls/amstream/filter.c | 95 +++++++++++++++++- dlls/amstream/tests/amstream.c | 177 ++++++++++++++++++++++++++++++++- 2 files changed, 266 insertions(+), 6 deletions(-)
diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index daeb1fafe71..763ecd434aa 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -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 list *entry;
TRACE("iface %p.\n", iface);
@@ -268,6 +289,17 @@ static HRESULT WINAPI filter_Stop(IMediaStreamFilter *iface)> set_state(filter, State_Stopped);
- while ((entry = list_head(&filter->used_events)))
- {
struct event *event = LIST_ENTRY(entry, struct event, entry);
list_remove(entry);
It strikes me as a little bit simpler to get rid of this list_remove(), use a simple LIST_FOR_EACH_ENTRY in this function, and then always list_remove in WaitUntil().
Done.
event->interrupted = TRUE;
IReferenceClock_Unadvise(filter->clock, event->cookie);
SetEvent(event->event);
}
LeaveCriticalSection(&filter->cs);
return S_OK;
@@ -664,11 +696,66 @@ 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);
- if (event->interrupted)
- {
hr = S_FALSE;
- }
- else
- {
list_remove(entry);
hr = S_OK;
- }
- list_add_tail(&filter->free_events, entry);
- LeaveCriticalSection(&filter->cs);
- return hr;
}
static HRESULT WINAPI filter_Flush(IMediaStreamFilter *iface, BOOL bCancelEOS)> @@ -948,6 +1035,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");>
diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index dbde4f6e20e..d89f1ecb109 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c
@@ -3052,11 +3052,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;
};
@@ -3100,7 +3110,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) {
- SetEvent((HANDLE)event);
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;
}
@@ -3112,8 +3134,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 =
@@ -6673,6 +6696,153 @@ 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,
¶ms1, 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.advise_time_cookie = &cookie1;
- params1.filter = filter;
- params1.time = 23456789;
- params1.expected_hr = S_OK;
- thread1 = CreateThread(NULL, 0, mediastreamfilter_wait_until,
¶ms1, 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,
¶ms2, 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,
¶ms1, 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,
¶ms2, 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);
Can you please also check whether flushing unblocks waits? The documentation implies it does, but it's also been wrong before...
MediaStreamFilter::Flush was unimplemented, so I added another patch to the series that implements it and adds tests.
- 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();
@@ -8318,6 +8488,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();
}
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/ddrawstream.c | 44 ++++++++--- dlls/amstream/tests/amstream.c | 137 +++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 9 deletions(-)
diff --git a/dlls/amstream/ddrawstream.c b/dlls/amstream/ddrawstream.c index 190034fa008..da1d65ff170 100644 --- a/dlls/amstream/ddrawstream.c +++ b/dlls/amstream/ddrawstream.c @@ -1344,25 +1344,51 @@ static HRESULT WINAPI ddraw_meminput_Receive(IMemInputPin *iface, IMediaSample * LeaveCriticalSection(&stream->cs); return S_FALSE; } - if (!list_empty(&stream->update_queue)) + while (!list_empty(&stream->update_queue)) { struct ddraw_sample *sample = LIST_ENTRY(list_head(&stream->update_queue), struct ddraw_sample, entry); + struct ddraw_sample *sample2; + IMediaStreamFilter *filter; + HRESULT update_hr;
- sample->update_hr = process_update(sample, top_down_stride, top_down_pointer, + update_hr = process_update(sample, top_down_stride, top_down_pointer, start_stream_time, end_stream_time);
- if (sample->continuous_update && SUCCEEDED(sample->update_hr)) + filter = stream->filter; + + LeaveCriticalSection(&stream->cs); + IMediaStreamFilter_WaitUntil(filter, start_time); + EnterCriticalSection(&stream->cs); + + if (stream->state == State_Stopped) { - list_remove(&sample->entry); - list_add_tail(&sample->parent->update_queue, &sample->entry); + LeaveCriticalSection(&stream->cs); + return S_OK; } - else + if (stream->flushing) { - remove_queued_update(sample); + LeaveCriticalSection(&stream->cs); + return S_FALSE; }
- LeaveCriticalSection(&stream->cs); - return S_OK; + LIST_FOR_EACH_ENTRY(sample2, &stream->update_queue, struct ddraw_sample, entry) + { + if (sample2 == sample) + { + sample->update_hr = update_hr; + if (sample->continuous_update && SUCCEEDED(sample->update_hr)) + { + list_remove(&sample->entry); + list_add_tail(&sample->parent->update_queue, &sample->entry); + } + else + { + remove_queued_update(sample); + } + LeaveCriticalSection(&stream->cs); + return S_OK; + } + } }
SleepConditionVariableCS(&stream->update_queued_cv, &stream->cs, INFINITE); diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index d89f1ecb109..50e75fd0395 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -7861,9 +7861,11 @@ static void test_ddrawstreamsample_completion_status(void) IDirectDrawStreamSample *stream_sample1; IDirectDrawStreamSample *stream_sample2; IDirectDrawMediaStream *ddraw_stream; + struct advise_time_cookie cookie = { 0 }; IMediaSample *media_sample; IMediaFilter *media_filter; struct testfilter source; + struct testclock clock; VIDEOINFO video_info; IGraphBuilder *graph; IMediaStream *stream; @@ -7889,6 +7891,8 @@ static void test_ddrawstreamsample_completion_status(void) testfilter_init(&source); hr = IGraphBuilder_AddFilter(graph, &source.filter.IBaseFilter_iface, NULL); ok(hr == S_OK, "Got hr %#x.\n", hr); + testclock_init(&clock); + cookie.advise_time_called_event = CreateEventW(NULL, FALSE, FALSE, NULL);
hr = IMediaFilter_SetSyncSource(media_filter, NULL); ok(hr == S_OK, "Got hr %#x.\n", hr); @@ -8246,9 +8250,142 @@ static void test_ddrawstreamsample_completion_status(void) hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, COMPSTAT_WAIT, INFINITE); ok(hr == S_OK, "Got hr %#x.\n", hr);
+ hr = IMediaFilter_SetSyncSource(media_filter, &clock.IReferenceClock_iface); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + clock.advise_time_cookie = &cookie; + + hr = IDirectDrawStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + media_sample = ammediastream_allocate_sample(&source, test_data, sizeof(test_data)); + + ammediastream_mem_input_pin = source.source.pMemInputPin; + ammediastream_media_sample = media_sample; + ammediastream_sleep_time = 0; + ammediastream_expected_hr = S_OK; + thread = CreateThread(NULL, 0, ammediastream_receive, NULL, 0, NULL); + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n"); + + hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, 0, INFINITE); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + SetEvent(cookie.event); + + ok(!WaitForSingleObject(thread, 2000), "Wait timed out.\n"); + CloseHandle(thread); + + ref = IMediaSample_Release(media_sample); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, 0, INFINITE); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IDirectDrawStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + media_sample = ammediastream_allocate_sample(&source, test_data, sizeof(test_data)); + + ammediastream_mem_input_pin = source.source.pMemInputPin; + ammediastream_media_sample = media_sample; + ammediastream_sleep_time = 0; + ammediastream_expected_hr = S_OK; + thread = CreateThread(NULL, 0, ammediastream_receive, NULL, 0, NULL); + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n"); + + hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, COMPSTAT_NOUPDATEOK, INFINITE); + ok(hr == MS_S_NOUPDATE, "Got hr %#x.\n", hr); + + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n"); + + SetEvent(cookie.event); + + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n"); + + hr = IDirectDrawStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n"); + + SetEvent(cookie.event); + + ok(!WaitForSingleObject(thread, 2000), "Wait timed out.\n"); + CloseHandle(thread); + + hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, 0, INFINITE); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + ref = IMediaSample_Release(media_sample); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + hr = IDirectDrawStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + media_sample = ammediastream_allocate_sample(&source, test_data, sizeof(test_data)); + + ammediastream_mem_input_pin = source.source.pMemInputPin; + ammediastream_media_sample = media_sample; + ammediastream_sleep_time = 0; + ammediastream_expected_hr = S_OK; + thread = CreateThread(NULL, 0, ammediastream_receive, NULL, 0, NULL); + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n"); + + hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, COMPSTAT_ABORT, INFINITE); + ok(hr == MS_S_NOUPDATE, "Got hr %#x.\n", hr); + + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n"); + + SetEvent(cookie.event); + + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n"); + + hr = IDirectDrawStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n"); + + SetEvent(cookie.event); + + ok(!WaitForSingleObject(thread, 2000), "Wait timed out.\n"); + CloseHandle(thread); + + hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, 0, INFINITE); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + ref = IMediaSample_Release(media_sample); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + hr = IDirectDrawStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + media_sample = ammediastream_allocate_sample(&source, test_data, sizeof(test_data)); + + ammediastream_mem_input_pin = source.source.pMemInputPin; + ammediastream_media_sample = media_sample; + ammediastream_sleep_time = 0; + ammediastream_expected_hr = S_OK; + thread = CreateThread(NULL, 0, ammediastream_receive, NULL, 0, NULL); + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n"); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + ok(!WaitForSingleObject(thread, 2000), "Wait timed out.\n"); + CloseHandle(thread); + + ref = IMediaSample_Release(media_sample); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, 0, INFINITE); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + IGraphBuilder_Disconnect(graph, pin); IGraphBuilder_Disconnect(graph, &source.source.pin.IPin_iface);
+ CloseHandle(cookie.advise_time_called_event); ref = IDirectDrawStreamSample_Release(stream_sample1); ok(!ref, "Got outstanding refcount %d.\n", ref); ref = IDirectDrawStreamSample_Release(stream_sample2);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=80855
Your paranoid android.
=== w2008s64 (32 bit report) ===
amstream: amstream.c:8271: Test failed: Receive returned prematurely. amstream.c:8274: Test failed: Got hr 0. amstream.c:8297: Test failed: Receive returned prematurely. amstream.c:8300: Test failed: Got hr 0. amstream.c:8302: Test failed: Receive returned prematurely. amstream.c:8306: Test failed: Receive returned prematurely. amstream.c:8311: Test failed: Receive returned prematurely. amstream.c:8319: Test failed: Got hr 0x40001. amstream.c:8325: Test failed: Got hr 0x80040406. amstream.c:8334: Test failed: Receive returned prematurely. amstream.c:8337: Test failed: Got hr 0. amstream.c:8339: Test failed: Receive returned prematurely. amstream.c:8343: Test failed: Receive returned prematurely. amstream.c:8348: Test failed: Receive returned prematurely. amstream.c:8356: Test failed: Got hr 0x40001. amstream.c:8362: Test failed: Got hr 0x80040406. amstream.c:8371: Test failed: Receive returned prematurely. amstream.c:8383: Test failed: Got hr 0.
On 10/22/20 2:06 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov baskanov@gmail.com
dlls/amstream/ddrawstream.c | 44 ++++++++--- dlls/amstream/tests/amstream.c | 137 +++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 9 deletions(-)
diff --git a/dlls/amstream/ddrawstream.c b/dlls/amstream/ddrawstream.c index 190034fa008..da1d65ff170 100644 --- a/dlls/amstream/ddrawstream.c +++ b/dlls/amstream/ddrawstream.c @@ -1344,25 +1344,51 @@ static HRESULT WINAPI ddraw_meminput_Receive(IMemInputPin *iface, IMediaSample * LeaveCriticalSection(&stream->cs); return S_FALSE; }
if (!list_empty(&stream->update_queue))
while (!list_empty(&stream->update_queue)) { struct ddraw_sample *sample = LIST_ENTRY(list_head(&stream->update_queue), struct ddraw_sample, entry);
struct ddraw_sample *sample2;
IMediaStreamFilter *filter;
HRESULT update_hr;
sample->update_hr = process_update(sample, top_down_stride, top_down_pointer,
update_hr = process_update(sample, top_down_stride, top_down_pointer, start_stream_time, end_stream_time);
if (sample->continuous_update && SUCCEEDED(sample->update_hr))
filter = stream->filter;
LeaveCriticalSection(&stream->cs);
IMediaStreamFilter_WaitUntil(filter, start_time);
EnterCriticalSection(&stream->cs);
if (stream->state == State_Stopped) {
list_remove(&sample->entry);
list_add_tail(&sample->parent->update_queue, &sample->entry);
LeaveCriticalSection(&stream->cs);
return S_OK; }
else
if (stream->flushing) {
remove_queued_update(sample);
LeaveCriticalSection(&stream->cs);
return S_FALSE; }
LeaveCriticalSection(&stream->cs);
return S_OK;
LIST_FOR_EACH_ENTRY(sample2, &stream->update_queue, struct ddraw_sample, entry)
{
if (sample2 == sample)
{
sample->update_hr = update_hr;
if (sample->continuous_update && SUCCEEDED(sample->update_hr))
{
list_remove(&sample->entry);
list_add_tail(&sample->parent->update_queue, &sample->entry);
}
else
{
remove_queued_update(sample);
}
LeaveCriticalSection(&stream->cs);
return S_OK;
}
} } SleepConditionVariableCS(&stream->update_queued_cv, &stream->cs, INFINITE);
This implies that if you:
* queue sample 1 * queue sample 2 * call Receive * remove sample 1 * signal presentation time
that Receive() won't return, and moreover that if you
* queue sample 1 * queue sample 2 * call Receive * remove sample 1 * queue sample 1 * signal presentation time
that it will return (and fill sample 1). Which seems plausible, but also not the simplest way to implement this behaviour—that seems to me to be to just wait for presentation time and then pick the first sample queued if there is one.
diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index d89f1ecb109..50e75fd0395 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -7861,9 +7861,11 @@ static void test_ddrawstreamsample_completion_status(void) IDirectDrawStreamSample *stream_sample1; IDirectDrawStreamSample *stream_sample2; IDirectDrawMediaStream *ddraw_stream;
- struct advise_time_cookie cookie = { 0 }; IMediaSample *media_sample; IMediaFilter *media_filter; struct testfilter source;
- struct testclock clock; VIDEOINFO video_info; IGraphBuilder *graph; IMediaStream *stream;
@@ -7889,6 +7891,8 @@ static void test_ddrawstreamsample_completion_status(void) testfilter_init(&source); hr = IGraphBuilder_AddFilter(graph, &source.filter.IBaseFilter_iface, NULL); ok(hr == S_OK, "Got hr %#x.\n", hr);
testclock_init(&clock);
cookie.advise_time_called_event = CreateEventW(NULL, FALSE, FALSE, NULL);
hr = IMediaFilter_SetSyncSource(media_filter, NULL); ok(hr == S_OK, "Got hr %#x.\n", hr);
@@ -8246,9 +8250,142 @@ static void test_ddrawstreamsample_completion_status(void) hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, COMPSTAT_WAIT, INFINITE); ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IMediaFilter_SetSyncSource(media_filter, &clock.IReferenceClock_iface);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- clock.advise_time_cookie = &cookie;
- hr = IDirectDrawStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0);
- ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr);
- media_sample = ammediastream_allocate_sample(&source, test_data, sizeof(test_data));
- ammediastream_mem_input_pin = source.source.pMemInputPin;
- ammediastream_media_sample = media_sample;
- ammediastream_sleep_time = 0;
- ammediastream_expected_hr = S_OK;
- thread = CreateThread(NULL, 0, ammediastream_receive, NULL, 0, NULL);
- ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n");
- hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, 0, INFINITE);
Mildly weird to pass INFINITE without COMPSTAT_WAIT.
ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr);
SetEvent(cookie.event);
ok(!WaitForSingleObject(thread, 2000), "Wait timed out.\n");
CloseHandle(thread);
ref = IMediaSample_Release(media_sample);
ok(!ref, "Got outstanding refcount %d.\n", ref);
hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, 0, INFINITE);
ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IDirectDrawStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0);
ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr);
media_sample = ammediastream_allocate_sample(&source, test_data, sizeof(test_data));
ammediastream_mem_input_pin = source.source.pMemInputPin;
ammediastream_media_sample = media_sample;
ammediastream_sleep_time = 0;
ammediastream_expected_hr = S_OK;
thread = CreateThread(NULL, 0, ammediastream_receive, NULL, 0, NULL);
ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n");
hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, COMPSTAT_NOUPDATEOK, INFINITE);
ok(hr == MS_S_NOUPDATE, "Got hr %#x.\n", hr);
ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n");
SetEvent(cookie.event);
ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n");
hr = IDirectDrawStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0);
ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr);
ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n");
SetEvent(cookie.event);
ok(!WaitForSingleObject(thread, 2000), "Wait timed out.\n");
CloseHandle(thread);
hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, 0, INFINITE);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ref = IMediaSample_Release(media_sample);
ok(!ref, "Got outstanding refcount %d.\n", ref);
hr = IDirectDrawStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0);
ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr);
media_sample = ammediastream_allocate_sample(&source, test_data, sizeof(test_data));
ammediastream_mem_input_pin = source.source.pMemInputPin;
ammediastream_media_sample = media_sample;
ammediastream_sleep_time = 0;
ammediastream_expected_hr = S_OK;
thread = CreateThread(NULL, 0, ammediastream_receive, NULL, 0, NULL);
ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n");
hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, COMPSTAT_ABORT, INFINITE);
ok(hr == MS_S_NOUPDATE, "Got hr %#x.\n", hr);
ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n");
SetEvent(cookie.event);
ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n");
hr = IDirectDrawStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0);
ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr);
ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n");
SetEvent(cookie.event);
ok(!WaitForSingleObject(thread, 2000), "Wait timed out.\n");
CloseHandle(thread);
hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, 0, INFINITE);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ref = IMediaSample_Release(media_sample);
ok(!ref, "Got outstanding refcount %d.\n", ref);
hr = IDirectDrawStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0);
ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr);
media_sample = ammediastream_allocate_sample(&source, test_data, sizeof(test_data));
ammediastream_mem_input_pin = source.source.pMemInputPin;
ammediastream_media_sample = media_sample;
ammediastream_sleep_time = 0;
ammediastream_expected_hr = S_OK;
thread = CreateThread(NULL, 0, ammediastream_receive, NULL, 0, NULL);
ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "Receive returned prematurely.\n");
hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP);
ok(hr == S_OK, "Got hr %#x.\n", hr);
ok(!WaitForSingleObject(thread, 2000), "Wait timed out.\n");
CloseHandle(thread);
ref = IMediaSample_Release(media_sample);
ok(!ref, "Got outstanding refcount %d.\n", ref);
hr = IDirectDrawStreamSample_CompletionStatus(stream_sample1, 0, INFINITE);
ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr);
IGraphBuilder_Disconnect(graph, pin); IGraphBuilder_Disconnect(graph, &source.source.pin.IPin_iface);
CloseHandle(cookie.advise_time_called_event); ref = IDirectDrawStreamSample_Release(stream_sample1); ok(!ref, "Got outstanding refcount %d.\n", ref); ref = IDirectDrawStreamSample_Release(stream_sample2);
On 10/22/20 2:06 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov baskanov@gmail.com
dlls/amstream/multimedia.c | 4 ++ dlls/amstream/tests/amstream.c | 84 ++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+)
diff --git a/dlls/amstream/multimedia.c b/dlls/amstream/multimedia.c index 82ef4822344..967baacb747 100644 --- a/dlls/amstream/multimedia.c +++ b/dlls/amstream/multimedia.c @@ -156,7 +156,11 @@ static HRESULT WINAPI multimedia_stream_SetState(IAMMultiMediaStream *iface, STR { hr = IMediaControl_Run(This->media_control); if (SUCCEEDED(hr))
{
FILTER_STATE state;
IMediaControl_GetState(This->media_control, INFINITE, (OAFilterState *)&state); hr = S_OK;
} else if (new_state == STREAMSTATE_STOP) hr = IMediaControl_Stop(This->media_control);}
diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index b436840e407..cb2b3dd76db 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -924,11 +924,13 @@ struct testfilter LONGLONG current_position; LONGLONG stop_position; const AM_MEDIA_TYPE *preferred_mt;
- CONDITION_VARIABLE wait_state_cv; HRESULT get_duration_hr; HRESULT get_stop_position_hr; HRESULT set_positions_hr; HRESULT init_stream_hr; HRESULT cleanup_stream_hr;
- HRESULT wait_state_hr;
};
static inline struct testfilter *impl_from_BaseFilter(struct strmbase_filter *iface) @@ -971,12 +973,37 @@ static HRESULT testfilter_cleanup_stream(struct strmbase_filter *iface) return filter->cleanup_stream_hr; }
+static HRESULT testfilter_wait_state(struct strmbase_filter *iface, DWORD timeout) +{
- struct testfilter *filter = impl_from_BaseFilter(iface);
- DWORD start_time = GetTickCount();
- DWORD elapsed = 0;
- HRESULT hr;
- EnterCriticalSection(&filter->filter.csFilter);
strmbase actually already grabs this.
- while (filter->wait_state_hr == VFW_S_STATE_INTERMEDIATE && elapsed < timeout)
- {
DWORD sleep_time = timeout - elapsed;
if (!SleepConditionVariableCS(&filter->wait_state_cv, &filter->filter.csFilter, sleep_time))
break;
elapsed = GetTickCount() - start_time;
- }
This is fine, though it'd also be fine, and perhaps a little simpler, to use an event.
(I know I may sound inconsistent. I do think that the usage patterns in prior patches are ideal for condition variables, where the condition needs to be rechecked *anyway*, and there may be multiple threads trying to use the same resource. The fact that condition variables are "fast" in Wine, at least on some platforms, is also a benefit for implementation code. Here the usage pattern is a little different, i.e. much more linear and controlled, so using an event gets a little easier, and of course speed doesn't matter as much.)
- hr = filter->wait_state_hr;
- LeaveCriticalSection(&filter->filter.csFilter);
- return hr;
+}
static const struct strmbase_filter_ops testfilter_ops = { .filter_get_pin = testfilter_get_pin, .filter_destroy = testfilter_destroy, .filter_init_stream = testfilter_init_stream, .filter_cleanup_stream = testfilter_cleanup_stream,
- .filter_wait_state = testfilter_wait_state,
};
static inline struct testfilter *impl_from_base_pin(struct strmbase_pin *iface) @@ -1060,6 +1087,7 @@ static void testfilter_init(struct testfilter *filter) strmbase_filter_init(&filter->filter, NULL, &clsid, &testfilter_ops); strmbase_source_init(&filter->source, &filter->filter, L"", &testsource_ops); filter->stop_position = 0x8000000000000000ULL;
- InitializeConditionVariable(&filter->wait_state_cv);
}
static inline struct testfilter *impl_from_IMediaSeeking(IMediaSeeking *iface) @@ -2319,11 +2347,25 @@ static void test_initialize(void) IUnknown_Release(graph_inner_unk); }
+static IAMMultiMediaStream *mmstream_mmstream; +static STREAM_STATE mmstream_state;
+static DWORD CALLBACK mmstream_set_state(void *param) +{
- HRESULT hr;
- hr = IAMMultiMediaStream_SetState(mmstream_mmstream, mmstream_state);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- return 0;
+}
static void test_set_state(void) { IAMMultiMediaStream *mmstream = create_ammultimediastream(); struct testfilter source; IGraphBuilder *graph;
- HANDLE thread; HRESULT hr; ULONG ref;
@@ -2353,6 +2395,41 @@ static void test_set_state(void) hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); ok(hr == S_OK, "Got hr %#x.\n", hr);
- source.wait_state_hr = E_FAIL;
- hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- source.wait_state_hr = S_OK;
- hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- source.wait_state_hr = VFW_S_STATE_INTERMEDIATE;
- mmstream_mmstream = mmstream;
- mmstream_state = STREAMSTATE_RUN;
- thread = CreateThread(NULL, 0, mmstream_set_state, NULL, 0, NULL);
- ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "SetState returned prematurely.\n");
- EnterCriticalSection(&source.filter.csFilter);
- source.wait_state_hr = S_OK;
- WakeAllConditionVariable(&source.wait_state_cv);
- LeaveCriticalSection(&source.filter.csFilter);
- ok(!WaitForSingleObject(thread, 2000), "Wait timed out.\n");
- CloseHandle(thread);
- hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- source.init_stream_hr = E_FAIL;
- source.wait_state_hr = VFW_S_STATE_INTERMEDIATE;
- hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN);
- ok(hr == E_FAIL, "Got hr %#x.\n", hr);
- source.init_stream_hr = S_OK;
- source.wait_state_hr = S_OK;
- hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); ok(hr == S_OK, "Got hr %#x.\n", hr); source.cleanup_stream_hr = E_FAIL;
@@ -2369,6 +2446,13 @@ static void test_set_state(void) ok(hr == S_FALSE, "Got hr %#x.\n", hr); source.cleanup_stream_hr = S_OK;
- hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- source.wait_state_hr = VFW_S_STATE_INTERMEDIATE;
- hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- source.wait_state_hr = S_OK;
- ref = IAMMultiMediaStream_Release(mmstream); ok(!ref, "Got outstanding refcount %d.\n", ref); ref = IGraphBuilder_Release(graph);