MediaPlayer.setDataSource(String) 不适用于本地文件
Posted
技术标签:
【中文标题】MediaPlayer.setDataSource(String) 不适用于本地文件【英文标题】:MediaPlayer.setDataSource(String) not working with local files 【发布时间】:2016-01-10 05:52:15 【问题描述】:如果我使用静态方法 MediaPlayer.create(context, id),我可以播放本地 mp3,但如果我使用非静态方法 MediaPlayer.setDataSource(String),它就不起作用。发生的事情是当我调用 MediaPlayer.prepare() 时出现同步异常:
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();
请注意,我没有收到有关未找到文件或任何内容的错误。文件全名为test0.mp3,我放在Eclipse的/res/raw/目录下。
我假设我设置的路径不正确,但是我在网上找到的所有示例都使用 setDataPath 的 FileDescriptor 版本而不是 setDataPath 的 String 版本。
编辑:如果我使用方法 MediaPlayer.setDataSource(FileDescriptor) 并将文件放在 Eclipse 的 /assets/ 目录中,我也可以播放本地 mp3。
编辑#2:我接受了这是不可能的答案,但后来意识到我正在使用的库(openFrameworks)实际上确实使用字符串方法来加载文件。见这里:
https://github.com/openframeworks/openFrameworks/blob/master/addons/ofxAndroid/ofAndroidLib/src/cc/openframeworks/OFAndroidSoundPlayer.java
【问题讨论】:
不知道有没有用,看看这个帖子:[播放mp3文件](***.com/questions/5466882/…) @LamaSonmez 该代码正在使用 MediaPlayer.create(context, id) 方法。我正在尝试获取使用字符串文件路径工作的版本:developer.android.com/reference/android/media/… 设置倒数计时器并调用 setDataSource 可能会在 5 秒后不确定可能是资源尚未加载所以让它冷却然后尝试访问资源......你也可以添加 onerrorlistener 和给它大约 4 次尝试重试播放......只是一个镜头 也许这个答案会帮助到这里的人:***.com/a/53390067/826946 【参考方案1】:替代解决方案 #1:使用 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();
我已经测试了这段代码,它可以工作。
更新 #1:
替代解决方案 #2:使用 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();
更新 #2:答案是否定的
关于 setDataSource(String) 调用
看到您的评论后,您似乎确实希望将 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) 使用的 setDataSource(Context context, Uri uri, Map headers),它使用 AssetFileDescriptor, ContentResolver from your context 和 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());
总而言之,您不能像使用资源 mp3 文件一样使用 setDataSource(String) 覆盖。相反,如果您想使用字符串播放资源文件,您可以使用 MediaPlayer.create() 静态函数和上面给出的 getIdentifier() 或 Update#1 中给出的 setDataSource(context,uri)。
更多理解请参考完整源代码:Android MediaPlayer
更新 #3:
openFrameworks setDataSource(String):
正如我在下面的 cmets 中提到的,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(),您仍然会遇到与我相同的异常在更新#2 中解释。话虽如此,我只是碰碰运气,在 Google 上搜索以确认我在说什么,并找到了 openFrameworks forum link,其中一位 openFrameworks 核心开发人员 arturo 说,
不知道 mediaPlayer 是如何工作的,但一切都在 res/raw 中 或 bin/data 被复制到 /sdcard/cc.openframeworks.packagename
根据该评论,您可以尝试在 setDataSource() 中使用复制的路径。在 MediaPlayer 的 setDataSource(String) 上使用资源文件是不可能的,因为它不能接受资源文件路径。请注意,我说“资源文件路径”以 android.resource// 方案开头,它实际上是一个 jar 位置(在您的 apk 中),而不是物理位置。本地文件将使用以 file:// 开头的 setDataSource(String)。
为了让您清楚地了解您要对资源文件执行的操作,请尝试执行以下代码并在 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
这是为了向您展示您尝试访问的资源文件实际上不是物理路径中的文件,而是您无法使用 setDataSource(String) 方法访问的 jar 位置(在 apk 中)。 (尝试使用 7zip 解压您的 apk 文件,您将在其中看到 res/raw/test0)。
希望对您有所帮助。
PS:我知道它有点冗长的答案,但我希望这能详细解释它。如果可以帮助其他人,请将替代解决方案留在顶部。
【讨论】:
我的问题肯定措辞不佳——我是在专门询问 MediaPlayer.setDataSource(String)。我对该方法的其他版本不感兴趣。 @racarate,你的意思是 update#1 中的解决方案也不适合你。这使用 setDataSource(context,uri)。正如我在答案中提到的,我只是将您的代码从 mp.setDataSource(filename) 更改为 mp.setDataSource(this, Uri.parse(filename)) 并且它与您的资源文件完美配合。 请检查更新后的答案(更新#2)。 setDataSource(String) 覆盖不能按原样使用。您可以将 setDataSource(context,uri) 与您的字符串资源路径或 getIdentifier() 与 MediaPlayer.create() 一起使用 好的,所以答案是否定的。感谢您提供详细信息,如果您摆脱答案的前两部分并且编辑#2成为官方的“这是不可能的”答案,也许这会更有帮助。不得不说,官方文档对此一点都不清楚。 我刚刚意识到openFrameworks似乎使用这种方法来加载本地文件。我无法取消奖励赏金,但我确实将其标记为未答复。【参考方案2】:就像安卓documentation说的那样
以原始形式保存的任意文件。打开这些资源 使用原始 InputStream,调用 Resources.openRawResource() 资源ID,即R.raw.filename。
但是,如果您需要访问原始文件名和文件层次结构, 您可能会考虑在 assets/ 目录中保存一些资源 (而不是 res/raw/)。 assets/ 中的文件没有资源 ID, 所以你只能使用 AssetManager 来阅读它们。
所以您需要先使用 InputStream 读取音频文件,然后再将其设置为媒体播放器。
我建议你像你说的那样把音频文件放在assets文件夹里
:)
【讨论】:
我对 InputStream 如何适应感到困惑?我正在尝试获取需要文件路径字符串才能工作的版本。 也许这个答案可以帮助你:) ***.com/questions/15098657/…【参考方案3】:在处理原始资源时,您应该依赖以下构造函数:
public static MediaPlayer create(上下文上下文,int resid)
为给定资源 ID 创建 MediaPlayer 的便捷方法。成功时,prepare() 将已经被调用,并且不能再次调用。
代码看起来像
mediaPlayer = MediaPlayer.create(this, R.raw.test0);
mediaPlayer.start();
完成后不要忘记致电mediaPlayer.release()
。
(source)
【讨论】:
是的,这种方法对我有用,但我正在尝试让字符串工作的版本:developer.android.com/reference/android/media/…【参考方案4】:下面的代码对我有用,我认为这段代码会对你有所帮助
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) 方法的。 ***.com/questions/18977809/… 看到您不能将 MadiaPlayer.setDataSource(String) 用于本地文件夹文件。 @RaviVGHL,您评论中的链接没有说 setDataSource(String) 不能用于本地文件。它说 MediaPlayer.create() 在内部使用 setDataSource() 和 Prepare() 方法。你的答案是一个有效的代码,但你的评论陈述不正确,并且提出的问题也不是关于使用 R.raw.test0 。 racarate 想使用字符串作为参数(而不是 id)来播放声音。【参考方案5】:来自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();
希望对你有帮助!
【讨论】:
是的,这个方法对我有用,但我特别询问 MediaPlayer.setDataSource(String) 方法。【参考方案6】:你必须使用setDataSource(@NonNull Context context, @NonNull Uri uri)
而不是setDataSource(String path)
由于您要解析资源内部应用程序资源,您必须提供Context
用于解决这些问题
此外,如果您深入了解这两种方法,您会发现它们使用不同的策略来查找结果资源。
【讨论】:
以上是关于MediaPlayer.setDataSource(String) 不适用于本地文件的主要内容,如果未能解决你的问题,请参考以下文章
Android MediaPlayer setDataSource failed
Caused by:java.lang.IllegalStateException at android.media.MediaPlayer._setDataSource(Native Method)
MediaPlayer.setDataSource 导致有效文件的 IOException
Nullpointer 异常“无效 android.media.MediaPlayer.setDataSource(android.content.Context,android.net.Uri)”
MediaPlayer setDataSource 失败,状态 = 0x80000000 为 2.3.4 上的文件路径设置的铃声