2

J'ai un problème étrange, je travaille sur une caméra Bluetooth que nous voulons fournir une interface mjpeg au monde. Mjpeg est juste un serveur http répondant un jpeg après l'autre avec la connexion maintenue ouverte. Mon serveur droite me donne maintenant:multipart/x-mixed-replace ActionScript3 et Google Chrome (et d'autres aussi)

 
HTTP/1.1 200 OK 
Transfer-Encoding: chunked 
Cache-Directive: no-cache 
Expires: 0 
Pragma-Directive: no-cache 
Server: TwistedWeb/10.0.0 
Connection: Keep-Alive 
Pragma: no-cache 
Cache-Control: no-cache, no-store, must-revalidate; 
Date: Sat, 26 Feb 2011 20:29:56 GMT 
Content-Type: multipart/x-mixed-replace; boundary=myBOUNDARY 

HTTP/1.1 200 OK 
Transfer-Encoding: chunked 
Cache-Directive: no-cache 
Expires: 0 
Pragma-Directive: no-cache 
Server: TwistedWeb/10.0.0 
Connection: Keep-Alive 
Pragma: no-cache 
Cache-Control: no-cache, no-store, must-revalidate; 
Cate: Sat, 26 Feb 2011 20:29:56 GMT 
Content-Type: multipart/x-mixed-replace; boundary=myBOUNDARY 

Et pour chaque image:

 
--myBOUNDARY 
Content-Type: image/jpeg 
Content-Size: 25992 

BINARY JPEG CONTENT..... 
(new line) 

J'ai fait un client Flash pour elle, afin que nous puissions utiliser le même code sur tout appareil, le serveur est mis en œuvre en Python en utilisant twisted et vise Android entre autres, le problème dans Android est Google oublié pour inclure le support mjpeg .... Ce client utilise URLStream.

Le code est le suivant:

 
package net.aircable { 
    import flash.errors.*; 
    import flash.events.*; 
    import flash.net.URLRequest; 
    import flash.net.URLRequestMethod; 
    import flash.net.URLRequestHeader; 
    import flash.net.URLStream; 
    import flash.utils.ByteArray; 
    import flash.utils.Dictionary; 
    import flash.system.Security; 
    import mx.utils.Base64Encoder; 
    import flash.external.ExternalInterface; 
    import net.aircable.XHRMultipartEvent; 

    public class XHRMultipart extends EventDispatcher{ 

    private function trc(what: String): void{ 
     //ExternalInterface.call("console.log", what); //for android 
     trace(what); 
    } 

    private var uri: String; 
    private var username: String; 
    private var password: String; 
    private var stream: URLStream; 
    private var buffer: ByteArray; 
    private var pending: int; 
    private var flag: Boolean; 
    private var type: String; 
    private var browser: String; 

    private function connect(): void { 
     stream = new URLStream(); 
     trc("connect") 
     var request:URLRequest = new URLRequest(uri); 
     request.method = URLRequestMethod.POST; 
     request.contentType = "multipart/x-mixed-replace"; 
     trc(request.contentType) 
/*  request.requestHeaders = new Array(
     new URLRequestHeader("Content-type", "multipart/x-mixed-replace"), 
     new URLRequestHeader("connection", "keep-alive"), 
     new URLRequestHeader("keep-alive", "115")); 
*/ 
     trace(request.requestHeaders); 
     trc("request.requestHeaders") 
     configureListeners(); 
     try { 
     trc("connecting"); 
     stream.load(request); 
     trc("connected") 
     } catch (error:Error){ 
      trc("Unable to load requested resource"); 
     } 
     this.pending = 0; 
     this.flag = false; 
     this.buffer = new ByteArray(); 
    } 

    public function XHRMultipart(uri: String = null, 
             username: String = null, 
             password: String = null){ 
     trc("XHRMultipart()"); 
     var v : String = ExternalInterface.call("function(){return navigator.appVersion+'-'+navigator.appName;}"); 
     trc(v); 
     v=v.toLowerCase(); 
     if (v.indexOf("chrome") > -1){ 
     browser="chrome"; 
     } else if (v.indexOf("safari") > -1){ 
     browser="safari"; 
     } 
     else { 
     browser=null; 
     } 
     trc(browser); 
     if (uri == null) 
     uri = "../stream?ohhworldIhatethecrap.mjpeg"; 
     this.uri = uri; 
     connect(); 
    } 


    private function configureListeners(): void{ 
     stream.addEventListener(Event.COMPLETE, completeHandler, false, 0, true); 
     stream.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler, false, 0, true); 
     stream.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler, false, 0, true); 
     stream.addEventListener(Event.OPEN, openHandler, false, 0, true); 
     stream.addEventListener(ProgressEvent.PROGRESS, progressHandler, false, 0, true); 
     stream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler, false, 0, true); 
    } 

    private function propagatePart(out: ByteArray, type: String): void{ 
     trc("found " + out.length + " mime: " + type); 
     dispatchEvent(new XHRMultipartEvent(XHRMultipartEvent.GOT_DATA, true, false, out)); 
    } 

    private function readLine(): String { 
     var out: String = ""; 
     var temp: String; 

     while (true){ 
      if (stream.bytesAvailable == 0) 
       break; 
      temp = stream.readUTFBytes(1); 
      if (temp == "\n") 
       break; 
      out+=temp; 
     } 
     return out; 
    } 

    private function extractHeader(): void { 
     var line: String; 
     var headers: Object = {}; 
     var head: Array; 

     while ((line=readLine()) != ""){ 
      if (stream.bytesAvailable == 0) 
       return; 
      if (line.indexOf('--') > -1){ 
       continue; 
      } 
      head = line.split(":"); 
      if (head.length==2){ 
       headers[head[0].toLowerCase()]=head[1]; 
      } 
     } 

     pending=int(headers["content-size"]); 
     type = headers["content-type"]; 
     if (pending > 0 && type != null) 
      flag = true; 
     trc("pending: " + pending + " type: " + type); 
    } 

    private function firefoxExtract(): void { 
     trc("firefoxPrepareToExtract"); 
     if (stream.bytesAvailable == 0){ 
      trc("No more bytes, aborting") 
      return; 
     } 

     while (flag == false) { 
      if (stream.bytesAvailable == 0){ 
       trc("No more bytes, aborting - can't extract headers"); 
       return; 
      } 
      extractHeader() 
     } 

     trc("so far have: " + stream.bytesAvailable); 
     trc("we need: " + pending); 
     if (stream.bytesAvailable =0; x-=1){ 
      buffer.position=x; 
      buffer.readBytes(temp, 0, 2); 
      // check if we found end marker 
      if (temp[0]==0xff && temp[1]==0xd9){ 
       end=x; 
       break; 
      } 
     } 

     trc("findImageInBuffer, start: " + start + " end: " + end); 
     if (start >-1 && end > -1){ 
      var output: ByteArray = new ByteArray(); 
      buffer.position=start; 
      buffer.readBytes(output, 0 , end-start); 
      propagatePart(output, type); 
      buffer.position=0; // drop everything 
      buffer.length=0; 
     } 
    } 

    private function safariExtract(): void { 
     trc("safariExtract()"); 
     stream.readBytes(buffer, buffer.length); 
     findImageInBuffer(); 
    } 

    private function chromeExtract(): void { 
     trc("chromeExtract()"); 
     stream.readBytes(buffer, buffer.length); 
     findImageInBuffer(); 
    } 

    private function extractImage(): void { 
     trc("extractImage"); 

     if (browser == null){ 
      firefoxExtract(); 
     } 
     else if (browser == "safari"){ 
      safariExtract(); 
     } 
     else if (browser == "chrome"){ 
      chromeExtract(); 
     } 
    } 

    private function isCompressed():Boolean { 
     return (stream.readUTFBytes(3) == ZLIB_CODE); 
    } 

    private function completeHandler(event:Event):void { 
     trc("completeHandler: " + event); 
     //extractImage(); 
     //connect(); 
    } 

    private function openHandler(event:Event):void { 
     trc("openHandler: " + event); 
    } 

    private function progressHandler(event:ProgressEvent):void { 
     trc("progressHandler: " + event) 
     trc("available: " + stream.bytesAvailable); 
     extractImage(); 
     if (event.type == ProgressEvent.PROGRESS) 
      if (event.bytesLoaded > 1048576) { //1*1024*1024 bytes = 1MB 
       trc("transfered " + event.bytesLoaded +" closing") 
       stream.close(); 
       connect(); 
      } 
    } 

    private function securityErrorHandler(event:SecurityErrorEvent):void { 
     trc("securityErrorHandler: " + event); 
    } 

    private function httpStatusHandler(event:HTTPStatusEvent):void { 
     trc("httpStatusHandler: " + event); 
     trc("available: " + stream.bytesAvailable); 
     extractImage(); 
     //connect(); 
    } 

    private function ioErrorHandler(event:IOErrorEvent):void { 
     trc("ioErrorHandler: " + event); 
    } 

    } 
}; 

Le client fonctionne assez bien sur Firefox où je reçois tous les en-tête http:

 
--myBOUNDARY 
Content-Type: image/jpeg 
Content-Size: 25992 

J'utilise la taille du contenu de savoir combien d'octets aller de l'avant. Même chose dans IE8 (même IE buggé est compatible!)

Sur Safari, ça marche un peu différemment (c'est peut-être le webkit qui le fait) Je ne reçois pas le morceau http juste le contenu binaire, ce qui me force à chercher sur le tampon pour le début et la fin de la trame.

Problème est Chrome, crois ou pas, ça ne fonctionne pas. Quelque chose de bizarre se passe, semble-t-je obtenir le premier paquet tcp/ip, puis pour une raison Chrome décide de fermer la connexion, la sortie du journal est la suivante:

 
XHRMultipart() 
5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.114 Safari/534.16-Netscape 
chrome 
connect 
multipart/x-mixed-replace 

request.requestHeaders 
connecting 
connected 
openHandler: [Event type="open" bubbles=false cancelable=false eventPhase=2] 
openHandler: [Event type="open" bubbles=false cancelable=false eventPhase=2] 
progressHandler: [ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=3680 bytesTotal=0] 
available: 3680 
extractImage 
chromeExtract() 
findImageInBuffer, start: 0 end: -1 
httpStatusHandler: [HTTPStatusEvent type="httpStatus" bubbles=false cancelable=false eventPhase=2 status=200 responseURL=null] 
available: 0 
extractImage 
chromeExtract() 
findImageInBuffer, start: 0 end: -1 

Je ne devrais pas être obtenir httpStatus jusqu'à le serveur ferme la connexion ce qui n'est pas le cas ici. S'il vous plaît ne me dites pas d'utiliser HTML5 Canvas ou Video Je suis déjà prêt, le problème est que nous voulons que cette application fonctionne dans de nombreux systèmes d'exploitation et la compilation d'un encodeur vidéo pour tous (ffmpeg par exemple) ne fera pas le travail est plus facile. En outre, nous voulons fournir avec l'audio SCO qui est juste un flux PCM, donc je ne peux pas utiliser mjpeg simple. La toile est trop lente, j'ai testé ça, spécialement sur Android.

+0

J'ai très peu corrigé le code pour le rendre compatible même avec Konqueror Mon principal problème est que je ne suis pas un expert AS3, je viens du monde Python, j'ai un arrière-plan Java sombre et du C/C++ ainsi que. – manuelnaranjo

Répondre

3

Enfin, j'ai trouvé le problème!

Content-type est mauvais selon le plugin flash de Chrome, le bon est: Content-Type: multipart/x-mixed-replace

Et pas Content-Type: multipart/x-mixed-replace; boundary=myBOUNDARY

Alors mon serveur envoie maintenant ou non la limite selon un argument de demande.

1

Cela n'a pas fonctionné pour moi non plus - finalement je l'ai eu à travailler en chrome en utilisant:

Content-Type: text/html; boundary = - myboundary

Il va 6 heures de ma vie :(

Questions connexes