gpt4 book ai didi

java - MediaPlayer.setDataSource(String)不适用于本地文件

转载 作者:IT老高 更新时间:2023-10-28 20:21:24 31 4
gpt4 key购买 nike

如果使用静态方法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)实际上确实使用String方法来加载文件。看这里:

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

最佳答案

替代解决方案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 Functions

setDataSource(String)在内部调用setDataSource(String路径,String []键,String []值)函数。如果您可以检查其 source
public void setDataSource(String path)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
setDataSource(path, null, null);
}

并且如果您检查setDataSource(String path,String []键,String []值)代码,您将看到以下条件根据其方案过滤路径,特别是如果它是"file"方案,则它将调用setDataSource(FileDescriptor)或如果scheme为非"file",则调用本地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方案不会为null(android.resource://),并且 setDataSource(String)将尝试使用 native JNI函数nativeSetDataSource(),以为您的路径是http/https/rtsp,并且显然该调用也将失败而不会引发任何异常。这就是为什么您对setDataSource(String)的调用无异常(exception)地进行转义,并在发生以下异常的情况下进行prepare()调用的原因。
Prepare failed.: status=0x1

因此setDataSource(String)覆盖无法处理您的资源文件。您需要为此选择另一个替代。

另一方面,检查setDataSource(Context context,Uri uri)使用的setDataSource(Context context,Uri uri,Map headers),它使用AssetFileDescriptor,上下文中的ContentResolver和openAssetFileDescriptor来打开URI,该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):

正如我在下面的评论中提到的,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中解释的相同的异常。话虽这么说,我只是想靠运气搜索Google来再次确定我在说什么,然后找到了这个 openFrameworks forum link,其中 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



基于该注释,您可以尝试在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

这表明您尝试访问的资源文件实际上不是物理路径中的文件,而是一个jar位置(apk内),您无法使用setDataSource(String)方法访问该文件。 (尝试使用7zip解压缩apk文件,您将在其中看到res/raw/test0)。

希望能有所帮助。

PS:我知道它的回答有点冗长,但是我希望这能详细解释它。如果可以帮助其他人,则将替代解决方案放在第一位。

关于java - MediaPlayer.setDataSource(String)不适用于本地文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33086417/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com