2012-07-23 9 views
19

Ho un'app di streaming audio, che esegue un server proxy locale. Il server proxy locale effettua una connessione http a un'origine di streaming Internet, riceve e memorizza localmente i dati di streaming. Poi, all'interno della app, io uso MediaPlayer per connettersi al server proxy locale, utilizzando il metodolettore multimediale Android - come disabilitare la richiesta di intervallo? (streaming audio interrotto su Nexus 7)

mediaPlayer.setDataSource(...); // the url of the local proxy server 

Tutto è andato bene (con un sacco di dispositivi Android e le diverse versioni del sistema operativo - 1,5 ... 4,0), fino al rilascio di Nexus 7.

In Nexus 7, il lettore multimediale rifiuta di riprodurre l'origine dal server proxy locale.

Quando ho dato un'occhiata ai log, sembra che MediaPlayer utilizzi internamente le richieste di intervallo. Il mio server proxy locale non gestisce quello. Restituisce HTTP/1.0 200 OK e i dati. Tuttavia, il lettore multimediale non piace questo e genera un'eccezione:

Caused by: libcore.io.ErrnoException 
?:??: W/?(?): [ 07-18 00:08:35.333 4962: 5149 E/radiobee ] 
?:??: W/?(?): : sendto failed: ECONNRESET (Connection reset by peer) 
?:??: W/?(?): at libcore.io.Posix.sendtoBytes(Native Method) 
?:??: W/?(?): at libcore.io.Posix.sendto(Posix.java:146) 
?:??: W/?(?): at libcore.io.BlockGuardOs.sendto(BlockGuardOs.java: 
?:??: W/?(?): at libcore.io.IoBridge.sendto(IoBridge.java:473) 
We requested a content range, but server didn't support that. (responded with 200) 

Secondo le specifiche HTTP, se il server risponde con HTTP/1.0 invece 1.1, il client non deve sparare una richiesta di gamma (1.0 non lo supporta comunque),

anche se il server non supporta la richiesta di intervallo, dovrebbe andare bene, se risponde con 200 OK (e questo è quello che sto facendo), ma l'implementazione di MediaPlayer su Nexus 7 non piace.

ho preso uno sguardo a questa discussione: HTTP: How should I respond to "Range: bytes=" when Range is unsupported?

, dove essi sostengono che la risposta con 200 OK deve essere abbastanza buono, ma purtroppo non aiuta.

Non sono sicuro che si tratti di un problema con Jelly Bean o di un problema con l'implementazione di Nexus 7, ma per me è ancora un problema che devo risolvere.

Ancora, non ci sono richieste di intervallo su molti altri dispositivi Android, utilizzando la stessa app. Per qualche ragione queste richieste di portata stanno accadendo ora su Nexus 7. (Può succedere anche su altri dispositivi Android, ma ancora una volta, non mi è mai successo fino ad ora).

Qualsiasi modo possibile per disattivare le richieste di intervallo per MediaPlayer?

Se non ce ne sono, qualcuno può suggerire una soluzione rapida per la mia logica del server proxy (che cosa deve restituire esattamente, se riceve questa richiesta di intervallo?), Senza cambiare la mia altra logica, se possibile?

Sembra che forse devo restituire qualcosa come "HTTP/1.0 206 OK \ r \ nPartial Content \ r \ n \ r \ n", ma probabilmente ci dovrebbe essere qualche valore alla fine del contenuto parziale - non certo cosa dovrebbe essere questo.

Il vostro aiuto sarebbe apprezzato.

Thanks ..

+2

Posso confermare che è un problema di Jelly Bean (non solo Nexus 7). È successo anche su diversi dispositivi Nexus, recentemente aggiornati da Ice Cream Sandwich a Jelly Bean. Qualcuno è riuscito a utilizzare MediaPlayer contro un server proxy locale presso Jelly Bean? – user1512464

+0

Posso confermare anche questo problema (su Jelly Bean). Però non stiamo usando un proxy, accediamo al flusso audio direttamente attraverso il lettore multimediale. – pulse00

+0

Sembra una regressione in Jelly Bean. Ho creato un problema sul bugtracker: https://code.google.com/p/android/issues/detail?id=35790 – pulse00

risposta

8

Io lavoro su larga scala streaming audio app (che utilizza un proxy HTTP host locale per lo streaming audio al MediaPlayer) e mi sono imbattuto in questo problema, non appena ho ottenuto un dispositivo di Jellybean nel mio mani su Google I/O 2012.

Quando testiamo la nostra applicazione su diversi dispositivi (e con le informazioni ricevute dai nostri registri degli arresti automatici e dai registri inviati dall'utente), abbiamo notato che alcune implementazioni di MediaPlayer si sono comportate in quello che chiamerei un modo erratico (e talvolta addirittura psicotico).

Senza entrare troppo nel dettaglio, questo è quello che ho visto: alcune implementazioni avrebbero richiesto più richieste (alcune volte più di 5) per lo stesso URL. Queste richieste erano tutte leggermente diverse l'una dall'altra in quanto ognuna era per un intervallo di byte diverso (di solito per il primo o l'ultimo 128 byte). La conclusione è stata che MediaPlayer stava cercando di trovare metadati incorporati, quindi, dopo un certo punto, si sarebbe arreso e avrebbe appena fatto una richiesta regolare senza intervallo.

Questo non è ciò che sta facendo l'implementazione di JellyBean MediaPlayer, ma solo un esempio di whacky-ness e di frammentazione generale del framework multimediale su Android. Tuttavia, la soluzione per la situazione di cui sopra è stata anche la soluzione al problema Jellybean, e cioè:

avere il vostro proxy locale risponde con chunked encoding

Questo sostituisce l'intestazione Content-Length con un transfer-Encoding: Chunked intestazione. Ciò significa che il client richiedente non conoscerà la lunghezza totale della risorsa e quindi non può effettuare una richiesta di intervallo, ma deve semplicemente gestire i blocchi non appena vengono ricevuti.

Come ho detto questo è un trucco, ma funziona. Non è privo di effetti collaterali: il progresso del buffer del lettore multimediale sarà errato in quanto non conosce la lunghezza dell'audio (è uno di questi), per evitare che si debba usare il proprio calcolo del buffer (si sta eseguendo lo streaming da qualche parte attraverso il tuo proxy per MediaPlayer, giusto? Così saprai la lunghezza totale).

+0

Questo è un approccio intrigante a un problema che ho incontrato anche sui dispositivi Jelly Bean . Il mio server trasmette file MP3 e risponde a richieste di intervallo. MediaPlayer fa una singola richiesta per la maggior parte dei file, ma alcuni file causano questo comportamento irregolare (il numero di richieste e i loro offset di byte variano, ma il comportamento per un determinato file sembra deterministico). La codifica di trasferimento Chunked non ha aiutato. MediaPlayer ha effettuato una richiesta di intervallo poco dopo la prima risposta e ha ignorato tale intestazione. Traccia pacchetto: http://pastebin.com/LiN6sKYH C'è qualcosa che mi manca? L'endpoint di streaming deve essere locale? –

11

Abbiamo finalmente risolto questo in modo pulito. Nel nostro caso abbiamo il pieno controllo sul server di streaming, ma suppongo che potresti farlo anche con un proxy locale. Dal Build.VERSION_CODES.ICE_CREAM_SANDWICH è possibile impostare le intestazioni per l'oggetto MediaPlayer. Poiché la nostra app consente all'utente di cercare all'interno del flusso audio, abbiamo implementato questa intestazione Range sul client. Se il server risponde con le intestazioni corrette, lo MediaPlayer non tenterà più volte di richiedere lo streaming.

Questo è ciò che i nostri intestazioni di server sembrano ora per il client Android:

Content-Type: audio/mpeg 
Accept-Ranges: bytes 
Content-Length: XXXX 
Content-Range: bytes XXX-XXX/XXX 
Status: 206 

La parte importante è il codice 206 stato (contenuto parziale). Se non si invia questa intestazione, il client Android tenterà di ri-richiedere la fonte, non importa quale.

Se il lettore non consente la ricerca nel flusso, è possibile semplicemente impostare sempre l'intestazione Range su 0-some arbitrary large number.

6

Quando si cerca o si salta o si perde la connessione e MediaPlayer continua a riconnettersi al server proxy, è necessario inviare questa risposta con Stato 206 dopo aver ottenuto la richiesta e l'intervallo (int) dal client.

String headers += "HTTP/1.0 206 OK\r\n"; 
headers += "Content-Type: audio/mpeg\r\n"; 
headers += "Accept-Ranges: bytes\r\n"; 
headers += "Content-Length: " + (fileSize-range) + "\r\n"; 
headers += "Content-Range: bytes "+range + "-" + fileSize + "/*\r\n"; 
headers += "\r\n";