【问题标题】:Streaming Audio from A URL in Android using MediaPlayer?使用 MediaPlayer 从 Android 中的 URL 流式传输音频?
【发布时间】:2010-12-30 06:18:26
【问题描述】:

我一直在尝试使用 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)

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

【问题讨论】:

  • 几个问题:(1)您使用的是哪个SDK版本? (2) 您在哪些设备上进行测试?这在 SDK 2.0.1 上运行良好,在 Droid 上进行测试。
  • 您好 Roman,感谢您抽出宝贵时间。我正在尝试针对 1.6 进行此操作,并且我正在使用 HTC Hero。我会根据你的 cmets 在 2.01 上试用它,但如果这仅适用于 2.x 及更高版本的开箱即用设备,那将是一个荒谬的结果。
  • 刚刚在 2.01 模拟器上试了一下。不幸的是不起作用。我很想在真正的 1.6 设备和真正的 2.01 设备上尝试这个。我四号在谷歌测试。也许我得等到那时。不过,我宁愿不必这样做。
  • 我不怀疑 2.0 与 2.0.1 会有所不同,但模拟器与实时设备可能会有所不同。我很惊讶这对英雄不起作用。我会调查一下,看看我是否能得到更好的答案。哦,另外,作为一个健全的检查,你应该确保你已经在清单中请求了 INTERNET 权限。
  • 嘿,刚刚结束讨论,我有一个问题。如果我使用 mp.setDataSource(URL_OF_FILE);我们不需要为音频流保存任何文件。不是吗?因此,这是从任何位置流式传输音频的最佳方式。有什么想法吗?

标签: android mp3 android-mediaplayer audio-streaming


【解决方案1】:

带有流媒体示例的简单媒体播放器。对于 xml 部分,您需要一个 ID 为 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 时锁定手机,解锁时我的应用会崩溃。
  • 我刚刚尝试在 onPause() 中注释掉“mediaPlayer.release()”行,现在我的应用在解锁时不会崩溃。跨度>
  • @PiyushMishra 这些功能被开发者控制台接受了吗?由于我的应用被拒绝了,因为我使用了 Vitamio 4.x 版本
【解决方案2】:

Android MediaPlayer 在 2.2 之前不支持原生 MP3 流式传输。在旧版本的操作系统中,它似乎只能原生流式传输 3GP。您可以尝试使用 pocketjourney 代码,虽然它很旧(有一个 new version here)而且我很难让它变得粘稠 - 每当它重新填充缓冲区时它就会结结巴巴。

适用于 Android 的 NPR News 应用程序是开源的,并使用本地代理服务器来处理 2.2 之前版本的操作系统中的 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=e4984187f45c39a54ea6c88f71197762dbe10e72

NPR 应用在流式传输时有时仍会收到“错误 (-38, 0)”。这可能是线程问题或网络更改问题。检查issue tracker for updates

【讨论】:

  • 您对此完全确定吗?我的理解是它与 mime 类型有关。您能否检查一下 t 是否适用于 2.1 之前的正确 MIME 类型?我现在正在处理其他事情,暂时无法检查。
  • 根据 2.2 发行说明 (developer.android.com/sdk/android-2.2-highlights.html),它包括一个“支持本地文件播放和 HTTP 渐进式流媒体的新媒体框架 (Stagefright)”。在我所有的测试中,我都无法让 2.1 设备直接从直播服务器流式传输。我认为问题在于广播服务器返回 ICY/1.1 而不是 HTTP/1.1 的协议,并且媒体播放器会在此问题上绊倒,因为它不知道如何响应该内容。
  • @jwadsa​​ck 如果音频文件应该下载一次并且用户也可以离线播放文件怎么办?
  • @DevendraSingh 我不知道您是否可以在使用当前媒体播放器实现流式传输文件时保存文件(这个答案已经将近五年了,自 2.2 以来发生了很多变化)。如果不出意外,您可以按照此示例构建代理,并在通过代理传递文件时将文件写入存储。
【解决方案3】:

我猜你正在尝试直接播放 .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 测试了上述代码,没有任何问题。

祝你好运!

【讨论】:

  • 请记住,如果您想流式传输音乐,您必须使用 mediaplayer.prepareAsync 而不是 mediaplayer.prepare。因此,您不能使用 mediaplayer.create(),因为任何 .create() 函数都会将您的媒体播放器对象直接带入 Prepared 状态,您不能从中调用 prepareAsync,而您必须这样做才能进行流式传输。 developer.android.com/reference/android/media/MediaPlayer.html
【解决方案4】:

使用

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

【讨论】:

    【解决方案5】:

    我遇到了和你一样的错误,结果证明代码没有问题。问题是网络服务器发送了错误的 Content-Type 标头。

    尝试使用 wireshark 或类似的东西来查看网络服务器发送的内容类型。

    【讨论】:

    • 对于 mp3 文件,它应该是 Content-Type: audio/mpeg 这解决了我的问题 :)
    • 你是如何流式传输该文件的?
    【解决方案6】:

    查看我的项目:

    1. https://github.com/master255/ImmortalPlayer http/FTP 支持,一个线程读取、发送和保存缓存数据。最简单的方法和最快的工作。复杂的逻辑 - 最好的方法!
    2. https://github.com/master255/VideoViewCache 带缓存的简单视频视图。两个线程用于播放和保存数据。逻辑不好,但如果需要,请使用它。

    【讨论】:

      【解决方案7】:

      不使用 OnPreparedListener 调用 mp.start 以避免日志中的零状态..

      【讨论】:

      • 我仍然得到日志行 05-22 20:26:13.625: E/MediaPlayer(23818): stop 在状态 0 中调用,即使我在prepared() 函数中启动我的媒体播放器。我还有一个 onError 函数,用于重置媒体播放器对象。我的流开始播放仍需要 2 分钟。在这里查看我的问题:stackoverflow.com/questions/16672568/…
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-05
      • 1970-01-01
      • 2017-02-09
      • 1970-01-01
      • 1970-01-01
      • 2010-12-11
      • 2014-05-05
      相关资源
      最近更新 更多