BogdanovKirill / RtspClientSharp

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

High memory usage #74

Open moraismateus opened 4 years ago

moraismateus commented 4 years ago

Hi, thank you for your library, it is very helpful.

I am using some pieces of your playr code, but my player is running using more memory, and the GC us acting all the time, if you have some time, could you see if I am missing something on this code below?

I am decoding the RawVideoFrame than converting it to a Bitmap, and then to a BitmapImage to be show on my WPF . Thank you in advance:

using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Threading; using System.Threading.Tasks; using RtspClientSharp; using RtspClientSharp.RawFrames; using RtspClientSharp.RawFrames.Video; using RtspClientSharp.Rtsp; using EncoderService.RawFramesDecoding; using EncoderService.RawFramesDecoding.DecodedFrames; using EncoderService.RawFramesDecoding.FFmpeg; using System.Windows.Media.Imaging;

namespace EncoderService {

public class MyEncoder
{
    private static readonly Dictionary<FFmpegVideoCodecId, FFmpegVideoDecoder> _videoDecodersMap =
       new Dictionary<FFmpegVideoCodecId, FFmpegVideoDecoder>();

    //static readonly int intervalMs = 100;
    //static int lastTimeSnapshotSaved;
    private RtspClient rtspClient;
    public delegate void SampleEventHandler(BitmapImage e);
    public event SampleEventHandler NewBitmap;

    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 ticksNow = Environment.TickCount;

        //if (Math.Abs(ticksNow - lastTimeSnapshotSaved) < intervalMs) return;

        //lastTimeSnapshotSaved = ticksNow;

        int bufferSize;

        bufferSize = decodedFrameParameters.Height *
                     ImageUtils.GetStride(decodedFrameParameters.Width, RawFramesDecoding.PixelFormat.Bgr24);
        byte[] _decodedFrameBuffer = new byte[0];
        if (_decodedFrameBuffer.Length != bufferSize)
            _decodedFrameBuffer = new byte[bufferSize];

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

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

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

        CopyDataToBitmap(decodedFrameParameters.Width, decodedFrameParameters.Height, decodedFrame.DecodedBytes.Array);     
    }

    public void CopyDataToBitmap(int Width, int Height, byte[] data)
    {
        //Here create the Bitmap to the know height, width and format
        using (Bitmap bmp = new Bitmap(Width, Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb))
        {

            //Create a BitmapData and Lock all pixels to be written 
            BitmapData bmpData = bmp.LockBits(
                                 new Rectangle(0, 0, bmp.Width, bmp.Height),
                                 ImageLockMode.WriteOnly, bmp.PixelFormat);

            //Copy the data from the byte array into BitmapData.Scan0
            System.Runtime.InteropServices.Marshal.Copy(data, 0, bmpData.Scan0, data.Length);

            //Unlock the pixels
            bmp.UnlockBits(bmpData);

            //Return the bitmap 
            using (var ms = new System.IO.MemoryStream())
            {
                bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
                var bi = new BitmapImage();
                bi.BeginInit();
                bi.StreamSource = ms;
                bi.CacheOption = BitmapCacheOption.OnLoad;
                bi.EndInit();
                BitmapEvent(bi);
            }
        }
    }

    private void BitmapEvent(BitmapImage bitmap)
    {
        NewBitmap(bitmap);
    }

    private static ImageCodecInfo GetEncoder(ImageFormat format)
    {

        ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();

        foreach (ImageCodecInfo codec in codecs)
        {
            if (codec.FormatID == format.Guid)
            {
                return codec;
            }
        }
        return null;
    }
    private static FFmpegVideoDecoder GetDecoderForFrame(RawVideoFrame videoFrame)
    {
        FFmpegVideoCodecId codecId = DetectCodecId(videoFrame);
        if (!_videoDecodersMap.TryGetValue(codecId, out FFmpegVideoDecoder decoder))
        {
            decoder = FFmpegVideoDecoder.CreateDecoder(codecId);
            _videoDecodersMap.Add(codecId, decoder);
        }

        return decoder;
    }

    private static FFmpegVideoCodecId DetectCodecId(RawVideoFrame videoFrame)
    {
        if (videoFrame is RawJpegFrame)
            return FFmpegVideoCodecId.MJPEG;
        if (videoFrame is RawH264Frame)
            return FFmpegVideoCodecId.H264;

        throw new ArgumentOutOfRangeException(nameof(videoFrame));
    }
    public async Task ConnectAsync(Uri uri, CancellationToken token)
    {
        try
        {
            ConnectionParameters connectionParameters = new ConnectionParameters(uri);

            if (!(rtspClient is null))
                rtspClient.Dispose();
            using (rtspClient = new RtspClient(connectionParameters))
            {

                rtspClient.FrameReceived += OnFrameReceived;

                while (true)
                {

                    try
                    {
                        await rtspClient.ConnectAsync(token);                            
                    }
                    catch (OperationCanceledException)
                    {
                        return;
                    }
                    catch (RtspClientException e)
                    {

                        continue;
                    }

                    try
                    {
                        await rtspClient.ReceiveAsync(token);
                    }
                    catch (OperationCanceledException)
                    {
                        return;
                    }
                    catch (RtspClientException e)
                    {

                    }

                }                   

            }
        }
        catch (OperationCanceledException)
        {
        }
    }

}

}