需要更快地实现 Image.FromStream 来实时显示图像

发布于 2024-11-04 06:44:40 字数 11494 浏览 1 评论 0原文

我正在创建一个远程桌面程序,因此我开发了一个类,它可以拍摄客户端计算机的快照并将每个图像以字节数组格式发送到服务器。然后服务器读取这些字节并将流转换为 System.Drawing.Image 类型。但 Image.FromStream 花费的时间太长。每次我断点时,它下面的这条线就会卡住。没有抛出异常,并且该行代码之后什么也没有发生。

导致问题的线路:

StreamingData(client, new DataEventArgs(Image.FromStream(stream, false, false)));

完整代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
using System.Drawing;
namespace Simply_Remote_Desktop_Library
{
    public sealed class RemoteDesktopListener
    {
        private TcpListener listner;
        private TcpClient client;
        public delegate void ConnectedEventHandler(TcpClient sender, EventArgs e);
        public event ConnectedEventHandler Connected;
        public delegate void IncommingDataEventHandler(TcpClient sender, DataEventArgs e);
        public event IncommingDataEventHandler StreamingData;
        public delegate void ConnectionEndedEventHandler(RemoteDesktopListener sender, EventArgs e);
        public event ConnectionEndedEventHandler ConnectionEnded;
        /// <summary>
        /// Constructor (+1 Overloads)
        /// </summary>
        /// <param name="port">The port to listen for incoming connections.</param>
        /// <param name="host_ip">The IP address of the client computer.</param>
        public RemoteDesktopListener(int port, string host_ip)
        {
            IPAddress addr;
            if (IPAddress.TryParse(host_ip, out addr))
            {
                if (port > 0 || port < 65535)
                {
                    listner = new TcpListener(addr, port);
                }
                else if (port < 0 || port > 65535)
                {
                    throw new RemoteDesktopException(new ArgumentOutOfRangeException("port"));
                }
            }
            else
            {
                throw new RemoteDesktopException("Error: Invalid IP address in string format. Make sure it is in the right format.");
            }
        }
        /// <summary>
        /// Constructor (+1 Overloads)
        /// </summary>
        /// <param name="port">Port for server to listen for incoming connections.</param>
        /// <param name="host_ip">Ip address of client.</param>
        public RemoteDesktopListener(int port, IPAddress host_ip)
        {
            if (port > 0 && port < 65535)
            {
                listner = new TcpListener(host_ip, port);
            }
            else if (port < 0 && port > 65535)
            {
                throw new RemoteDesktopException("Error: Port number invalid! Range is from 0 - 65535.");
            }
        }
        /// <summary>
        /// Starts the listening process and establishes a connection to a client.
        /// </summary>
        public void BeginListening()
        {
            if (listner == null)
            {
                throw new RemoteDesktopException(new NullReferenceException());
            }
            else if (listner != null)
            {
                byte[] bytes = new byte[204800];
                bool connected = false;
                listner.Start();
                //int i;
                while (true)
                {
                    client = listner.AcceptTcpClient();
                    if (connected == false)
                    {
                        Connected(client, new EventArgs());
                        connected = true;
                    }
                    NetworkStream stream = client.GetStream();
                    while (stream.DataAvailable == true)
                    {

                        StreamingData(client, new DataEventArgs(Image.FromStream(stream, false, false)));
                    }
                    client.Close();
                    ConnectionEnded(this, new EventArgs());
                }

            }
            return;
        }
        /// <summary>
        /// Starts listening for incoming connection requests.
        /// </summary>
        /// <param name="backlog">Maximum number of pending connection</param>
        public void BeginListening(int backlog)
        {
            if (listner == null)
            {
                throw new RemoteDesktopException(new NullReferenceException());
            }
            else if (listner != null)
            {
                //byte[] bytes = new byte[204800];
                bool connected = false;
                listner.Start(backlog);
                //int i;
                while (true)
                {
                    client = listner.AcceptTcpClient();
                    if (connected == false)
                    {
                        Connected(client, new EventArgs());
                        connected = true;
                    }
                    NetworkStream stream = client.GetStream();
                    while (stream.DataAvailable == true)
                    {

                        StreamingData(client, new DataEventArgs(Image.FromStream(stream, false, false)));
                    }
                    client.Close();
                    ConnectionEnded(this, new EventArgs());
                }

            }
            return;
        }
        public void StopListening()
        {
            client.Close();
            listner.Stop();
        }
        /// <summary>
        /// Returns the System.Net.Sockets.Socket of the current session.
        /// </summary>
        public Socket Socket
        {
            get
            {
                return listner.Server;
            }
        }
        ~RemoteDesktopListener()
        {
            client.Close();
            listner.Stop();
            ConnectionEnded(this, new EventArgs());
        }

    }
    public sealed class RemoteDesktopClient
    {
        private TcpClient client;
        private Timer timer;
        private Bitmap bitmap;
        private System.Drawing.Imaging.PixelFormat format;
        private Graphics g;
        private NetworkStream stream;
        public delegate void ConnectionCloseEventHandler(RemoteDesktopClient sender, EventArgs e);
        public event ConnectionCloseEventHandler ConnectionClosed;
        /// <summary>
        /// Constructor (1 Overload)
        /// </summary>
        /// <param name="pixel_format">The bitmap's pixel format that will be used for every frame that is sent across the network.</param>
        public RemoteDesktopClient(System.Drawing.Imaging.PixelFormat pixel_format)
        {
            format = pixel_format;
        }
        public void BeginConnection(int port, string host_ip)
        {
            IPAddress addr;
            if (IPAddress.TryParse(host_ip, out addr))
            {
                if (port > 0 && port < 65535)
                {
                    client = new TcpClient(host_ip, port);
                    timer = new Timer();
                    timer.Interval = Convert.ToInt32(Math.Pow(24, -1) * 1000); // 24 frames per second.
                    timer.Tick += new EventHandler(timer_Tick);
                    stream = client.GetStream();
                    timer.Enabled = true;

                }
                else if (port > 0 && port > 65535)
                {
                    throw new RemoteDesktopException(new ArgumentOutOfRangeException("port"));
                }
            }
            else
            {
                throw new RemoteDesktopException("Error: Invalid IP address in string format. Make sure it is in the right format.");
            }
        }

        void timer_Tick(object sender, EventArgs e)
        {
            bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);

            g = Graphics.FromImage(bitmap);
            g.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
            Cursors.Default.Draw(g, new Rectangle(Cursor.Position, Cursors.Default.Size));
            byte[] buffer = imageToByteArray(bitmap);
            stream.Write(buffer, 0, buffer.Length);

        }
        public void EndConnection()
        {
            g.Dispose();
            stream.Close();
            stream.Dispose();
            bitmap.Dispose();
            timer.Enabled = false;
            client.Close();
            ConnectionClosed(this, new EventArgs());
            return;
        }
        public Socket Socket
        {
            get
            {
                return client.Client;
            }
        }
        byte[] imageToByteArray(System.Drawing.Image imageIn)
        {
            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
            return ms.ToArray();
        }
    }
    /// <summary>
    /// Represents all remote desktop runtime errors. Inherits from System.Exception.
    /// </summary>
    public class RemoteDesktopException : Exception
    {
        private string message;
        private Exception ex;
        /// <summary>
        /// Constructor (+1 Overloads)
        /// </summary>
        /// <param name="error">The error message in string format.</param>
        public RemoteDesktopException(string error)
        {
            message = error;
        }
        /// <summary>
        /// Constructor (+1 Overloads)
        /// </summary>
        /// <param name="error">The error in System.Exception format.</param>
        public RemoteDesktopException(Exception error)
        {
            ex = error;
        }
        public override string StackTrace
        {
            get
            {
                return base.StackTrace;
            }
        }
        public override string HelpLink
        {
            get
            {
                return base.HelpLink;
            }
            set
            {
                base.HelpLink = value;
            }
        }
        public override string Message
        {
            get
            {
                if (message != null)
                {
                    return message;
                }
                else
                {
                    return ex.Message;
                }
            }
        }
    }
    public class DataEventArgs : EventArgs
    {
        private Image image;
        public DataEventArgs(Image img)
        {
            image = img;
        }
        public System.Drawing.Image CurrentFrame
        {
            get
            {

                return image;
            }
        }

    }
}

我在这里添加了flush

 void timer_Tick(object sender, EventArgs e)
        {
            bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);

            g = Graphics.FromImage(bitmap);
            g.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
            Cursors.Default.Draw(g, new Rectangle(Cursor.Position, Cursors.Default.Size));
            byte[] buffer = imageToByteArray(bitmap);
            stream.Write(buffer, 0, buffer.Length);
            stream.Flush();


        }

这是您建议我放置stream.Flush() 子例程的地方吗? *因为它仍然不起作用。我还尝试将 NoDelay 属性设置为 true。*

I am creating a Remote Desktop program, so I have developed a class that takes snapshot of the client's computer and sends each image in byte array format to the server. Then the server reads those bytes and converts the stream to type System.Drawing.Image. But Image.FromStream takes too long. Everytime I break point this line below it get stuck. No exception gets thrown and nothing happens after that line of code.

Line causing problem:

StreamingData(client, new DataEventArgs(Image.FromStream(stream, false, false)));

Full code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
using System.Drawing;
namespace Simply_Remote_Desktop_Library
{
    public sealed class RemoteDesktopListener
    {
        private TcpListener listner;
        private TcpClient client;
        public delegate void ConnectedEventHandler(TcpClient sender, EventArgs e);
        public event ConnectedEventHandler Connected;
        public delegate void IncommingDataEventHandler(TcpClient sender, DataEventArgs e);
        public event IncommingDataEventHandler StreamingData;
        public delegate void ConnectionEndedEventHandler(RemoteDesktopListener sender, EventArgs e);
        public event ConnectionEndedEventHandler ConnectionEnded;
        /// <summary>
        /// Constructor (+1 Overloads)
        /// </summary>
        /// <param name="port">The port to listen for incoming connections.</param>
        /// <param name="host_ip">The IP address of the client computer.</param>
        public RemoteDesktopListener(int port, string host_ip)
        {
            IPAddress addr;
            if (IPAddress.TryParse(host_ip, out addr))
            {
                if (port > 0 || port < 65535)
                {
                    listner = new TcpListener(addr, port);
                }
                else if (port < 0 || port > 65535)
                {
                    throw new RemoteDesktopException(new ArgumentOutOfRangeException("port"));
                }
            }
            else
            {
                throw new RemoteDesktopException("Error: Invalid IP address in string format. Make sure it is in the right format.");
            }
        }
        /// <summary>
        /// Constructor (+1 Overloads)
        /// </summary>
        /// <param name="port">Port for server to listen for incoming connections.</param>
        /// <param name="host_ip">Ip address of client.</param>
        public RemoteDesktopListener(int port, IPAddress host_ip)
        {
            if (port > 0 && port < 65535)
            {
                listner = new TcpListener(host_ip, port);
            }
            else if (port < 0 && port > 65535)
            {
                throw new RemoteDesktopException("Error: Port number invalid! Range is from 0 - 65535.");
            }
        }
        /// <summary>
        /// Starts the listening process and establishes a connection to a client.
        /// </summary>
        public void BeginListening()
        {
            if (listner == null)
            {
                throw new RemoteDesktopException(new NullReferenceException());
            }
            else if (listner != null)
            {
                byte[] bytes = new byte[204800];
                bool connected = false;
                listner.Start();
                //int i;
                while (true)
                {
                    client = listner.AcceptTcpClient();
                    if (connected == false)
                    {
                        Connected(client, new EventArgs());
                        connected = true;
                    }
                    NetworkStream stream = client.GetStream();
                    while (stream.DataAvailable == true)
                    {

                        StreamingData(client, new DataEventArgs(Image.FromStream(stream, false, false)));
                    }
                    client.Close();
                    ConnectionEnded(this, new EventArgs());
                }

            }
            return;
        }
        /// <summary>
        /// Starts listening for incoming connection requests.
        /// </summary>
        /// <param name="backlog">Maximum number of pending connection</param>
        public void BeginListening(int backlog)
        {
            if (listner == null)
            {
                throw new RemoteDesktopException(new NullReferenceException());
            }
            else if (listner != null)
            {
                //byte[] bytes = new byte[204800];
                bool connected = false;
                listner.Start(backlog);
                //int i;
                while (true)
                {
                    client = listner.AcceptTcpClient();
                    if (connected == false)
                    {
                        Connected(client, new EventArgs());
                        connected = true;
                    }
                    NetworkStream stream = client.GetStream();
                    while (stream.DataAvailable == true)
                    {

                        StreamingData(client, new DataEventArgs(Image.FromStream(stream, false, false)));
                    }
                    client.Close();
                    ConnectionEnded(this, new EventArgs());
                }

            }
            return;
        }
        public void StopListening()
        {
            client.Close();
            listner.Stop();
        }
        /// <summary>
        /// Returns the System.Net.Sockets.Socket of the current session.
        /// </summary>
        public Socket Socket
        {
            get
            {
                return listner.Server;
            }
        }
        ~RemoteDesktopListener()
        {
            client.Close();
            listner.Stop();
            ConnectionEnded(this, new EventArgs());
        }

    }
    public sealed class RemoteDesktopClient
    {
        private TcpClient client;
        private Timer timer;
        private Bitmap bitmap;
        private System.Drawing.Imaging.PixelFormat format;
        private Graphics g;
        private NetworkStream stream;
        public delegate void ConnectionCloseEventHandler(RemoteDesktopClient sender, EventArgs e);
        public event ConnectionCloseEventHandler ConnectionClosed;
        /// <summary>
        /// Constructor (1 Overload)
        /// </summary>
        /// <param name="pixel_format">The bitmap's pixel format that will be used for every frame that is sent across the network.</param>
        public RemoteDesktopClient(System.Drawing.Imaging.PixelFormat pixel_format)
        {
            format = pixel_format;
        }
        public void BeginConnection(int port, string host_ip)
        {
            IPAddress addr;
            if (IPAddress.TryParse(host_ip, out addr))
            {
                if (port > 0 && port < 65535)
                {
                    client = new TcpClient(host_ip, port);
                    timer = new Timer();
                    timer.Interval = Convert.ToInt32(Math.Pow(24, -1) * 1000); // 24 frames per second.
                    timer.Tick += new EventHandler(timer_Tick);
                    stream = client.GetStream();
                    timer.Enabled = true;

                }
                else if (port > 0 && port > 65535)
                {
                    throw new RemoteDesktopException(new ArgumentOutOfRangeException("port"));
                }
            }
            else
            {
                throw new RemoteDesktopException("Error: Invalid IP address in string format. Make sure it is in the right format.");
            }
        }

        void timer_Tick(object sender, EventArgs e)
        {
            bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);

            g = Graphics.FromImage(bitmap);
            g.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
            Cursors.Default.Draw(g, new Rectangle(Cursor.Position, Cursors.Default.Size));
            byte[] buffer = imageToByteArray(bitmap);
            stream.Write(buffer, 0, buffer.Length);

        }
        public void EndConnection()
        {
            g.Dispose();
            stream.Close();
            stream.Dispose();
            bitmap.Dispose();
            timer.Enabled = false;
            client.Close();
            ConnectionClosed(this, new EventArgs());
            return;
        }
        public Socket Socket
        {
            get
            {
                return client.Client;
            }
        }
        byte[] imageToByteArray(System.Drawing.Image imageIn)
        {
            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
            return ms.ToArray();
        }
    }
    /// <summary>
    /// Represents all remote desktop runtime errors. Inherits from System.Exception.
    /// </summary>
    public class RemoteDesktopException : Exception
    {
        private string message;
        private Exception ex;
        /// <summary>
        /// Constructor (+1 Overloads)
        /// </summary>
        /// <param name="error">The error message in string format.</param>
        public RemoteDesktopException(string error)
        {
            message = error;
        }
        /// <summary>
        /// Constructor (+1 Overloads)
        /// </summary>
        /// <param name="error">The error in System.Exception format.</param>
        public RemoteDesktopException(Exception error)
        {
            ex = error;
        }
        public override string StackTrace
        {
            get
            {
                return base.StackTrace;
            }
        }
        public override string HelpLink
        {
            get
            {
                return base.HelpLink;
            }
            set
            {
                base.HelpLink = value;
            }
        }
        public override string Message
        {
            get
            {
                if (message != null)
                {
                    return message;
                }
                else
                {
                    return ex.Message;
                }
            }
        }
    }
    public class DataEventArgs : EventArgs
    {
        private Image image;
        public DataEventArgs(Image img)
        {
            image = img;
        }
        public System.Drawing.Image CurrentFrame
        {
            get
            {

                return image;
            }
        }

    }
}

I have added flush here

 void timer_Tick(object sender, EventArgs e)
        {
            bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);

            g = Graphics.FromImage(bitmap);
            g.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
            Cursors.Default.Draw(g, new Rectangle(Cursor.Position, Cursors.Default.Size));
            byte[] buffer = imageToByteArray(bitmap);
            stream.Write(buffer, 0, buffer.Length);
            stream.Flush();


        }

Was that the place you were referring me to place the stream.Flush() subroutine?
*Because it still doesn't work. I also tried setting the NoDelay property to true.*

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

很酷不放纵 2024-11-11 06:44:40

Image.FromStream 的速度不太可能是您的问题。您的代码尝试每秒发送 24 帧。假设您的屏幕设置为 1024 x 768,即 786,432 像素。对于 24 位颜色,大约为 2.25 MB。每秒 24 帧意味着每秒传输 54 兆字节。

您的 imageToByteArray 方法创建一个 GIF,它将压缩该部分。你返回的字节数组有多大?也就是说,在您的 timer_tick 方法中,您有:

byte[] buffer = imageToByteArray(bitmap);
stream.Write(buffer, 0, buffer.Length);

buffer.Length 的值是多少?

由于 TCP 开销,千兆位网络每秒的传输速度将低于 100 兆字节。我怀疑您在这里遇到了网络限制。

通常,传输视频的应用程序(这实际上就是您正在做的事情)会进行差分压缩。他们找出哪些像素发生了变化并只发送这些像素。这大大减少了网络带宽。如果您确实想要在任何尺寸的屏幕上实现 24 FPS,您可能必须执行类似的操作。

It's unlikely that the speed of Image.FromStream is your problem. Your code is trying to send 24 frames per second. Assuming that your screen is set to 1024 x 768, that's 786,432 pixels. With 24 bit color, that's about 2.25 megabytes. 24 frames per second would be 54 megabytes per second going down the pipe.

Your imageToByteArray method creates a GIF, which will compress that some. How large is the byte array you get back? That is, in your timer_tick method, you have:

byte[] buffer = imageToByteArray(bitmap);
stream.Write(buffer, 0, buffer.Length);

What is the value of buffer.Length?

A gigabit network will give you something less that 100 megabytes per second due to TCP overhead. I suspect you're bumping up against network limitations here.

Typically, applications that transmit video (which is in effect what you're doing) do differential compression. They figure out which pixels have changed and only send those pixels. That greatly reduces network bandwidth. You'll probably have to do something like that if you really want 24 FPS with a screen of any size.

玩心态 2024-11-11 06:44:40

你不是在服务器端刷新流吗?尝试在服务器代码中调用 Stream.Flush() 。这听起来像是客户端正在等待更多数据来完成图像,但它仍然位于服务器流的缓冲区中。

Are you not flushing the stream on the server side? Try calling Stream.Flush() in your server code. This sounds like the client is waiting for more data to finish the image, but it is still sitting in the server stream's buffer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文