Потокове передавання аудіо з URL-адреси в Android за допомогою MediaPlayer?


86

Я намагався передавати mp3-файли через http за допомогою Android, вбудованого в MediaPlayer-клас. Документація підказує мені, що це має бути настільки просто, як:

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

Однак я неодноразово отримую наступне. Я також пробував різні URL-адреси. Будь ласка, не кажіть мені, що потокове передавання не працює на 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)

Будь-яка допомога дуже вдячна, дякую S


Кілька питань: (1) яку версію SDK ви використовуєте? (2) На яких пристроях ви тестуєте? Це чудово працює на SDK 2.0.1, тестування на Droid.
Роман Нурік

Привіт Романе, дякую, що знайшов час. Я пробую це проти 1.6, і я використовую HTC Hero. Я спробую це на 2.01 з огляду на ваші коментарі, але це було б смішним результатом, якби це працювало лише на пристроях 2.x і новіших версій із коробки.
Pandalover

Просто спробував на емуляторі 2.01. На жаль, не працює. Мені заінтриговано спробувати це проти реального пристрою 1.6 та справжнього пристрою 2.01. Я беру участь у тестуванні Google 4-го. Можливо, мені доведеться почекати до того часу. Я волів би, щоб не довелося, хоча.
Pandalover

Я не підозрюю, що 2.0 проти 2.0.1 буде мати якесь значення, але емулятор проти живого пристрою може змінити ситуацію. Я здивований, що це не спрацювало на Героя. Я розгляну це і подивлюсь, чи можу я отримати кращу відповідь. Окрім того, як перевірку стану здоров'я, ви повинні переконатися, що ви просили дозвіл на ІНТЕРНЕТ у маніфесті.
Роман Нурік

Ей, просто поза дискусією, у мене питання. Якщо я використовую mp.setDataSource (URL_OF_FILE); Нам не потрібно зберігати будь-який файл для потокового аудіо. Чи не так? Таким чином, це найкращий спосіб передавати аудіо з будь-якого місця. Будь-які ідеї?
Богемський

Відповіді:


78

простий медіаплеєр із прикладом потокового передавання. Для частини xml вам потрібна одна кнопка з ідентифікатором button1 та два зображення у вашій папці, що малюється, з назвою button_pause та button_play, і, будь ласка, не забудьте додати дозвіл на доступ до Інтернету у своєму маніфесті.

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

З цим є невелика проблема. Якщо я заблокую телефон, коли відтворюється MediaPlayer, моя програма аварійно завершує роботу, коли я її розблокую.
CiaranC94

3
Я щойно спробував прокоментувати рядок "mediaPlayer.release ()" в onPause (), і тепер моя програма не розбивається після розблокування.
CiaranC94

@PiyushMishra цю функцію приймає консоль розробника? Оскільки мою програму відхиляють b'coz, я використав версію Vitamio 4.x
Pallavi

35

Android MediaPlayer не підтримує потокову передачу MP3 у форматі до 2.2. У старих версіях ОС, схоже, лише трансляція 3GP відбувається спочатку. Ви можете спробувати код pocketjourney, хоча він старий (тут є нова версія ), і я мав проблеми з тим, щоб зробити його липким - він заїкався, коли він поповнював буфер.

Додаток NPR News для Android є відкритим кодом і використовує локальний проксі-сервер для обробки потокової передачі MP3 у версіях ОС до 2.2. Ви можете побачити відповідний код у рядках 199-216 (r94) тут: http://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/ PlaybackService.java?r=7cf2352b5c3c0fbcdc18a5a8c67d836577e7e8e3

А це клас StreamProxy: http://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/StreamProxy.java?r=e4984187f45c39a54ea6c88f71197762dbe10e72

Додаток NPR також усе ще отримує "помилку (-38, 0)" іноді під час потокової передачі. Це може бути проблема потоків або проблема із зміною мережі. Перевірте відстежувач проблем на наявність оновлень .


Ви в цьому абсолютно впевнені? Я розумію, що це було пов’язано з типом міму. Чи можете ви перевірити, чи працює t з правильним типом MIME до 2.1? Зараз я працюю над чимось іншим і якийсь час не міг перевірити.
Pandalover

1
Згідно з примітками до випуску 2.2 ( developer.android.com/sdk/android-2.2-highlights.html ), він включає "Нову медіафрейм (Stagefright), яка підтримує локальне відтворення файлів та прогресивне потокове передавання HTTP". Під час мого тестування я не зміг отримати пристрій 2.1 для прямої трансляції з сервера shoutcast. Я вважаю, що проблема полягає в тому, що сервери shoutcast повертають протокол ICY / 1.1, а не HTTP / 1.1, і медіаплеєр відмовляється від цього, оскільки він не знає, як реагувати на цей вміст.
jwadsack

@jwadsack що, якщо аудіофайли потрібно завантажити один раз, і тоді користувач також може відтворювати файли в автономному режимі?
Devendra Singh

@DevendraSingh Я не знаю, чи можете ви зберегти файл під час потокового передавання за допомогою поточної реалізації медіаплеєра (цій відповіді майже п’ять років, і багато що змінилося з 2.2). Якщо нічого іншого, ви можете створити проксі-сервер за цим прикладом і записати файл у сховище, поки пропускаєте його через проксі-сервер.
jwadsack

11

Я припускаю, що ви намагаєтесь відтворити .pls безпосередньо або щось подібне.

спробуйте це:

1: код

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

2: файл .pls

Ця URL-адреса надіслана BBC лише як приклад. Це був файл .pls, який я завантажив у Linux

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

а потім я відкрив за допомогою vim (використовуйте ваш улюблений редактор;), і я бачив справжні URL-адреси всередині цього файлу. На жаль, не всі .pls - це звичайний текст.

Я читав, що 1.6 не підтримує потокове передавання mp3 через http, але я щойно перевірив код obove на Android 1.6 та 2.2 і не мав жодних проблем.

Щасти!


7
Пам'ятайте, що якщо ви хочете транслювати музику, ви повинні використовувати mediaplayer.prepareAsync, а не mediaplayer.prepare. Тому ви не можете використовувати mediaplayer.create (), оскільки будь-яка з функцій .create () переводить ваш об'єкт Media Player безпосередньо у стан підготовки, з якого ви не можете викликати pripremAsync, який вам потрібно зробити для потокової передачі. developer.android.com/reference/android/media/MediaPlayer.html
marienke

4

Використовуйте

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

2

У мене була така ж помилка, як і у вас, і виявилося, що в коді нічого не було. Проблема полягала в тому, що веб-сервер надсилав неправильний заголовок Content-Type.

Спробуйте wireshark або щось подібне, щоб побачити, який тип вмісту надсилає веб-сервер.


для mp3-файлів це має бути Content-Type: audio / mpeg Це вирішило мою проблему :)
doep

1
Як ви передали цей файл?
Науман Халід

2

Переглядаю мої проекти:

  1. https://github.com/master255/ImmortalPlayer підтримка http / FTP, Один потік для читання, надсилання та збереження в кеш-даних. Найпростіший спосіб і найшвидша робота. Складна логіка - найкращий спосіб!
  2. https://github.com/master255/VideoViewCache Простий перегляд відео з кешем. Два потоки для відтворення та збереження даних. Погана логіка, але якщо вам потрібно, використовуйте це.

1

Немає виклику mp.start за допомогою OnPreparedListener, щоб уникнути нульового стану в журналі.


Я все ще отримую рядок журналу 05-22 20: 26: 13.625: E / MediaPlayer (23818): зупинити виклик у стані 0, хоча я запускаю свій Media Player у підготовленій функції (). У мене також є функція onError, де я скидаю об'єкт Media Player. Для початку відтворення мого потоку все ще потрібно 2 хвилини. Дивіться моє запитання тут: stackoverflow.com/questions/16672568 / ...
marienke
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.