我正在构建一个应用程序,在该应用程序中准确搜索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秒偏移。
I found this issue on ExoPlayer's github。看起来这是Mp3格式的ExoPlayer的固有限制,不会被修复。
I also found this question,这似乎表明Android的本地MadiaPlayer和MediaExtractor中存在相同的问题。
是否可以在Android上的本地(例如设备上)MP3文件中执行精确查找?我非常乐意接受任何破解或解决方法。
答案 0 :(得分:2)
MP3文件不是天生可以搜索的。它们不包含任何时间戳。它只是一系列的MPEG帧,一个接一个。这使这个棘手。寻找MP3的方法有两种,每种方法都需要权衡。
最常见(也是最快)的方法是从第一个帧头读取比特率(或者也许从前几个帧头读取平均比特率),也许是128k。然后,取整个文件的字节长度,将其除以该比特率即可估算出文件的时间长度。然后,让用户搜索文件。如果他们将1:00
放入2:00
文件中,则将文件的字节大小除以50%的标记,然后将“ needle drop”放入流中。读取文件,直到出现下一帧标题的同步字,然后开始解码。
您可以想象,这种方法不准确。充其量,您将平均处于目标的半帧以内。帧大小为576个样本,这非常准确。但是,首先需要计算落针点。最常见的问题是ID3标签等会增加文件的大小,从而影响大小计算。更严重的问题是可变比特率(VBR)文件。如果您使用VBR编码音乐,并且曲目的开头是无声的或易于编码,则开头可能是32 kbps,而一秒钟的可能是320 kbps。计算文件时间长度时出现10倍错误!
第二种方法是将整个文件解码为原始PCM样本。这意味着您可以保证样本精确的寻道,但是您至少必须解码到寻道点。如果您想为整个曲目设置适当的时间长度,则必须 解码整个文件。大约20年前,这非常缓慢。聆听曲目几乎要花费聆听曲目所需的时间!如今,对于短文件,您可能可以将它们解码得如此之快,以至于没有太大关系。
TL; DR;如果必须进行精确采样,请先对文件进行解码,然后再将其放入播放器中,但在决定此折衷方案之前,请先了解性能损失。
答案 1 :(得分:2)
恒定比特率mp3更好。我使用的系统是将mp3中每个帧头的示例偏移位置记录到列表中。然后寻找,我将通过使用列表中的值寻找所需样本之前最接近的帧头,然后从该位置读取所需样本。这工作得很好,但不是完美的,因为渲染波形是从参考帧解码的,而不是从文件开始解码的值。如果需要准确性,请使用libmpg123,它似乎几乎是样本准确的。请注意如果是商业应用程序,请检查许可。
答案 2 :(得分:2)
对于那些将来可能会遇到此问题的人,我最终只是将mp3转换为m4a。这是我特定情况下最简单的解决方案。