简体   繁体   English

C# 通过 NetworkStream / TCPClient 流式传输视频

[英]C# Streaming video over NetworkStream / TCPClient

I am sending a video feed from an Xbox Kinect from a client program to a server.我正在将来自 Xbox Kinect 的视频源从客户端程序发送到服务器。 I have everything working but the problem is the frame rate.我一切正常,但问题是帧速率。 I'm thinking what is happening is it is sending faster than it can read.我在想正在发生的事情是它的发送速度比它可以读取的速度快。 So when it can't send anymore it stores what it is about to send and waits until there is room in the buffer.因此,当它不能再发送时,它会存储将要发送的内容并等待缓冲区中有空间。 The reason I think this is what is happening is because I can see the memory usage of the program steadily growing.我认为这是正在发生的原因是因为我可以看到程序的内存使用量稳步增长。 Also because when I watch the video feed, I see everything that happened about 10 seconds ago and on a slower play back but it's not skipping any frames.还因为当我观看视频源时,我看到了大约 10 秒前发生的所有事情,并且播放速度较慢,但​​没有跳过任何帧。 So what I have done is decrease the frame rate to 5 fps, when I do that it is steady.所以我所做的是将帧速率降低到 5 fps,当我这样做时它是稳定的。 But this is not the best way to do it.但这并不是最好的方法。 What I want to do is when the buffer is full just skip that frame and wait until there is room on the buffer to send a frame.我想要做的是当缓冲区已满时只需跳过该帧并等待缓冲区上有空间发送帧。 Does this sound like it could be the problem, and if so how should I fix it?这听起来是否可能是问题所在,如果是这样,我应该如何解决? Thanks.谢谢。

Here is the code from sending and receiving data.这是发送和接收数据的代码。

    private const int constChunkSize = 4096;
    private const int constIntSize = 4;

    protected TcpClient tcpObject;
    protected NetworkStream tcpStream;

    private void HandleComm()
    {
        try
        {
            tcpStream = tcpObject.GetStream();
            byte[] totalByteAray = new byte[constIntSize];
            byte[] message = new byte[constChunkSize];
            byte[] fullMessage = new byte[0];

            //this is how many bytes long the message will be
            int totalBytes = 0;
            int currentBytes = 0;
            int chunkSize = constChunkSize;
            int bytesRead = 0;

            pingThread = new Thread(sendPing);
            pingThread.Start();

            while (true)
            {                    
                //skip reading if no data is available
                //DataAvailable does not tell you when all the data has arrived
                //it just tell you if some data has arrived
                if (tcpStream.CanRead)
                {
                    totalBytes = 0;
                    currentBytes = 0;
                    message = new byte[constChunkSize];
                    chunkSize = constChunkSize;
                    bytesRead = 0;

                    //The first 4 bytes of the message will always contain the length of the message, not including
                    //the first 4 bytes. This is how you know when to stop reading.                                                
                    bytesRead = tcpStream.Read(totalByteAray, 0, constIntSize);
                    if (bytesRead == 0)                        
                        Disconnect();                        
                    //there are 4 bytes in a 32 bit number, so totalByteArrayContains 4 index that is a byte which is
                    //the 32 bit int that tells us how many bytes the whole message will be.
                    //now convert the totalByteArray to a 32bit int
                    totalBytes = BitConverter.ToInt32(totalByteAray, 0);
                    //fullMessage will contain the entire message but it has to be built message by message.                    
                    fullMessage = new byte[totalBytes];
                    //keep reading until we get all the data
                    while (currentBytes < totalBytes)
                    {
                        //when you send something over TCP it will some times get split up
                        //this is why you only read in chuncks, 4096 is a safe amount of bytes
                        //to split the data into.
                        if (totalBytes - currentBytes < constChunkSize)
                        {
                            chunkSize = totalBytes - currentBytes;
                            message = new byte[chunkSize];
                        }

                        bytesRead = tcpStream.Read(message, 0, chunkSize);
                        if (bytesRead == 0)                            
                            Disconnect();                            
                        //since we know each chunk will always come in at 4096 bytes if it doesn't that means that it's the end
                        //this part cuts off the extra empty bytes                           

                        //copy the message to fullMessage starting at current bytes and ending with the bytes left
                        message.CopyTo(fullMessage, currentBytes);
                        currentBytes += bytesRead;                            
                    }

                    //message has successfully been received
                    if (totalBytes != 0)
                    {
                        //if the message was a ping handle it here to reduce the size of the packet
                        if (fullMessage.Length == 1 && (fullMessage[0] == 0 || fullMessage[0] == 255))
                        {
                            //if the message matches your ping byte, then it's yours
                            if (fullMessage[0] == pingByte[0])
                            {
                                lastReceivedPing = DateTime.Now;
                                latency = (lastReceivedPing - lastSentPing).TotalMilliseconds;

                                if (OnPingReceived != null)
                                {
                                    PingReceivedArgs args = new PingReceivedArgs();
                                    args.receivedTime = lastReceivedPing;
                                    args.latency = latency;
                                    OnPingReceived(this, args);
                                }
                            }
                            //if it doesn't then send it off
                            else
                            {
                                sendData(fullMessage);
                            }
                        }
                        //if it's anything else pass it on
                        else
                        {
                            if (OnRawDataReceived != null)
                            {
                                RawDataReceivedArgs args = new RawDataReceivedArgs();
                                args.Data = new byte[fullMessage.Length];
                                fullMessage.CopyTo(args.Data, 0);
                                OnRawDataReceived(this, args);
                            }
                        }
                        totalBytes = 0;
                    }
                }
            }
        }
        catch
        {
            Disconnect();
        }
    }

    protected void sendData(byte[] data)
    {
        try
        {
            //we need to know how big the data that we are sending will be
            int length = data.Length;
            //convert the 32bit int to a 4 byte array
            byte[] lengthArray = BitConverter.GetBytes(length);

            //init the main byte array that will be sent over
            byte[] buffer = new byte[length + constIntSize];

            //the first 4 bytes will contain the length of the data
            lengthArray.CopyTo(buffer, 0);

            //the rest of the buffer will contain the data being sent
            data.CopyTo(buffer, constIntSize);

            tcpStream.BeginWrite(buffer, 0, buffer.Length, new AsyncCallback(sendingData), tcpStream);
        }
        catch
        {
            Disconnect();
        }
    }

I looked into using the Socket.Available property (http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.available.aspx) to see how much data is on the buffer but it seems to never be full.我研究了使用 Socket.Available 属性(http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.available.aspx)来查看缓冲区上有多少数据,但似乎永远不要吃饱。

TCP might be inefficient in this task. TCP 在此任务中可能效率低下。 You should use the connectionless and not reliable transfer with UDP (datagram sockets).您应该使用无连接且不可靠的 UDP(数据报套接字)传输。 Because of TCP requires connection and provides security, it is slower than UDP and therefore it should not be preffered during a video streaming.由于 TCP 需要连接并提供安全性,它比 UDP 慢,因此在视频流期间不应首选。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM