2013-02-11 3 views
13

После нескольких дней поиска SO и google я начинаю сдаваться, поэтому я подумал, что могу также опубликовать здесь.Android: потоковая передача камеры как mjpeg

Я создаю приложение для Android, которое должно предложить какой-то видеочат. Поскольку это должно быть как можно ближе к реальному времени, я читал о различных протоколах и решил попробовать MJPEG для стартеров (не относящихся к аудио на данный момент).

Прямо сейчас передача данных приводит меня в бешенство. Соединение устанавливается, приложение начинает записывать кадры предварительного просмотра камеры в поток, но ни VLC, ни mplayer не начинают воспроизводить видео. Мониторинг подключения показывает, что данные поступают.

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

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

На моем компьютере я исполню «MPlayer http://tabletIP:8080» и планшет регистрирует соединение (и, таким образом, начинается мой стример и предварительный просмотр камеры). Это также работает с VLC.

Streaming Об этом пишет заголовок потока:

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

После потоковая срабатывает через Camera.onPreviewFrame() Обратный звонок:

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

Там не исключение брошено. MHandler работает в собственном HandlerThread. Просто чтобы убедиться, что я попытался использовать AsyncTask, безрезультатно (кстати, это лучше?).

Закодированные фреймы на стороне андроида, я сохранил их в jpg-файлах и мог их открыть.

Я предполагаю, что мне нужно как-то скопировать данные или установить некоторые параметры для сокета или что-то в этом роде, но ... ну, я застрял.

TL; др: VLC не играет поток, MPlayer говорит: «кэш не заполнение», проблема, вероятно, в последнем сегменте кода, нужна помощь ~ :)

Спасибо любезно!

ответ

11

У меня есть. Похоже, что мои заголовки http// content были испорчены. Правильные заголовки должны быть:

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

и

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

Конечно, где поставить границу ваш собственный выбор. Также есть некоторые поля, которые являются необязательными (например, большинство в Cache-Control), но это работает, и до сих пор я был слишком ленив, чтобы снести их. Важная часть состоит в том, чтобы помнить о разрывах линий (\r\n штук) ...

+1

Не могли бы вы выслать код для получения MJPEG с камеры. – Sourav301

+0

@Sourav Ваш класс должен реализовать Camera.PreviewCallback. После инициализации камеры вызовите camera.setPreviewCallback (...). Остальное для получения JPEGS уже в моем вопросе. – Managarm

+1

Для тех, кто задается вопросом, какая граница должна быть, это может быть любая строка java (состоящая из алфавитов, не уверена, будут ли номера и специальные символы работать). –

 Смежные вопросы

  • Нет связанных вопросов^_^