DirectShow cast sampleGrabber to ISampleGrabber

2019-06-27 20:47发布

I have a strange error that I cannot wrap my head around. I have a graph created in a separate thread that runs and I'm trying to access the IBaseFilter sampleGrabber outside the thread which worked in a console application but I moved the code to a new project and where I'm trying to cast sampleGrabber to ISampleGrabber the runtime complains with a null reference exception. If I debug sampleGrabber it does have the interface ISampleGrabber however I cannot cast it anymore. Moving the code inside the thread running the graph allows me to cast it but its not ideal for my application.

How can a null reference exception appear by casting what clearly is a sampleGrabber to ISampleGrabber fail?

2条回答
冷血范
2楼-- · 2019-06-27 21:21

The problem is that DirectShow filters are early COM classes that implement a subset of COM in part of reference counting, interfaces, monikers, persistence - basically all good things lasted for years - however they ignore apartments completely. DirectShow is multithreaded on its own and it is typical that there is a controlling thread, and there are worker streaming threads aside. DirectShow concepts assume you can easily pass interface pointers between threads and no marshaling is involved, expected and required.

Then came .NET with checking COM wrappers, and DirectShow.NET wrapped interface pointers as if they were fully featured apartment-aware COM pointers. At the same time Microsoft stopped giving updates to DirectShow (such as for example supplying Sample Grabber with free threaded marshaler) and eventually you hit the issue when on .NET you cannot do a supposedly simple thing with the interface pointer.

There is still absolutely no problem working with APIs in native code domain because you can skip marshaling there and work with direct pointers.

You build the graph on one apartment, and then you get a call back from Sample Grabber on another apartment (or, otherwise, in your scenario you just do something on a worker thread). You cannot use original interface pointers, esp. those in member variables, because .NET runtime check would hit apartment mismatch, esp, trying to marshal interface pointer for another apartment.

If it were your custom filter with source code, you could have added a custom IMarshal implementation or leverage free threaded marshaler to fix the .NET issue on native code side, or otherwise add a helper to pass pointers across apartments.

In .NET code domain the best approach would be to avoid working with pointers from multiple apartments. There possibly are choices, but the easiest one I think of off the top of my head is to

  • work in MTA to be able to have multiple threads accessing DirectShow interface pointers
  • use CLSID_FilterGraphNoThread version of Filter Graph Manager
  • initialize and terminate filter graph on a dedicated thread, which during graph operation dispatches window messages

That is, either use STA and no extra threads touching pointers, or otherwise not use STA.

查看更多
Lonely孤独者°
3楼-- · 2019-06-27 21:25

After playing around with IGlobalInterface and pondering over Roman's comment, I realized setting up the function that handles samplegrabber is best to be in another thread, thus bypassing STA.

查看更多
登录 后发表回答