问题描述
如果我使用静态方法MediaPlayer.create(context,id),我可以播放本地mp3,但如果我使用非静态方法MediaPlayer.setDataSource(String),则无法正常播放.发生的是,当我调用MediaPlayer.prepare()时,我收到了一个同步异常:
I am able to play a local mp3 if I use the static method MediaPlayer.create(context, id) but it's not working if I use the non-static method MediaPlayer.setDataSource(String). What's happening is that I am getting a synchronous exception when I call 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/目录中.
Note that I am not getting an errors about file not found or anything. The full name of the file is test0.mp3 and I place it in the /res/raw/ directory in Eclipse.
我假设我设置的路径不正确,但是我在网上找到的所有示例都使用setDataPath的FileDescriptor版本而不是setDataPath的String版本.
I assume that I am setting the path incorrectly but all the examples I find online use the FileDescriptor version of setDataPath instead of the String version of setDataPath.
如果我使用MediaPlayer.setDataSource(FileDescriptor)方法并将文件放在Eclipse的/assets/目录中,我也可以播放本地mp3.
I am also able to play a local mp3 if I use the method MediaPlayer.setDataSource(FileDescriptor) and place the files in the /assets/ directory in Eclipse.
编辑#2:我接受了不可能的答案,但随后意识到我正在使用的库(openFrameworks)实际上确实使用String方法来加载文件.看到这里:
EDIT #2: I accepted the answer that this is not possible, but then realized that the library I am using (openFrameworks) actually does use the String method to load a file. See here:
推荐答案
替代解决方案1:使用Resources.getIdentifier()
为什么不使用getResources().getIdentifier()获取资源ID并照常使用静态MediaPlayer.create()?
Alternative Solution #1: Using Resources.getIdentifier()
Why not use getResources().getIdentifier() to get id of the resource and use the static MediaPlayer.create() as usual?
public int getIdentifier (String name, String defType, String defPackage)
获取您的资源名称(test0),资源类型(raw),您的包名称,并返回实际的资源ID.
getIdentifier() takes your resource name (test0), resource type(raw), your package name and returns the actual resource id.
MediaPlayer mp;
//String filename = "android.resource://" + this.getPackageName() + "/raw/test0";
mp=MediaPlayer.create(getApplicationContext(), getResources().getIdentifier("test0","raw",getPackageName()));
mp.start();
我已经测试了此代码,并且可以正常工作.
更新#1:
I've tested this code and it works.
Update #1:
我也测试了此代码,它也可以工作.将您的资源路径作为URI传递给setDataSource().我刚刚对您的代码进行了更改,以使其正常工作.
I've tested this code as well and it works too. Pass your resource path as URI to setDataSource(). I just made that change to your code to get it work.
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)调用的函数是不可能的.原因如下,
After seeing your comment, it looks like you exactly want setDataSource(string) to be used for your purpose. I don't understand why. But, what I assume is, for some reason you are trying to avoid using "context". If that is not the case then the above two solutions should work perfectly for you or if you are trying to avoid context, I'm afraid that is not possible with the function with signature setDataSource(String) call. The reason is as below,
MediaPlayer setDataSource()函数具有以下这些选项,您仅对setDataSource(String)感兴趣,
MediaPlayer setDataSource() function has these below options out of which you are only interested in setDataSource(String),
setDataSource(String)在内部调用setDataSource(String路径,String []键,String []值)函数.如果您可以查看其源,
setDataSource(String) internally calls setDataSource(String path, String[] keys, String[] values) function. If you can check its source,
public void setDataSource(String path)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
setDataSource(path, null, null);
}
,如果您检查setDataSource(String path,String []键,String []值)代码,您将看到以下条件根据其方案过滤路径,特别是如果它是文件"方案,它将调用setDataSource( FileDescriptor),或者如果scheme不是"file",那么它将调用本机JNI媒体功能.
and if you check setDataSource(String path, String[] keys, String[] values) code, you will see the below condition filtering the path based on its scheme, particularly if it is "file" scheme it calls setDataSource(FileDescriptor) or if scheme is non "file", it calls native JNI media 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.");
}
}
在上面的代码中,您的资源文件URI方案不会为空(android.resource://),并且 setDataSource(String)将尝试使用本机JNI函数nativeSetDataSource(),认为您的路径是http/https/rtsp,显然该调用也会失败,并且不会引发任何异常.这就是为什么您对setDataSource(String)的调用无例外地进行转义,而在发生以下异常的情况下进行prepare()调用的原因.
In the above code, your resource file URI scheme will not be null (android.resource://) and setDataSource(String) will try to use native JNI function nativeSetDataSource() thinking that your path is http/https/rtsp and obviously that call will fail as well without throwing any exception. Thats why your call to setDataSource(String) escapes without an exception and gets to prepare() call with the following exception.
Prepare failed.: status=0x1
因此setDataSource(String)覆盖无法处理您的资源文件.您需要为此选择另一个替代.
So setDataSource(String) override cannot handle your resource file. You need to choose another override for that.
另一方面,检查setDataSource(Context context,Uri uri)使用的setDataSource(Context context,Uri uri,Map headers),它从您的上下文中使用AssetFileDescriptor,ContentResolver和openAssetFileDescriptor打开成功的URI.因为openAssetFileDescriptor()可以打开您的资源文件,最后生成的fd用于调用setDataSource(FileDescriptor)覆盖.
On the other side, check setDataSource(Context context, Uri uri, Map headers) which is used by setDataSource(Context context, Uri uri), it uses AssetFileDescriptor, ContentResolver from your context and openAssetFileDescriptor to open the URI which gets success as openAssetFileDescriptor() can open your resource file and finally the resultant fd is used to call setDataSource(FileDescriptor) override.
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());
}
最后,不能像使用资源mp3文件一样直接使用setDataSource(String)覆盖.相反,如果您想使用字符串来播放资源文件,则可以将MediaPlayer.create()静态函数与上述getIdentifier()或Update#1中提供的setDataSource(context,uri)一起使用.
To conclude, you cannot use setDataSource(String) override as is to use your resource mp3 file. Instead, if you want use string to play your resource file you can use either MediaPlayer.create() static function with getIdentifier() as given above or setDataSource(context,uri) as given in Update#1.
在此处请参考完整的源代码以获得更多理解: Android MediaPlayer
更新#3:
Refer to the complete source code for more understanding here: Android MediaPlayer
Update #3:
正如我在下面的评论中提到的,openFrameworks使用android MediaPlayer代码asis.如果您可以参考第4行,
As I have mentioned in the comments below, openFrameworks uses android MediaPlayer code asis. If you can refer to Line no: 4,
import android.media.MediaPlayer;
和第26、27、28和218行
and Line no: 26, 27, 28 and 218
player = new MediaPlayer(); //26
player.setDataSource(fileName); //27
player.prepare(); //28
private MediaPlayer player; //218
因此,如果您尝试使用openFrameworks将 ardroid.resource//+ this.getPackageName()+"raw/test0" 传递给setDataSource(),您仍然会得到与我相同的异常在更新2中进行了解释.话虽如此,我只是想通过运气搜索Google来再次确定我在说什么,然后发现了这个 openFrameworks论坛链接,其中 openFrameworks核心开发人员arturo 说
So, if you try to pass ardroid.resource//+ this.getPackageName() + "raw/test0" to setDataSource() using openFrameworks, you will still get the same exception as I explained in Update#2. Having said that, I just tried my luck searching Google to double sure what I am saying and found this openFrameworks forum link where one of the openFrameworks core developer arturo says,
基于该评论,您可以尝试在setDataSource()中使用复制的路径.无法在MediaPlayer的setDataSource(String)上使用资源文件,因为它不能接受资源文件路径.请注意,我说的资源文件路径"以方案 android.resource//开头,该方案实际上是一个jar位置(在apk内),而不是物理位置.本地文件将使用以方案 file://开头的setDataSource(String).
Based on that comment, you may try using the copied path in setDataSource(). Using resource file on setDataSource(String) of MediaPlayer is not possible as it cannot accept resource file path. Please note that, I said "resource file path" starts with the scheme android.resource// which is actually a jar location (within your apk), not a physical location. Local file will work with setDataSource(String) which starts with the scheme file://.
为使您清楚地了解要使用资源文件执行的操作,请尝试执行以下代码,并在logcat中查看结果,
To make you clearly understand what you are trying to do with a resource file, try executing this below code and see the result in 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
这是向您显示您尝试访问的资源文件实际上不是物理路径中的文件,而是jar位置(apk内),您无法使用setDataSource(String)方法访问该文件. (尝试使用7zip解压缩apk文件,您将在其中看到res/raw/test0).
that is to show you that the resource file you are trying to access is not actually a file in physical path but a jar location (within apk) which you cannot access using setDataSource(String) method. (Try using 7zip to extract your apk file and you will see the res/raw/test0 in it).
希望有帮助.
PS:我知道它的回答有点冗长,但是我希望这能详细解释它.如果可以帮助其他人,则将替代解决方案放在最前面.
PS: I know its bit lengthy answer, but I hope this explains it in detail. Leaving the alternative solutions in the top if that can help others.
这篇关于MediaPlayer.setDataSource(String)不适用于本地文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!