NanoHttpd сервер не может передавать большие видео на Android - PullRequest
11 голосов
/ 29 февраля 2012

Код сервера NanoHttpd можно найти здесь .

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

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

Ответы [ 4 ]

14 голосов
/ 13 мая 2014

В случае, если другие столкнутся с этим и захотят увидеть, что на самом деле представляет собой код в этом решении, я публикую свой код здесь.Я использую свое устройство 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);
    }
}
4 голосов
/ 15 декабря 2014

Это проблема из-за того, что заголовок 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.

3 голосов
/ 01 июня 2013

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

2 голосов
/ 06 марта 2012

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

...