【问题标题】:Need a faster implemenation of Image.FromStream to display images in real-time需要更快的 Image.FromStream 实现来实时显示图像
【发布时间】:2011-08-16 03:17:11
【问题描述】:

我正在创建一个远程桌面程序,因此我开发了一个类,它可以拍摄客户端计算机的快照并将每个图像以字节数组格式发送到服务器。然后服务器读取这些字节并将流转换为 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。*

【问题讨论】:

    标签: c# c#-4.0 remote-desktop


    【解决方案1】:

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

    您的 imageToByteArray 方法创建了一个 GIF,它会压缩一些。你得到的字节数组有多大?也就是说,在您的 timer_tick 方法中,您有:

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

    buffer.Length 的值是多少?

    由于 TCP 开销,千兆网络将为您提供每秒不到 100 兆字节的数据。我怀疑您在这里遇到了网络限制。

    通常,传输视频的应用程序(实际上就是您正在执行的操作)执行差分压缩。他们找出哪些像素发生了变化,然后只发送这些像素。这大大降低了网络带宽。如果您真的想要 24 FPS 和任何尺寸的屏幕,您可能必须这样做。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 从不刷新流。我会试试的。
      • 感谢您的回答,但我对在哪里调用 Flush 方法感到困惑。我只是不知道该放在哪里。
      • 它必须在服务器代码中,看起来不像您发布的那样。将图像写入流之后的某个位置。
      • 但是客户端在写,服务端在读;顺便说一句。
      猜你喜欢
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 2020-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-12
      相关资源
      最近更新 更多