2009-10-20 155 views
7

我想創建一個MJPEG流,我有一系列jpegs,我想放在一起成流,以便用戶可以打一個URL並獲得一個mjpeg流。 過去幾天我一直在努力讓這個工作,它可能是不可能的。我已經提出了空靈,並聽取了來自某個網​​絡上的軸相機的數據包,並試圖模仿它。我最初嘗試使用WCF,並返回一個「流」,但後來發現,我需要在該流上設置內容類型,所以我然後嘗試了WCF REST API,但也遭受同樣的問題。所以我現在只是使用一個簡單的HTTPListener,並處理事件。我非常喜歡使用WCF,但我不確定它會允許我返回具有正確內容類型的流。 所以這裏是我的httpListener。創建我自己的MJPEG流

在監聽器的處理程序中回調我把以下內容。

 HttpListenerResponse response = context.Response; 
     response.ProtocolVersion = new System.Version(1, 0); 
     response.StatusCode = 200; 
     response.StatusDescription = "OK"; 
     response.ContentType = "multipart/x-mixed-replace;boundary=" + BOUNDARY + "\r\n"; 
     System.IO.Stream output = response.OutputStream; 
     Render(output); 

Render方法看起來像這樣

 var writer = new StreamWriter(st); 
     writer.Write("--" + BOUNDARY + "\r\n"); 
     while (true) 
     { 
      for (int i = 0; i < imageset.Length; i++) 
      { 
       var resource = Properties.Resources.ResourceManager.GetObject(imageset[i]) as Bitmap; 
       var memStream = new MemoryStream(); 
       resource.Save(memStream,ImageFormat.Jpeg); 
       byte[] imgBinaryData = memStream.ToArray(); 
       string s = Convert.ToBase64String(imgBinaryData); 
       writer.Write("Content-type: image/jpeg\r\n"); 
       foreach (var s1 in imgBinaryData) 
       { 
        writer.Write((char)s1); 
       } 
       writer.Write("\n--" + BOUNDARY + "\n"); 
       writer.Flush(); 
       Thread.Sleep(500); 
      } 
     } 

在這一點上,我只是添加了一些JPEG圖像上的dll屬性,我遍歷他們,最終這將是動態圖像,但現在我只想讓這件事情起作用。

根據我對MJPEG(spec)的理解,內容必須設置爲multipart/x-mixed-replace和邊界集。然後您只需通過邊界消除流內的jpeg。

這似乎應該是更簡單,然後我正在做它,但我想知道我要去哪裏錯了。如果我在IE或Firefox中加載這個URL,它就會掛起。如果我嘗試使用img標記創建一個存根html頁面,其源代碼是URL,那麼我會得到一個破碎的圖像。

任何想法,感謝

喬希

回答

7

嘛,據我所知,這是你的問題:

  1. StreamWriter是不是一個正確的選擇。使用普通的流寫功能很好。意思是,你應該在Byte數組中寫入數據而不是字符串。

  2. 您將圖像的二進制數據轉換爲String64,瀏覽器不知道,仍然認爲它是32位數據。

  3. 您的jpeg幀格式不正確。您還應該將Content-Length添加到幀頭,以便接收該流的應用程序知道何時停止讀取,而不必在每次讀取時檢查下一個邊界字符串。這將使數據讀取速度提高約4-5倍。而且你的新行字符也有不一致,有些是「\ r \ n」,而另一些則是「\ n」。

  4. while循環是一個無限循環。

所以,這裏是解決方案。

注意:可能有一些語法錯誤,但您可能會得到一般想法。

private byte[] CreateHeader(int length) 
{ 
    string header = 
     "--" + BOUDARY + "\r\n" + 
     "Content-Type:image/jpeg\r\n" + 
     "Content-Length:" + length + "\r\n" + 
     + "\r\n"; // there are always 2 new line character before the actual data 

    // using ascii encoder is fine since there is no international character used in this string. 
    return ASCIIEncoding.ASCII.GetBytes(header); 
} 

public byte[] CreateFooter() 
{ 
    return ASCIIEncoding.ASCII.GetBytes("\r\n"); 
} 

private void WriteFrame(Stream st, Bitmap image) 
{ 
    // prepare image data 
    byte[] imageData = null; 

    // this is to make sure memory stream is disposed after using 
    using (MemoryStream ms = new MemoryStream()) 
    { 
     image.Save(ms, ImageFormat.Jpeg); 

     imageData = ms.ToArray(); 
    } 

    // prepare header 
    byte[] header = CreateHeader(imageData.Length); 
    // prepare footer 
    byte[] footer = CreateFooter(); 

    // Start writing data 
    st.Write(header, 0, header.Length); 
    st.Write(imageData, 0, imageData.Length); 
    st.Write(footer, 0, footer.Length); 
} 

private void Render(Stream st) 
{ 
    for (int i = 0; i < imageset.Length; i++) 
    { 
     var resource = Properties.Resources.ResourceManager.GetObject(imageset[i]) as Bitmap; 
     WriteFrame(st, resource); 
     Thread.Sleep(500); 
    } 
} 
+3

我意識到這已經過去了一年多了......但依然如此。規範說,應該在冒號名稱之後的冒號和標題值之間有一個空格('',ASCII 32)。 – 2010-09-19 00:51:14