1
using System;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

namespace ScreenShotDemo
{
    public class ScreenCapture
    {



        [StructLayout(LayoutKind.Sequential)]
        struct CURSORINFO
        {
            public Int32 cbSize;
            public Int32 flags;
            public IntPtr hCursor;
            public POINTAPI ptScreenPos;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct POINTAPI
        {
            public int x;
            public int y;
        }

        [DllImport("user32.dll")]
        static extern bool GetCursorInfo(out CURSORINFO pci);

        [DllImport("user32.dll")]
        static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon);

        const Int32 CURSOR_SHOWING = 0x00000001;

        public static Bitmap CaptureScreen(bool CaptureMouse)
        {
            Bitmap result = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);

            try
            {
                using (Graphics g = Graphics.FromImage(result))
                {
                    g.CopyFromScreen(0, 0, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);

                    if (CaptureMouse)
                    {
                        CURSORINFO pci;
                        pci.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(CURSORINFO));

                        if (GetCursorInfo(out pci))
                        {
                            if (pci.flags == CURSOR_SHOWING)
                            {
                                DrawIcon(g.GetHdc(), pci.ptScreenPos.x, pci.ptScreenPos.y, pci.hCursor);
                                g.ReleaseHdc();
                            }
                        }
                    }
                }
            }
            catch
            {
                result = null;
            }

            return result;
        }
    }

我的电脑上有 6gb 内存,所以它永远不会到达 null 部分。但是我的兄弟只有 2gb 的内存,他到了 null 部分。

我的程序使用此代码在 Form1 中使用计时器每 40 毫秒截取一次屏幕截图:

private void timer1_Tick(object sender, EventArgs e)
        {
            {
            using (bitmap = (Bitmap)ScreenCapture.CaptureScreen(true))            
            ffmp.PushFrame(bitmap);

            }  

        }

我能做些什么来解决这个问题?为什么会发生?

这是我拥有的 ffmpeg 类:

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 ScreenVideoRecorder
{
    class Ffmpeg
    {
        NamedPipeServerStream p;
        String pipename = "mytestpipe";
        byte[] b;
        System.Diagnostics.Process process;
        string ffmpegFileName = "ffmpeg.exe";
        string workingDirectory;

        public Ffmpeg()
        {
            workingDirectory = Path.GetDirectoryName(Application.ExecutablePath);//System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);//Application.StartupPath; //Path.GetDirectoryName(Application.ExecutablePath);// +@"\workingDirectory";
            Logger.Write("workingDirectory: " + workingDirectory);
            if (!Directory.Exists(workingDirectory))
            {
                Directory.CreateDirectory(workingDirectory);
            }
            ffmpegFileName = Path.Combine(workingDirectory, ffmpegFileName);//@"\ffmpeg.exe";
            Logger.Write("FfmpegFilename: " + ffmpegFileName);
        }

        public void Start(string pathFileName, int BitmapRate)
        {
            try
            {
                string outPath = pathFileName;
                Logger.Write("Output Video File Directory: " + outPath);
                Logger.Write("Frame Rate: " + BitmapRate.ToString());
                p = new NamedPipeServerStream(pipename, PipeDirection.Out, 1, PipeTransmissionMode.Byte);
                b = new byte[1920 * 1080 * 3]; // some buffer for the r g and b of pixels of an image of size 720p

                ProcessStartInfo psi = new ProcessStartInfo();
                psi.WindowStyle = ProcessWindowStyle.Hidden;
                psi.UseShellExecute = false;
                psi.CreateNoWindow = true;
                psi.FileName = ffmpegFileName;
                Logger.Write("psi.FileName: " + psi.FileName);
                psi.WorkingDirectory = workingDirectory;
                Logger.Write("psi.WorkingDirectory: " + psi.WorkingDirectory);
                psi.Arguments = @"-f rawvideo -pix_fmt bgr0 -video_size 1920x1080 -i \\.\pipe\mytestpipe -map 0 -c:v libx264 -r " + BitmapRate + " " + outPath;
                Logger.Write("ProcessStartInfo Arguments" + @"-f rawvideo -pix_fmt bgr0 -video_size 1920x1080 -i \\.\pipe\mytestpipe -map 0 -c:v libx264 -r " + BitmapRate + " " + outPath);
                //psi.RedirectStandardOutput = true;
                process = Process.Start(psi);
                process.EnableRaisingEvents = false;
                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());
            }

我每 40 毫秒在 Form1 中截屏一次,并将位图推送到 ffmpeg 类中的管道。ffmpeg.exe 以带有参数的进程启动一次,然后等待连接。

当我查看任务管理器时,我看到 ffmpeg.exe 以 800-900mb 内存开始,并且非常快速地跳转到 1020 或更多 mb 内存。

我应该如何处理像我兄弟这样的用户只有 2gb 内存的情况?当他内存不足时,屏幕捕获类返回 null 给他,程序崩溃。

编辑

我为测试更改了计时器代码:

private void timer1_Tick(object sender, EventArgs e)
        {           
            Image mScreenImage = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
            ffmp.PushFrame((Bitmap)mScreenImage);
            mScreenImage.Dispose();

        }

所以现在我在没有连接到屏幕捕获类的情况下截取屏幕截图,并每 40 毫秒将此图像发送到管道。我在任务管理器中看到 ffmpeg.exe 超过 1gb 的内存。所以难怪我的兄弟在他的电脑上只有 2GB 内存时会遇到问题。

  1. 为什么 ffmpeg.exe 占用这么多内存?泄漏 ?
  2. 我该如何处理?
4

1 回答 1

2

如果你想知道为什么会发生异常,我建议你处理你在 catch 中发现的任何异常。

所以而不是

catch 
{  
  result = null; 
}

尝试

catch (Exception ex) 
{
   // log the value of ex.ToString() to a file, the screen, something.
   System.IO.File.AppendAllText("debug.log",ex.ToString());
   result = null;
}
于 2013-06-10T14:43:20.210 回答