2013-02-11 4 views
13

Nach mehreren Tagen der Suche SO und Google Ich fange an aufzugeben, so dass ich dachte, ich könnte auch hier posten.Android: streaming die Kamera als mjpeg

Ich erstelle eine Android-App, die eine Art Video-Chat anbieten soll. Da dies so nah wie möglich an Echtzeit sein sollte, habe ich über verschiedene Protokolle gelesen und mich entschieden, MJPEG als Vorspeise zu versuchen (nicht im Zusammenhang mit Audio für jetzt).

Gerade jetzt streamen die Daten mich verrückt. Die Verbindung wird hergestellt, die App beginnt, die Vorschaubilder der Kamera in den Stream zu schreiben, aber weder VLC noch MPlayer beginnen Video abzuspielen. Die Überwachung der Verbindung zeigt, dass die Daten ankommen.

Connecting Dieser Code durch eine Asynchron-Aufgabe ausgeführt wird, wird ein Zuhörer auf Erfolg gemeldet:

try 
    { 
     ServerSocket server = new ServerSocket(8080); 

     socket = server.accept(); 

     server.close(); 

     Log.i(TAG, "New connection to :" + socket.getInetAddress()); 

     stream = new DataOutputStream(socket.getOutputStream()); 
     prepared = true; 
    } 
    catch (IOException e) 
    { 
     Log.e(TAG, e.getMessage(); 
    } 

Auf meinem PC I ‚mplayer http://tabletIP:8080‘ ausführen und die Tablette registriert eine Verbindung (und damit beginnt mein Streamer und die Kameravorschau). Dies funktioniert auch mit VLC.

Streaming Dies schreibt die Header in den Stream:

if (stream != null) 
{ 
    try 
    { 
     // send the header 
     stream.write(("HTTP/1.0 200 OK\r\n" + 
         "Server: iRecon\r\n" + 
         "Connection: close\r\n" + 
         "Max-Age: 0\r\n" + 
         "Expires: 0\r\n" + 
         "Cache-Control: no-cache, private\r\n" + 
         "Pragma: no-cache\r\n" + 
         "Content-Type: multipart/x-mixed-replace; " + 
         "boundary=--" + boundary + 
         "\r\n\r\n").getBytes()); 

     stream.flush(); 

     streaming = true; 
    } 
    catch (IOException e) 
    { 
     notifyOnEncoderError(this, "Error while writing header: " + e.getMessage()); 
     stop(); 
    } 
} 

Danach wird durch die Camera.onPreviewFrame() Callback ausgelöst Streaming:

@Override 
public void onPreviewFrame(byte[] data, Camera camera) 
{ 
    frame = data; 

    if (streaming) 
     mHandler.post(this); 
} 

@Override 
public void run() 
{ 
    // TODO: cache not filling? 
    try 
    { 
        // buffer is a ByteArrayOutputStream 
     buffer.reset(); 

     switch (imageFormat) 
     { 
      case ImageFormat.JPEG: 
       // nothing to do, leave it that way 
       buffer.write(frame); 
       break; 

      case ImageFormat.NV16: 
      case ImageFormat.NV21: 
      case ImageFormat.YUY2: 
      case ImageFormat.YV12: 
       new YuvImage(frame, imageFormat, w, h, null).compressToJpeg(area, 100, buffer); 
       break; 

      default: 
       throw new IOException("Error while encoding: unsupported image format"); 
     } 

     buffer.flush(); 

     // write the content header 
     stream.write(("--" + boundary + "\r\n" + 
         "Content-type: image/jpg\r\n" + 
         "Content-Length: " + buffer.size() + 
         "\r\n\r\n").getBytes()); 

     // Should omit the array copy 
     buffer.writeTo(stream); 

     stream.write("\r\n\r\n".getBytes()); 
     stream.flush(); 
    } 
    catch (IOException e) 
    { 
     stop(); 
     notifyOnEncoderError(this, e.getMessage()); 
    } 
} 

Es gibt keine Ausnahme ausgelöst. Der mHandler läuft in seinem eigenen HandlerThread. Nur um sicher zu sein, habe ich versucht, eine AsyncTask zu verwenden, ohne Erfolg (übrigens, ist das besser?).

Die kodierten Frames sind gut auf der Android-Seite, ich speicherte sie in JPG-Dateien und konnte sie öffnen.

Meine Vermutung ist, dass ich die Daten irgendwie gruppieren muss oder einige Optionen für den Sockel oder etwas einstellen muss, aber .... nun, ich stecke fest.

tl; dr: VLC nicht Stream spielen, mplayer sagt: 'Cache nicht füllen', Problem wahrscheinlich in den letzten Code-Segment, brauchen Hilfe ~ :)

Sie herzlich danken!

Antwort

11

Ich habe es. Scheint so, als wären meine http-/content-header kaputt. Die richtigen Header sollte sein:

stream.write(("HTTP/1.0 200 OK\r\n" + 
          "Server: iRecon\r\n" + 
          "Connection: close\r\n" + 
          "Max-Age: 0\r\n" + 
          "Expires: 0\r\n" + 
          "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\r\n" + 
          "Pragma: no-cache\r\n" + 
          "Content-Type: multipart/x-mixed-replace; " + 
          "boundary=" + boundary + "\r\n" + 
          "\r\n" + 
          "--" + boundary + "\r\n").getBytes()); 

und

stream.write(("Content-type: image/jpeg\r\n" + 
         "Content-Length: " + buffer.size() + "\r\n" + 
         "X-Timestamp:" + timestamp + "\r\n" + 
         "\r\n").getBytes()); 

buffer.writeTo(stream); 
stream.write(("\r\n--" + boundary + "\r\n").getBytes()); 

Natürlich, wo die Grenze setzen Sie Ihre eigene Wahl. Außerdem gibt es wahrscheinlich einige Felder, die optional sind (z. B. die meisten in der Cache-Steuerung), aber das funktioniert und bis jetzt war ich zu faul, um sie zu entfernen. Der wichtige Teil ist, sich an die Zeilenumbrüche zu erinnern (\r\n Dinger) ...

+1

Könnten Sie bitte den Code, um MJPEG von Kamera zu bekommen. – Sourav301

+0

@Sourav Ihre Klasse muss Camera.PreviewCallback implementieren. Nach dem Initialisieren des Kameraaufrufs camera.setPreviewCallback (...). Der Rest zum Abrufen von JPEGS steht schon in meiner Frage. – Managarm

+1

Für diejenigen, die sich fragen, welche Grenze sein sollte, kann es eine beliebige Java-Zeichenfolge sein (bestehend aus Alphabeten, nicht sicher, ob Zahlen und Sonderzeichen funktionieren). –