Brubaker Haim
Brubaker Haim

Reputation: 55

Why when using ffmpeg to create video file in real time the ffmpeg.exe take over 1GB of memory?

This is my class where i'm using the ffmpeg.exe.

using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.IO.Pipes;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
using DannyGeneral;

namespace Manager
{
    public class Ffmpeg
    {
        NamedPipeServerStream p;
        String pipename = "mytestpipe";
        System.Diagnostics.Process process;
        string ffmpegFileName = "ffmpeg.exe";
        string workingDirectory;

        public Ffmpeg()
        {
            workingDirectory = Path.GetDirectoryName(Application.ExecutablePath);
            Logger.Write("workingDirectory: " + workingDirectory);
            if (!Directory.Exists(workingDirectory))
            {
                Directory.CreateDirectory(workingDirectory);
            }
            ffmpegFileName = Path.Combine(workingDirectory, ffmpegFileName);
            Logger.Write("FfmpegFilename: " + ffmpegFileName);
        }

        public void Start(string pathFileName, int BitmapRate)
        {
            try
            {

                string outPath = pathFileName;
                p = new NamedPipeServerStream(pipename, PipeDirection.Out, 1, PipeTransmissionMode.Byte);

                ProcessStartInfo psi = new ProcessStartInfo();
                psi.WindowStyle = ProcessWindowStyle.Hidden;
                psi.UseShellExecute = false;
                psi.CreateNoWindow = false;
                psi.FileName = ffmpegFileName;
                psi.WorkingDirectory = workingDirectory;
                psi.Arguments = @"-f rawvideo -pix_fmt bgra -video_size 1920x1080 -i \\.\pipe\mytestpipe -c:v libx264 -crf 20 -r " + BitmapRate + " " + outPath;
                process = Process.Start(psi);
                process.EnableRaisingEvents = false;
                psi.RedirectStandardError = true;
                p.WaitForConnection();
            }
            catch (Exception err)
            {
                Logger.Write("Exception Error: " + err.ToString());
            }
        }

        public void PushFrame(Bitmap bmp)
        {
            try
            {
                int length;
                // Lock the bitmap's bits.
                //bmp = new Bitmap(1920, 1080);
                Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
                //Rectangle rect = new Rectangle(0, 0, 1280, 720);
                System.Drawing.Imaging.BitmapData bmpData =
                    bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly,
                    bmp.PixelFormat);

                int absStride = Math.Abs(bmpData.Stride);
                // Get the address of the first line.
                IntPtr ptr = bmpData.Scan0;

                // Declare an array to hold the bytes of the bitmap.
                //length = 3 * bmp.Width * bmp.Height;
                length = absStride * bmpData.Height;
                byte[] rgbValues = new byte[length];

                //Marshal.Copy(ptr, rgbValues, 0, length);
                int j = bmp.Height - 1;
                for (int i = 0; i < bmp.Height; i++)
                {
                    IntPtr pointer = new IntPtr(bmpData.Scan0.ToInt32() + (bmpData.Stride * j));
                    System.Runtime.InteropServices.Marshal.Copy(pointer, rgbValues, absStride * (bmp.Height - i - 1), absStride);
                    j--;
                }
                p.Write(rgbValues, 0, length);
                bmp.UnlockBits(bmpData);
            }
            catch(Exception err)
            {
                Logger.Write("Error: " + err.ToString());
            }

        }

        public void Close()
        {
            p.Close();
        }
    }
}

ffmpegFileName contain the ffmpeg.exe

Then i have a timer tick event in another form:

ScreenShot shot = new ScreenShot();
    public static int counter = 0;
    private void timer1_Tick(object sender, EventArgs e)
    {
        counter++;
        shot.GetScreenShot(@"e:\screenshots\", "screenshot");
        if (counter == 1200)
        {
            timer1.Stop();
            ScreenShot.fmpeg.Close();
            this.Close();
            ScreenShotsPlayer ssp = new ScreenShotsPlayer();
            ssp.Show();
        }
    }

This timer interval set to 100ms. So it's taking a screenshot every 100ms.

In the class ScreenShot i'm using the Ffmpeg class: In the top:

public static Ffmpeg fmpeg;

In the constructor:

fmpeg = new Ffmpeg();
fmpeg.Start(@"e:\screenshots\test.mp4", 25);

Then in the GetScreenShot method i'm calling the Ffmpeg PushFrame method:

public Bitmap GetScreenShot(string folder, string name)
    {
        _screenShot = new Bitmap(GetScreen());
        System.GC.Collect();
        System.GC.WaitForPendingFinalizers();
        string ingName = folder + name + Elgato_Video_Capture.counter.ToString("D6") + ".bmp";
        _screenShot.Save(ingName,System.Drawing.Imaging.ImageFormat.Bmp);
        fmpeg.PushFrame(_screenShot);
        _screenShot.Dispose();
        return _screenShot;
    }

I can't figure out why once i start the timer to take the screenshots the ffmpeg.exe eat so much memory also the hard disk is working about 53MB/s and the memory is over 90% and over 1GB.

I'm doing the memory leak somehow ?

ffmpeg memory

In the ScreenShot class when i'm making an instance once for the Ffmpeg class the ffmpeg.exe is on very low memory usage and the hard disk usage is low.

The ffmpeg.exe is about only 0.1MB of memory usage.

fmpeg.Start(@"e:\screenshots\test.mp4", 25);

But then when i'm clicking the button start the timer and taking screenshots every 100ms and calling PushFrame method the ffmpeg.exe very fast after few seconds getting to over 1GB of memory usage.

EDIT

I tried this arguments:

psi.Arguments = @"-f rawvideo -pix_fmt bgra -video_size 1920x1080 -i \\.\pipe\mytestpipe -c:v libx264 -crf 20 -r 750k e:\screenshots\test.mp4";

The memory usage was about 1gb of ffmpeg.exe and cpu usage 99-100% The hard disk was on 0 but now the cpu usage got to 100% and the memory over 1gb.

And it didn't create the video file I got this warnings/errors:

Warnings/Errors

Upvotes: 2

Views: 2357

Answers (1)

EladG
EladG

Reputation: 804

It seems like you're endlessly encoding 1920x1080 video (referring to: -f rawvideo -pix_fmt bgra -video_size 1920x1080 -i \\.\pipe\mytestpipe). Encoding (and also decoding) h264 high quality video takes a lot of CPU power. I recommend reading more about H.264 encoding.

Answering this question is not really possible as:

  1. the FFmpeg piece is very hidden in this code implementation (which basically just run a system shell command). Export the full FFmpeg command that is being executing will be a good start. The ffmpeg command should look like ffmpeg -f rawvideo -pix_fmt bgra -video_size 1920x1080 -i \\.\pipe\mytestpipe -c:v libx264 -crf 20 -r 750k e:\screenshots\test.mp4.

  2. FFmpeg changes depending on the input and the output streams. If for example, you will choose a different encoder (say change: -c:v libx264 to: -c:v mpeg2video) I can promise a significant performance improvment as mpeg2video is a light encoder, yet the file size will be about 20 times bigger.

Good luck. -E

Upvotes: 1

Related Questions