2009-10-20 2 views
7

Я пытаюсь создать поток MJPEG, у меня есть серия jpegs, которую я хочу собрать в поток, чтобы пользователь мог просто нажать URL-адрес и получить поток mjpeg. Я пытался в течение последних нескольких дней заставить это работать, и это может быть просто невозможно. Я поднял эфир и прислушался к пакетам, идущим от оси камеры в сети где-то, и попытался обмануть его. Сначала я попытался использовать WCF и возвратил «поток», но потом выяснил, что мне нужно будет установить тип содержимого в этом потоке, поэтому я попробовал api для WCF REST, но это страдает от той же проблемы. поэтому теперь я просто использую HTTP-фильтр barebones и обрабатываю событие. Я бы предпочел использовать 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); 

рендера метод выглядит следующим образом

 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 и набор границ. а затем вы просто ограничиваете jpegs внутри потока по границе.

Кажется, что это должно быть проще, чем я это делаю, но мне интересно, где я ошибаюсь. если я загружу этот URL-адрес в IE или Firefox, он просто зависает. если я попытаюсь сделать страницу stub html с тегом img, источником которого является URL-адрес, тогда я получаю сломанное изображение.

Любые идеи, благодаря

Джош

ответ

7

Ну, насколько я могу судить, вот ваши вопросы:

  1. StreamWriter не является правильным выбором. Использовать регулярную функцию записи потока отлично. Значит, вы должны писать данные в массиве байтов вместо строки.

  2. Вы преобразовываете двоичные данные изображения в String64, браузер не знает, что, все еще думая, что это 32-битные данные.

  3. Неверный формат фрейма jpeg. Вы также должны добавить Content-Length в заголовок фрейма, чтобы приложение, получающее поток, узнало, когда прекратить чтение, а не проверять следующую строку границы. Это приведет к примерно 4-5 раз быстрее при чтении данных. И есть также несогласованность в вашем новом символе линии, некоторые из них «\ r \ n», а некоторые другие - «\ n».

  4. В то время как цикл представляет собой бесконечный цикл.

Итак, вот решение.

Примечание: могут возникнуть некоторые ошибки синтаксиса, но вы, вероятно, получите общую идею.

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) между двоеточием после имени заголовка 'и значением заголовка. –