使用MediaPlayer在Android中流式传输来自URL的音频?


86

我一直在尝试使用Android内置的MediaPlayer类通过HTTP流mp3。该文档向我建议,这应该很简单:

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)

任何帮助,非常感谢,谢谢


几个问题:(1)您使用的是哪个SDK版本?(2)您要在哪些设备上进行测试?在SDK 2.0.1上运行正常,在Droid上进行测试。
Roman Nurik

嗨,罗马,谢谢您抽出宝贵的时间。我正在针对1.6进行尝试,并且正在使用HTC Hero。根据您的评论,我将在2.01上进行尝试,但如果仅在2.x及更高版本的设备上可用,这将是一个荒谬的结果。
Pandalover

刚刚在2.01模拟器上尝试过。不幸的是没有工作。我很想针对真正的1.6设备和真正的2.01设备尝试此操作。我正在4号参加Google测试。也许我要等到那时。我不想,虽然不必。
Pandalover

我不认为2.0与2.0.1会有任何区别,但是模拟器与实时设备可能会有所不同。我很惊讶这对英雄没有用。我会调查一下,看看是否可以获得更好的答案。哦,同样,作为健全性检查,您应确保已在清单中请求了INTERNET权限。
Roman Nurik

嘿,别讨论了,我有一个问题。如果我使用mp.setDataSource(URL_OF_FILE); 我们不需要为音频流保存任何文件。是不是 因此,这是从任何位置流式传输音频的最佳方法。有任何想法吗?
波西米亚风2009年

Answers:


78

简单的带媒体流示例的Media Player。对于xml部分,您需要在ID的一个按钮和可绘制文件夹中的两个图像(名称分别为button_pause和button_play),并且请不要忘记在清单中添加Internet权限。

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 '16

3
我刚刚尝试注释掉onPause()中的“ mediaPlayer.release()”行,现在我的应用程序在解锁时不会崩溃。
CiaranC94 '16

@PiyushMishra这些功能是否被开发者控制台接受?由于我的应用被拒绝了,所以我使用了Vitamio 4.x版本
Pallavi

35

Android MediaPlayer直到2.2才原生支持MP3流。在较旧版本的OS中,它似乎只能以本地方式流式传输3GP。您可以尝试Pocketjourney代码,尽管它很旧(这里有一个新版本),但是我很难使它保持粘性-每当重新填充缓冲区时,它都会结结巴巴。

适用于Android的NPR新闻应用程序是开源的,并使用本地代理服务器来处理2.2之前版本的OS中的MP3流。您可以在以下行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=e4984187f45c39a54ea6c88f7119776262dbe10e72

NPR应用有时在流式传输时仍会出现“错误(-38,0)”。这可能是线程问题或网络更改问题。检查问题跟踪器是否有更新


您对此绝对确定吗?我的理解是,这与mime类型有关。您能否检查t是否适用于2.1之前的正确MIME类型?我现在正在做其他事情,暂时无法检查。
Pandalover

1
根据2.2发行说明(developer.android.com/sdk/android-2.2-highlights.html),它包含一个“支持本地文件回放和HTTP渐进式流传输的新媒体框架(Stagefright)”。在所有测试中,我无法直接从shoutcast服务器获得2.1设备的流。我认为问题在于,广播服务器返回的协议是ICY / 1.1而不是HTTP / 1.1,并且媒体播放器对此不满意,因为它不知道如何响应该内容。
jwadsa​​ck,2010年

@jwadsa​​ck如果应该一次下载音频文件,然后用户也可以脱机播放文件怎么办?
Devendra Singh

@DevendraSingh我不知道您是否可以在使用当前的媒体播放器实现流式传输文件时保存文件(此答案已有近五年的历史,自2.2版以来发生了很多更改)。如果没有其他问题,您可以按照此示例构建代理,然后在通过代理传递文件时将文件写入存储。
jwadsa​​ck 2015年

11

我猜您正在尝试直接播放.pls或类似内容。

试试看:

1:代码

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

2:.pls文件

作为示例,此URL来自BBC。这是我在Linux上下载的.pls文件

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

然后我用vim打开(使用您最喜欢的编辑器;),我已经看到了该文件中的真实URL。不幸的是,并非所有.pls都是纯文本格式。

我读过1.6将不支持通过HTTP流mp3,但是,我刚刚用android 1.6和2.2测试了obove代码,没有任何问题。

祝好运!


7
请记住,如果要流式传输音乐,则必须使用mediaplayer.prepareAsync而不是mediaplayer.prepare。因此,您不能使用mediaplayer.create(),因为任何.create()函数都会将Media Player对象直接带入Prepared状态,从中不能调用prepareAsync,而必须通过它进行流传输。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或类似的方法来查看Web服务器正在发送的内容类型。


对于mp3文件,它应该是Content-Type:audio / mpeg解决了我的问题:)
doep 2010年

1
您是如何流式传输该文件的?
Nauman Khalid 2014年


1

请勿使用OnPreparedListener调用mp.start来避免日志中的零状态。


我仍然得到日志行05-22 20:26:13.625:E / MediaPlayer(23818):即使我在prepare()函数中启动了媒体播放器,也要在状态0下停止调用。我还有一个onError函数,可在其中重置Media Player对象。我的信息流仍需要2分钟才能开始播放。看到这里,我的问题:stackoverflow.com/questions/16672568/...
marienke
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.