Fazendo streaming de áudio de uma URL no Android usando o MediaPlayer?

86

Tenho tentado transmitir mp3 em http usando a classe MediaPlayer do Android. A documentação sugere que isso deve ser tão fácil quanto:

MediaPlayer mp = new MediaPlayer();
mp.setDataSource(URL_OF_FILE);
mp.prepare();
mp.start();

No entanto, estou recebendo o seguinte repetidamente. Também tentei URLs diferentes. Por favor, não me diga que o streaming não funciona em mp3.

E/PlayerDriver(   31): Command PLAYER_SET_DATA_SOURCE completed with an error or info PVMFErrNotSupported
W/PlayerDriver(   31): PVMFInfoErrorHandlingComplete
E/MediaPlayer(  198): error (1, -4)
E/MediaPlayer(  198): start called in state 0
E/MediaPlayer(  198): error (-38, 0)
E/MediaPlayer(  198): Error (1,-4)
E/MediaPlayer(  198): Error (-38,0)

Qualquer ajuda muito apreciada, obrigado S

Pandalover
fonte
Algumas perguntas: (1) qual versão do SDK você está usando? (2) Em qual (is) dispositivo (s) você está testando? Isso funciona bem no SDK 2.0.1, testando em um Droid.
Roman Nurik
Oi Roman, obrigado por dedicar seu tempo. Estou tentando fazer isso contra o 1.6 e estou usando um HTC Hero. Vou testá-lo no 2.01 à luz dos seus comentários, mas seria um resultado ridículo se isso só funcionasse em dispositivos 2.xe posteriores prontos para uso.
Pandalover
Apenas tentei em um emulador 2.01. Não funciona, infelizmente. Estou intrigado em tentar isso contra um dispositivo real 1.6 e um dispositivo real 2.01. Estou no Google testando no dia 4. Talvez tenha que esperar até então. Eu prefiro não ter que fazer isso.
Pandalover
Não suspeito que 2.0 vs. 2.0.1 fará alguma diferença, mas o emulador vs. um dispositivo ao vivo pode fazer a diferença. Estou surpreso que isso não funcionou no Herói. Vou dar uma olhada e ver se consigo uma resposta melhor. Além disso, apenas para verificar a integridade, você deve se certificar de que solicitou a permissão INTERNET no manifesto.
Roman Nurik
Ei, acabei de sair da discussão, tenho uma pergunta. Se eu usar mp.setDataSource (URL_OF_FILE); Não precisamos salvar nenhum arquivo para o streaming de áudio. Não é? Portanto, é a melhor maneira de transmitir áudio de qualquer local. Alguma ideia?
Bohemian

Respostas:

78

Media Player simples com exemplo de streaming. Para a parte xml, você precisa de um botão com id button1 e duas imagens em sua pasta drawable com o nome button_pause e button_play e não se esqueça de adicionar a permissão de internet em seu manifesto.

public class MainActivity extends Activity {
private Button btn;
/**
 * help to toggle between play and pause.
 */
private boolean playPause;
private MediaPlayer mediaPlayer;
/**
 * remain false till media is not completed, inside OnCompletionListener make it true.
 */
private boolean intialStage = true;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    btn = (Button) findViewById(R.id.button1);
    mediaPlayer = new MediaPlayer();
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    btn.setOnClickListener(pausePlay);

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.activity_main, menu);
    return true;
}

private OnClickListener pausePlay = new OnClickListener() {

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        // TODO Auto-generated method stub

        if (!playPause) {
            btn.setBackgroundResource(R.drawable.button_pause);
            if (intialStage)
                new Player()
                        .execute("http://www.virginmegastore.me/Library/Music/CD_001214/Tracks/Track1.mp3");
            else {
                if (!mediaPlayer.isPlaying())
                    mediaPlayer.start();
            }
            playPause = true;
        } else {
            btn.setBackgroundResource(R.drawable.button_play);
            if (mediaPlayer.isPlaying())
                mediaPlayer.pause();
            playPause = false;
        }
    }
};
/**
 * preparing mediaplayer will take sometime to buffer the content so prepare it inside the background thread and starting it on UI thread.
 * @author piyush
 *
 */

class Player extends AsyncTask<String, Void, Boolean> {
    private ProgressDialog progress;

    @Override
    protected Boolean doInBackground(String... params) {
        // TODO Auto-generated method stub
        Boolean prepared;
        try {

            mediaPlayer.setDataSource(params[0]);

            mediaPlayer.setOnCompletionListener(new OnCompletionListener() {

                @Override
                public void onCompletion(MediaPlayer mp) {
                    // TODO Auto-generated method stub
                    intialStage = true;
                    playPause=false;
                    btn.setBackgroundResource(R.drawable.button_play);
                    mediaPlayer.stop();
                    mediaPlayer.reset();
                }
            });
            mediaPlayer.prepare();
            prepared = true;
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            Log.d("IllegarArgument", e.getMessage());
            prepared = false;
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            prepared = false;
            e.printStackTrace();
        } catch (IllegalStateException e) {
            // TODO Auto-generated catch block
            prepared = false;
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            prepared = false;
            e.printStackTrace();
        }
        return prepared;
    }

    @Override
    protected void onPostExecute(Boolean result) {
        // TODO Auto-generated method stub
        super.onPostExecute(result);
        if (progress.isShowing()) {
            progress.cancel();
        }
        Log.d("Prepared", "//" + result);
        mediaPlayer.start();

        intialStage = false;
    }

    public Player() {
        progress = new ProgressDialog(MainActivity.this);
    }

    @Override
    protected void onPreExecute() {
        // TODO Auto-generated method stub
        super.onPreExecute();
        this.progress.setMessage("Buffering...");
        this.progress.show();

    }
}

@Override
protected void onPause() {
    // TODO Auto-generated method stub
    super.onPause();
    if (mediaPlayer != null) {
        mediaPlayer.reset();
        mediaPlayer.release();
        mediaPlayer = null;
    }
}
PiyushMishra
fonte
Há um pequeno problema com isso, se eu bloquear meu telefone enquanto o MediaPlayer estiver em execução, meu aplicativo trava ao desbloqueá-lo.
CiaranC94
3
Acabei de tentar comentar a linha "mediaPlayer.release ()" em onPause () e agora meu aplicativo não trava ao desbloquear.
CiaranC94 de
@PiyushMishra esse recurso é aceito pelo console do desenvolvedor? Como meu aplicativo foi rejeitado b'coz, usei a versão do Vitamio 4.x
Pallavi
35

O Android MediaPlayer não suporta streaming de MP3 nativamente até a versão 2.2. Em versões mais antigas do sistema operacional, ele parece apenas transmitir 3GP nativamente. Você pode tentar o código pocketjourney, embora seja antigo (há uma nova versão aqui ) e eu tive problemas para torná-lo pegajoso - ele gaguejava sempre que recarregava o buffer.

O aplicativo NPR News para Android é de código aberto e usa um servidor proxy local para lidar com streaming de MP3 em versões do sistema operacional anteriores a 2.2. Você pode ver o código relevante nas linhas 199-216 (r94) aqui: http://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/ PlaybackService.java?r=7cf2352b5c3c0fbcdc18a5a8c67d836577e7e8e3

E esta é a classe StreamProxy: http://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/StreamProxy.java?r=e4984187f45c39a54ea6c88f71197762dbe10e72

O aplicativo NPR também ainda está recebendo o "erro (-38, 0)" às vezes durante a transmissão. Isso pode ser um problema de threading ou de mudança de rede. Verifique o rastreador de problemas para atualizações .

jwadsack
fonte
Você tem certeza absoluta disso? Meu entendimento é que tinha a ver com o tipo de mímica. Você poderia verificar se t funciona com o tipo MIME certo pré 2.1? Estou trabalhando em outra coisa agora e não pude verificar por um tempo.
Pandalover
1
De acordo com as notas de versão 2.2 ( developer.android.com/sdk/android-2.2-highlights.html ), inclui uma "nova estrutura de mídia (Stagefright) que suporta reprodução de arquivo local e streaming progressivo de HTTP". Em todos os meus testes, não consegui fazer com que um dispositivo 2.1 transmitisse diretamente de um servidor shoutcast. Eu acredito que o problema é que os servidores shoutcast retornam um protocolo de ICY / 1.1 em vez de HTTP / 1.1 e o reprodutor de mídia tropeça nisso, pois não sabe como responder a esse conteúdo.
jwadsack
@jwadsack e se os arquivos de áudio precisassem ser baixados uma vez e o usuário também pudesse reproduzi-los offline?
Devendra Singh
@DevendraSingh Não sei se você pode salvar o arquivo durante o streaming com a implementação do media player atual (esta resposta tem quase cinco anos e muita coisa mudou desde 2.2). No mínimo, você pode construir um proxy seguindo este exemplo e gravar o arquivo no armazenamento enquanto o passa pelo proxy.
jwadsack
11

Acho que você está tentando reproduzir um .pls diretamente ou algo semelhante.

experimente isto:

1: o código

mediaPlayer = MediaPlayer.create(this, Uri.parse("http://vprbbc.streamguys.net:80/vprbbc24.mp3"));
mediaPlayer.start();

2: o arquivo .pls

Este URL é da BBC apenas como exemplo. Era um arquivo .pls que baixei no linux com

wget http://foo.bar/file.pls

e então abri com o vim (use seu editor favorito;) e vi os URLs reais dentro deste arquivo. Infelizmente, nem todos os .pls são texto simples como esse.

Eu li que 1.6 não suportaria streaming de mp3 em http, mas acabei de testar o código obove com android 1.6 e 2.2 e não tive nenhum problema.

boa sorte!

Pabluez
fonte
7
Lembre-se de que, se quiser transmitir música, você deve usar mediaplayer.prepareAsync e não mediaplayer.prepare. Portanto, você não pode usar mediaplayer.create () porque qualquer uma das funções .create () leva seu objeto Media Player diretamente para o estado Preparado, a partir do qual você não pode chamar prepareAsync, que é necessário para transmitir. developer.android.com/reference/android/media/MediaPlayer.html
marienke
4

Usar

 mediaplayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
 mediaplayer.prepareAsync();
 mediaplayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
      @Override
      public void onPrepared(MediaPlayer mp) {
          mediaplayer.start();
      }
 });
Shshank Bhong
fonte
2

Eu tive o mesmo erro que você e descobri que não havia nada de errado com o código. O problema era que o servidor da web estava enviando o cabeçalho Content-Type errado.

Tente WireShark ou algo semelhante para ver que tipo de conteúdo o servidor está enviando.

pio
fonte
para arquivos mp3, deve ser Content-Type: audio / mpeg Isso resolveu meu problema :)
doep
1
Como você transmitiu esse arquivo?
Nauman Khalid,
1

Não chame mp.start com um OnPreparedListener para evitar o estado zero no log.

Fred Grott
fonte
Ainda recebo a linha de log 05-22 20: 26: 13.625: E / MediaPlayer (23818): parar chamado no estado 0, embora eu inicie meu Media Player na função preparada (). Também tenho uma função onError em que redefino o objeto Media Player. Ainda leva até 2 minutos para meu stream começar a tocar. Veja minha pergunta aqui: stackoverflow.com/questions/16672568/…
marienke