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

时间:2012-03-09 00:30:11

标签: c# video buffer tcpclient networkstream

我正在将Xbox Kinect的视频源从客户端程序发送到服务器。我有一切工作,但问题是帧速率。我在想发生的事情是发送速度超过它能读取的速度。因此,当它不能再发送时,它会存储它要发送的内容并等待缓冲区中有空间。我认为这是正在发生的事情的原因是因为我可以看到程序的内存使用量稳步增长。另外,因为当我观看视频输入时,我会看到大约10秒前发生的所有事情以及较慢的播放,但它并没有跳过任何帧。所以我所做的是将帧速率降低到5 fps,当我这样做时它是稳定的。但这不是最好的方法。我想要做的是当缓冲区已满时跳过该帧并等待缓冲区中有空间发送帧。听起来这可能是问题,如果是这样,我该如何解决?感谢。

以下是发送和接收数据的代码。

    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();
        }
    }

我研究了使用Socket.Available属性(http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.available.aspx)查看缓冲区中有多少数据但它似乎永远不会充满。

1 个答案:

答案 0 :(得分:0)

TCP在此任务中可能效率低下。您应该使用UDP(数据报套接字)进行无连接且不可靠的传输。由于TCP需要连接并提供安全性,因此它比UDP慢,因此在视频流中不应该提供它。