Some filters (e.g. MediaStreamFilter) can become seekable when they are already in the graph.
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/quartz/filtergraph.c | 30 +++++++++--- dlls/quartz/tests/filtergraph.c | 86 +++++++++++++++++++++++++++++++-- 2 files changed, 105 insertions(+), 11 deletions(-)
diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c index 6217516db1..fa0a15a3fc 100644 --- a/dlls/quartz/filtergraph.c +++ b/dlls/quartz/filtergraph.c @@ -585,6 +585,18 @@ static BOOL has_output_pins(IBaseFilter *filter) return FALSE; }
+static void update_seeking(struct filter *filter) +{ + if (!filter->seeking) + { + /* The Legend of Heroes: Trails of Cold Steel II destroys its filter when + * its IMediaSeeking interface is released, so cache the interface instead + * of querying for it every time. */ + if (FAILED(IBaseFilter_QueryInterface(filter->filter, &IID_IMediaSeeking, (void **)&filter->seeking))) + filter->seeking = NULL; + } +} + static BOOL is_renderer(struct filter *filter) { IAMFilterMiscFlags *flags; @@ -596,8 +608,12 @@ static BOOL is_renderer(struct filter *filter) ret = TRUE; IAMFilterMiscFlags_Release(flags); } - else if (filter->seeking && !has_output_pins(filter->filter)) - ret = TRUE; + else + { + update_seeking(filter); + if (filter->seeking && !has_output_pins(filter->filter)) + ret = TRUE; + } return ret; }
@@ -663,15 +679,10 @@ static HRESULT WINAPI FilterGraph2_AddFilter(IFilterGraph2 *iface,
IBaseFilter_AddRef(entry->filter = filter);
- /* The Legend of Heroes: Trails of Cold Steel II destroys its filter when - * its IMediaSeeking interface is released, so cache the interface instead - * of querying for it every time. */ - if (FAILED(IBaseFilter_QueryInterface(filter, &IID_IMediaSeeking, (void **)&entry->seeking))) - entry->seeking = NULL; - list_add_head(&graph->filters, &entry->entry); list_add_head(&graph->sorted_filters, &entry->sorted_entry); entry->sorting = FALSE; + entry->seeking = NULL; ++graph->version;
if (is_renderer(entry)) @@ -2249,6 +2260,7 @@ static HRESULT all_renderers_seek(IFilterGraphImpl *This, fnFoundSeek FoundSeek,
LIST_FOR_EACH_ENTRY(filter, &This->filters, struct filter, entry) { + update_seeking(filter); if (!filter->seeking) continue; hr = FoundSeek(This, filter->seeking, arg); @@ -2454,6 +2466,7 @@ static HRESULT WINAPI MediaSeeking_GetStopPosition(IMediaSeeking *iface, LONGLON
LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry) { + update_seeking(filter); if (!filter->seeking) continue;
@@ -2562,6 +2575,7 @@ static HRESULT WINAPI MediaSeeking_SetPositions(IMediaSeeking *iface, LONGLONG * { LONGLONG current = current_ptr ? *current_ptr : 0, stop = stop_ptr ? *stop_ptr : 0;
+ update_seeking(filter); if (!filter->seeking) continue;
diff --git a/dlls/quartz/tests/filtergraph.c b/dlls/quartz/tests/filtergraph.c index f0aa084192..10f76d36ce 100644 --- a/dlls/quartz/tests/filtergraph.c +++ b/dlls/quartz/tests/filtergraph.c @@ -3658,9 +3658,6 @@ static void test_graph_seeking(void) testfilter_init(&filter1, NULL, 0); testfilter_init(&filter2, NULL, 0);
- filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; - filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; - IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control); IFilterGraph2_QueryInterface(graph, &IID_IMediaSeeking, (void **)&seeking); IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter); @@ -3823,6 +3820,8 @@ static void test_graph_seeking(void)
IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL); IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL); + filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
filter1.seek_caps = AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetCurrentPos; filter2.seek_caps = AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetDuration; @@ -3832,6 +3831,15 @@ static void test_graph_seeking(void) ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref); ok(filter2.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter2.seeking_ref);
+ IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface); + IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface); + filter1.IMediaSeeking_iface.lpVtbl = NULL; + filter2.IMediaSeeking_iface.lpVtbl = NULL; + IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL); + IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL); + filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + caps = AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetCurrentPos; hr = IMediaSeeking_CheckCapabilities(seeking, &caps); ok(hr == S_FALSE, "Got hr %#x.\n", hr); @@ -3847,6 +3855,15 @@ static void test_graph_seeking(void) ok(hr == E_FAIL, "Got hr %#x.\n", hr); ok(!caps, "Got caps %#x.\n", caps);
+ IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface); + IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface); + filter1.IMediaSeeking_iface.lpVtbl = NULL; + filter2.IMediaSeeking_iface.lpVtbl = NULL; + IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL); + IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL); + filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + hr = IMediaSeeking_IsFormatSupported(seeking, &testguid); ok(hr == S_FALSE, "Got hr %#x.\n", hr);
@@ -3904,6 +3921,15 @@ static void test_graph_seeking(void) hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &testguid, 0x123456789a, &TIME_FORMAT_NONE); todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
+ IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface); + IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface); + filter1.IMediaSeeking_iface.lpVtbl = NULL; + filter2.IMediaSeeking_iface.lpVtbl = NULL; + IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL); + IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL); + filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + filter1.seek_duration = 0x12345; filter2.seek_duration = 0x23456; hr = IMediaSeeking_GetDuration(seeking, &time); @@ -3916,6 +3942,15 @@ static void test_graph_seeking(void) ok(hr == S_OK, "Got hr %#x.\n", hr); ok(time == 0x23456, "Got time %s.\n", wine_dbgstr_longlong(time));
+ IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface); + IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface); + filter1.IMediaSeeking_iface.lpVtbl = NULL; + filter2.IMediaSeeking_iface.lpVtbl = NULL; + IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL); + IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL); + filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + filter1.seek_stop = 0x54321; filter2.seek_stop = 0x65432; hr = IMediaSeeking_GetStopPosition(seeking, &time); @@ -3947,16 +3982,43 @@ static void test_graph_seeking(void) ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr); filter1.seek_hr = filter2.seek_hr = S_OK;
+ IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface); + IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface); + filter1.IMediaSeeking_iface.lpVtbl = NULL; + filter2.IMediaSeeking_iface.lpVtbl = NULL; + IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL); + IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL); + filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + hr = IMediaSeeking_GetCurrentPosition(seeking, &time); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(!time, "Got time %s.\n", wine_dbgstr_longlong(time));
+ IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface); + IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface); + filter1.IMediaSeeking_iface.lpVtbl = NULL; + filter2.IMediaSeeking_iface.lpVtbl = NULL; + IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL); + IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL); + filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + current = stop = 0xdeadbeef; hr = IMediaSeeking_GetPositions(seeking, ¤t, &stop); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current)); ok(stop == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(stop));
+ IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface); + IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface); + filter1.IMediaSeeking_iface.lpVtbl = NULL; + filter2.IMediaSeeking_iface.lpVtbl = NULL; + IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL); + IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL); + filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + current = 0x123; stop = 0x321; hr = IMediaSeeking_SetPositions(seeking, ¤t, AM_SEEKING_AbsolutePositioning, @@ -4024,6 +4086,15 @@ static void test_graph_seeking(void) ok(filter2.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_current)); ok(filter2.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_stop));
+ IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface); + IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface); + filter1.IMediaSeeking_iface.lpVtbl = NULL; + filter2.IMediaSeeking_iface.lpVtbl = NULL; + IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL); + IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL); + filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + hr = IMediaSeeking_SetRate(seeking, 2.0); ok(hr == S_OK, "Got hr %#x.\n", hr); todo_wine ok(filter1.seek_rate == 2.0, "Got rate %.16e.\n", filter1.seek_rate); @@ -4039,6 +4110,15 @@ static void test_graph_seeking(void) todo_wine ok(filter1.seek_rate == -1.0, "Got rate %.16e.\n", filter1.seek_rate); todo_wine ok(filter2.seek_rate == -1.0, "Got rate %.16e.\n", filter2.seek_rate);
+ IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface); + IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface); + filter1.IMediaSeeking_iface.lpVtbl = NULL; + filter2.IMediaSeeking_iface.lpVtbl = NULL; + IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL); + IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL); + filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl; + hr = IMediaSeeking_GetRate(seeking, &rate); ok(hr == S_OK, "Got hr %#x.\n", hr); todo_wine ok(rate == -1.0, "Got rate %.16e.\n", rate);
Some filters (e.g. MediaStreamFilter) can become renderers when they are already in the graph.
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/quartz/filtergraph.c | 9 +++------ dlls/quartz/tests/filtergraph.c | 11 +++++++---- 2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c index fa0a15a3fc..76d64e4ca4 100644 --- a/dlls/quartz/filtergraph.c +++ b/dlls/quartz/filtergraph.c @@ -685,9 +685,6 @@ static HRESULT WINAPI FilterGraph2_AddFilter(IFilterGraph2 *iface, entry->seeking = NULL; ++graph->version;
- if (is_renderer(entry)) - ++graph->nRenderers; - return duplicate_name ? VFW_S_DUPLICATE_NAME : hr; }
@@ -760,9 +757,6 @@ static HRESULT WINAPI FilterGraph2_RemoveFilter(IFilterGraph2 *iface, IBaseFilte hr = IBaseFilter_JoinFilterGraph(pFilter, NULL, NULL); if (SUCCEEDED(hr)) { - if (is_renderer(entry)) - --This->nRenderers; - IBaseFilter_SetSyncSource(pFilter, NULL); IBaseFilter_Release(pFilter); if (entry->seeking) @@ -5222,6 +5216,7 @@ static HRESULT WINAPI MediaFilter_Run(IMediaFilter *iface, REFERENCE_TIME start) return S_OK; } graph->EcCompleteCount = 0; + graph->nRenderers = 0;
if (graph->defaultclock && !graph->refClock) IFilterGraph2_SetDefaultSyncSource(&graph->IFilterGraph2_iface); @@ -5239,6 +5234,8 @@ static HRESULT WINAPI MediaFilter_Run(IMediaFilter *iface, REFERENCE_TIME start) filter_hr = IBaseFilter_Run(filter->filter, stream_start); if (hr == S_OK) hr = filter_hr; + if (is_renderer(filter)) + ++graph->nRenderers; }
graph->state = State_Running; diff --git a/dlls/quartz/tests/filtergraph.c b/dlls/quartz/tests/filtergraph.c index 10f76d36ce..e0883ed258 100644 --- a/dlls/quartz/tests/filtergraph.c +++ b/dlls/quartz/tests/filtergraph.c @@ -3462,10 +3462,6 @@ static void test_ec_complete(void) testsource_init(&source_pins[2], NULL, 0); testfilter_init(&source, source_pins, 3);
- filter1.IAMFilterMiscFlags_iface.lpVtbl = &testmiscflags_vtbl; - filter2.IAMFilterMiscFlags_iface.lpVtbl = &testmiscflags_vtbl; - filter1.misc_flags = filter2.misc_flags = AM_FILTER_MISC_FLAGS_IS_RENDERER; - IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control); IFilterGraph2_QueryInterface(graph, &IID_IMediaEvent, (void **)&eventsrc); IFilterGraph2_QueryInterface(graph, &IID_IMediaEventSink, (void **)&eventsink); @@ -3480,8 +3476,15 @@ static void test_ec_complete(void)
/* EC_COMPLETE is only delivered to the user after all renderers deliver it. */
+ filter1.IAMFilterMiscFlags_iface.lpVtbl = &testmiscflags_vtbl; + filter2.IAMFilterMiscFlags_iface.lpVtbl = &testmiscflags_vtbl; + filter3.IAMFilterMiscFlags_iface.lpVtbl = &testmiscflags_vtbl; + filter1.misc_flags = filter2.misc_flags = AM_FILTER_MISC_FLAGS_IS_RENDERER; + IMediaControl_Run(control);
+ filter3.misc_flags = AM_FILTER_MISC_FLAGS_IS_RENDERER; + while ((hr = IMediaEvent_GetEvent(eventsrc, &code, ¶m1, ¶m2, 0)) == S_OK) { ok(code != EC_COMPLETE, "Got unexpected EC_COMPLETE.\n");
This patch breaks the qcap:filewriter test:
../../../../wine/tools/runtest -q -P wine -T ../../.. -M qcap.dll -p qcap_test.exe filewriter && touch filewriter.ok 037c:fixme:ole:CoCreateInstanceEx no instance created for interface {56a86895-0ad4-11ce-b03a-0020af0ba770} of class {8596e5f0-0da5-11d0-bd21-00a0c911ce86}, hres is 0x80004002 037c:fixme:strmbase:MemInputPin_NotifyAllocator Read only flag not handled yet! 037c:fixme:strmbase:MemInputPin_NotifyAllocator Read only flag not handled yet! filewriter.c:725: Test failed: Expected EC_COMPLETE. make: *** [Makefile:435: filewriter.ok] Error 1
I suspect you want to count in Pause rather than Run.
On 6/8/20 1:00 PM, Anton Baskanov wrote:
Some filters (e.g. MediaStreamFilter) can become renderers when they are already in the graph.
This too could probably be a comment in the code, though if there's a comment in update_seeking I guess it's less important.
Some applications (e.g. Earth 2150) call IPin::Connect directly instead of IFilterGraph::ConnectDirect.
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/quartz/filtergraph.c | 68 +++++++++++++++++---------------- dlls/quartz/tests/filtergraph.c | 11 ++++-- 2 files changed, 44 insertions(+), 35 deletions(-)
diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c index 76d64e4ca4..bfb6ea78d3 100644 --- a/dlls/quartz/filtergraph.c +++ b/dlls/quartz/filtergraph.c @@ -187,17 +187,7 @@ typedef struct _IFilterGraphImpl { LONG ref; IUnknown *punkFilterMapper2;
- /* We keep two lists of filters, one unsorted and one topologically sorted. - * The former is necessary for functions like IGraphBuilder::Connect() and - * IGraphBuilder::Render() that iterate through the filter list but may - * add to it while doing so; the latter is for functions like - * IMediaControl::Run() that should propagate messages to all filters - * (including unconnected ones) but must do so in topological order from - * sinks to sources. We can easily guarantee that the loop in Connect() will - * touch each filter exactly once so long as we aren't reordering it, but - * using the sorted filters list there would be hard. This seems to be the - * easiest and clearest solution. */ - struct list filters, sorted_filters; + struct list filters; unsigned int name_index;
IReferenceClock *refClock; @@ -680,7 +670,6 @@ static HRESULT WINAPI FilterGraph2_AddFilter(IFilterGraph2 *iface, IBaseFilter_AddRef(entry->filter = filter);
list_add_head(&graph->filters, &entry->entry); - list_add_head(&graph->sorted_filters, &entry->sorted_entry); entry->sorting = FALSE; entry->seeking = NULL; ++graph->version; @@ -762,7 +751,6 @@ static HRESULT WINAPI FilterGraph2_RemoveFilter(IFilterGraph2 *iface, IBaseFilte if (entry->seeking) IMediaSeeking_Release(entry->seeking); list_remove(&entry->entry); - list_remove(&entry->sorted_entry); CoTaskMemFree(entry->name); heap_free(entry); This->version++; @@ -887,11 +875,11 @@ out: #endif }
-static struct filter *find_sorted_filter(IFilterGraphImpl *graph, IBaseFilter *iface) +static struct filter *find_sorted_filter(struct list *sorted_filters, IBaseFilter *iface) { struct filter *filter;
- LIST_FOR_EACH_ENTRY(filter, &graph->sorted_filters, struct filter, sorted_entry) + LIST_FOR_EACH_ENTRY(filter, sorted_filters, struct filter, sorted_entry) { if (filter->filter == iface) return filter; @@ -900,7 +888,7 @@ static struct filter *find_sorted_filter(IFilterGraphImpl *graph, IBaseFilter *i return NULL; }
-static void sort_filter_recurse(IFilterGraphImpl *graph, struct filter *filter, struct list *sorted) +static void sort_filter_recurse(struct list *sorted_filters, struct filter *filter, struct list *sorted) { struct filter *peer_filter; IEnumPins *enumpins; @@ -924,8 +912,8 @@ static void sort_filter_recurse(IFilterGraphImpl *graph, struct filter *filter, { IPin_QueryPinInfo(peer, &info); /* Note that the filter may have already been sorted. */ - if ((peer_filter = find_sorted_filter(graph, info.pFilter))) - sort_filter_recurse(graph, peer_filter, sorted); + if ((peer_filter = find_sorted_filter(sorted_filters, info.pFilter))) + sort_filter_recurse(sorted_filters, peer_filter, sorted); IBaseFilter_Release(info.pFilter); IPin_Release(peer); } @@ -939,17 +927,25 @@ static void sort_filter_recurse(IFilterGraphImpl *graph, struct filter *filter, list_add_head(sorted, &filter->sorted_entry); }
-static void sort_filters(IFilterGraphImpl *graph) +static void sort_filters(IFilterGraphImpl *graph, struct list *sorted_filters) { struct list sorted = LIST_INIT(sorted), *cursor; + struct filter *filter; + + list_init(sorted_filters); + + LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry) + { + list_add_tail(sorted_filters, &filter->sorted_entry); + }
- while ((cursor = list_head(&graph->sorted_filters))) + while ((cursor = list_head(sorted_filters))) { - struct filter *filter = LIST_ENTRY(cursor, struct filter, sorted_entry); - sort_filter_recurse(graph, filter, &sorted); + filter = LIST_ENTRY(cursor, struct filter, sorted_entry); + sort_filter_recurse(sorted_filters, filter, &sorted); }
- list_move_tail(&graph->sorted_filters, &sorted); + list_move_tail(sorted_filters, &sorted); }
/* NOTE: despite the implication, it doesn't matter which @@ -1002,9 +998,6 @@ static HRESULT WINAPI FilterGraph2_ConnectDirect(IFilterGraph2 *iface, IPin *ppi } }
- if (SUCCEEDED(hr)) - sort_filters(This); - return hr; }
@@ -5120,6 +5113,7 @@ static HRESULT WINAPI MediaFilter_Stop(IMediaFilter *iface) { IFilterGraphImpl *graph = impl_from_IMediaFilter(iface); HRESULT hr = S_OK, filter_hr; + struct list sorted_filters; struct filter *filter;
TRACE("graph %p.\n", graph); @@ -5132,9 +5126,11 @@ static HRESULT WINAPI MediaFilter_Stop(IMediaFilter *iface) return S_OK; }
+ sort_filters(graph, &sorted_filters); + if (graph->state == State_Running) { - LIST_FOR_EACH_ENTRY(filter, &graph->sorted_filters, struct filter, sorted_entry) + LIST_FOR_EACH_ENTRY(filter, &sorted_filters, struct filter, sorted_entry) { filter_hr = IBaseFilter_Pause(filter->filter); if (hr == S_OK) @@ -5142,7 +5138,7 @@ static HRESULT WINAPI MediaFilter_Stop(IMediaFilter *iface) } }
- LIST_FOR_EACH_ENTRY(filter, &graph->sorted_filters, struct filter, sorted_entry) + LIST_FOR_EACH_ENTRY(filter, &sorted_filters, struct filter, sorted_entry) { filter_hr = IBaseFilter_Stop(filter->filter); if (hr == S_OK) @@ -5163,6 +5159,7 @@ static HRESULT WINAPI MediaFilter_Pause(IMediaFilter *iface) { IFilterGraphImpl *graph = impl_from_IMediaFilter(iface); HRESULT hr = S_OK, filter_hr; + struct list sorted_filters; struct filter *filter;
TRACE("graph %p.\n", graph); @@ -5175,6 +5172,8 @@ static HRESULT WINAPI MediaFilter_Pause(IMediaFilter *iface) return S_OK; }
+ sort_filters(graph, &sorted_filters); + if (graph->defaultclock && !graph->refClock) IFilterGraph2_SetDefaultSyncSource(&graph->IFilterGraph2_iface);
@@ -5186,7 +5185,7 @@ static HRESULT WINAPI MediaFilter_Pause(IMediaFilter *iface) graph->current_pos += graph->stream_elapsed; }
- LIST_FOR_EACH_ENTRY(filter, &graph->sorted_filters, struct filter, sorted_entry) + LIST_FOR_EACH_ENTRY(filter, &sorted_filters, struct filter, sorted_entry) { filter_hr = IBaseFilter_Pause(filter->filter); if (hr == S_OK) @@ -5204,6 +5203,7 @@ static HRESULT WINAPI MediaFilter_Run(IMediaFilter *iface, REFERENCE_TIME start) IFilterGraphImpl *graph = impl_from_IMediaFilter(iface); REFERENCE_TIME stream_start = start; HRESULT hr = S_OK, filter_hr; + struct list sorted_filters; struct filter *filter;
TRACE("graph %p, start %s.\n", graph, debugstr_time(start)); @@ -5218,6 +5218,8 @@ static HRESULT WINAPI MediaFilter_Run(IMediaFilter *iface, REFERENCE_TIME start) graph->EcCompleteCount = 0; graph->nRenderers = 0;
+ sort_filters(graph, &sorted_filters); + if (graph->defaultclock && !graph->refClock) IFilterGraph2_SetDefaultSyncSource(&graph->IFilterGraph2_iface);
@@ -5229,7 +5231,7 @@ static HRESULT WINAPI MediaFilter_Run(IMediaFilter *iface, REFERENCE_TIME start) stream_start += 500000; }
- LIST_FOR_EACH_ENTRY(filter, &graph->sorted_filters, struct filter, sorted_entry) + LIST_FOR_EACH_ENTRY(filter, &sorted_filters, struct filter, sorted_entry) { filter_hr = IBaseFilter_Run(filter->filter, stream_start); if (hr == S_OK) @@ -5249,6 +5251,7 @@ static HRESULT WINAPI MediaFilter_GetState(IMediaFilter *iface, DWORD timeout, F IFilterGraphImpl *graph = impl_from_IMediaFilter(iface); DWORD end = GetTickCount() + timeout; HRESULT hr = S_OK, filter_hr; + struct list sorted_filters; struct filter *filter;
TRACE("graph %p, timeout %u, state %p.\n", graph, timeout, state); @@ -5258,9 +5261,11 @@ static HRESULT WINAPI MediaFilter_GetState(IMediaFilter *iface, DWORD timeout, F
EnterCriticalSection(&graph->cs);
+ sort_filters(graph, &sorted_filters); + *state = graph->state;
- LIST_FOR_EACH_ENTRY(filter, &graph->sorted_filters, struct filter, sorted_entry) + LIST_FOR_EACH_ENTRY(filter, &sorted_filters, struct filter, sorted_entry) { FILTER_STATE filter_state; int wait; @@ -5681,7 +5686,6 @@ static HRESULT filter_graph_common_create(IUnknown *outer, IUnknown **out, BOOL fimpl->IGraphVersion_iface.lpVtbl = &IGraphVersion_VTable; fimpl->ref = 1; list_init(&fimpl->filters); - list_init(&fimpl->sorted_filters); fimpl->name_index = 1; fimpl->refClock = NULL; fimpl->hEventCompletion = CreateEventW(0, TRUE, FALSE, 0); diff --git a/dlls/quartz/tests/filtergraph.c b/dlls/quartz/tests/filtergraph.c index e0883ed258..3a14a1a6e9 100644 --- a/dlls/quartz/tests/filtergraph.c +++ b/dlls/quartz/tests/filtergraph.c @@ -3165,7 +3165,9 @@ static void test_filter_state(void) IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL); IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL); IFilterGraph2_AddFilter(graph, &dummy.IBaseFilter_iface, NULL); - IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL); + /* Using IPin::Connect instead of IFilterGraph2::ConnectDirect to show that */ + /* FilterGraph does not rely on ::ConnectDirect to track filter connections. */ + IPin_Connect(&source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
check_filter_state(graph, State_Stopped);
@@ -3240,9 +3242,12 @@ static void test_filter_state(void) IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter); IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control);
- IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL); + /* Add the filters in reverse order this time. */ IFilterGraph2_AddFilter(graph, &sink.IBaseFilter_iface, NULL); - IFilterGraph2_ConnectDirect(graph, &source_pin.IPin_iface, &sink_pin.IPin_iface, NULL); + IFilterGraph2_AddFilter(graph, &source.IBaseFilter_iface, NULL); + /* Using IPin::Connect instead of IFilterGraph2::ConnectDirect to show that */ + /* FilterGraph does not rely on ::ConnectDirect to track filter connections. */ + IPin_Connect(&source_pin.IPin_iface, &sink_pin.IPin_iface, NULL);
hr = IMediaFilter_Pause(filter); ok(hr == S_OK, "Got hr %#x.\n", hr);
Could we sort directly into the graph's filter list, instead of creating a temporary one on stack?
On 6/8/20 2:48 PM, Zebediah Figura wrote:
Could we sort directly into the graph's filter list, instead of creating a temporary one on stack?
I guess this might be a bit unclear as stated; obviously we can't sort into the filter list while we're iterating over it, but I mean we can construct a list on stack in sort_filters() and then move all of the elements of that list into the graph's filter list. That way we can get rid of "sorted_entry".
Signed-off-by: Anton Baskanov baskanov@gmail.com --- Otherwise the streaming thread sometimes fails to allocate a sample. --- dlls/winegstreamer/gstdemux.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index 299a7ddf56..7c38e421f9 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -1391,6 +1391,15 @@ static HRESULT gstdemux_init_stream(struct strmbase_filter *iface) if (!filter->container) return VFW_E_NOT_CONNECTED;
+ for (i = 0; i < filter->source_count; ++i) + { + if (SUCCEEDED(pin_hr = BaseOutputPinImpl_Active(&filter->sources[i]->pin))) + hr = pin_hr; + } + + if (FAILED(hr)) + return hr; + if (filter->no_more_pads_event) ResetEvent(filter->no_more_pads_event);
@@ -1422,11 +1431,6 @@ static HRESULT gstdemux_init_stream(struct strmbase_filter *iface) stop_type, seeking->llStop * 100)); }
- for (i = 0; i < filter->source_count; ++i) - { - if (SUCCEEDED(pin_hr = BaseOutputPinImpl_Active(&filter->sources[i]->pin))) - hr = pin_hr; - } return hr; }
Signed-off-by: Zebediah Figura z.figura12@gmail.com
On 6/8/20 1:00 PM, Anton Baskanov wrote:
Some filters (e.g. MediaStreamFilter) can become seekable when they are already in the graph.
I think this should be a comment, in update_seeking along with the comment regarding The Legend of Heroes.
Signed-off-by: Anton Baskanov baskanov@gmail.com
dlls/quartz/filtergraph.c | 30 +++++++++--- dlls/quartz/tests/filtergraph.c | 86 +++++++++++++++++++++++++++++++-- 2 files changed, 105 insertions(+), 11 deletions(-)
diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c index 6217516db1..fa0a15a3fc 100644 --- a/dlls/quartz/filtergraph.c +++ b/dlls/quartz/filtergraph.c @@ -585,6 +585,18 @@ static BOOL has_output_pins(IBaseFilter *filter) return FALSE; }
+static void update_seeking(struct filter *filter) +{
- if (!filter->seeking)
- {
/* The Legend of Heroes: Trails of Cold Steel II destroys its filter when
* its IMediaSeeking interface is released, so cache the interface instead
* of querying for it every time. */
if (FAILED(IBaseFilter_QueryInterface(filter->filter, &IID_IMediaSeeking, (void **)&filter->seeking)))
filter->seeking = NULL;
- }
+}
static BOOL is_renderer(struct filter *filter) { IAMFilterMiscFlags *flags; @@ -596,8 +608,12 @@ static BOOL is_renderer(struct filter *filter) ret = TRUE; IAMFilterMiscFlags_Release(flags); }
- else if (filter->seeking && !has_output_pins(filter->filter))
ret = TRUE;
- else
- {
update_seeking(filter);
if (filter->seeking && !has_output_pins(filter->filter))
ret = TRUE;
- } return ret;
}
@@ -663,15 +679,10 @@ static HRESULT WINAPI FilterGraph2_AddFilter(IFilterGraph2 *iface,
IBaseFilter_AddRef(entry->filter = filter);
- /* The Legend of Heroes: Trails of Cold Steel II destroys its filter when
* its IMediaSeeking interface is released, so cache the interface instead
* of querying for it every time. */
- if (FAILED(IBaseFilter_QueryInterface(filter, &IID_IMediaSeeking, (void **)&entry->seeking)))
entry->seeking = NULL;
- list_add_head(&graph->filters, &entry->entry); list_add_head(&graph->sorted_filters, &entry->sorted_entry); entry->sorting = FALSE;
entry->seeking = NULL; ++graph->version;
if (is_renderer(entry))
@@ -2249,6 +2260,7 @@ static HRESULT all_renderers_seek(IFilterGraphImpl *This, fnFoundSeek FoundSeek,
LIST_FOR_EACH_ENTRY(filter, &This->filters, struct filter, entry) {
update_seeking(filter); if (!filter->seeking) continue; hr = FoundSeek(This, filter->seeking, arg);
@@ -2454,6 +2466,7 @@ static HRESULT WINAPI MediaSeeking_GetStopPosition(IMediaSeeking *iface, LONGLON
LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry) {
update_seeking(filter); if (!filter->seeking) continue;
@@ -2562,6 +2575,7 @@ static HRESULT WINAPI MediaSeeking_SetPositions(IMediaSeeking *iface, LONGLONG * { LONGLONG current = current_ptr ? *current_ptr : 0, stop = stop_ptr ? *stop_ptr : 0;
update_seeking(filter); if (!filter->seeking) continue;
diff --git a/dlls/quartz/tests/filtergraph.c b/dlls/quartz/tests/filtergraph.c index f0aa084192..10f76d36ce 100644 --- a/dlls/quartz/tests/filtergraph.c +++ b/dlls/quartz/tests/filtergraph.c @@ -3658,9 +3658,6 @@ static void test_graph_seeking(void) testfilter_init(&filter1, NULL, 0); testfilter_init(&filter2, NULL, 0);
- filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
- filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
- IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control); IFilterGraph2_QueryInterface(graph, &IID_IMediaSeeking, (void **)&seeking); IFilterGraph2_QueryInterface(graph, &IID_IMediaFilter, (void **)&filter);
@@ -3823,6 +3820,8 @@ static void test_graph_seeking(void)
IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL); IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
filter1.seek_caps = AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetCurrentPos; filter2.seek_caps = AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetDuration;
@@ -3832,6 +3831,15 @@ static void test_graph_seeking(void) ok(filter1.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter1.seeking_ref); ok(filter2.seeking_ref > 0, "Unexpected seeking refcount %d.\n", filter2.seeking_ref);
- IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
- IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface);
- filter1.IMediaSeeking_iface.lpVtbl = NULL;
- filter2.IMediaSeeking_iface.lpVtbl = NULL;
- IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
- IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
- filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
- filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
You could probably factor this out into a helper function, along the lines of:
/* Remove and re-add the filter, to flush the graph's internal IMediaSeeking cache. Don't expose IMediaSeeking when adding, to show that it's only queried when needed. */ void flush_cached_seeking(IFilterGraph *, struct testfilter *);
caps = AM_SEEKING_CanDoSegments | AM_SEEKING_CanGetCurrentPos; hr = IMediaSeeking_CheckCapabilities(seeking, &caps); ok(hr == S_FALSE, "Got hr %#x.\n", hr);
@@ -3847,6 +3855,15 @@ static void test_graph_seeking(void) ok(hr == E_FAIL, "Got hr %#x.\n", hr); ok(!caps, "Got caps %#x.\n", caps);
- IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
- IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface);
- filter1.IMediaSeeking_iface.lpVtbl = NULL;
- filter2.IMediaSeeking_iface.lpVtbl = NULL;
- IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
- IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
- filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
- filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
- hr = IMediaSeeking_IsFormatSupported(seeking, &testguid); ok(hr == S_FALSE, "Got hr %#x.\n", hr);
@@ -3904,6 +3921,15 @@ static void test_graph_seeking(void) hr = IMediaSeeking_ConvertTimeFormat(seeking, &time, &testguid, 0x123456789a, &TIME_FORMAT_NONE); todo_wine ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr);
- IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
- IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface);
- filter1.IMediaSeeking_iface.lpVtbl = NULL;
- filter2.IMediaSeeking_iface.lpVtbl = NULL;
- IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
- IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
- filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
- filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
- filter1.seek_duration = 0x12345; filter2.seek_duration = 0x23456; hr = IMediaSeeking_GetDuration(seeking, &time);
@@ -3916,6 +3942,15 @@ static void test_graph_seeking(void) ok(hr == S_OK, "Got hr %#x.\n", hr); ok(time == 0x23456, "Got time %s.\n", wine_dbgstr_longlong(time));
- IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
- IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface);
- filter1.IMediaSeeking_iface.lpVtbl = NULL;
- filter2.IMediaSeeking_iface.lpVtbl = NULL;
- IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
- IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
- filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
- filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
- filter1.seek_stop = 0x54321; filter2.seek_stop = 0x65432; hr = IMediaSeeking_GetStopPosition(seeking, &time);
@@ -3947,16 +3982,43 @@ static void test_graph_seeking(void) ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr); filter1.seek_hr = filter2.seek_hr = S_OK;
IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface);
filter1.IMediaSeeking_iface.lpVtbl = NULL;
filter2.IMediaSeeking_iface.lpVtbl = NULL;
IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
hr = IMediaSeeking_GetCurrentPosition(seeking, &time); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(!time, "Got time %s.\n", wine_dbgstr_longlong(time));
IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface);
filter1.IMediaSeeking_iface.lpVtbl = NULL;
filter2.IMediaSeeking_iface.lpVtbl = NULL;
IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
current = stop = 0xdeadbeef; hr = IMediaSeeking_GetPositions(seeking, ¤t, &stop); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(!current, "Got time %s.\n", wine_dbgstr_longlong(current)); ok(stop == 0x65432, "Got time %s.\n", wine_dbgstr_longlong(stop));
IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface);
filter1.IMediaSeeking_iface.lpVtbl = NULL;
filter2.IMediaSeeking_iface.lpVtbl = NULL;
IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
current = 0x123; stop = 0x321; hr = IMediaSeeking_SetPositions(seeking, ¤t, AM_SEEKING_AbsolutePositioning,
@@ -4024,6 +4086,15 @@ static void test_graph_seeking(void) ok(filter2.seek_current == 0x123, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_current)); ok(filter2.seek_stop == 0x321, "Got time %s.\n", wine_dbgstr_longlong(filter2.seek_stop));
- IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
- IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface);
- filter1.IMediaSeeking_iface.lpVtbl = NULL;
- filter2.IMediaSeeking_iface.lpVtbl = NULL;
- IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
- IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
- filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
- filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
- hr = IMediaSeeking_SetRate(seeking, 2.0); ok(hr == S_OK, "Got hr %#x.\n", hr); todo_wine ok(filter1.seek_rate == 2.0, "Got rate %.16e.\n", filter1.seek_rate);
@@ -4039,6 +4110,15 @@ static void test_graph_seeking(void) todo_wine ok(filter1.seek_rate == -1.0, "Got rate %.16e.\n", filter1.seek_rate); todo_wine ok(filter2.seek_rate == -1.0, "Got rate %.16e.\n", filter2.seek_rate);
- IFilterGraph2_RemoveFilter(graph, &filter1.IBaseFilter_iface);
- IFilterGraph2_RemoveFilter(graph, &filter2.IBaseFilter_iface);
- filter1.IMediaSeeking_iface.lpVtbl = NULL;
- filter2.IMediaSeeking_iface.lpVtbl = NULL;
- IFilterGraph2_AddFilter(graph, &filter1.IBaseFilter_iface, NULL);
- IFilterGraph2_AddFilter(graph, &filter2.IBaseFilter_iface, NULL);
- filter1.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
- filter2.IMediaSeeking_iface.lpVtbl = &testseek_vtbl;
- hr = IMediaSeeking_GetRate(seeking, &rate); ok(hr == S_OK, "Got hr %#x.\n", hr); todo_wine ok(rate == -1.0, "Got rate %.16e.\n", rate);