相关文章推荐
刚毅的斑马  ·  MediaPlayer.setDataSou ...·  1 年前    · 

MediaPlayer.setDataSource(String)对本地文件不起作用

37 人关注

如果我使用静态方法MediaPlayer.create(context, id),我就能播放本地MP3,但如果我使用非静态方法MediaPlayer.setDataSource(String),它就不工作了。 发生的情况是,当我调用MediaPlayer.prepare()时,我得到一个同步异常。

prepare exceptionjava.io.IOException: Prepare failed.: status=0x1

Here is my code (omitted logging):

String filename = "android.resource://" + this.getPackageName() + "/raw/test0";
mp = new MediaPlayer();
try { mp.setDataSource(filename); } catch (Exception e) {}
try { mp.prepare(); } catch (Exception e) {}
mp.start();

请注意,我没有收到关于找不到文件或任何东西的错误。 文件的全名是test0.mp3,我把它放在Eclipse的/res/raw/目录下。

我假设我的路径设置不正确,但我在网上找到的所有例子都是使用FileDescriptor版本的setDataPath,而不是字符串版本的setDataPath。

编辑:如果我使用MediaPlayer.setDataSource(FileDescriptor)方法并将文件放在Eclipse的/assets/目录中,我也能播放本地的MP3。

编辑#2:我接受了这个答案,即这是不可能的,但后来发现我使用的库(openFrameworks)实际上确实使用了String方法来加载文件。 请看这里。

https://github.com/openframeworks/openFrameworks/blob/master/addons/ofxAndroid/ofAndroidLib/src/cc/openframeworks/OFAndroidSoundPlayer.java

4 个评论
我不知道这是否会有帮助 ,所以看看这个帖子吧。 [播放MP3文件]( stackoverflow.com/questions/5466882/... )
@LamaSonmez 那段代码使用的是MediaPlayer.create(context, id)方法。 我正试图让使用字符串文件路径的版本工作。 developer.android.com/reference/android/media/...
设置一个countdowntimer并调用setDataSource,可能是在5秒之后,不确定可能是资源尚未加载,所以让它冷却下来,然后尝试访问资源......也可以添加onerrorlistener并给它4次左右的尝试来重新播放......只是一个尝试。
也许这个答案会帮助在这里寻找的人们。 stackoverflow.com/a/53390067/826946
java
android
android-mediaplayer
user755921
发布于 2015-10-13
7 个回答
Naren Neelamegam
Naren Neelamegam
发布于 2022-10-23
已采纳
0 人赞同

Alternative Solution #1: Using Resources.getIdentifier()

为什么不使用getResources().getIdentifier()来获得资源的ID,并像往常一样使用静态MediaPlayer.create()?

public int getIdentifier (String name, String defType, String defPackage)

getIdentifier()接受你的资源名称(test0)、资源类型(raw)、你的软件包名称并返回实际的资源ID。

 MediaPlayer mp;
 //String filename = "android.resource://" + this.getPackageName() + "/raw/test0";
 mp=MediaPlayer.create(getApplicationContext(), getResources().getIdentifier("test0","raw",getPackageName()));
 mp.start();

我已经测试了这个代码,它是有效的。 Update #1:

Alternative Solution #2: Using Uri.parse()

我也测试了这段代码,它也能工作。将你的资源路径作为URI传给setDataSource()。我刚刚对你的代码做了这个改动,使其得以工作。

String filename = "android.resource://" + this.getPackageName() + "/raw/test0";
mp = new MediaPlayer();
try { mp.setDataSource(this,Uri.parse(filename)); } catch (Exception e) {}
try { mp.prepare(); } catch (Exception e) {}
mp.start();
Update #2: Answer is NO

About setDataSource(String) call

看到你的评论后,看起来你正是希望setDataSource(string)能用于你的目的。我不明白为什么。但是,我猜想的是,出于某种原因,你想避免使用 "上下文"。如果不是这样,那么上面的两个解决方案对你来说应该是完美的,如果你想避免使用上下文,恐怕用签名的函数setDataSource(String)调用是不可能的。原因如下。

MediaPlayer setDataSource()函数有以下几个选项,其中你只对setDataSource(String)感兴趣。

setDataSource(String)内部调用setDataSource(String path, String[] keys, String[] values) 函数。如果你能检查其source,

public void setDataSource(String path)
            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
        setDataSource(path, null, null);

如果你检查setDataSource(String path, String[] keys, String[] values)代码,你会看到下面的条件,根据其方案过滤路径,特别是如果它是 "文件 "方案,它调用setDataSource(FileDescriptor),或者如果方案不是 "文件",它调用本地JNI媒体函数。

final Uri uri = Uri.parse(path); final String scheme = uri.getScheme(); if ("file".equals(scheme)) { path = uri.getPath(); } else if (scheme != null) { // handle non-file sources nativeSetDataSource( MediaHTTPService.createHttpServiceBinderIfNecessary(path), path, keys, values); return; final File file = new File(path); if (file.exists()) { FileInputStream is = new FileInputStream(file); FileDescriptor fd = is.getFD(); setDataSource(fd); is.close(); } else { throw new IOException("setDataSource failed.");

在上面的代码中,你的资源文件URI方案将不会是空的(android.resource://),并且setDataSource(String)将尝试使用本地JNI函数nativeSetDataSource(),认为你的路径是http/https/rtsp,显然这个调用也会失败,不会抛出任何异常。这就是为什么你对setDataSource(String)的调用在没有异常的情况下逃脱了,而在调用prepare()时出现了如下异常。

Prepare failed.: status=0x1

所以setDataSource(String)覆盖不能处理你的资源文件。你需要选择另一个覆盖来处理。

在另一边,检查setDataSource(Context context, Uri uri, Map headers),这是由setDataSource(Context context, Uri uri)使用的,它使用AssetFileDescriptor,来自你的上下文的ContentResolver和openAssetFileDescriptor来打开URI,获得成功,因为openAssetFileDescriptor()可以打开你的资源文件,最后,结果fd被用来调用setDataSource(FileDescriptor) 覆盖。

    AssetFileDescriptor fd = null;
    try {
        ContentResolver resolver = context.getContentResolver();
        fd = resolver.openAssetFileDescriptor(uri, "r");
        //  :
        //  :
        //  :
        if (fd.getDeclaredLength() < 0) {
                setDataSource(fd.getFileDescriptor());
            } else {
                setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getDeclaredLength());

总而言之,你不能使用setDataSource(String)覆盖来使用你的资源mp3文件。相反,如果你想使用字符串来播放你的资源文件,你可以使用MediaPlayer.create()静态函数和上面给出的getIdentifier()或setDataSource(context,uri),如Update#1中给出的。

请参考完整的源代码,以便在此获得更多理解。安卓媒体播放器 Update #3:

openFrameworks setDataSource(String):

正如我在下面的评论中提到的,openFrameworks使用android MediaPlayer代码。如果你能参考第4行。

import android.media.MediaPlayer;

和线号:26、27、28和218

        player = new MediaPlayer();       //26
        player.setDataSource(fileName);   //27
        player.prepare();                 //28
        private MediaPlayer player;       //218

因此,如果你试图通过ardroid.resource//+ this.getPackageName() + "raw/test0"使用openFrameworks来设置DataSource(),你仍然会得到与我在Update#2中解释的相同的异常。说到这里,我试着在谷歌上搜索了一下,以进一步确定我所说的内容,结果发现了以下内容openFrameworks论坛链接 where one of the openFrameworks的核心开发人员Arturo说。

我不知道媒体播放器是如何工作的,但在res/raw 或bin/data中的内容都会被复制到/sdcard/cc.openframeworks.packagename中。

基于这个评论,你可以尝试在setDataSource()中使用复制的路径。在MediaPlayer的setDataSource(String)中使用资源文件是不可能的,因为它不能接受资源文件路径。请注意,我说的 "资源文件路径 "是以方案开头的android.resource//这实际上是一个jar位置(在你的apk内),而不是一个物理位置。本地文件将与setDataSource(String)一起工作,它以方案开始file://.

为了让你清楚地了解你要用资源文件做什么,试着执行下面这段代码,看看logcat中的结果。

Log.d("RESURI", this.getClass().getClassLoader().getResource("res/raw/test0").toURI().toString()); catch(Exception e) {

你会得到这样的结果。

jar:file:/data/app/<packagename>/<apkname>.apk!/res/raw/test0

这是为了告诉你,你试图访问的资源文件实际上不是物理路径上的文件,而是一个jar位置(在apk内),你不能用setDataSource(String)方法访问。(试试用7zip解压你的apk文件,你会看到其中的res/raw/test0)。

希望这有帮助。

PS:我知道这个答案有点长,但我希望这能详细地解释它。如果能帮助别人的话,就把其他的解决方案留在上面。

我一定是把我的问题措辞不当 -- 我问的是MediaPlayer.setDataSource(String)的具体问题。 我对该方法的其他版本不感兴趣。
@racarate,你是说update#1中的解决方案对你来说也是无效的吗。那是使用setDataSource(context,uri)。正如我在答案中提到的,我刚刚把你的代码从mp.setDataSource(filename)改为mp.setDataSource(this, Uri.parse(filename)),它在你的资源文件中完美运行。
请查看更新后的答案(Update #2)。setDataSource(String)覆盖不能按原样使用。你可以使用setDataSource(context,uri)来代替你的字符串资源路径或使用MediaPlayer.create()的getIdentifier()。
好吧,那么答案是否定的。 谢谢你提供的细节,如果你把答案的前两部分去掉,编辑2号成为官方的 "这是不可能的 "答案,也许这将更有帮助。 我不得不说,官方文档对这个问题一点都不清楚。
我刚刚意识到,openFrameworks似乎使用这种方法来加载本地文件。 我不能取消赏金,但我确实将此标记为未回答。
DeKoServidoni
DeKoServidoni
发布于 2022-10-23
0 人赞同

像安卓手机一样 文件

任意文件以其原始形式保存。要打开这些资源 要用一个原始的InputStream打开这些资源,请调用Resources.openRawResource(),并注明 资源ID,也就是R.raw.filename。

然而,如果你需要访问原始文件名和文件层次结构。 你可以考虑将一些资源保存在assets/目录下 (而不是res/raw/)。assets/目录中的文件没有被赋予一个资源ID。 所以你只能用AssetManager读取它们。

所以你需要使用InputStream来读取音频文件,然后再把它设置到媒体播放器。

I suggest you to put the audio file in the assets folder like you说 you played

我很困惑,InputStream是如何发挥作用的? 我正试图让接受文件路径字符串的版本工作。
也许这个答案可以帮助你 :) stackoverflow.com/questions/15098657/...
Ravi Vaghela
Ravi Vaghela
发布于 2022-10-23
0 人赞同

Below code working for me i think this code will help you

    player = MediaPlayer.create(this,R.raw.test0);
    player.setLooping(true); // Set looping
    player.setVolume(100,100);
    player.start();
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
player.stop();
player.release();
    
这个例子使用MediaPlayer.create(context, id)方法,我的问题是关于MediaPlayer.setDataSource(String)方法。
stackoverflow.com/questions/18977809/... 看到你不能对本地文件夹文件使用MadiaPlayer.setDataSource(String)。
@RaviVGHL,你评论中的链接并没有说setDataSource(String)不能用于本地文件。它说MediaPlayer.create()内部使用setDataSource()和Prepare()方法。你的答案是一个有效的代码,但你的评论的说法是不正确的,而且racarate问的问题也不是关于使用R.raw.test0,racarate想用字符串作为参数(而不是id)来播放声音以达到他的目的。
Sebastiano
Sebastiano
发布于 2022-10-23
0 人赞同

当处理一个原始资源时,你应该依靠以下构造函数。

公共的静态媒体播放器 创建 (Context context, int resid)

Convenience method to 创建 a MediaPlayer for a given resource id. On success, prepare() will already have been called and must not be called again.

The code looks like

mediaPlayer = MediaPlayer.create(this, R.raw.test0);
mediaPlayer.start();

当你完成后,别忘了打电话给mediaPlayer.release()

(source)

是的,这个方法对我来说确实有效,但我正试图让接受字符串的版本发挥作用。 developer.android.com/reference/android/media/...
Akhunzaada
Akhunzaada
发布于 2022-10-23
0 人赞同

From here ,

当path指向一个本地文件时,该文件实际上可能是由调用应用程序以外的一个进程打开的。这意味着路径名应该是一个绝对路径(因为任何其他进程都是以未指定的当前工作目录运行的),并且路径名应该引用一个世界可读的文件。 作为一种选择,应用程序可以首先打开文件进行读取,然后使用文件描述符形式setDataSource(FileDescriptor)。

String filePath = "path/file.mp3";
File file = new File(filePath);
FileInputStream inputStream = new FileInputStream(file);
mediaPlayer.setDataSource(inputStream.getFD());
inputStream.close();

Hope it Helps!

是的,这个方法对我有用,但我特别问的是MediaPlayer.setDataSource(String)方法。
Dmitry
Dmitry
发布于 2022-10-23
0 人赞同

你必须使用 setDataSource(@NonNull Context context, @NonNull Uri uri) 而不是 setDataSource(String path)

由于你想解析资源的内部应用资源,你必须提供 Context ,它将被用来解析这个东西

另外,如果你看一下这两种方法的内部,你会发现它们使用不同的策略来寻找结果资源。

Yor PG
Yor PG
发布于 2022-10-23
0 人赞同

Try this:

  if(mediaPlayer != null ){
                        if (mediaPlayer.isPlaying()){mediaPlayer.stop();}
                        mediaPlayer.reset(); //this line is important!
                        String path = File.separator + "sdcard" + File.separator + utilsFields.repoDirRoot + File.separator + media.mp4;
                        try {
                            mediaPlayer.setDataSource(path);
                        }catch (Exception ignored){}
                        try {
                            mediaPlayer.prepare();
                        }catch (Exception ignored){}