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

Java MP3:一种以毫秒为单位获取任何给定字节位置的方法?,java,mp3,audio-streaming,Java,Mp3,Audio Streaming,我创建了一个servlet,它返回一个流(来自MP3文件),该流从客户端请求的任何给定字节位置开始。这允许客户端在任何给定字节位置立即开始播放,而无需进行任何本地搜索 现在,我有一个滑块,它可以显示进度。我正在使用当前字节位置来更新滑块。但是,我还想以秒为单位显示当前位置 这要求服务器可以将以字节为单位的当前位置“转换”为以毫秒为单位的位置。然后,服务器可以以毫秒为单位提供流开始位置作为响应头 有没有人有过以字节到毫秒为单位计算当前位置的经验 更新 从评论中可以清楚地看出,如果不将MP3文件解码

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

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

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

有没有人有过以字节到毫秒为单位计算当前位置的经验

更新

从评论中可以清楚地看出,如果不将MP3文件解码到某一点(以毫秒或字节为单位),然后确定读取了多少字节或播放了多少毫秒,则无法精确地将字节转换为毫秒,反之亦然。然而,考虑到一台服务器,例如,100个用户同时请求文件,这种方法显然不能很好地执行。然后,服务器必须将MP3文件解码到请求的位置,然后从该位置返回流。我选择了用性能来交换精度,并采用了一种方法,这种方法可以给我提供大致的位置,对于一个只播放一首曲目(而不是将音频与其他来源同步到毫秒)的播放器来说,这已经足够好了

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

}

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

MP3文件或流是一系列帧,其中每个帧由一个MP3头和一个MP3数据部分组成

标题和数据部分信息用于创建“听起来像原始的”音频帧

因此,mp3文件或流中的位置不能转换为结果音频流中的时间戳

最“难”的方法是循环。在循环中,您调用
play()
player.play()
方法,然后将线程休眠足够的毫秒,这样歌曲就可以在不启动下一首歌曲的情况下完成,但是你必须估计一首7mb大小的歌曲大约有3分钟的持续时间,所以你必须在这个空间中睡眠。您的歌曲长度由
文件给出。readAllBytes(路径)
。诸如此类:

//i put 5 for example lets say 5 songs//
//1000000 bytes is 1MB the same calculation is the same for the rest  length numbers//
for(int i=0; i<5; i++){
   play();
   if(length>1000000 && length<7500000){
     try{
        Thread.sleep(milis);
     }
     catch(Exception e){
        e.printStackTrace;
}
//我放了5首,比如说5首歌//
//1000000字节为1MB,剩余长度数字的计算相同//

对于(int i=0;i1000000&&lengthMP3与WAVE文件不一样。MP3在帧中工作(它们的数据在帧块中“分块”),而WAVE文件在字节数组中工作(WAVE文件是原始的、未处理的数据)。只是一个提示!:)精英先生:谢谢你的提示。然而,对于任何给定的字节位置,都应该可以得到ms位置,即使它只是一个近似位置(最近的)?“虽然它只是一个近似位置(最近的)?”与其说是“近似”,不如说是“任意的”。你熟悉VBR MP3吗?@sbratla,不太熟悉。有些MP3文件具有恒定比特率(CBR),有些则具有可变比特率(VBR)。这些决定了帧的大小,因此您需要阅读帧并进行近似精确的计算。您认为我在Kilian Foth答案下的评论中描述的方法是否可行。我知道流的总长度(以字节和秒为单位),然后将byteposition“转换”为时间刻度。在可变比特率的情况下,它可能无法正常工作,但在恒定比特率的情况下,YouTube如何在几秒钟内给出位置?它是否只知道流的总长度(从0到结束),然后将当前byteposition“转换”为时间刻度上的任何值?@sbratla,如果你仔细观察,它实际上不知道。尝试前后跳跃。您将发现,您只能搜索“关键帧”,即完整数据的帧。后续帧