2012-02-29 5 views
11

Код сервера NanoHttpd можно найти here.Сервер NanoHttpd не может передавать большие видео на android

Я начинаю новую тему в службе, которая использует сервер NanoHttpd для потоковой передачи больших видеороликов (около 150 мб), но при этом просто приостанавливается, пока отображается диалоговое окно загрузки. Я пытался увеличивать и уменьшать чтение буфера безрезультатно. Кажется, что сервер не может нормально работать на устройстве Android.

Тот же код отлично работает при запуске сервера через настольное приложение. Я могу транслировать более 150 мб. При запуске сервера с телефона я только пробовал файлы 20 МБ, и они тоже были хороши. Однако мне нужно потопить гораздо больше.

ответ

2

Решил мою собственную проблему. Проблема в том, что MediaPlayer (s) (wmp, vlc, android player) выдают запрос GET с указанным диапазоном. Правильно выполнил этот запрос, проблема решена.

+0

Не могли бы вы поделиться своим кодом? Я ищу создать приложение, которое передает видео из InputStream, и мне бы хотелось посмотреть, могу ли я получить какие-либо указатели от вашего кода. Я был бы рад предоставить вам щедрость за вашу помощь. Заранее спасибо! –

+0

Потоковое видео с InputStream на самом деле было моим первым выстрелом в проблему, но, как видно, когда я пробовал его на медиаплеере Android, он фактически не потребляет InputStream с использованием методов read(), а использует только InputStream в качестве ссылки – josephus

+0

Фактически вы можете использовать код сервера NanoHttpd из ссылки в моем вопросе, и он должен работать достаточно хорошо, чтобы вы могли передавать видео от большинства игроков. Мой случай был конкретным, хотя, чтобы мне пришлось выполнять манипуляции с байтом, прежде чем записывать данные клиенту. – josephus

3

Больше из FYI, чем что-либо, но самая новая версия NanoHttpd (доступна по адресу http://github.com/NanoHttpd/nanohttpd) была оптимизирована для лучшей поддержки больших загрузок с уменьшенной площадью памяти. Используемый код содержит входящую загрузку в память, более новую версию записывает на диск. Проверьте это и посмотрите, не может ли это решить проблемы с памятью.

+0

отлично! спасибо за хедз-ап. посмотрю на это, когда вернусь к своему столу. – josephus

13

В случае, если другие сталкиваются с этим и хотят увидеть, что представляет собой настоящий код в этом решении, я размещаю свой код здесь. Я использую свое устройство Android для потоковой передачи видеофайла с SD-карты для запроса Chromecast. Используя этот код, я могу запустить поток посередине и/или перейти к определенному местоположению в потоке.

@Override 
@SuppressWarnings("deprecation") 
public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files) { 
    String mimeType = getMimeType(); 
    String currentUri = getCurrentUri(); 
    if (currentUri != null && currentUri.equals(uri)) { 
     String range = null; 
     Log.d(TAG, "Request headers:"); 
     for (String key : headers.keySet()) { 
      Log.d(TAG, " " + key + ":" + headers.get(key)); 
      if ("range".equals(key)) { 
       range = headers.get(key); 
      } 
     } 
     try { 
      if (range == null) { 
       return getFullResponse(mimeType); 
      } else { 
       return getPartialResponse(mimeType, range); 
      } 
     } catch (IOException e) { 
      Log.e(TAG, "Exception serving file: " + filePath, e); 
     } 
    } else { 
     Log.d(TAG, "Not serving request for: " + uri); 
    } 

    return new Response(Response.Status.NOT_FOUND, mimeType, "File not found"); 
} 

private Response getFullResponse(String mimeType) throws FileNotFoundException { 
    cleanupStreams(); 
    fileInputStream = new FileInputStream(filePath); 
    return new Response(Response.Status.OK, mimeType, fileInputStream); 
} 

private Response getPartialResponse(String mimeType, String rangeHeader) throws IOException { 
    File file = new File(filePath); 
    String rangeValue = rangeHeader.trim().substring("bytes=".length()); 
    long fileLength = file.length(); 
    long start, end; 
    if (rangeValue.startsWith("-")) { 
     end = fileLength - 1; 
     start = fileLength - 1 
       - Long.parseLong(rangeValue.substring("-".length())); 
    } else { 
     String[] range = rangeValue.split("-"); 
     start = Long.parseLong(range[0]); 
     end = range.length > 1 ? Long.parseLong(range[1]) 
       : fileLength - 1; 
    } 
    if (end > fileLength - 1) { 
     end = fileLength - 1; 
    } 
    if (start <= end) { 
     long contentLength = end - start + 1; 
     cleanupStreams(); 
     fileInputStream = new FileInputStream(file); 
     //noinspection ResultOfMethodCallIgnored 
     fileInputStream.skip(start); 
     Response response = new Response(Response.Status.PARTIAL_CONTENT, mimeType, fileInputStream); 
     response.addHeader("Content-Length", contentLength + ""); 
     response.addHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileLength); 
     response.addHeader("Content-Type", mimeType); 
     return response; 
    } else { 
     return new Response(Response.Status.RANGE_NOT_SATISFIABLE, HTML_MIME_TYPE, rangeHeader); 
    } 
} 
+2

вы можете обновить этот ответ с полной реализацией сервера? –

+0

Вау, это идеальный код !. Он работает так, как ожидается при использовании VideoView, но я хотел играть в видео с помощью MediaExtractor. Не могли бы вы помочь мне с кодом на стороне клиента? – Boobalan

4

Это проблема из-за заголовка HTTP не инициализирован, поэтому, если вызов пользователю GET с запросом диапазона, во-первых, а затем вызвать другой запрос GET без поля диапазона, предыдущее поле диапазон будет по-прежнему держать там, но на самом деле второй Запрос GET не ожидается, чтобы прочитать его из диапазона.

Android MediaPlayer - это такой случай для файла mp4 с полем moov в конце, что фактически приведет к чтению данных.

Чтобы решить эту проблему, вы можете попробовать ниже патч:

diff --git a/core/src/main/java/fi/iki/elonen/NanoHTTPD.java b/core/src/main/java/fi/iki/elonen/NanoHTTPD.java 
index ce292a4..aba21c4 100644 
--- a/core/src/main/java/fi/iki/elonen/NanoHTTPD.java 
+++ b/core/src/main/java/fi/iki/elonen/NanoHTTPD.java 
@@ -1039,6 +1039,7 @@ public abstract class NanoHTTPD { 
      */ 
     private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, String> parms, Map<String, St 
      throws ResponseException { 
+   headers.put("range","bytes=0-"); 
      try { 
       // Read the request line 
       String inLine = in.readLine(); 

С помощью этого исправления, он отлично работает для меня на Android 5.0 устройства.

+0

Это отличный ответ. Я использовал NanoHTTPD для потокового видео, он отлично работает на многих телефонах, но не работает для Samsung S4. Это исправление помогло мне, спасибо – thomasdao

+0

Спасибо за это, много помогли. –