2009-10-20 36 views
7

J'essaie de créer un flux MJPEG, j'ai une série de jpeg que je veux mettre dans un flux de sorte qu'un utilisateur peut simplement frapper une URL et obtenir un flux mjpeg. J'ai essayé ces derniers jours pour que cela fonctionne, et ce n'est peut-être pas possible. J'ai soulevé éthéré et écouté les paquets provenant d'une caméra d'axe sur le net quelque part, et j'ai essayé de le mimmick. J'ai essayé à l'origine d'utiliser WCF et de retourner un "flux", mais j'ai ensuite découvert que je devais définir le type de contenu sur ce flux, donc j'ai essayé l'API WCF REST, mais cela souffre du même problème. donc je suis maintenant en train d'utiliser un simple HTTPListener et de gérer l'événement. Je préférerais grandement utiliser WCF, mais je ne suis pas sûr que cela me permettra de retourner un flux avec le bon type de contenu. alors voici ce que j'ai pour le httpListener.créer mon propre flux MJPEG

dans le gestionnaire du rappel de l'auditeur Je mets ce qui suit.

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

la méthode de rendu ressemble à ceci

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

À ce stade, je viens d'ajouter quelques images jpeg en tant que propriétés sur le dll, et je suis itérer sur eux, finalement ce seront des images dynamiques , mais pour l'instant je veux juste que ça marche. D'après ce que je comprends sur le MJPEG (spec), le contenu doit être défini sur multipart/x-mixed-replace et un ensemble de limites. et puis vous venez de supprimer les jpeg dans le flux par la frontière.

Cela semble être plus simple que je le fais, mais je me demande où je me trompe. Si je charge cette URL dans IE ou Firefox, ça bloque. Si j'essaie de créer une page stub html avec une balise img, dont la source est l'URL, j'obtiens une image cassée.

Toutes les idées, grâce

Josh

Répondre

7

Eh bien, pour autant que je peux dire, voici vos questions:

  1. Le StreamWriter n'est pas un bon choix. Utilisez une fonction d'écriture de flux régulier est très bien. Cela signifie que vous devez écrire des données dans le tableau Byte au lieu de la chaîne.

  2. Vous convertir les données binaires de l'image à String64, le navigateur n'a pas connu que, pensant encore ce sont des données de 32 bits.

  3. Votre format de trame jpeg est incorrect. Vous devez également ajouter Content-Length à l'en-tête de cadre afin que l'application qui reçoivent le flux de savoir quand arrêter la lecture plutôt que d'avoir à vérifier la chaîne suivante limite à chaque lecture. Cela se traduira par environ 4-5 fois plus rapide dans la lecture des données. Et il y a aussi des incohérences dans le caractère de votre nouvelle ligne, certains sont "\ r \ n" tandis que d'autres sont "\ n".

  4. Alors que la boucle est une boucle infinie.

Donc, voici la solution.

Note: Il pourrait y avoir des erreurs de syntaxe, mais vous obtenez probablement l'idée générale.

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

Je me rends compte que c'est plus d'un an ... mais quand même. Les spécifications indiquent qu'il devrait y avoir un seul espace ('', ASCII 32) entre les deux points après le nom de l'en-tête '' et la valeur de l'en-tête. –