BogdanovKirill / RtspClientSharp

Pure C# RTSP client for .NET Standard without external dependencies and with true async nature. I welcome contributions.
MIT License
694 stars 284 forks source link

Memory leak issue in RtspSnapshotMaker from previous closed issue #40 #135

Closed zoekatakuzinos closed 5 months ago

zoekatakuzinos commented 5 months ago
          Hi @BogdanovKirill 

I am also wondering about this memory leak issue. I am using the decoding github project "rtspcapture - working" which gives me the SnapshotMaker example. I changed it to a Winforms project so I could stream to a picture box, and then take a snapshot, and put it in another picture box.

I commented out the ToImage() function so I took out all displaying of Images in my picture boxes. The memory seems more stable.

When calling the ToImage function, my memory spikes up when taking shapshots, even though I am clearing my Picture Boxes. As I take more snapshots, the memory climbs higher.

It never come down, even when calling the GC explicitly. I am not sure how to clean up ? Can you help?

(This code comes from my crude test program)

    private void OnFrameReceived(object sender, RawFrame rawFrame)
    {
        if (!(rawFrame is RawVideoFrame rawVideoFrame))
            return;

        FFmpegVideoDecoder decoder = GetDecoderForFrame(rawVideoFrame);
        if (!decoder.TryDecode(rawVideoFrame, out DecodedVideoFrameParameters decodedFrameParameters))
            return;

        int bufferSize;

        bufferSize = decodedFrameParameters.Height *
                     ImageUtils.GetStride(decodedFrameParameters.Width, RawFramesDecoding.PixelFormat.Abgr32);

        if (_decodedFrameBuffer.Length != bufferSize)
            _decodedFrameBuffer = new byte[bufferSize];

        var bufferSegment = new ArraySegment<byte>(_decodedFrameBuffer);

        var postVideoDecodingParameters = new PostVideoDecodingParameters(RectangleF.Empty,
            new Size(decodedFrameParameters.Width, decodedFrameParameters.Height),
            ScalingPolicy.Stretch, RawFramesDecoding.PixelFormat.Bgr24, ScalingQuality.Bicubic);

        IDecodedVideoFrame decodedFrame = decoder.GetDecodedFrame(bufferSegment, postVideoDecodingParameters);

        //ArraySegment<byte> frameSegment = decodedFrame.DecodedBytes;
        //string snapshotName = decodedFrame.Timestamp.ToString("O").Replace(":", "_") + ".jpg";

        //ToImage(decodedFrameParameters.Width, decodedFrameParameters.Height, snapshotName, System.Drawing.Imaging.PixelFormat.Format24bppRgb, frameSegment.Array);

    }

    private void ToImage(int Width, int Height, string imgName, System.Drawing.Imaging.PixelFormat pixelFormat, byte[] rgbValues)
    {
        Bitmap bitMap = new Bitmap(Width, Height, pixelFormat);

        Rectangle BoundsRect = new Rectangle(0, 0, Width, Height);

        BitmapData bitmapData = bitMap.LockBits(BoundsRect, ImageLockMode.WriteOnly, bitMap.PixelFormat);

        IntPtr _dataPointer = bitmapData.Scan0;

        int _byteData = bitmapData.Stride * bitMap.Height;

        System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, _dataPointer, _byteData);

        bitMap.UnlockBits(bitmapData);

        if (pictureBox1.Image != null)
        {
            pictureBox1.Image.Dispose();
            pictureBox1.Image = null;
        }

        pictureBox1.Image = bitMap;
        pictureBox1.Refresh();

        if (MustTakeSnapshot)
        {
            Thread t = new Thread(new ParameterizedThreadStart(TakeSnapshot));
            t.Start(bitMap);
        }
    }

    private void TakeSnapshot(object bitMap)
    {

        Bitmap b = (Bitmap)bitMap;

        ImageCodecInfo jgpEncoder = GetEncoder(ImageFormat.Jpeg);

        System.Drawing.Imaging.Encoder encoder = System.Drawing.Imaging.Encoder.Quality;

        EncoderParameters encoderParams = new EncoderParameters(1);

        EncoderParameter myEncoderParameter = new EncoderParameter(encoder, 50L);

        encoderParams.Param[0] = myEncoderParameter;

        string filename = @"c:\capture\frame" + DateTime.Now.Millisecond.ToString();
        b.Save(filename, jgpEncoder, encoderParams);

        if (pictureBox2.Image != null)
        {
            pictureBox2.Image.Dispose();
            pictureBox2.Image = null;
        }

        pictureBox2.Image = Image.FromFile(filename);

        b.Dispose();
        b = null;
        bitMap = null;

        MustTakeSnapshot = false;
    }

Originally posted by @zoekatakuzinos in https://github.com/BogdanovKirill/RtspClientSharp/issues/40#issuecomment-1914546992

zoekatakuzinos commented 5 months ago

I have made it work. I did not start a new thread from the ToImage function (which was called from OnFrameReceived) I rather put the code directly into ToImage function and the memory seems stable now. It was perhaps not a good design to start a new thread from within an async function