jjzjj

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

coder 2023-05-17 原文

如果使用静态方法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(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/

有关java - MediaPlayer.setDataSource(String)不适用于本地文件的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  5. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  6. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  9. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  10. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

随机推荐