Java MediaPlayer.setDataSource(String) 不适用于本地文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33086417/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
MediaPlayer.setDataSource(String) not working with local files
提问by
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():
如果我使用静态方法 MediaPlayer.create(context, id),我可以播放本地 mp3,但如果我使用非静态方法 MediaPlayer.setDataSource(String),它就不起作用。发生的情况是,当我调用 MediaPlayer.prepare() 时出现同步异常:
prepare exceptionjava.io.IOException: Prepare failed.: status=0x1
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();
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.
请注意,我没有收到有关找不到文件或任何内容的错误。该文件的全名是 test0.mp3,我将它放在 Eclipse 的 /res/raw/ 目录中。
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.
我假设我设置的路径不正确,但我在网上找到的所有示例都使用 setDataPath 的 FileDescriptor 版本,而不是 setDataPath 的 String 版本。
EDIT: 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.
编辑:如果我使用 MediaPlayer.setDataSource(FileDescriptor) 方法并将文件放在 Eclipse 的 /assets/ 目录中,我也可以播放本地 mp3。
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:
编辑#2:我接受了这是不可能的答案,但后来意识到我正在使用的库(openFrameworks)实际上确实使用 String 方法来加载文件。看这里:
采纳答案by Naren Neelamegam
Alternative Solution #1: Using Resources.getIdentifier()
替代解决方案 #1:使用 Resources.getIdentifier()
Why not use getResources().getIdentifier() to get id of the resource and use the static MediaPlayer.create() as usual?
为什么不使用 getResources().getIdentifier() 来获取资源的 id 并像往常一样使用静态 MediaPlayer.create() ?
public int getIdentifier (String name, String defType, String defPackage)
getIdentifier()takes your resource name (test0), resource type(raw), your package name and returns the actual resource id.
getIdentifier()获取您的资源名称(test0)、资源类型(原始)、您的包名称并返回实际的资源 ID。
MediaPlayer mp;
//String filename = "android.resource://" + this.getPackageName() + "/raw/test0";
mp=MediaPlayer.create(getApplicationContext(), getResources().getIdentifier("test0","raw",getPackageName()));
mp.start();
I've tested this code and it works.
我已经测试了这段代码并且它有效。
Update #1:更新 #1:
Alternative Solution #2: Using Uri.parse()
替代解决方案 #2:使用 Uri.parse()
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.
我也测试了这段代码,它也能工作。将您的资源路径作为 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更新 #2:答案是否定的
About setDataSource(String) call
关于 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,
看到您的评论后,您似乎确实希望将 setDataSource(string) 用于您的目的。我不明白为什么。但是,我假设的是,出于某种原因,您试图避免使用“上下文”。如果情况并非如此,那么上述两种解决方案应该非常适合您,或者如果您试图避免上下文,恐怕对于具有签名 setDataSource(String) 调用的函数来说这是不可能的。原因如下,
MediaPlayer setDataSource() function has these below options out of which you are only interested in setDataSource(String),
MediaPlayer setDataSource() 函数具有以下选项,您只对 setDataSource(String) 感兴趣,
setDataSource(String) internally calls setDataSource(String path, String[] keys, String[] values) function. If you can check its source,
setDataSource(String) 在内部调用 setDataSource(String path, String[] keys, String[] values) 函数。如果你可以检查它的来源,
public void setDataSource(String path)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
setDataSource(path, null, null);
}
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.
如果您检查 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.");
}
}
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.
在上面的代码中,您的资源文件 URI 方案不会为空 (android.resource://) 并且setDataSource(String) 将尝试使用原生 JNI 函数 nativeSetDataSource() 认为您的路径是 http/https/rtsp 并且显然调用也会失败而不会抛出任何异常。这就是为什么您对 setDataSource(String) 的调用无一例外地转义,并在出现以下异常的情况下转到 prepare() 调用。
Prepare failed.: status=0x1
So setDataSource(String) override cannot handle your resource file. You need to choose another override for that.
所以 setDataSource(String) 覆盖无法处理您的资源文件。您需要为此选择另一个覆盖。
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.
另一方面,检查 setDataSource(Context context, Uri uri) 使用的 setDataSource(Context context, Uri uri, Map headers),它使用您的上下文中的 AssetFileDescriptor、ContentResolver 和 openAssetFileDescriptor 打开作为 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());
}
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.
总而言之,您不能像使用资源 mp3 文件那样使用 setDataSource(String) 覆盖。相反,如果你想使用字符串来播放你的资源文件,你可以使用 MediaPlayer.create() 静态函数和上面给出的 getIdentifier() 或 setDataSource(context,uri) ,如 Update#1 中给出的。
Refer to the complete source code for more understanding here: Android MediaPlayer
更多理解请参考这里的完整源代码:Android MediaPlayer
Update #3:更新 #3:
openFrameworks setDataSource(String):
openFrameworks setDataSource(String):
As I have mentioned in the comments below, openFrameworks uses android MediaPlayer code asis. If you can refer to Line no: 4,
正如我在下面的评论中提到的,openFrameworks 使用 android MediaPlayer 代码 asis。如果您可以参考第 4 行,
import android.media.MediaPlayer;
and Line no: 26, 27, 28 and 218
和线号:26、27、28 和 218
player = new MediaPlayer(); //26
player.setDataSource(fileName); //27
player.prepare(); //28
private MediaPlayer player; //218
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 linkwhere one of the openFrameworks core developer arturosays,
因此,如果您尝试使用 openFrameworks将ardroid.resource//+ this.getPackageName() + "raw/test0"传递给 setDataSource(),您仍然会遇到与我在 Update#2 中解释的相同的异常。话虽如此,我只是尝试在谷歌上搜索运气以确保我在说什么,并找到了这个openFrameworks 论坛链接,其中一位openFrameworks 核心开发人员 arturo说,
don't know exactly how the mediaPlayer works but everything in res/raw or bin/data gets copied to /sdcard/cc.openframeworks.packagename
不知道 mediaPlayer 是如何工作的,但 res/raw 或 bin/data 中的所有内容都被复制到 /sdcard/cc.openframeworks.packagename
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://.
根据该评论,您可以尝试在 setDataSource() 中使用复制的路径。无法在 MediaPlayer 的 setDataSource(String) 上使用资源文件,因为它不能接受资源文件路径。请注意,我说“资源文件路径”以方案android.resource//开头,它实际上是一个 jar 位置(在您的 apk 中),而不是一个物理位置。本地文件将与以方案file://开头的 setDataSource(String) 一起使用。
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,
为了让您清楚地了解您要对资源文件做什么,请尝试执行以下代码并在 logcat 中查看结果,
try{
Log.d("RESURI", this.getClass().getClassLoader().getResource("res/raw/test0").toURI().toString());
}
catch(Exception e) {
}
You'll get the result as,
你会得到这样的结果,
jar:file:/data/app/<packagename>/<apkname>.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).
也就是说,您尝试访问的资源文件实际上不是物理路径中的文件,而是一个 jar 位置(在 apk 内),您无法使用 setDataSource(String) 方法访问它。(尝试使用 7zip 提取您的 apk 文件,您将在其中看到 res/raw/test0)。
Hope that helps.
希望有帮助。
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.
PS:我知道它有点冗长的答案,但我希望这能详细解释它。如果可以帮助其他人,则将替代解决方案留在顶部。
回答by DeKoServidoni
Like the android documentationsaid
就像 android文档所说的
Arbitrary files to save in their raw form. To open these resources with a raw InputStream, call Resources.openRawResource() with the resource ID, which is R.raw.filename.
However, if you need access to original file names and file hierarchy, you might consider saving some resources in the assets/ directory (instead of res/raw/). Files in assets/ are not given a resource ID, so you can read them only using AssetManager.
以原始形式保存的任意文件。要使用原始 InputStream 打开这些资源,请使用资源 ID(即 R.raw.filename)调用 Resources.openRawResource()。
但是,如果您需要访问原始文件名和文件层次结构,您可以考虑在 assets/ 目录(而不是 res/raw/)中保存一些资源。assets/ 中的文件没有资源 ID,因此您只能使用 AssetManager 读取它们。
So you need to use a InputStream to read the audio file before set it to the media player.
因此,您需要使用 InputStream 读取音频文件,然后再将其设置为媒体播放器。
I suggest you to put the audio file in the assets folder like you said you played
我建议你像你说的那样把音频文件放在assets文件夹中
:)
:)
回答by Sebastiano
When dealing with a raw resource, you should rely on the following constructor:
在处理原始资源时,您应该依赖以下构造函数:
public static MediaPlayer create(Context context, int resid)
Convenience method to create a MediaPlayer for a given resource id. On success, prepare() will already have been called and must not be called again.
公共静态媒体播放器创建(上下文上下文,int resid)
为给定资源 ID 创建 MediaPlayer 的便捷方法。成功后,prepare() 将已被调用,不得再次调用。
The code looks like
代码看起来像
mediaPlayer = MediaPlayer.create(this, R.raw.test0);
mediaPlayer.start();
Don't forget to call mediaPlayer.release()
when you're done with it.
完成后不要忘记打电话mediaPlayer.release()
。
(source)
(来源)
回答by Ravi Vaghela
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();
}
回答by Akhunzaada
From here,
从这里,
When path refers to a local file, the file may actually be opened by a process other than the calling application. This implies that the pathname should be an absolute path (as any other process runs with unspecified current working directory), and that the pathname should reference a world-readable file. As an alternative, the application could first open the file for reading, and then use the file descriptor form setDataSource(FileDescriptor).
当路径引用本地文件时,该文件实际上可能由调用应用程序以外的进程打开。这意味着路径名应该是绝对路径(因为任何其他进程在未指定的当前工作目录下运行),并且路径名应该引用一个世界可读的文件。作为替代方案,应用程序可以首先打开文件进行读取,然后使用文件描述符形式 setDataSource(FileDescriptor)。
Try,
尝试,
String filePath = "path/file.mp3";
File file = new File(filePath);
FileInputStream inputStream = new FileInputStream(file);
mediaPlayer.setDataSource(inputStream.getFD());
inputStream.close();
Hope it Helps!
希望能帮助到你!
回答by Dmitry
You have to use setDataSource(@NonNull Context context, @NonNull Uri uri)
instead of setDataSource(String path)
你必须使用setDataSource(@NonNull Context context, @NonNull Uri uri)
而不是setDataSource(String path)
Since you want to resolve resource internal application resources, you have to provide Context
which would be used to resolve this stuff
由于您要解析资源内部应用程序资源,因此您必须提供Context
用于解析这些内容的资源
Also if you will take a look inside these two methods you will notice they use different strategies to find resulting resource.
此外,如果您深入了解这两种方法,您会注意到它们使用不同的策略来查找结果资源。