在安卓设备上精准搜索MP3文件

9
我正在开发一个应用程序,其中准确地查找 MP3 文件非常重要。
目前,我正在以下方式使用 ExoPlayer:
public void playPeriod(long startPositionMs, long endPositionMs) {

    MediaSource mediaSource = new ClippingMediaSource(
            new ExtractorMediaSource.Factory(mDataSourceFactory).createMediaSource(mFileUri),
            startPositionMs * 1000,
            endPositionMs * 1000
    );

    mExoPlayer.prepare(mediaSource);
    mExoPlayer.setPlayWhenReady(true);

}

在某些情况下,这种方法会导致相对于预期播放时间偏移1-3秒。 我在 ExoPlayer 的 Github 上发现了这个问题。看起来这是 ExoPlayer 在 Mp3 格式上的内在限制,并且不会被修复。 我还发现了这个问题,它似乎表明 Android 的本地 MadiaPlayer 和 MediaExtractor 中存在同样的问题。
有没有办法在 Android 上对本地(例如设备上的)Mp3 文件进行准确的搜索?我非常愿意尝试任何黑客或解决方法。
3个回答

15

MP3文件本身并不具备可寻址的特性。它们不包含任何时间戳信息,只是一系列的MPEG帧依次排列而成。这使得定位变得棘手。有两种方法可以用来寻址MP3文件,每种方法都存在一些权衡。

最常见(也是最快)的方法是从第一个帧头中读取比特率(或者从前几个帧头中读取平均比特率),例如128k。然后,将整个文件的字节长度除以比特率,估算出文件的时间长度。然后,让用户寻址文件。如果他们在2:00的文件中寻找1:00,就将文件的字节大小除以50%标记,并“针落”到流中。读取文件直到下一个帧头的同步字出现,然后开始解码。

正如您所想象的那样,这种方法并不准确。最好情况下,您平均会在目标帧的一半处。对于每帧大小为576个采样的情况,这还算比较准确。但是,在首先计算针落点时存在问题。最常见的问题是ID3标签等会增加文件大小的元素会扰乱大小计算。更严重的问题是变量比特率(VBR)文件。如果您有使用VBR编码的音乐,并且曲目的开头是比较安静或者容易编码的部分,那么开头可能是32 kbps,而一秒钟后可能是320 kbps。计算文件时间长度时存在10倍的误差!

第二种方法是将整个文件解码为原始的PCM样本。这意味着您可以保证样本精确的查找,但您必须至少解码到查找点。如果您想要完整曲目的正确时间长度,则必须解码整个文件。大约20年前,这种方法非常缓慢。进入曲目几乎需要花费听完该曲目所需的时间!如今,对于短文件,您可能可以快速解码它们,以至于这并不太重要。
简而言之,如果您必须进行样本精确的查找,请在将文件放入播放器之前先解码文件,但在决定这种权衡之前,请先了解性能惩罚。

感谢您提供如此详尽的回复。我很愿意探索您有关先解码文件的建议,但我不知道您指的是什么。您是否知道我可以阅读有关此主题的资源?否则,您能否为我提供几个搜索词,以便我可以通过谷歌了解您的建议的一般想法? - Vasiliy
好的,非常感谢您的帮助。这应该能让我开始了。如果没有更具体的内容,我稍后会接受这个答案。 - Vasiliy
感谢您非常有帮助的答案。我遇到了一个问题,您的答案帮助我理解了问题并找到了解决方案。https://dev59.com/-FvUa4cB1Zd3GeqPx-Mu#57916886 - Shady Mohamed Sherif
@shadysherif 没问题!很高兴你觉得这个有帮助。 - Brad
@jayarjo 问题不在于帧数,而在于字节偏移量。编码一帧所需的字节数可能会有所不同。 - Brad
显示剩余4条评论

3

对于未来可能遇到此问题的人,我最终选择将mp3转换为m4a格式。这是在我的情况下最简单的解决方案。


@Brad,有时候你只有mp3文件,但你需要确保准确的查找功能。即使这意味着会有质量损失... - Vasiliy
3
您可以将 MP3 音频放入 MP4 容器中,就像使用 AAC 一样,并以此获得容器中的时间戳优势。ffmpeg -i yourfile.mp3 -acodec copy output.mp4 - Brad
我测试了使用模拟的慢速网络将MP3转换为MP4。事实证明,这对于浏览器也有效,有时浏览器会尝试通过范围请求来寻找MP3文件,但它们失败了。然而,使用MP4似乎更好地执行范围请求。 - Ciantic
我在iOS Safari中测试了ffmpeg -i yourfile.mp3 -acodec copy output.mp4,但它无法将文件作为音频播放器打开。但是,如果您有m4a文件并将其重命名为mp4,则可以正常工作。 - Ciantic
1
@Brad,我一开始对更改容器持怀疑态度,但实际上,即使是12小时的VBR mp3文件,在HTML5上使用新容器确实可以提供精准的搜索,并解决了我的问题,而不需要涉及那些令人痛苦的解决方案。 - rlf89
显示剩余3条评论

2

恒定比特率的mp3更好。我的系统是将mp3中每个帧头的示例偏移位置记录到列表中。然后,为了寻找,我会使用列表中的值在所需示例之前最接近的帧头处寻找,然后从该位置读取到所需示例。这种方法效果还不错,但不完美,因为渲染的波形是从参考帧解码而来,而不是从文件开头解码的值。如果需要精确度,请使用libmpg123,它似乎几乎是样本精确的。请注意,如果用于商业应用程序,请检查许可证。


那么样本和寻道时间之间确实存在关系。您能详细说明一下吗? - jayarjo

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