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

时间:2015-10-12 16:55:45

标签: java android android-mediaplayer

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

我假设我正在错误地设置路径,但我在网上找到的所有示例都使用了setDatacript的setDescriptor版本而不是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

6 个答案:

答案 0 :(得分:36)

替代解决方案#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(字符串)用于你的目的。我不明白为什么。但是,我认为,由于某种原因,你试图避免使用&#34; context&#34;。如果不是这种情况,那么上述两种解决方案应该适合您,或者如果您试图避免上下文,我担心使用带有签名setDataSource(String)调用的函数是不可能的。原因如下,

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

setDataSource Functions

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)代码,您将看到以下条件根据其方案过滤路径,特别是如果它是&#34; file&#34; scheme调用setDataSource(FileDescriptor)或者如果scheme不是&#34; file&#34;,它调用本机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)将尝试使用本机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()+&#34; raw / test0&#34; 传递给setDataSource(),您仍会获得与我在Update#2中解释的相同的例外。话虽如此,我只是试着用我的运气搜索谷歌,以确定我说的是什么,并发现这个openFrameworks forum link其中一个 openFrameworks核心开发人员arturo 说,

  

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

根据该评论,您可以尝试在setDataSource()中使用复制的路径。无法在MediaPlayer的setDataSource(String)上使用资源文件,因为它无法接受资源文件路径。请注意,我说&#34;资源文件路径&#34;从方案 android.resource // 开始,它实际上是一个jar位置(在你的apk中),而不是物理位置。本地文件将使用setDataSource(String),它以方案 file:// 开头。

为了让您清楚地了解您要对资源文件执行的操作,请尝试执行以下代码并在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:我知道它有点冗长的答案,但我希望这能详细解释。如果可以帮助其他人,可以将替代解决方案放在首位。

答案 1 :(得分:2)

像android documentation所说

  

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

     

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

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

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

:)

答案 2 :(得分:2)

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

  

public static MediaPlayer create (Context context,int resid)

     

为给定资源ID创建MediaPlayer的便捷方法。成功时,prepare()已经被调用,不能再被调用。

代码看起来像

mediaPlayer = MediaPlayer.create(this, R.raw.test0);
mediaPlayer.start();

当你完成它时,别忘了给mediaPlayer.release()打电话。

source

答案 3 :(得分:2)

下面代码为我工作,我认为此代码将帮助您

    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();
}

答案 4 :(得分:1)

来自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();

希望它有帮助!

答案 5 :(得分:0)

您必须使用setDataSource(@NonNull Context context, @NonNull Uri uri)代替setDataSource(String path)

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

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