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

Gabriel Ivăncescu gabrielopcode at gmail.com
Mon Apr 6 08:07:30 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 | 42 ++++++++++++++++++++++-----------------
 1 file changed, 24 insertions(+), 18 deletions(-)

diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c
index c659cf9..0de16c4 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;
 };
@@ -569,6 +570,15 @@ static IBaseFilter *find_filter_by_name(IFilterGraphImpl *graph, const WCHAR *na
     return NULL;
 }
 
+static IMediaSeeking *get_filter_seeking(struct filter *filter)
+{
+    /* Cache the IMediaSeeking object as some broken apps actually depend on this. */
+    if (!filter->seeking)
+        if (FAILED(IBaseFilter_QueryInterface(filter->filter, &IID_IMediaSeeking, (void**)&filter->seeking)))
+            filter->seeking = NULL;
+    return filter->seeking;
+}
+
 static BOOL has_output_pins(IBaseFilter *filter)
 {
     IEnumPins *enumpins;
@@ -593,24 +603,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 (get_filter_seeking(filter) && !has_output_pins(filter->filter))
+        ret = TRUE;
     return ret;
 }
 
@@ -675,12 +680,13 @@ static HRESULT WINAPI FilterGraph2_AddFilter(IFilterGraph2 *iface,
     }
 
     IBaseFilter_AddRef(entry->filter = filter);
+    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 +761,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 +2227,11 @@ static HRESULT all_renderers_seek(IFilterGraphImpl *This, fnFoundSeek FoundSeek,
 
     LIST_FOR_EACH_ENTRY(filter, &This->filters, struct filter, entry)
     {
-        IMediaSeeking *seek = NULL;
+        IMediaSeeking *seek = get_filter_seeking(filter);
 
-        IBaseFilter_QueryInterface(filter->filter, &IID_IMediaSeeking, (void **)&seek);
         if (!seek)
             continue;
         hr = FoundSeek(This, seek, arg);
-        IMediaSeeking_Release(seek);
         if (hr_return != E_NOTIMPL)
             allnotimpl = FALSE;
         if (hr_return == S_OK || (FAILED(hr) && hr != E_NOTIMPL && SUCCEEDED(hr_return)))
@@ -2429,11 +2435,11 @@ 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)))
+        seeking = get_filter_seeking(filter);
+        if (!seeking)
             continue;
 
         filter_hr = IMediaSeeking_GetStopPosition(seeking, &filter_stop);
-        IMediaSeeking_Release(seeking);
         if (SUCCEEDED(filter_hr))
         {
             hr = S_OK;
@@ -2539,12 +2545,12 @@ 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)))
+        seeking = get_filter_seeking(filter);
+        if (!seeking)
             continue;
 
         filter_hr = IMediaSeeking_SetPositions(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