2012-02-29 19 views
11

Kod serwera NanoHttpd można znaleźć here.Serwer NanoHttpd nie może przesyłać strumieniowo dużych filmów na Androidzie

Zaczynam nowy wątek w usłudze, która używa serwera NanoHttpd do strumieniowania dużych filmów (około 150 MB), ale po prostu zatrzymuje się zaraz po wyświetleniu okna dialogowego ładowania. Próbowałem zwiększać i zmniejszać odczyt bufora bezskutecznie. Wygląda na to, że serwer nie działa poprawnie na urządzeniu z systemem Android.

Ten sam kod działa poprawnie, gdy uruchamiam serwer za pomocą aplikacji komputerowej. Mogę przesyłać strumieniowo ponad 150 MB. Podczas uruchamiania serwera z telefonu próbowałem tylko 20 MB plików i były one również dobre. Jednak muszę przesyłać strumieniowo o wiele więcej.

Odpowiedz

2

Rozwiązałem mój własny problem. Problem polega na tym, że MediaPlayer (smp, vlc, odtwarzacz android) wysyła żądanie GET z określonym RANGE. Obsługiwany poprawnie, problem został rozwiązany.

+0

Czy mógłbyś podzielić się swoim kodem? Chcę utworzyć aplikację, która przesyła strumieniowo wideo z InputStream, i chciałbym zobaczyć, czy mogę uzyskać jakieś wskazówki ze swojego kodu. Z przyjemnością nagrodzę cię nagrodą za twoją pomoc. Z góry dziękuję! –

+0

Strumieniowanie wideo z InputStream było moim pierwszym ujęciem problemu, ale wygląda na to, gdy próbowałem go na odtwarzaczu multimedialnym Androida, tak naprawdę nie zużywa InputStream przy użyciu swoich metod read(), zamiast tego używa tylko InputStream jako odniesienia – josephus

+0

W rzeczywistości możesz użyć kodu serwera NanoHttpd z linku w moim pytaniu i powinien on działać wystarczająco dobrze, aby przesyłać strumieniowo filmy wideo z większości odtwarzaczy. Moja sprawa była jednak specyficzna, tak że musiałem przeprowadzić manipulację bajtową przed zapisaniem danych do klienta. – josephus

3

Więcej FYI niż cokolwiek, ale najnowsza wersja NanoHttpd (dostępna pod adresem http://github.com/NanoHttpd/nanohttpd) została zoptymalizowana pod kątem lepszej obsługi dużych przesyłanych plików przy zmniejszonym zużyciu pamięci. Kod, którego używasz, zawiera przychodzące przesyłanie do pamięci, nowsza wersja zapisuje na dysku. Sprawdź to i sprawdź, czy może rozwiązać problemy z pamięcią.

+0

świetnie! dzięki za heads-up. zajrzę do tego, kiedy wrócę do mojego biurka. – josephus

13

W przypadku, gdy inni natkną się na to i chcą zobaczyć, jaki jest aktualny kod w tym rozwiązaniu, zamieszczam tutaj mój kod. Używam urządzenia z Androidem do strumieniowego przesyłania pliku wideo z karty SD na żądanie Chromecasta. Korzystając z tego kodu, jestem w stanie uruchomić strumień w środku i/lub szukać konkretnej lokalizacji w strumieniu.

@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

Czy możesz zaktualizować tę odpowiedź za pomocą kompletnej implementacji serwera? –

+0

Wow, to jest idealny kod !. Działa zgodnie z oczekiwaniami podczas korzystania z VideoView, ale chciałem odtworzyć wideo za pomocą MediaExtractor. Czy możesz mi pomóc z kodem po stronie klienta? – Boobalan

4

Jest to problem ze względu na nagłówek HTTP nie zainicjowany, więc jeśli wezwanie użytkownik GET z prośbą zakresie po pierwsze, a następnie wywołać inną żądanie GET bez pola zasięgu, pole poprzednia oferta będzie nadal trzymać, ale w rzeczywistości drugi Żądanie GET nie oczekuje odczytu z zakresu.

Android MediaPlayer jest taki przypadek dla pliku mp4 z moov box na końcu, co spowoduje, że dane odczytu faktycznie nie chcemy.

Aby rozwiązać ten problem, można spróbować poniżej poprawkę:

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

Z tej poprawki, to działa dobrze dla mnie na Android 5.0 urządzenia.

+0

To jest świetna odpowiedź. Użyłem NanoHTTPD do streamowania wideo, działa dobrze w wielu telefonach, ale nie działa na Samsung S4. Ta poprawka pomogła mi, dzięki – thomasdao

+0

Dziękuję za to, bardzo mi pomogło. –

Powiązane problemy