2016-09-15 7 views
1

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(及び他の情報)からバッファを渡すコールバックインターフェースに別の方法を追加しましたメモリリークはありません。

答えて

0

私は

 // Call the Garbage Collector twice (only once is not enough) 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 

ありUnder what circumstances, we need to call GC.Collect twice

+0

私がすることを試みた理由についてのSOに質問だが、GC C#アプリケーションからのイベントにCOMオブジェクトで動作します私のコードでこのコメントをしました私が呼び出すと.WaitForPendingFinalizers()がハングします。 – wta

関連する問題