[PATCH v3 1/2] quartz: Cache IMediaSeeking for filters.

Gabriel Ivăncescu gabrielopcode at gmail.com
Wed Apr 8 07:39:34 CDT 2020


The Legend of Heroes: Trails of Cold Steel II crashes on its intro videos
because it mistakenly destroys and frees its own filter when the IMediaSeeking
object is destroyed, which is retrieved as a separate object when we query
for it, with a refcount of 1 regardless of the filter's refcount. It does
so with msvcrt operator delete, even if the filter had outstanding ref
counts. It happens to work on Windows because Windows caches the object
exposing the interface.

Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---
 dlls/quartz/filtergraph.c | 45 +++++++++++++++++----------------------
 1 file changed, 20 insertions(+), 25 deletions(-)

diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c
index c659cf9..1013087 100644
--- a/dlls/quartz/filtergraph.c
+++ b/dlls/quartz/filtergraph.c
@@ -153,6 +153,7 @@ struct filter
 {
     struct list entry, sorted_entry;
     IBaseFilter *filter;
+    IMediaSeeking *seeking;
     WCHAR *name;
     BOOL sorting;
 };
@@ -593,24 +594,19 @@ static BOOL has_output_pins(IBaseFilter *filter)
     return FALSE;
 }
 
-static BOOL is_renderer(IBaseFilter *filter)
+static BOOL is_renderer(struct filter *filter)
 {
     IAMFilterMiscFlags *flags;
-    IMediaSeeking *seeking;
     BOOL ret = FALSE;
 
-    if (SUCCEEDED(IBaseFilter_QueryInterface(filter, &IID_IAMFilterMiscFlags, (void **)&flags)))
+    if (SUCCEEDED(IBaseFilter_QueryInterface(filter->filter, &IID_IAMFilterMiscFlags, (void **)&flags)))
     {
         if (IAMFilterMiscFlags_GetMiscFlags(flags) & AM_FILTER_MISC_FLAGS_IS_RENDERER)
             ret = TRUE;
         IAMFilterMiscFlags_Release(flags);
     }
-    else if (SUCCEEDED(IBaseFilter_QueryInterface(filter, &IID_IMediaSeeking, (void **)&seeking)))
-    {
-        IMediaSeeking_Release(seeking);
-        if (!has_output_pins(filter))
-            ret = TRUE;
-    }
+    else if (filter->seeking && !has_output_pins(filter->filter))
+        ret = TRUE;
     return ret;
 }
 
@@ -675,12 +671,17 @@ static HRESULT WINAPI FilterGraph2_AddFilter(IFilterGraph2 *iface,
     }
 
     IBaseFilter_AddRef(entry->filter = filter);
+
+    /* Cache the IMediaSeeking object as some broken apps actually depend on this. */
+    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;
     ++graph->version;
 
-    if (is_renderer(filter))
+    if (is_renderer(entry))
         ++graph->nRenderers;
 
     return duplicate_name ? VFW_S_DUPLICATE_NAME : hr;
@@ -755,11 +756,13 @@ static HRESULT WINAPI FilterGraph2_RemoveFilter(IFilterGraph2 *iface, IBaseFilte
             hr = IBaseFilter_JoinFilterGraph(pFilter, NULL, NULL);
             if (SUCCEEDED(hr))
             {
-                if (is_renderer(pFilter))
+                if (is_renderer(entry))
                     --This->nRenderers;
 
                 IBaseFilter_SetSyncSource(pFilter, NULL);
                 IBaseFilter_Release(pFilter);
+                if (entry->seeking)
+                    IMediaSeeking_Release(entry->seeking);
                 list_remove(&entry->entry);
                 list_remove(&entry->sorted_entry);
                 CoTaskMemFree(entry->name);
@@ -2219,13 +2222,9 @@ static HRESULT all_renderers_seek(IFilterGraphImpl *This, fnFoundSeek FoundSeek,
 
     LIST_FOR_EACH_ENTRY(filter, &This->filters, struct filter, entry)
     {
-        IMediaSeeking *seek = NULL;
-
-        IBaseFilter_QueryInterface(filter->filter, &IID_IMediaSeeking, (void **)&seek);
-        if (!seek)
+        if (!filter->seeking)
             continue;
-        hr = FoundSeek(This, seek, arg);
-        IMediaSeeking_Release(seek);
+        hr = FoundSeek(This, filter->seeking, arg);
         if (hr_return != E_NOTIMPL)
             allnotimpl = FALSE;
         if (hr_return == S_OK || (FAILED(hr) && hr != E_NOTIMPL && SUCCEEDED(hr_return)))
@@ -2414,7 +2413,6 @@ static HRESULT WINAPI MediaSeeking_GetStopPosition(IMediaSeeking *iface, LONGLON
 {
     IFilterGraphImpl *graph = impl_from_IMediaSeeking(iface);
     HRESULT hr = E_NOTIMPL, filter_hr;
-    IMediaSeeking *seeking;
     struct filter *filter;
     LONGLONG filter_stop;
 
@@ -2429,11 +2427,10 @@ static HRESULT WINAPI MediaSeeking_GetStopPosition(IMediaSeeking *iface, LONGLON
 
     LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry)
     {
-        if (FAILED(IBaseFilter_QueryInterface(filter->filter, &IID_IMediaSeeking, (void **)&seeking)))
+        if (!filter->seeking)
             continue;
 
-        filter_hr = IMediaSeeking_GetStopPosition(seeking, &filter_stop);
-        IMediaSeeking_Release(seeking);
+        filter_hr = IMediaSeeking_GetStopPosition(filter->seeking, &filter_stop);
         if (SUCCEEDED(filter_hr))
         {
             hr = S_OK;
@@ -2507,7 +2504,6 @@ static HRESULT WINAPI MediaSeeking_SetPositions(IMediaSeeking *iface, LONGLONG *
 {
     IFilterGraphImpl *graph = impl_from_IMediaSeeking(iface);
     HRESULT hr = E_NOTIMPL, filter_hr;
-    IMediaSeeking *seeking;
     struct filter *filter;
     FILTER_STATE state;
 
@@ -2539,12 +2535,11 @@ static HRESULT WINAPI MediaSeeking_SetPositions(IMediaSeeking *iface, LONGLONG *
     {
         LONGLONG current = current_ptr ? *current_ptr : 0, stop = stop_ptr ? *stop_ptr : 0;
 
-        if (FAILED(IBaseFilter_QueryInterface(filter->filter, &IID_IMediaSeeking, (void **)&seeking)))
+        if (!filter->seeking)
             continue;
 
-        filter_hr = IMediaSeeking_SetPositions(seeking, &current,
+        filter_hr = IMediaSeeking_SetPositions(filter->seeking, &current,
                 current_flags | AM_SEEKING_ReturnTime, &stop, stop_flags);
-        IMediaSeeking_Release(seeking);
         if (SUCCEEDED(filter_hr))
         {
             hr = S_OK;
-- 
2.21.0




More information about the wine-devel mailing list