Microsoft Smooth Streaming Format SDKを使用して、基本的にCCTVシステムを実装しようとしています。 SDKにはC++のサンプルアプリケーションがありますが、私はC#で書いています。コードが移植され、COMオブジェクトを渡すCOM - > .NETコールバックの明らかなメモリリークを除いて正しく動作します。COMインターフェイスが.NETアプリケーションにCOMコールバックで渡されたときのメモリリーク
ETA:RedGate ANTSメモリプロファイラ8.10の試用版でメモリリークを測定しました。それは、プライベート・バイトとワーキング・セット - プライベート・グラフを着実に登っています。また、「モジュール別管理されていないメモリの内訳」では、「CLR(見積もり)」が継続的に増加していることが示されています。一晩中実行されたテストでは、16時間で332MBの増加となりました。これはかなり遅い成長率ですが、最終的にOutOfMemoryの状況を引き起こします。前に述べたように、このアプリは24時間365日のストリーミング配信を予定しています。
COMオブジェクトは、カスタムのDirectShowサンプルグラバーフィルターです。 SSF SDKにはサンプルグラバーフィルターの実装がありますが、これは同じ動作を示し、とにかく再配布されません。
サンプルグラバフィルタIDLのようなある:
[
object,
uuid(17b2823e-a24b-483f-a0b0-002edaf56035),
helpstring("ISampleGrabberCB interface"),
pointer_default(unique)
]
interface ISampleGrabberCallback : IUnknown
{
HRESULT OnSample([in] IMediaSample *pSample);
}
[
object,
uuid(9495f2d0-35fd-451f-b831-f89f1af8589f),
dual,
helpstring("ISampleGrabberFilter Interface"),
pointer_default(unique)
]
interface ISampleGrabberFilter : IDispatch
{
HRESULT SetCallback([in] ISampleGrabberCallback *callbackIf);
};
[
uuid(2bd7d268-bb30-4a6f-b715-6223c006d973),
helpstring("SampleGrabber Class")
]
coclass SampleGrabberFilter
{
[default] interface ISampleGrabberFilter;
};
IMediaSampleがstrmif.idlでのDirectShowによって定義され、IDLファイルにインポートされます。
私は同様に(IMediaSampleの定義についてDirectShowLib.Netを使用する)COM相互運用ファイルを定義:
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using DirectShowLib;
namespace Interop.SampleGrabber
{
[Guid("9495F2D0-35FD-451F-B831-F89F1AF8589F")]
[TypeLibType(TypeLibTypeFlags.FDual | TypeLibTypeFlags.FDispatchable)]
[ComImport]
public interface ISampleGrabberFilter
{
[DispId(1610743808)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
int SetCallback([MarshalAs(UnmanagedType.Interface), In] ISampleGrabberCallback callbackIf);
}
[Guid("17B2823E-A24B-483F-A0B0-002EDAF56035")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface ISampleGrabberCallback
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[PreserveSig]
int OnSample([MarshalAs(UnmanagedType.Interface), In] IMediaSample pSample);
}
[CoClass(typeof(SampleGrabberFilterClass))]
[Guid("9495F2D0-35FD-451F-B831-F89F1AF8589F")]
[ComImport]
public interface SampleGrabberFilter : ISampleGrabberFilter
{
}
[TypeLibType(TypeLibTypeFlags.FCanCreate)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("2BD7D268-BB30-4A6F-B715-6223C006D973")]
[ComImport]
public class SampleGrabberFilterClass : ISampleGrabberFilter, SampleGrabberFilter
{
[DispId(1610743808)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[PreserveSig]
public extern virtual int SetCallback([MarshalAs(UnmanagedType.Interface), In] ISampleGrabberCallback callbackIf);
}
}
COM相互運用ファイルが有するtlbimpと、生成された相互運用DLLの逆コンパイルを微調整することによって作製しましたDotPeek 2016.1(IMediaSampleのDirectShowLib.NET版を使用するため)カスタムinteropファイルはプログラム内で直接リンクされています。すなわち、別個のアセンブリではない。コールバックを作る
C++のコードは次のようになります。
// in SampleGrabberFilter.h file
CComPtr<ISampleGrabberCallback> callback;
// in SampleGrabberFilter.cpp file
HRESULT SampleGrabberFilter::SetCallback(ISampleGrabberCallback *cb)
{
callback = cb;
return S_OK;
}
HRESULT SampleGrabberFilter::DoRenderSample(
IMediaSample *pSample)
{
HRESULT hr = S_OK;
if (callback != nullptr)
{
hr = callback->OnSample(pSample);
}
pSample->Release();
return hr;
}
とコールバックを実装するC#のコードは次のようになります。IMediaSampleを解放する
public class StreamContext : Interop.SampleGrabber.ISampleGrabberCallback,
{
// ...
int OnSample_(IMediaSample sample)
{
Console.WriteLine("OnSample_ : Size = {0}", sample.GetSize());
if (sample == null)
return 0;
int hr = 0;
hr = ProcessSample(sample);
//Marshal.Release(Marshal.GetIUnknownForObject(sample));
Marshal.ReleaseComObject(sample);
//int n = Marshal.FinalReleaseComObject(sample);
GC.Collect();
return hr;
}
//...
}
注すべての私の別の試みを。
このコードはすべて、実際のIMediaSampleをコールバックメソッドに渡すとリークが発生することを除いて素晴らしいものです。上記のProcessSampleへのコールをコメントアウトしても、コールバックを効果的にNOOPにすることができます。しかし、nullptrをコールバックに渡すと、漏れはありません。
IMediaSampleをAddRefしてリリースしないようです。
私はここで何が欠けていますか?
更新:コールバックのこの形態が行われると
HRESULT OnSampleBuffer(REFERENCE_TIME startTime, REFERENCE_TIME endTime, int bufferLen, LPBYTE buffer, BOOL isSyncPoint)
:私は代わりにそうような全体IMediaSampleのIMediaSample(及び他の情報)からバッファを渡すコールバックインターフェースに別の方法を追加しましたメモリリークはありません。
私がすることを試みた理由についてのSOに質問だが、GC C#アプリケーションからのイベントにCOMオブジェクトで動作します私のコードでこのコメントをしました私が呼び出すと.WaitForPendingFinalizers()がハングします。 – wta