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