2012-03-09 108 views
0

我正在將Xbox Kinect中的視頻源從客戶端程序發送到服務器。我有一切工作,但問題是幀率。我在想,它發送的信息比它能讀取的要快。所以當它不能發送時,它會存儲它將發送的內容,並等待緩衝區中有空間。我認爲這是發生的原因是因爲我可以看到該程序的內存使用情況穩步增長。另外,因爲當我觀看視頻播放時,我會在10秒前看到發生的所有事情,並且播放速度較慢,但​​不會跳過任何幀。所以我所做的是將幀速率降低到5 fps,當我這樣做的時候它是穩定的。但這不是最好的辦法。我想要做的是,當緩衝區已滿時,跳過該幀並等待緩衝區上有空間發送幀。這聽起來像是可能是問題,如果是的話,我應該如何解決它?謝謝。C#通過NetworkStream/TCPClient流式傳輸視頻

以下是發送和接收數據的代碼。

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),看看有多少數據在緩衝區,但它似乎永遠不會滿。

+0

你能解釋一次發送一幀的含義嗎?你介意發佈代碼嗎? – 2012-09-27 11:29:02

回答

0

在這項任務中,TCP可能效率低下。您應該使用UDP(數據報套接字)進行無連接且不可靠的傳輸。由於TCP需要連接並提供安全性,因此它比UDP慢,因此在視頻流時不應優先使用。

+0

我認爲你是對的,我只是不想學習如何使用UDP,但我可能不得不。 – Rickyman35 2012-03-09 01:17:20

+1

我最終得到它與TCP一起工作。解決方案是降低幀分辨率,並確保我一次只發送一幀。因爲我正在發送一個新線程,它允許我一次發送多個幀,這導致了我的問題。 – Rickyman35 2012-03-12 17:02:10

+0

實際上,使用TCP比UDP流式傳輸視頻更爲常見。例如,Adobe Flash Player(由YouTube等使用)使用TCP來流式傳輸視頻。通過最小化發送的數據量來獲得效率。它不發送每個消息的整個幀,而是發送一個代表完整幀的「關鍵幀」,然後發送消息來更新與該關鍵幀相關的視覺效果。每隔一段時間發送一個新的關鍵幀,並且以下消息相對於最後一個關鍵幀進行更新。 – blachniet 2012-09-27 11:47:04