0

Question

I have a managed class written in C# that implements a native COM interface. Instances of this class are passed to native code. Is it possible to override AddRef() and Release() to track the COM reference count? If not, then is there any other way to determine when all native references have been released (when the only reference left is the RCW managed reference)?

Background

I have a camera device and a managed API to access the video data captured by the camera. I have created a C# program which uses P/Invoke and COM interop to render and encode the video data using Windows MediaFoundation and created a managed class implementing a custom IMFMediaSource. This all works.

The problem is that the performance of my current implementation is terrible and my workstation can barely keep up with 30 frames of video a second. The camera is capable of capturing at 60 fps.

Currently I am doing the following for every single video frame:

  1. Call MFCreateSample to create an IMFSample
  2. Call MFCreateMemoryBuffer to create an IMFMediaBuffer
  3. Copy video data from managed source to the native IMFMediaBuffer
  4. Attach buffer to sample
  5. Raise the MEMediaSample event

This works but memory usage of the application starts at around 40 MiB and very quickly rises to over 2 GiB and then the entire application freezes for a second as the garbage collector kicks in and collects the RCW references to hundreds of IMFSample and IMFMediaBuffer objects and then the memory drops back down to around 40 MiB.

So, to avoid the rediculuous overhead of allocating new buffers and copying the video data for every frame I would like to implement my own IMFSample and IMFMediaBuffer in managed code and reuse these objects. But I need determine when the rest of the MediaFoundation pipeline is finished using the IMFSample (when COM reference count drops to 1 indicating only the managed RCW reference remains) so that I know when it is safe for me to reclaim and reuse the sample and buffer objects. Is there any other way to determine when the rest of MediaFoundation is done using an IMFSample other than by tracking COM reference count?

  • AFAIK no it is not possible to override those when doing purely managed COM interop. The runtime must manage that because COM memory is pinned whereas purely managed memory can move around as the GC works. That said I would highly discourage this use case for managed code unless you bypass MediaFoundation and COM all together. They really aren't intended for use from managed code directly and most wrappers do any direct interaction with those APIs in native code. – Mgetz Jan 07 '22 at 17:42
  • Does this answer your question? [Is it possible to intercept (or be aware of) COM Reference counting on CLR objects exposed to COM](https://stackoverflow.com/questions/2223147/is-it-possible-to-intercept-or-be-aware-of-com-reference-counting-on-clr-objec) – Mgetz Jan 07 '22 at 17:44
  • 1
    You shouldn't reimplement MF's native classes (IMFSample, etc.) Instead make sure you narrow the use of native objects you got from native functions (MFCreateSample, etc.), ie: do not pass/keep these around like use them in a single .NET method, and call Marshal.ReleaseComObject on it once your work is finished. (or wrap this with IDispose and call ReleaseComObject in dispose) This should ensure the RCW is freed and also free the allocated native memory before GC kicks in – Simon Mourier Jan 07 '22 at 17:45
  • @SimonMourier I tried calling ReleaseComObject/FinalReleaseComObject immediately after passing the objects back to media foundation. This does not provide any improvement. All of the MF projects I have seen involving a custom media source do re-implement their own IMFSample and IMFMediaBuffer but they are all written in native C++ and rely on overriding AddRef/Release. – dcawley Jan 07 '22 at 20:40
  • @Mgetz Thanks for the response. I don't think the solutions in that thread are viable for my case. I find Darren's solution very interesting and was thinking of something like that myself but I don't think it is a good idea in production code as it relies on internal details of the CCW/RCW. I am looking into the new ComWrappers API in .NET 5 now which I think allows creating a custom CCW/RCW but I have never worked with it before. – dcawley Jan 07 '22 at 20:44
  • Post a reproducing project so we can investigate. – Simon Mourier Jan 08 '22 at 09:12

0 Answers0