MP3:一种以毫秒为单位获取任何给定字节位置的方法?

sbr*_*tla 5 java mp3 audio-streaming

我创建了一个 servlet,它返回一个流(来自 MP3 文件),从客户端请求的任何给定字节位置开始。这允许客户端在任何给定的字节位置立即开始播放,而无需进行任何本地搜索。

现在,我有一个滑块来可视化进度。我正在使用当前字节位置来更新滑块。但是,我还想以秒为单位显示当前位置。

这要求服务器可以将当前以字节为单位的位置“转换”为以毫秒为单位的位置。然后服务器可以只提供以毫秒为单位的流开始位置作为响应头。

有没有人有关于如何计算当前位置(以字节到毫秒为单位)的经验?

更新

很明显,从评论中可以看出,没有精确的方法可以将字节转换为毫秒,反之亦然,而无需将 MP3 文件解码到点(以毫秒或字节为单位),然后确定已读取的字节数或毫秒数那些玩过的。然而,考虑到一个服务器,比如 100 个用户将同时请求文件,这种方法显然不会表现得很好。然后服务器必须将 MP3 文件解码到请求的位置,然后从该位置返回流。我选择用性能来交换精度,并采用了一种方法,该方法可以为我提供近似位置,并且对于仅用于播放曲目(而不是将音频与其他源的音频同步到毫秒)的播放器来说已经足够了。

我所做的是玩家(在客户端)现在只关心毫秒(MS)。也就是说,进度条的当前值和最大值以 MS 为单位,而不是像最初那样以字节为单位。要从任何给定位置开始播放,客户端请求服务器 (servlet) 提供从 MS 中任何给定位置开始的音频流。servlet 使用 JAudioTagger 获取文件的详细信息,然后近似计算 MS 位置对应的字节位置。我已经对它进行了测试,它与 CBR(恒定比特率)文件配合得很好。该方法不适用于 VBR(可变比特率)文件,因为帧大小可能会有所不同。请注意,这只是一个播放器,目的是播放音乐文件。它不打算将音频与其他一些媒体同步到 MS。

更新(2012 年 7 月 3 日)

servlet 已经使用以下代码运行了一段时间,并且运行良好。已经播放了数千个 MP3,从毫秒到字节的近似效果很好。

更新(2017 年 1 月 3 日)

servlet 仍在使用完全相同的代码运行,并且已经很好地播放了数十万个 MP3。在制作期间,没有任何关于播放和时间的抱怨。

/**
 * Returns the approximate byte position for any given position in
 * milliseconds.
 *
 * http://www.java2s.com/Open-Source/Android/Mp3/needletagger/org/jaudiotagger/audio/mp3/MP3AudioHeader.java.htm
 * http://www.autohotkey.com/forum/topic29420.html
 *
 * @param   file the <code>File</code> for which the byte position for the
 *          provided position in milliseconds is to be returned.
 * @param   ms a <code>long</code> being the position in milliseconds for
 *          which the corresponding byte position is to be returned.
 * @return  a <code>long</code> being the byte position, or <b>-1</b> if the
 *          position in bytes could not be obtained.
 */
public static long getApproximateBytePositionForMilliseconds(File file, long ms) {

    long bytePosition = -1;

    try {

        AudioFile audioFile = AudioFileIO.read(file);
        AudioHeader audioHeader = audioFile.getAudioHeader();

        if (audioHeader instanceof MP3AudioHeader) {
            MP3AudioHeader mp3AudioHeader = (MP3AudioHeader) audioHeader;
            long audioStartByte = mp3AudioHeader.getMp3StartByte();
            long audioSize = file.length() - audioStartByte;
            long frameCount = mp3AudioHeader.getNumberOfFrames();
            long frameSize = audioSize / frameCount;

            double frameDurationInMs = (mp3AudioHeader.getPreciseTrackLength() / (double) frameCount) * 1000;
            double framesForMs = ms / frameDurationInMs;
            long bytePositionForMs = (long) (audioStartByte + (framesForMs * frameSize));
            bytePosition = bytePositionForMs;
        }

        return bytePosition;

    } catch (Exception e) {
        return bytePosition;
    }

}
Run Code Online (Sandbox Code Playgroud)

Kil*_*oth 3

它不是那样工作的。即使您的 MP3 文件采用恒定比特率编码,流中的哪一秒的字节位置编码也是可变的。(当然,VBR 编码使其比 CBR更具可变性,但都是一样的。)可靠地获取此信息的唯一方法是实际解码流到该点,您可能不想这样做。这就是为什么即使是像 XMMS 这样的专业玩家在你跳跃时也无法可靠地更新滑块。