The problem is that DirectShow filters are early COM classes that implement a subset of COM in terms of reference counting, interfaces, nicknames, persistence - basically all the good things went on for years, but they completely ignore the apartments. DirectShow is multithreaded in itself, and it is typical that there is a control thread, and the streams of the streaming traffic flows are delayed. DirectShow concepts suggest that you can easily pass interface pointers between threads, and no marshaling is involved, expected, or required.
Then came .NET with COM shell checking, and DirectShow.NET with interfaces, as if they were full-featured COM pointers compatible with apartments. At the same time, Microsoft stopped giving DirectShow updates (for example, by supplying Sample Grabber with a free stream marshaller), and as a result, you got into a problem when in .NET you cannot do a simple thing with an interface pointer.
There is still no problem working with the API in your own code domain, since you can skip marshaling there and work with direct pointers.
You build a schedule in one apartment, and then get a call from Sample Grabber in another apartment (or, otherwise, in your scenario, you just do something on the workflow). You cannot use original interface pointers, especially. those in member variables because .NET runtime checking would check for apartment mismatch, especially when trying to marshal the interface pointer for another apartment.
If it was your custom filter with source code, you could add a custom IMarshal implementation or use the free stream marshaler to fix .NET on the native side of the code or otherwise add an assistant to pass the pointers around the apartments.
In the .NET code domain, the best approach would be to avoid working with pointers from multiple apartments. There may be a choice, but the easiest one that I think from the top of my head is
- work in MTA in order to be able to have multiple threads accessing DirectShow interface pointers.
- use
CLSID_FilterGraphNoThread version of Filter Graph Manager - initialize and complete the filter schedule in a dedicated thread, which sends window messages while the schedule is running
That is, use the STA and additional threads by touching pointers, or else do not use the STA.