2009-10-20 12 views
7

Próbuję utworzyć strumień MJPEG, mam serię plików jpeg, które chcę umieścić w strumieniu, aby użytkownik mógł po prostu trafić adres URL i uzyskać strumień mjpeg. Próbowałem przez ostatnie kilka dni, aby to zadziałało, i może nie będzie to możliwe. Podniosłem eteryczność i słuchałem pakietów pochodzących z kamery osiowej gdzieś w sieci i próbowałem ją zminimalizować. Początkowo próbowałem używać WCF i zwracając "strumień", ale później dowiedziałem się, że będę musiał ustawić typ zawartości w tym strumieniu, więc wypróbowałem aplet WCF REST, ale cierpi na ten sam problem. więc teraz używam tylko HTTPListener o gołych kościach i obsługuję to wydarzenie. Zdecydowanie wolałbym używać WCF, ale nie jestem pewien, czy pozwoli mi zwrócić strumień z właściwym typem treści. Oto, co mam dla httpListener.Tworzenie własnego strumienia MJPEG

w programie obsługi wywołania zwrotnego odbiornika Dodałem następujące.

 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 metoda wygląda następująco

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

W tym momencie Właśnie dodałem kilka obrazów JPEG jako właściwości na dll, a ja iteracji nad nimi, w końcu będą one dynamiczne obrazy , ale na razie chcę tylko, żeby to zadziałało.

Z tego, co rozumiem na temat MJPEG (specyfikacja), wynika, że ​​treść musi być ustawiona na multipart/x-mixed-replace i zestaw granic. a następnie oddzielasz jpeg w strumieniu od granicy.

Wygląda na to, że powinienem być prostszy, niż robię, ale zastanawiam się, gdzie idę źle. jeśli załaduję ten adres URL w przeglądarce IE lub Firefox, po prostu się zawiesi. jeśli próbuję utworzyć stronę html z tagiem img, której źródłem jest adres URL, otrzymam zepsuty obraz.

Wszelkie pomysły, dzięki

Josh

Odpowiedz

7

Cóż, o ile mogę powiedzieć, tu są twoje problemy:

  1. StreamWriter nie jest właściwym wyborem. Używanie zwykłej funkcji zapisu strumienia jest w porządku. Oznacza to, że powinieneś zapisywać dane w tablicy Bajtów zamiast ciągu.

  2. Konwertujesz dane binarne obrazu na String64, przeglądarka nie wie o tym, wciąż myśląc, że to 32-bitowe dane.

  3. Twój format ramki jpeg jest nieprawidłowy. Powinieneś również dodać Content-Length do nagłówka ramki, aby aplikacja, która odbiera strumień, wiedziała, kiedy przerwać czytanie, zamiast sprawdzać następny ciąg graniczny przy każdym odczycie. Spowoduje to około 4-5-krotnie szybsze czytanie danych. Są też niespójności w nowej linii, niektóre są "\ r \ n", podczas gdy inne są "\ n".

  4. Podczas gdy pętla jest nieskończoną pętlą.

Oto rozwiązanie.

Uwaga: mogą występować błędy składniowe, ale prawdopodobnie masz ogólny pomysł.

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

Zdaję sobie sprawę, że ma ponad rok ... ale wciąż. Specyfikacja mówi, że powinna istnieć jedna spacja (", ASCII 32) między dwukropkiem po nazwie nagłówka" a wartością nagłówka. –