能否在不下载远程音频文件的情况下获取其持续时间?

4

我有一个远程音频文件的URL。我需要构建适配器列表的数据以获取曲目详情。以下是代码的一部分:

    Log.d("audioURL", audio.getUrl());
    MediaPlayer tmpMedia;
    tmpMedia = MediaPlayer.create(getContext(), Uri.parse(audio.getUrl()));
    holder.txtDuration.setDuration(tmpMedia.getDuration()/1000);
    tmpMedia.release();

但是它的运行速度太慢了。LogCat会记录下类似这样的信息:
15:05:51.783: D/audioURL(776): http://cs4859.vk.me/u14195999/audios/0cbd695ddf50.mp3
15:05:51.783: D/MediaPlayer(776): Couldn't open file on client side, trying server side
15:05:53.813: D/audioURL(776): http://cs4859.vk.me/u14195999/audios/0cbd695ddf50.mp3
15:05:53.823: D/MediaPlayer(776): Couldn't open file on client side, trying server side
15:05:55.373: D/audioURL(776): http://cs4859.vk.me/u14195999/audios/0cbd695ddf50.mp3
15:05:55.383: D/MediaPlayer(776): Couldn't open file on client side, trying server side
15:05:58.143: D/audioURL(776): http://cs1626.vk.me/u149968/audios/04298447cd3c.mp3
15:05:58.153: D/MediaPlayer(776): Couldn't open file on client side, trying server side

...等等。因此,我的大约30首歌曲的播放列表初始化需要约7分钟。

我想,MediaPlayer类方法getDuration()按顺序下载这些曲目(或其中的一些部分)以获取它们的持续时间。

有没有一种快速获得这些持续时间的方法,而不必下载曲目呢?

Halim Qarroum,似乎这是正确的方法,但我在使用MediaMetadataRetriever类时遇到了一些问题。

以下是我的代码:

    if (android.os.Build.VERSION.SDK_INT < 10){
        holder.txtDuration.setDuration(audio.getTrackDuration());
    } else {
        MediaMetadataRetriever mRetriever = new MediaMetadataRetriever();
        Log.d("URI", Uri.parse(audio.getUrl()).toString());
        mRetriever.setDataSource(getContext(), Uri.parse(audio.getUrl()));
        String s = mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
        holder.txtDuration.setDuration(Long.parseLong(s));
        mRetriever.release();
    }

应用程序在mRetriever.setDataSource(getContext(), Uri.parse(audio.getUrl()));处终止,因为出现了IllegalArgumentExceptionaudio.getUrl()字符串为http://cs4859.vk.me/u14195999/audios/134dfe90d1ec.mp3

为什么会出现异常?

3个回答

8
Dheeb发布了一个详细的答案。然而,mp3文件中并不总是存在ID3标签。相反,您可以使用随Android框架一起提供的MediaMetadataRetriever类,而不是寻找这些标签,这将迫使您将此方法限制为mp3文件。
该类可以从某些类型的音频/视频文件中提供多个元数据,其中之一是持续时间。此方法的优点是标准化,因为它随附Android SDK,不受限于一种音频格式。
从Android开发人员相关页面:

MediaMetadataRetriever类提供了一个统一的接口,用于从输入媒体文件检索帧和元数据。

以下是使用此类的简单示例代码:
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(your_data_source);
String time = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
long timeInmillisec = Long.parseLong( time );
long duration = timeInmillisec / 1000;
long hours = duration / 3600;
long minutes = (duration - hours * 3600) / 60;
long seconds = duration - (hours * 3600 + minutes * 60);

访问您的URI会抛出404错误。请尝试使用浏览器。您不必使用Uri.parse(),可以直接传递字符串。 - Halim Qarroum
我可以从我的位置访问我的Uri,没有任何问题。也许你有一些防火墙或访问列表限制?当然,我尝试使用setDataSource(String path)表单,但不幸的是效果相同。 - Carlos
但是 MediaPlayer.getDuration() 没有任何错误和异常。但是它需要很长时间。媒体似乎没有受到保护,我可以无限制地下载和播放它。 - Carlos
很奇怪。你能更新一下你发布的代码吗?同时也可以发布 logcat 吗? - Halim Qarroum
这是被接受的答案,因为它是正确和准确的,我自己也在使用这个例子。不过,在某些版本/变体的安卓系统中,远程URI可能没有被正确处理,Stack Overflow上有另一个帖子讨论了这个问题。 - Halim Qarroum
显示剩余6条评论

8

有一个与MediaMetadataRetriever相关的bug。你可以尝试以下方法:

 metaRetreiver.setDataSource("<remoteUrl>", new HashMap<String, String>());

6

我会假设是mp3,因为“音频文件”是一个笼统的短语。

方法1:获取ID3标签

变体1:第三方库

您需要查看mp3文件中的ID3标签。除非您在其他地方跟踪所需的元数据。

要具体获取文件的跟踪长度,您必须确保查看ID3元数据标签,特别是标签的“TRCK”帧。

要仅下载ID3标签部分,您必须先下载文件的ID3头部分。

网站包含有关ID3 Tag格式的非常具体的信息。您需要查看ID3标签的版本号,然后根据此找到有关ID3标签长度的信息。然后,您必须下载整个标签,因为帧没有任何特定顺序。

然后,您应该能够使用第三方库查找TRCK帧及其数据。

变体2:HTTP Hack

对于ID3v2标签,请获取文件的开头。(ID3v2帧可能在其他地方,但实际上它们总是在那里。)您无法提前知道标签的长度。对于仅文本标记,您很可能可以在前512-1024字节中找到所需的信息。不幸的是,越来越多的MP3包含嵌入的“专辑艺术”图片,这可能会更长;尝试选择一个能够优雅地忽略截断的ID3信息的ID3库。

ID3v1标签位于文件结尾。同样,您无法提前知道它们有多长。当然,您事先不知道文件是否具有ID3v1标签、ID3v2标签、两者或都没有。总体而言,现在ID3v2是更好的选择。

要通过HTTP读取文件的一部分,您需要使用Range标头。这也不被所有地方支持。

方法2:估计

您可以通过HTTP HEAD请求获取文件大小。播放时间(秒数)的持续时间无法在未获取整个文件的情况下获得。您可以猜测,通过获取前几个MP3帧,查看它们的比特率,并假设其余文件具有相同的比特率,但考虑到可变比特率编码的普及程度,这将是接近准确的可能性很低。

ID3标签理论上可以包含可能允许您更好地猜测长度的信息,在ASPI和ETCO标签中。但实际上很少存在这些。

鸣谢

感谢各位在SO和互联网上的作者,当然还要感谢我内心深处一楼的那个家伙。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接