2012-11-02 11 views
12

Ho un'applicazione che sta riproducendo file MP3 che sono disponibili su un URL pubblico. Sfortunatamente il server non supporta lo streaming, ma l'Android rende l'esperienza dell'utente abbastanza accettabile.Problema di rete Android JellyBean

Funziona tutto bene per tutte le piattaforme tranne JellyBean. Quando si richiede l'MP3, JB richiede una Range-Header per 10 volte. Solo dopo il 10 ° tentativo sembra tornare al vecchio comportamento. Looks like this already reported issue.

Ho trovato un altro SO thread in cui una soluzione consigliata è utilizzare Tranfer-Encoding: intestazione chunked. Ma appena sotto c'è un commento che questo non funziona.

Per il momento non ho alcun controllo per fornire le intestazioni di risposta sopra, ma fino a quando non sarò in grado di farlo ho pensato di cercare un'alternativa sul lato client. (anche così, posso solo restituire un Content-Range che contiene indici da 0 a Content-Length - 1. Ex. Content-Range: byte 0-3123456/3123457).

Quello che ho cercato di fare è quello di implementare una pseudo-streaming sul lato client:

  1. Aprire un flusso di input per l'MP3.
  2. Decodifica i byte in entrata tramite JLayer. Ho trovato la decodifica a this link.
  3. Invia i byte dell'array decodificato a uno stream_mode AudioTrack già riproducibile.

Il pezzo di codice che effettua la decodifica può essere trovato lì, ho modificato solo così riceverà un InputStream:

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

Sto richiedendo i byte decodificati in blocchi di tempo: iniziano (0, 5000) quindi avrò un array più grande da giocare in un primo momento, quindi richiedo gli array di byte successivi che si estendono su un secondo: (5000, 1000), (6000, 1000), (7000, 1000), ecc.

La decodifica è abbastanza veloce e viene eseguita in un altro thread e una volta decodificato un array di byte decodificato sto utilizzando una coda di blocco per scrivilo sull'AudioTrack che sta suonando in un'altra discussione.

Il problema è che la riproduzione non è fluida in quanto i pezzi non sono continui in una traccia (ogni blocco è continuo, ma aggiunto in AudioTrack risulta in una riproduzione sciatta).

Per concludere:

  1. Se avete urtato questo problema Jellybean, come hai fatto a risolverlo?
  2. Se qualcuno di voi ha provato il mio approccio, cosa sto facendo male nel codice sopra? Se questa è la soluzione che hai usato, posso pubblicare il resto del codice.

Grazie!

risposta

2

Sembra che tu stia cercando di sviluppare il tuo tipo di streaming. Ciò può avere una riproduzione bloccata o interrotta perché è necessario tentare un piping di informazioni continue senza esaurire i byte da cui leggere.

Fondamentalmente, è necessario tenere conto di tutte le situazioni che un normale client di streaming si prende cura di.Ad esempio, a volte alcuni blocchi possono essere rilasciati o persi durante la trasmissione; a volte la riproduzione audio può raggiungere il download; la CPU inizia ad andare in lag che influenza la riproduzione; ecc.

Qualcosa da ricercare se si vuole continuare su questa strada sarebbe l'implementazione di Sliding Window, è essenzialmente una tecnica astratta per cercare di mantenere la connettività di rete sempre attiva e fluida. Si dovrebbe essere in grado di trovare molti esempi tramite Google, qui è un punto di partenza: http://en.wikipedia.org/wiki/Sliding_window_protocol

Edit: Una soluzione che può aiutare fino a quando questo è fissato sarebbe quello di includere il codice sorgente per MediaPlayer.java e AudioManager.java da SDK < 16 nel tuo progetto e vedi se questo risolve il problema. Se non si dispone del codice sorgente, è possibile scaricarlo con SDK Manager.

+0

Grazie per aver risposto ... Non voglio seguire questo approccio per il progetto di cui avevo bisogno, ma se un giorno avrò un po 'di tempo libero, potrei provarlo per scopi educativi. Quindi le domande aperte rimangono valide (tutto funziona bene per tutte le piattaforme eccetto per JellyBean .Quando richiede l'MP3, JB richiede un Range-Header per 10 volte.) Solo dopo il 10 ° tentativo sembra tornare al vecchio comportamento.) Se ti sei imbattuto in questo, come hai risolto questo? Era qualcosa dal lato server o era lato client? – gunar

+0

Potete includere il vostro codice per questa linea: "Quando si richiede l'MP3, JB richiede una Range-Header per 10 volte, solo dopo il 10 ° tentativo sembra tornare al vecchio comportamento." – Matt

+0

Non ho aggiunto alcun codice per JB per richiedere l'intestazione di intervallo, per favore dare un'occhiata a [allegato problema Android] (https://code.google.com/p/android/issues/detail?id=35790). Tutto si riferisce ad esso. – gunar

1

AudioTrack sta bloccando per natura i documenti (Will block until all data has been written to the audio mixer.). Non sono sicuro che tu stia leggendo dal file e scrivendo su AudioTrack nello stesso thread; se è così, allora ti suggerisco di far girare una discussione per AudioTrack.