2015-12-22 48 views
15

最近我一直在做一個簡單的屏幕共享程序。C#屏幕流媒體程序

其實程序工作在TCP protocol並使用桌面複製API - 一個很酷的服務支持非常快速的屏幕捕捉,並且還提供了有關MovedRegions(即只改變了屏幕上的位置,但仍然領域存在)和信息UpdatedRegions(更改區域)。

桌面複製有2個重要屬性 - 2字節數組數組爲previouspixelsNewPixels數組。每4個字節代表了RGBA形式的像素因此,例如,如果我的屏幕爲1920×1080的緩衝區大小爲1920×1080×4

下面是我的戰略的重要亮點

  1. 在初始狀態下(第一次),我發送整個像素緩衝區(在我的情況下它是1920 x 1080 * 3) - alpha分量始終爲255在屏幕上:)
  2. 從現在起,我遍歷UpdatedRegions (這是一個矩形陣列),我發送區域邊界和像素這樣的像素:

    writer.Position = 0; 
        var n = frame._newPixels; 
        var w = 1920 * 4; //frame boundaries. 
        var p = frame._previousPixels; 
        foreach (var region in frame.UpdatedRegions) 
         { 
          writer.WriteInt(region.Top); 
          writer.WriteInt(region.Height); 
          writer.WriteInt(region.Left); 
          writer.WriteInt(region.Width); 
          for (int y = region.Top, yOffset = y * w; y < region.Bottom; y++, yOffset += w) 
          { 
           for (int x = region.Left, xOffset = x * 4, i = yOffset + xOffset; x < region.Right; x++, i += 4) 
           { 
            writer.WriteByte(n[i]^p[i]); //'n' is the newpixels buffer and 'p' is the previous.xoring for differences. 
            writer.WriteByte(n[i+1]^p[i+1]); 
            writer.WriteByte(n[i + 2]^p[i + 2]); 
    
           } 
          } 
         } 
    
  3. 我用c#編寫的lz4包裝器(參考[email protected])壓縮緩衝區。然後,我將數據寫入NetworkStream。
  4. 我合併在接收側的區域,以獲得更新的圖像 - 今天這個:)

「作家」是我寫的「QuickBinaryWriter」類實例(簡單地重複使用相同的不是我們的問題緩衝區)。

public class QuickBinaryWriter 
{ 
    private readonly byte[] _buffer; 
    private int _position; 

    public QuickBinaryWriter(byte[] buffer) 
    { 
     _buffer = buffer; 
    } 

    public int Position 
    { 
     get { return _position; } 
     set { _position = value; } 
    } 

    public void WriteByte(byte value) 
    { 
     _buffer[_position++] = value; 
    } 


    public void WriteInt(int value) 
    { 

     byte[] arr = BitConverter.GetBytes(value); 
     for (int i = 0; i < arr.Length; i++) 
      WriteByte(arr[i]); 
    } 

} 

從很多措施,我已經看到了發送的數據確實是巨大的,有時單個幀更新的數據可以得到高達200KB(壓縮後!)。 讓我們誠實 - 200kb是真的沒什麼,但如果我想流暢地流媒體屏幕,並能夠以高FPS速率觀看,我將不得不在這一點上工作 - 以最大限度地減少網絡流量和帶寬使用

我在尋找建議和創意,以提高程序的效率 - 主要是網絡部分發送的數據(通過用其他方式或任何其他想法包裝)我會很感激任何幫助和想法。謝謝。

+0

你問題有點含糊。您應該指定您想要優化的部分。現在這個問題有太多可能的答案,這可能會導致反對票被擱置,因爲過於寬泛而被擱置。我將舉一個廣泛的例子。你想優化代碼,它如何發送數據,壓縮,或者它如何更新屏幕? – dakre18

+0

@ dakre18感謝您的關注,我主要尋找數據壓縮 - 我需要關注最小化網絡流量 - 可能以其他方式打包圖形數據...我不知道這就是我寫我的問題:) – Slashy

+0

您以前問過這個問題。 – harold

回答

16

爲了您的1920×1080的屏幕,具有4個字節的顏色,你是在尋找大約每幀8 MB。 20 FPS,你有160 MB /秒。所以從8 MB到200 KB(4 MB/s @ 20 FPS)是一個很大的改進。

我想把你的注意力集中到我不確定你關注的某些方面,希望它有幫助。

  1. 你壓縮你的屏幕圖像越多,處理它可能需要
  2. 實際上,你需要把重點放在設計的一系列不斷變化的圖像,類似視頻編解碼器的壓縮機制(SANS音頻雖然)。例如:H.264
  3. 請記住,您需要使用某種實時協議來傳輸數據。這背後的想法是,如果你的某個幀以滯後的方式到達目標機器,那麼你可能會放下接下來的幾幀來追趕它。否則,你將處於長期處於滯後的狀況,我懷疑用戶會喜歡。
  4. 你總是可以爲了表現而犧牲品質。您在類似技術(如MS遠程桌面,VNC等)中看到的最簡單的機制是發送8位顏色(ARGB,每個2位)而不是您使用的3字節顏色。
  5. 改善您的情況的另一種方法是專注於您想要流式傳輸的屏幕上的特定矩形,而不是流式傳輸整個桌面。這將減少框架本身的大小。
  6. 另一種方法是在傳輸之前將屏幕圖像縮放爲較小的圖像,然後在顯示之前將其縮放回正常。
  7. 發送初始屏幕後,您始終可以發送newpixelspreviouspixels之間的差異。不用說原始屏幕和差異屏幕都將被LZ4壓縮/解壓縮。如果您使用一些有損耗的算法來壓縮差異,那麼您經常應該發送完整的數組而不是差異。
  8. 更新區域是否有重疊區域?可以優化不發送重複的像素信息嗎?

上述想法可以應用在另一個之上以獲得更好的用戶體驗。最終,這取決於您的應用程序和最終用戶的具體情況。

編輯:

+0

感謝您的幫助。幾點作爲評論。我雖然對不同的地區實行簡單的壓縮。 (在對他們的緩衝區進行着色之後) - 你認爲它可以獲得好的結果嗎? 2.關於質量部分 - 你真的意味着每個頻道2比特嗎? ?這意味着最多2^2選項。 。像素將從其源頭受到嚴重損壞。 – Slashy

+0

3.我現在針對完整桌面流媒體。 ;)4.縮放圖像也可以大規模破壞質量 - 也許轉換區域爲Jpeg將是一個好主意.8。沒有重疊區域;)桌面api工作得很好 - 這是我唯一滿意的東西。 – Slashy

+1

你將不得不看看rle在你的情況下是否比lz4更好,但我對此表示懷疑。如果你使用每種顏色2比特,它看起來不如每種顏色8比特好,但是你將會把你的帶寬需求減少4倍。你可以說每種顏色使用4比特,看看是否可以接受。你將不得不打電話。正如我在文章中提到的,爲了減少帶寬,你應該看看帶有損視頻編碼選項的RTP – Vikhram

1

Slashy,

由於您使用的是高分辨率幀,你想你有可能會良好的幀速率看着H.264編碼。我在高清/標清廣播視頻方面做了一些工作,完全依賴於H.264,稍微轉向H.265。廣播中使用的大多數庫都是用C++編寫的,以提高速度。

我建議在尋找這樣的事情https://msdn.microsoft.com/en-us/library/windows/desktop/dd797816(v=vs.85).aspx

+0

我很久以前就聽說過這種編碼器,但實際上我可以在c#項目中實現它嗎?感謝您的關注:) – Slashy

+1

我在C#中使用HiDef視頻流遇到的問題是,當您談論大量通過銅纜流式傳輸的實時數據時,該框架只會變慢。我最近使用提供的C#庫在一個非常昂貴的HD/SDI編碼卡上測試了一些幀速率以輸出HD/SDI數據。我的最大幀率大約是30fps,沒有任何圖形或疊加層在我的代碼中生成。由於這個原因,大多數硬件供應商不提供C#庫,這意味着你不能爲C++庫編寫CLI包裝器。或者將編碼過程分成一個C++ App –

+0

@ AaronThomas我明白了..但是,你是什麼意思「分離編碼過程」?我不認爲這裏存在真正的性能問題,因爲處理部分完成得相當快...... – Slashy