ISampleGrabber 中的内存泄漏

Memory Leak in ISampleGrabber

我已经实现了下面的代码。基本上我正在尝试从相机中抓取快照。该代码适用于集成凸轮,但当连接外部凸轮时,问题就开始了。它只需要第一次拍摄,第二次拍摄永远不会到来。

代码可以在

查看
public class ImageEventArgs : EventArgs
{
    public Image CapturedImage { get; set; }
}

public class Camera
{
    internal DsDevice Device { get; set; }
    internal bool IsRunning { get; set; }
    public string Name { get; set; }
    public int Delay { get; set; }
}

public class DsCameraHelper : ISampleGrabberCB, IDisposable
{

    public void Connect(Camera device)
    {
        if (runningCamera == null)
            runningCamera = device;
        if (runningCamera.Name != device.Name)
        {
            runningCamera.IsRunning = false;
            runningCamera = device;
        }

        if (runningCamera == null) return;
        if (runningCamera.IsRunning)
            capFilter.Run(10);
        else
            PrepareCam();
        captured = false;
        runningCamera.IsRunning = true;

        int hr;
        if (sampGrabber == null)
            return;

        if (savedArray == null)
        {
            int size = videoInfoHeader.BmiHeader.ImageSize;
            if ((size < 1000) || (size > 16000000))
                return;
            savedArray = new byte[size + 64000];
        }

        hr = sampGrabber.SetCallback(this, 1);
    }

    public void Disconnect(Camera device)
    {
        int hr;
        if (sampGrabber == null)
            return;
        hr = sampGrabber.SetCallback(null, 0);
    }

    public Bitmap TakeShot()
    {
        return LatestBitmapFrame;
    }

    private object _latestFrameLock = new object();
    private Bitmap _latestFrame = null;
    public Bitmap LatestBitmapFrame
    {
        get
        {
            lock (_latestFrameLock)
            {
                return _latestFrame;
            }
        }
        set
        {
            lock (_latestFrameLock)
            {
                _latestFrame = value;
                if (value == null)
                {
                    return;
                }
            }
        }
    }

    #region private members

    private Panel videoPanel;

    /// <summary> flag to detect first Form appearance </summary>
    private bool firstActive;

    /// <summary> base filter of the actually used video devices. </summary>
    private IBaseFilter capFilter;

    /// <summary> graph builder interface. </summary>
    private IGraphBuilder graphBuilder;

    /// <summary> capture graph builder interface. </summary>
    private ICaptureGraphBuilder2 capGraph;
    private ISampleGrabber sampGrabber;

    /// <summary> control interface. </summary>
    private IMediaControl mediaCtrl;

    /// <summary> event interface. </summary>
    private IMediaEventEx mediaEvt;

    /// <summary> video window interface. </summary>
    private IVideoWindow videoWin;

    /// <summary> grabber filter interface. </summary>
    private IBaseFilter baseGrabFlt;

    /// <summary> structure describing the bitmap to grab. </summary>
    private VideoInfoHeader videoInfoHeader;
    private bool captured = true;
    private int bufferedSize;

    /// <summary> buffer for bitmap data. </summary>
    private byte[] savedArray;

    /// <summary> list of installed video devices. </summary>
    private ArrayList capDevices;

    private const int WM_GRAPHNOTIFY = 0x00008001;  // message from graph

    private const int WS_CHILD = 0x40000000;    
    private const int WS_CLIPCHILDREN = 0x02000000;
    private const int WS_CLIPSIBLINGS = 0x04000000;


    private delegate void CaptureDone();

    #endregion

    #region SampleGrabber

    int ISampleGrabberCB.BufferCB(double SampleTime, IntPtr pBuffer,
     int BufferLen)
    {
        if (captured || (savedArray == null))
        {
            return 0;
        }

        captured = true;
        bufferedSize = BufferLen;
        if ((pBuffer != IntPtr.Zero) && (BufferLen > 1000) && 
         (BufferLen <= savedArray.Length))
            Marshal.Copy(pBuffer, savedArray, 0, BufferLen);
        OnCaptureDone();
        return 0;
    }

    int ISampleGrabberCB.SampleCB(double SampleTime, IMediaSample pSample)
    {
        return 0;
    }

    #endregion

    public DsCameraHelper()
    {
        InitDevices();
        Cameras = new List<Camera>();
        videoPanel = new Panel();
        foreach (DsDevice cam in capDevices)
        {

            Cameras.Add(new Camera() { Device = cam, Name = cam.Name });
        }
    }

    public EventHandler<ImageEventArgs> OnSnapShotCompleted;

    public List<Camera> Cameras { get; set; }
    Camera runningCamera = null;

    private void PrepareCam()
    {

        if (!StartupVideo(runningCamera.Device.Mon)) return;


    }
    /// <summary> handler for toolbar button clicks. </summary>
    public void ClickImage(Camera device)
    {
        if (runningCamera == null)
            runningCamera = device;
        if(runningCamera.Name != device.Name)
        {
            runningCamera.IsRunning = false;
            runningCamera = device;
        }

        if (runningCamera == null) return;
        if (runningCamera.IsRunning)
            capFilter.Run(10);
        else
            PrepareCam();
        captured = false;
        runningCamera.IsRunning = true;

        int hr;
        if (sampGrabber == null)
            return;

        if (savedArray == null)
        {
            int size = videoInfoHeader.BmiHeader.ImageSize;
            if ((size < 1000) || (size > 16000000))
                return;
            savedArray = new byte[size + 64000];
        }

        hr = sampGrabber.SetCallback(this, 1);

    }

    #region DS Implementation

    private void InitDevices()
    {
        if (!DsUtils.IsCorrectDirectXVersion())
        {
            return;
        }

        if (!DsDev.GetDevicesOfCat(FilterCategory.VideoInputDevice, 
          out capDevices))
        {
            return;
        }
    }

    /// <summary> capture event, triggered by buffer callback. </summary>
    void OnCaptureDone()
    {
        try
        {
            int hr;
            if (sampGrabber == null)
                return;
            //hr = sampGrabber.SetCallback(null, 0);

            int w = videoInfoHeader.BmiHeader.Width;
            int h = videoInfoHeader.BmiHeader.Height;
            if (((w & 0x03) != 0) || (w < 32) || (w > 4096) 
            || (h < 32) || (h > 4096))
                return;
            int stride = w * 3;

            GCHandle handle = GCHandle.Alloc(savedArray, 
            GCHandleType.Pinned);
            int scan0 = (int)handle.AddrOfPinnedObject();
            scan0 += (h - 1) * stride;
            Bitmap b = new Bitmap(w, h, -stride, 
              PixelFormat.Format24bppRgb, (IntPtr)scan0);
            handle.Free();
            savedArray = null;
            lastFrame = b;

            if (OnSnapShotCompleted != null)
                OnSnapShotCompleted(this, 
             new ImageEventArgs() { CapturedImage = b });
            capFilter.Stop();

            //Dispose();
            //StartupVideo(device.Mon);
        }
        catch (Exception ee)
        {
        }
    }
    public Bitmap LastFrame { get { return lastFrame; } }
    private Bitmap lastFrame;
    bool StartupVideo(UCOMIMoniker mon)
    {
        int hr;
        try
        {
            if (!CreateCaptureDevice(mon))
                return false;

            if (!GetInterfaces())
                return false;

            if (!SetupGraph())
                return false;

            if (!SetupVideoWindow())
                return false;


            hr = mediaCtrl.Run();
            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);
            Thread.Sleep(runningCamera.Delay);
            return true;
        }
        catch (Exception ee)
        {
            return false;
        }
    }


    bool GetInterfaces()
    {
        Type comType = null;
        object comObj = null;
        try
        {
            comType = Type.GetTypeFromCLSID(Clsid.FilterGraph);
            if (comType == null)
                throw new NotImplementedException(
                @"DirectShow FilterGraph not installed/registered!");
            comObj = Activator.CreateInstance(comType);
            graphBuilder = (IGraphBuilder)comObj; comObj = null;

            Guid clsid = Clsid.CaptureGraphBuilder2;
            Guid riid = typeof(ICaptureGraphBuilder2).GUID;
            comObj = DsBugWO.CreateDsInstance(ref clsid, ref riid);
            capGraph = (ICaptureGraphBuilder2)comObj; comObj = null;

            comType = Type.GetTypeFromCLSID(Clsid.SampleGrabber);
            if (comType == null)
                throw new NotImplementedException(
              @"DirectShow SampleGrabber not installed/registered!");
            comObj = Activator.CreateInstance(comType);
            sampGrabber = (ISampleGrabber)comObj; comObj = null;

            mediaCtrl = (IMediaControl)graphBuilder;
            videoWin = (IVideoWindow)graphBuilder;
            mediaEvt = (IMediaEventEx)graphBuilder;
            baseGrabFlt = (IBaseFilter)sampGrabber;
            return true;
        }
        catch (Exception ee)
        {
            return false;
        }
        finally
        {
            if (comObj != null)
                Marshal.ReleaseComObject(comObj); comObj = null;
        }
    }

    /// <summary> create the user selected capture device. </summary>
    bool CreateCaptureDevice(UCOMIMoniker mon)
    {
        object capObj = null;
        try
        {
            Guid gbf = typeof(IBaseFilter).GUID;
            mon.BindToObject(null, null, ref gbf, out capObj);
            capFilter = (IBaseFilter)capObj; capObj = null;
            return true;
        }
        catch (Exception ee)
        {
            return false;
        }
        finally
        {
            if (capObj != null)
                Marshal.ReleaseComObject(capObj); capObj = null;
        }

    }

    bool CloseAll()
    {
        videoWin.put_Owner(IntPtr.Zero);
        mediaCtrl.Stop();
        baseGrabFlt = null;
        if (sampGrabber != null)
            Marshal.ReleaseComObject(sampGrabber); sampGrabber = null;

        if (capGraph != null)
            Marshal.ReleaseComObject(capGraph); capGraph = null;

        if (graphBuilder != null)
            Marshal.ReleaseComObject(graphBuilder); graphBuilder = null;

        if (capFilter != null)
            Marshal.ReleaseComObject(capFilter); capFilter = null;
        return true;
    }


    bool SetupVideoWindow()
    {
        int hr;
        try
        {
            // Set the video window to be a child of the main window
            hr = videoWin.put_Owner(videoPanel.Handle);
            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);

            // Set video window style
            hr = videoWin.put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN);
            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);

            // Use helper function to position video 
            //window in client rect of owner window
            //ResizeVideoWindow();

            // Make the video window visible, now that 
            //it is properly positioned
            hr = videoWin.put_Visible(DsHlp.OAFALSE);
            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);

            hr = mediaEvt.SetNotifyWindow(videoPanel.Handle,
            WM_GRAPHNOTIFY, IntPtr.Zero);
            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);
            return true;
        }
        catch (Exception ee)
        {
            return false;
        }
    }

    /// <summary> build the capture graph for grabber. </summary>
    bool SetupGraph()
    {
        int hr;
        try
        {
            hr = capGraph.SetFiltergraph(graphBuilder);
            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);

            hr = graphBuilder.AddFilter(capFilter, 
             "Ds.NET Video Capture Device");
            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);

            //DsUtils.ShowCapPinDialog(capGraph, capFilter, this.Handle);

            AMMediaType media = new AMMediaType();
            media.majorType = MediaType.Video;
            media.subType = MediaSubType.RGB24;
            media.formatType = FormatType.VideoInfo;        // ???
            hr = sampGrabber.SetMediaType(media);
            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);

            hr = graphBuilder.AddFilter(baseGrabFlt, "Ds.NET Grabber");
            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);

            Guid cat = PinCategory.Preview;
            Guid med = MediaType.Video;
            hr = capGraph.RenderStream(ref cat, ref med, 
            capFilter, null, null); // baseGrabFlt 
            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);

            cat = PinCategory.Capture;
            med = MediaType.Video;
            hr = capGraph.RenderStream(ref cat, 
            ref med, capFilter, null, baseGrabFlt); // baseGrabFlt 
            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);

            media = new AMMediaType();
            hr = sampGrabber.GetConnectedMediaType(media);
            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);
            if ((media.formatType != FormatType.VideoInfo) || 
               (media.formatPtr == IntPtr.Zero))
                throw new NotSupportedException(
              "Unknown Grabber Media Format");

            videoInfoHeader = (VideoInfoHeader)Marshal.PtrToStructure(
            media.formatPtr, typeof(VideoInfoHeader));
            Marshal.FreeCoTaskMem(media.formatPtr); 
             media.formatPtr = IntPtr.Zero;

            hr = sampGrabber.SetBufferSamples(false);
            if (hr == 0)
                hr = sampGrabber.SetOneShot(false);
            if (hr == 0)
                hr = sampGrabber.SetCallback(null, 0);
            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);

            return true;
        }
        catch (Exception ee)
        {
            return false;
        }
    }

    #endregion

    #region IDisposable Implementation

    public void Dispose()
    {
        CloseInterfaces();
    }

    private void CloseInterfaces()
    {
        int hr;
        try
        {
            OnSnapShotCompleted = null;
            lastFrame.Dispose();
            lastFrame = null;
            if (graphBuilder != null)
            {
                hr = graphBuilder.RemoveFilter(capFilter);
                Marshal.ThrowExceptionForHR(hr);

                hr = graphBuilder.RemoveFilter(baseGrabFlt);
                Marshal.ThrowExceptionForHR(hr);

                Marshal.FinalReleaseComObject(graphBuilder);
            }

            if (mediaCtrl != null)
            {
                hr = mediaCtrl.Stop();
                Marshal.FinalReleaseComObject(mediaCtrl);
                mediaCtrl = null;
                Marshal.ThrowExceptionForHR(hr);

            }

            if (mediaEvt != null)
            {
                hr = mediaEvt.SetNotifyWindow(IntPtr.Zero, 
                    WM_GRAPHNOTIFY, IntPtr.Zero);
                Marshal.FinalReleaseComObject(mediaEvt);
                mediaEvt = null;
                Marshal.ThrowExceptionForHR(hr);
            }

            if (videoWin != null)
            {
                hr = videoWin.put_Visible(DsHlp.OAFALSE);
                Marshal.ThrowExceptionForHR(hr);
                hr = videoWin.put_Owner(IntPtr.Zero);
                Marshal.ThrowExceptionForHR(hr);

                Marshal.FinalReleaseComObject(videoWin);
                videoWin = null;

                videoPanel.Dispose();
                Marshal.FinalReleaseComObject(videoPanel);
                videoPanel = null;
            }

            Marshal.FinalReleaseComObject(videoInfoHeader);
            videoInfoHeader = null;

            Marshal.FinalReleaseComObject(baseGrabFlt);
            baseGrabFlt = null;

            if (sampGrabber != null)
                Marshal.FinalReleaseComObject(sampGrabber); 
             sampGrabber = null;

            if (capGraph != null)
                Marshal.FinalReleaseComObject(capGraph); capGraph = null;

            if (graphBuilder != null)
                Marshal.FinalReleaseComObject(graphBuilder); 
             graphBuilder = null;

            if (capFilter != null)
            {
                hr = capFilter.Stop();
                Marshal.ThrowExceptionForHR(hr);
                Marshal.FinalReleaseComObject(capFilter); capFilter = null;
            }

            if (capDevices != null)
            {
                foreach (DsDevice d in capDevices)
                {
                    d.Dispose();
                    Marshal.FinalReleaseComObject(d); ;
                }
                capDevices = null;
            }
            foreach (var cam in Cameras)
            {
                cam.Device.Dispose();
                Marshal.FinalReleaseComObject(cam.Device); 
            }
            Cameras = null;
            GC.Collect();
        }
        catch
        { }
    }

    #endregion
}

static void Main(string[] args)
    {
        DShowNET.DsCameraHelper c = new DShowNET.DsCameraHelper();
        Console.WriteLine("List of Attached Cams.");
        var count = 1;
        foreach (var cam in c.Cameras)
        {
            Console.WriteLine(string.Format("{0}. {1}", count++, cam.Name));
        }

        Console.WriteLine(string.Format("{0}. {1}", count++, "Exit"));

        int choosenCam = 0;
        while (choosenCam != count)
        {
            Console.WriteLine("Please choose a camera to take snapshot");

            var key = Console.ReadLine();

            if (int.TryParse(key, out choosenCam) && choosenCam <= c.Cameras.Count)
            {
                //c.ClickImage(c.Cameras[choosenCam - 1]);
                //var path = string.Format("img_{0}.png", DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss_fff"));
                //img.Save(path, ImageFormat.Png);
                //Console.WriteLine("Image save successfully at " + path);
                var task = GetSnap(c.Cameras[choosenCam - 1]);
                var data = task.Result;
                task.Dispose();
            }
        }
    }
    private static async Task<Bitmap> GetSnap(Camera cam)
    {
        Bitmap img = null;
        await System.Threading.Tasks.Task.Run(() =>
        {
            DsCameraHelper helper = new DsCameraHelper();
            AutoResetEvent waitHandle = new AutoResetEvent(false);
            EventHandler<ImageEventArgs> eventHandler = delegate(object sender, ImageEventArgs e)
            {
                //img = e.CapturedImage;

                waitHandle.Set();  // signal that the finished event was raised
            };
            helper.OnSnapShotCompleted += eventHandler;
            Camera dev = null;
            foreach (var camera in helper.Cameras)
            {
                Guid cId, coutId;
                if (camera.Name.Equals(cam.Name))
                    dev = camera;
            }

            if (dev != null)
            {
                helper.ClickImage(dev);
            }
            else
                throw new Exception("Invalid Cam Selected");
            waitHandle.WaitOne();
            helper.OnSnapShotCompleted -= eventHandler;
            helper.Dispose();
            waitHandle.Dispose();
            waitHandle = null;
            helper = null;
        }).ConfigureAwait(false);
        return img;
    }
}

https://onedrive.live.com/redir?resid=2B39C1B1D1F06A02!8546&authkey=!AGuDe-Q_6_sacM0&ithint=folder%2ccs

谢谢。

你不应该从帧回调中停止过滤图。在回调中做你的事情并立即 return(没有 OnCaptureDone 和朋友),停止从你首先构建和启动过滤器图的顶级代码中捕获。