2009-10-20 13 views
7

Sto provando a creare un flusso MJPEG, ho una serie di jpeg che voglio mettere insieme in uno stream in modo che un utente possa semplicemente premere un URL e ottenere un flusso mjpeg. Ho cercato per gli ultimi giorni di farlo funzionare, e potrebbe non essere possibile. Ho sollevato l'etereo e ho ascoltato i pacchetti provenienti da una telecamera dell'asse sulla rete da qualche parte, e ho cercato di schivarlo. Inizialmente ho provato a usare WCF e restituire uno "stream", ma in seguito ho scoperto che avrei dovuto impostare il tipo di contenuto su quel flusso, quindi ho provato l'API di WCF REST, ma questo ha lo stesso problema. quindi ora sto solo usando un HTTPListener nudo e gestendo l'evento. Preferisco di gran lunga utilizzare WCF, ma non sono sicuro che mi consentirà di restituire un flusso con il giusto tipo di contenuto. ecco cosa ho per httpListener.creazione del mio stream MJPEG

nel gestore della chiamata listener ho messo quanto segue.

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

il metodo Render assomiglia a questo

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

A questo punto ho aggiunto un paio di immagini JPEG come proprietà sul dll, e sto iterazione su di loro, alla fine questi saranno immagini dinamiche , ma per ora voglio solo far funzionare la cosa.

Da quello che ho capito sul MJPEG (spec) è che il contenuto deve essere impostato su multipart/x-mixed-replace e un set di limiti. e quindi basta eliminare i jpeg all'interno del flusso dal confine.

Sembra che dovrebbe essere più semplice di quello che sto facendo, ma mi chiedo dove sto andando male. se carico questo URL su IE o Firefox, si blocca. se provo a creare una pagina stub html con un tag img, la cui origine è l'URL, ricevo un'immagine spezzata.

tutte le idee, grazie

Josh

risposta

7

Beh, per quanto posso dire, qui ci sono i tuoi problemi:

  1. Il StreamWriter non è una scelta corretta. Utilizzare una normale funzione di scrittura stream va bene. Vale a dire, dovresti scrivere i dati nella matrice Byte anziché nella stringa.

  2. Si converte i dati binari dell'immagine in String64, il browser non lo sa, continuando a pensare che si tratti di dati a 32 bit.

  3. Il formato della cornice jpeg non è corretto. È inoltre necessario aggiungere Content-Length all'intestazione del frame in modo che l'applicazione che riceve il flusso sappia quando interrompere la lettura piuttosto che dover controllare la stringa di limite successiva ogni lettura. Ciò si tradurrà in circa 4-5 volte più veloce nella lettura dei dati. E ci sono anche incoerenze nel tuo carattere di nuova riga, alcuni sono "\ r \ n" mentre altri sono "\ n".

  4. Mentre il ciclo è un ciclo infinito.

Quindi, ecco la soluzione.

Nota: potrebbero esserci alcuni errori di sintassi ma probabilmente si ottiene l'idea generale.

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

Mi rendo conto che questo ha più di un anno ... ma ancora. Le specifiche dicono che dovrebbe esserci uno spazio singolo ('', ASCII 32) tra i due punti dopo il nome dell'intestazione 'e il valore dell'intestazione. –