繁体   English   中英

MediaPlayer.setDataSource(String)不使用本地文件

[英]MediaPlayer.setDataSource(String) not working with local files

如果我使用静态方法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 /目录中。

我假设我正在设置错误的路径,但我在网上找到的所有示例都使用setDescriptor版本的setDataPath而不是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),资源类型(原始),您的包名称并返回实际的资源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(字符串)用于你的目的。 我不明白为什么。 但是,我认为,由于某种原因,你试图避免使用“上下文”。 如果不是这样的话,那么上面两个解决方案应该适合您,或者如果您试图避免上下文,我担心使用带有签名setDataSource(String)调用的函数是不可能的。 原因如下,

MediaPlayer setDataSource()函数有以下选项,你只对setDataSource(String)感兴趣,

setDataSource函数

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

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

如果检查setDataSource(String path,String [] keys,String [] values)代码,您将看到以下条件根据其方案过滤路径,特别是如果它是“file”方案,它调用setDataSource(FileDescriptor)或如果scheme不是“file”,则调用本机JNI媒体函数。

Prepare failed.: status=0x1

在上面的代码中,您的资源文件URI方案将不为null(android.resource://),并且setDataSource(String)将尝试使用本机JNI函数nativeSetDataSource(),认为您的路径是http / https / rtsp,显然呼叫也会失败而不会抛出任何异常。 这就是为什么你对setDataSource(String)的调用在没有异常的情况下转义并且使用以下异常进行prepare()调用。

 Prepare failed.: status=0x1 

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

另一方面,检查setDataSource(上下文上下文,Uri uri)使用的setDataSource(上下文上下文,Uri uri,Map头),它使用上下文中的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文件。 相反,如果您想使用字符串来播放资源文件,您可以使用MediaPlayer.create()静态函数和上面给出的getIdentifier()或者更新#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中解释的相同的异常。 话虽如此,我只是试着运气搜索谷歌以确定我说的是什么,并找到了这个openFrameworks论坛链接 ,其中一个openFrameworks核心开发人员arturo说,

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

根据该注释,您可以尝试在setDataSource()中使用复制的路径。 无法在MediaPlayer的setDataSource(String)上使用资源文件,因为它无法接受资源文件路径。 请注意,我说“资源文件路径”以方案android.resource //开头,它实际上是一个jar位置(在你的apk中),而不是物理位置。 本地文件将使用以方案文件://开头的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:我知道它有点冗长的答案,但我希望这能详细解释。 如果可以帮助其他人,将替代解决方案放在首位。

就像android 文档说的那样

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

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

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

我建议你把音频文件放在资产文件夹中,就像你说的那样

:)

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

public static MediaPlayer create (Context context,int resid)

为给定资源ID创建MediaPlayer的便捷方法。 成功时,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指向本地文件时,该文件实际上可能由调用应用程序以外的进程打开。 这意味着路径名应该是绝对路径(因为任何其他进程与未指定的当前工作目录一起运行),并且路径名应该引用世界可读文件。 作为替代方案,应用程序可以首先打开文件进行读取,然后使用文件描述符形式setDataSource(FileDescriptor)。

尝试,

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

希望能帮助到你!

你必须使用setDataSource(@NonNull Context context, @NonNull Uri uri)而不是setDataSource(String path)

由于您要解析资源内部应用程序资源,因此必须提供用于解决此问题的Context

此外,如果您将了解这两种方法,您会发现它们使用不同的策略来查找结果资源。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM