Przesyłanie strumieniowe dźwięku z adresu URL w systemie Android za pomocą MediaPlayer?

86

Próbowałem przesyłać strumieniowo pliki mp3 przez http przy użyciu wbudowanej w system Android klasy MediaPlayer. Dokumentacja sugeruje mi, że powinno to być tak proste, jak:

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

Jednak wielokrotnie otrzymuję następujące informacje. Próbowałem też różnych adresów URL. Proszę nie mów mi, że przesyłanie strumieniowe nie działa na plikach 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)

Każda pomoc doceniona, dzięki S.

Pandalover
źródło
Kilka pytań: (1) której wersji SDK używasz? (2) Na jakich urządzeniach testujesz? Działa to dobrze na SDK 2.0.1, testowanie na Droidzie.
Roman Nurik
Cześć Roman, dzięki za poświęcenie czasu. Próbuję tego przeciwko 1.6 i używam HTC Hero. Spróbuję go na 2.01 w świetle twoich komentarzy, ale byłby to absurdalny wynik, gdyby działało to tylko na urządzeniach 2.xi nowszych po wyjęciu z pudełka.
Pandalover
Właśnie wypróbowałem to na emulatorze 2.01. Niestety nie działa. Jestem zaintrygowany, aby spróbować tego z prawdziwym urządzeniem 1.6 i prawdziwym urządzeniem 2.01. Jestem w Google testowania czwartego. Może będę musiał poczekać do tego czasu. Wolałbym jednak nie musieć.
Pandalover
Nie podejrzewam, że 2.0 kontra 2.0.1 zrobi jakąkolwiek różnicę, ale emulator kontra urządzenie na żywo może coś zmienić. Dziwię się, że to nie zadziałało na Hero. Przyjrzę się temu i zobaczę, czy znajdę lepszą odpowiedź. Och, także, jako test poczytalności, powinieneś upewnić się, że poprosiłeś o pozwolenie INTERNET w manifeście.
Roman Nurik
Hej, tylko poza dyskusją, mam pytanie. Jeśli używam mp.setDataSource (URL_OF_FILE); Nie musimy zapisywać żadnego pliku do strumieniowego przesyłania dźwięku. Prawda? W ten sposób jest to najlepszy sposób na strumieniowe przesyłanie dźwięku z dowolnego miejsca. Jakieś pomysły?
Bohemian

Odpowiedzi:

78

prosty odtwarzacz multimedialny z przykładem przesyłania strumieniowego. dla części XML potrzebujesz jednego przycisku z identyfikatorem button1 i dwóch obrazów w folderze do rysowania z nazwą button_pause i button_play i nie zapomnij dodać pozwolenia internetowego w swoim manifeście

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
źródło
Jest z tym niewielki problem, jeśli zablokuję telefon podczas odtwarzania MediaPlayer, moja aplikacja zawiesza się po odblokowaniu.
CiaranC94,
3
Właśnie próbowałem skomentować wiersz „mediaPlayer.release ()” w funkcji onPause (), a teraz moja aplikacja nie ulega awarii po odblokowaniu.
CiaranC94,
@PiyushMishra ta funkcja jest akceptowana przez konsolę programisty? Ponieważ moja aplikacja została odrzucona, ponieważ użyłem wersji Vitamio 4.x
Pallavi
35

Android MediaPlayer nie obsługuje natywnie strumieniowego przesyłania plików MP3 do wersji 2.2. W starszych wersjach systemu operacyjnego wydaje się, że natywnie strumieniuje tylko 3GP. Możesz wypróbować kod pocketjourney, chociaż jest stary (jest tutaj nowa wersja ) i miałem problem z uczynieniem go lepkim - zacinał się, gdy uzupełniał bufor.

Aplikacja NPR News na Androida jest open source i wykorzystuje lokalny serwer proxy do obsługi strumieniowego przesyłania plików MP3 w wersjach systemu operacyjnego wcześniejszych niż 2.2. Możesz zobaczyć odpowiedni kod w liniach 199-216 (r94) tutaj: http://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/ PlaybackService.java?r=7cf2352b5c3c0fbcdc18a5a8c67d836577e7e8e3

A to jest klasa StreamProxy: http://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/StreamProxy.java?r=e4984187f45c39a54ea6c88f71197762dbe10e72

Aplikacja NPR również czasami wyświetla komunikat „Error (-38, 0)” podczas przesyłania strumieniowego. Może to być problem z wątkami lub problem ze zmianą sieci. Sprawdź aktualizacje w narzędziu do śledzenia problemów .

jwadsack
źródło
Czy jesteś tego absolutnie pewien? Rozumiem, że miało to związek z typem mime. Czy możesz sprawdzić, czy t działa z właściwym typem MIME przed wersją 2.1? Pracuję teraz nad czymś innym i przez chwilę nie mogłem tego sprawdzić.
Pandalover
1
Zgodnie z uwagami do wydania 2.2 ( developer.android.com/sdk/android-2.2-highlights.html ) zawiera on „Strukturę nowych mediów (Stagefright), która obsługuje lokalne odtwarzanie plików i progresywne przesyłanie strumieniowe HTTP”. We wszystkich moich testach nie udało mi się uzyskać urządzenia 2.1 do bezpośredniego przesyłania strumieniowego z serwera shoutcast. Uważam, że problem polega na tym, że serwery shoutcast zwracają protokół ICY / 1.1 zamiast HTTP / 1.1, a odtwarzacz multimediów włącza się, ponieważ nie wie, jak odpowiedzieć na te treści.
jwadsack
@jwadsack a co, jeśli pliki audio powinny zostać pobrane raz, a użytkownik może odtwarzać je również offline?
Devendra Singh,
@DevendraSingh Nie wiem, czy możesz zapisać plik podczas przesyłania strumieniowego z obecną implementacją odtwarzacza multimediów (ta odpowiedź ma prawie pięć lat i wiele się zmieniło od 2.2). Jeśli nic więcej, możesz zbudować serwer proxy zgodnie z tym przykładem i zapisać plik w pamięci podczas przekazywania go przez serwer proxy.
jwadsack,
11

Wydaje mi się, że próbujesz bezpośrednio odtworzyć plik .pls lub coś podobnego.

wypróbuj to:

1: kod

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

2: plik .pls

Ten adres URL pochodzi z BBC jako przykład. Był to plik .pls, z którym pobrałem w systemie Linux

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

a potem otworzyłem za pomocą vima (użyj swojego ulubionego edytora;) i zobaczyłem prawdziwe adresy URL wewnątrz tego pliku. Niestety nie wszystkie pliki .pl są takie jak zwykły tekst.

Czytałem, że 1.6 nie obsługuje przesyłania strumieniowego mp3 przez http, ale właśnie przetestowałem kod obove z Androidem 1.6 i 2.2 i nie miałem żadnego problemu.

powodzenia!

Pabluez
źródło
7
Pamiętaj, że jeśli chcesz przesyłać strumieniowo muzykę, musisz użyć mediaplayer.prepareAsync, a nie mediaplayer.prepare. Dlatego nie możesz użyć mediaplayer.create (), ponieważ każda z funkcji .create () przenosi twój obiekt Media Player bezpośrednio do stanu Prepared, z którego nie możesz wywołać przygotowaniaAsync, co musisz zrobić, aby przesyłać strumieniowo. developer.android.com/reference/android/media/MediaPlayer.html
marienke
4

Posługiwać się

 mediaplayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
 mediaplayer.prepareAsync();
 mediaplayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
      @Override
      public void onPrepared(MediaPlayer mp) {
          mediaplayer.start();
      }
 });
Shshank Bhong
źródło
2

Miałem ten sam błąd co Ty i okazało się, że z kodem nie ma nic złego. Problem polegał na tym, że serwer WWW wysyłał nieprawidłowy nagłówek Content-Type.

Wypróbuj wireshark lub coś podobnego, aby zobaczyć, jaki typ zawartości wysyła serwer sieciowy.

doep
źródło
dla plików mp3 powinno to być Content-Type: audio / mpeg To rozwiązało mój problem :)
doep
1
Jak przesyłałeś strumieniowo ten plik?
Nauman Khalid
2

Szukam moich projektów:

  1. https://github.com/master255/ImmortalPlayer obsługa protokołu http / FTP, jeden wątek do odczytu, wysyłania i zapisywania w pamięci podręcznej danych. Najprostszy sposób i najszybsza praca. Złożona logika - najlepszy sposób!
  2. https://github.com/master255/VideoViewCache Prosty widok wideo z pamięcią podręczną. Dwa wątki do odtwarzania i zapisywania danych. Zła logika, ale jeśli potrzebujesz, użyj tego.
Mistrz
źródło
1

Brak wywołania mp.start z OnPreparedListener, aby uniknąć stanu zerowego w dzienniku.

Fred Grott
źródło
Wciąż otrzymuję wiersz dziennika 05-22 20: 26: 13.625: E / MediaPlayer (23818): zatrzymaj wywołanie w stanie 0, mimo że uruchamiam odtwarzacz multimediów w funkcji przygotowanej (). Mam również funkcję onError, w której resetuję obiekt Media Player. Nadal trwa do 2 minut, zanim mój strumień zacznie się odtwarzać. Zobacz moje pytanie tutaj: stackoverflow.com/questions/16672568/…
marienke