2012-11-02 6 views
12

Ich habe eine Anwendung, die MP3-Dateien, die an einer öffentlichen URL verfügbar sind. Leider unterstützt der Server kein Streaming, aber das Android macht die Benutzererfahrung durchaus akzeptabel.Android JellyBean Netzwerk Media-Problem

Es funktioniert alles für alle Plattformen außer JellyBean. Beim Anfordern der MP3 fordert JB 10 Mal einen Range-Header an. Erst nach dem 10. Versuch scheint es zu dem alten Verhalten zurückzukehren. Looks like this already reported issue.

fand ich eine andere SO thread wo eine empfohlene Lösung Tranfer-Encoding zu verwenden ist: chunked-Header. Aber gerade unten gibt es einen Kommentar, dass dies nicht funktioniert.

Im Moment habe ich keinerlei Kontrolle über die obigen Antwortheader, aber bis ich dazu in der Lage bin, habe ich gedacht, auf der Clientseite nach einer Alternative zu suchen. (Trotzdem kann ich nur einen Inhaltsbereich zurückgeben, der Indizes von 0 bis Content-Length enthält - 1. Ex. Content-Range: Bytes 0-3123456/3123457).

Was ich zu tun versucht, ist ein Pseudo-Streaming auf Client-Seite zu implementieren durch:

  1. Öffnen Sie eine Eingabestrom auf den MP3.
  2. Decodieren Sie die eingehenden Bytes mit JLayer. Ich fand die Decodierung bei this link.
  3. Senden Sie die decodierten Array-Bytes an einen bereits abspielbaren Stream-Modus AudioTrack.

Das Stück Code, der die Dekodierung tut sind dort zu finden, habe ich geändert es nur so ist es ein Input erhalten:

public byte[] decode(InputStream inputStream, int startMs, int maxMs) throws IOException { 
     ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024); 

     float totalMs = 0; 
     boolean seeking = true; 

     try { 
      Bitstream bitstream = new Bitstream(inputStream); 
      Decoder decoder = new Decoder(); 

      boolean done = false; 
      while (!done) { 
       Header frameHeader = bitstream.readFrame(); 
       if (frameHeader == null) { 
        done = true; 
       } else { 
        totalMs += frameHeader.ms_per_frame(); 

        if (totalMs >= startMs) { 
         seeking = false; 
        } 

        if (!seeking) { 
         // logger.debug("Handling header: " + frameHeader.layer_string()); 
         SampleBuffer output = (SampleBuffer) decoder.decodeFrame(frameHeader, bitstream); 

         if (output.getSampleFrequency() != 44100 || output.getChannelCount() != 2) { 
          throw new IllegalArgumentException("mono or non-44100 MP3 not supported"); 
         } 

         short[] pcm = output.getBuffer(); 
         for (short s : pcm) { 
          outStream.write(s & 0xff); 
          outStream.write((s >> 8) & 0xff); 
         } 
        } 

        if (totalMs >= (startMs + maxMs)) { 
         done = true; 
        } 
       } 
       bitstream.closeFrame(); 
      } 

      return outStream.toByteArray(); 
     } catch (BitstreamException e) { 
      throw new IOException("Bitstream error: " + e); 
     } catch (DecoderException e) { 
      throw new IOException("Decoder error: " + e); 
     } 
    } 

ich die decodierten Bytes in der Zeit chunks suche nach: beginnend mit (0, 5000), so dass ich zuerst ein größeres Array habe, dann suche ich nach den nächsten Byte - Arrays, die über eine Sekunde reichen: (5000, 1000), (6000, 1000), (7000, 1000) usw .

Die Decodierung ist schnell genug und wird in einem anderen Thread durchgeführt und sobald ein decodiertes Byte-Array verfügbar ist, verwende ich eine blockierende Warteschlange zu schreibe es in den AudioTrack, der in einem anderen Thread abgespielt wird.

Das Problem ist, dass die Wiedergabe nicht reibungslos ist, da die Chunks in einer Spur nicht kontinuierlich sind (jeder Chunk ist kontinuierlich, aber in den AudioTrack hinzugefügt führt zu einer schlampigen Wiedergabe).

Zum Abschluss:

  1. Wenn Sie in diese JellyBean Problem gestoßen haben, wie haben Sie es lösen?
  2. Wenn einer von euch meinen Ansatz versucht hat, was mache ich dann oben falsch? Wenn dies die von Ihnen verwendete Lösung ist, kann ich den Rest des Codes veröffentlichen.

Vielen Dank!

Antwort

2

Es sieht so aus, als ob Sie versuchen, Ihren eigenen Streaming-Typ zu entwickeln. Dies kann zu einer blockierten oder unterbrochenen Wiedergabe führen, da Sie versuchen müssen, kontinuierlich Daten zu übertragen, ohne dass die Bytes zum Lesen auslaufen.

Grundsätzlich müssen Sie alle Situationen berücksichtigen, für die ein normaler Streaming-Client zuständig ist.Zum Beispiel können manchmal einige Blöcke fallengelassen oder bei der Übertragung verloren gehen; manchmal kann die Audiowiedergabe dem Download entsprechen; Die CPU beginnt zu verzögern, was sich auf die Wiedergabe auswirkt. etc. etc.

Etwas zu erforschen, wenn Sie diesen Pfad weiter unten Sliding Window-Implementierung fortsetzen möchten, ist es im Wesentlichen eine abstrakte Technik zu versuchen, die Netzwerkkonnektivität immer aktiv und flüssig zu halten. Sie sollten einige Beispiele über Google zu finden, hier ist ein Anfang der Lage sein: http://en.wikipedia.org/wiki/Sliding_window_protocol

Edit: Eine Abhilfe, die Ihnen helfen kann, bis dieses Problem behoben ist, würde den Quellcode enthalten für MediaPlayer.java und AudioManager.java von SDK < 16 in Ihr Projekt und sehen, ob das das Problem löst. Wenn Sie den Quellcode nicht haben, können Sie ihn mit dem SDK Manager herunterladen.

+0

Danke für die Antwort ... Ich möchte diesen Ansatz nicht für das Projekt, das ich brauchte, befolgen, aber wenn ich eines Tages Zeit haben werde, werde ich das vielleicht aus pädagogischen Gründen versuchen. Die offenen Fragen stehen also immer noch (Für JellyBean funktioniert das alles gut. Bei der Anforderung der MP3 bittet JB 10 Mal um einen Range-Header. Erst nach dem 10. Versuch scheint es wieder zu dem alten Verhalten zu kommen.) Wenn du darauf gestoßen bist, wie hast du das behoben? War es etwas von der Server-Seite oder war es Client-Seite? – gunar

+0

Können Sie Ihren Code für diese Zeile einfügen: "Wenn JB die MP3 anfordert, bittet JB 10 Mal um einen Range-Header. Erst nach dem 10. Versuch scheint es wieder das alte Verhalten zu geben." – Matt

+0

Ich habe keinen Code für JB hinzugefügt, um Range-Header anzufordern. Bitte werfen Sie einen Blick auf die [angehängte Android-Ausgabe] (https://code.google.com/p/android/issues/detail?id=35790). Alles bezieht sich darauf. – gunar

1

AudioTrack blockiert von Natur aus aus der Dokumentation (Will block until all data has been written to the audio mixer.). Ich bin mir nicht sicher, ob Sie aus der Datei lesen und AudioTrack in demselben Thread schreiben; Wenn ja, dann würde ich vorschlagen, dass Sie einen Thread für AudioTrack drehen.

Verwandte Themen