MediaPlayer.setDataSource(String)不能与本地文件一起使用

如果我使用静态方法MediaPlayer.create(context,id),我可以播放本地mp3,但如果使用非静态方法MediaPlayer.setDataSource(String),则无法播放。 发生什么事情是,当我调用MediaPlayer.prepare()时,出现同步exception:

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

这是我的代码(省略日志):

 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(); 

请注意,我没有得到有关文件未find或任何错误。 文件的全名是test0.mp3,我把它放在Eclipse的/ res / raw /目录下。

我假设我设置的path不正确,但我在网上find的所有例子都使用setDataPath的FileDescriptor版本,而不是setDataPath的String版本。

编辑:如果我使用MediaPlayer.setDataSource(FileDescriptor)方法,并且将这些文件放在Eclipse中的/ assets /目录下,我也可以播放本地mp3。

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

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

替代解决scheme#1:使用Resources.getIdentifier()

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

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

getIdentifier()把你的资源名称(test0),资源types(raw),你的包名称,并返回实际的资源ID。

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

我testing了这个代码,它的工作原理。


更新#1:

替代解决scheme#2:使用Uri.parse()

我也testing了这个代码,它也可以。 将资源path作为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(); 

更新#2:答案是否定的

关于setDataSource(String)调用

看到你的评论后,看起来你确实想要setDataSource(string)用于你的目的。 我不明白为什么。 但是,我认为,出于某种原因,您正试图避免使用“上下文”。 如果不是这种情况,那么上述两个解决scheme应该完美地为你工作,或者如果你想避免上下文,恐怕这是不可能的与特征setDataSource(String)调用的函数。 原因如下,

MediaPlayer setDataSource()函数具有以下这些选项,您只对setDataSource(String)感兴趣,

setDataSource函数

setDataSource(String)在内部调用setDataSource(String path,String [] keys,String [] values)函数。 如果你可以检查它的来源 ,

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

如果你检查setDataSource(String path,String [] keys,String [] values)的代码,你会看到下面的条件过滤path基于它的scheme,特别是如果它是调用setDataSource(FileDescriptor)的“文件”如果scheme是非“文件”,它调用本地JNI媒体function。

 { 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."); } } 

在上面的代码中,你的资源文件的URIscheme不会为null(android.resource://), setDataSource(String)会尝试使用本地JNI函数nativeSetDataSource(),认为你的path是http / https / rtsp,调用将失败,而不会抛出任何exception。 这就是为什么你的调用setDataSource(string)转义没有一个例外,并准备()调用以下例外。

 Prepare failed.: status=0x1 

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

另一方面,检查setDataSource(Context context,Uri uri)使用的setDataSource(Context context,Uri uri,Map headers),它使用来自上下文的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文件。 相反,如果你想用string来播放你的资源文件,你可以使用上面给出的getIdentifier()或者更新#1中给出的setDataSource(context,uri)的MediaPlayer.create()静态函数。

请参阅完整的源代码以获得更多的理解: Android MediaPlayer


更新#3:

openFrameworks setDataSource(String):

正如我在下面的评论中提到的,openFrameworks使用Android MediaPlayer代码asis。 如果您可以参考第4行,

 import android.media.MediaPlayer; 

和26号,27号,28号和218号线

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

因此,如果您尝试使用openFrameworks将ardroid.resource // + this.getPackageName()+“raw / test0”传递给setDataSource(),您将仍然得到与我在Update#2中解释的相同的exception。 话虽如此,我只是试图find我的运气search谷歌,以确定我在说什么,并find这个openFrameworks的核心开发人员arturo之一说这个openFrameworks论坛链接 ,

不知道mediaPlayer是如何工作的,但res / raw或bin / data中的所有内容都被复制到/sdcard/cc.openframeworks.packagename

根据该评论,你可以尝试在setDataSource()中使用复制的path。 在MediaPlayer的setDataSource(String)上使用资源文件是不可能的,因为它不能接受资源文件path。 请注意,我说的“资源文件path”开始于android.resourcescheme,它实际上是一个jar文件位置(在你的apk中),而不是一个物理位置。 本地文件将与setDataSource(String)一起使用,该文件以scheme file://开头。

为了让你清楚地知道你正在用资源文件做什么,请尝试执行下面的代码,并在logcat中查看结果,

  try{ 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 

即向您显示您尝试访问的资源文件实际上不是物理path中的文件,而是使用setDataSource(String)方法无法访问的jar位置(在apk中)。 (尝试使用7zip来提取你的apk文件,你会看到它的res / raw / test0)。

希望有所帮助。

PS:我知道它有点冗长的答案,但我希望能详细解释它。 如果可以帮助其他人的话,将其他解决scheme留在最前面。

就像android 文档说的那样

任意文件以原始格式保存。 要使用原始InputStream打开这些资源,请使用资源ID(即R.raw.filename)调用Resources.openRawResource()。

但是,如果您需要访问原始文件名和文件层次结构,则可以考虑在assets /目录(而不是res / raw /)中保存一些资源。 assets /中的文件没有给定资源ID,所以只能使用AssetManager读取它们。

因此,在将其设置到媒体播放器之前,您需要使用InputStream来读取audio文件。

我build议你把audio文件放在资产文件夹中,就像你说的那样

🙂

处理原始资源时,应该依赖以下构造函数:

公共静态MediaPlayer 创build (上下文上下文,int resid)

为给定资源ID创buildMediaPlayer的便捷方法。 在成功的时候,prepare()将会被调用,不能再被调用。

代码看起来像

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

不要忘记在完成之后调用mediaPlayer.release()

( 来源 )

下面的代码为我工作,我认为这段代码将帮助你

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

从这里 ,

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

尝试,

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

希望能帮助到你!