我正在制作一个节奏游戏,我需要一个快速的方法来获取ogg文件的长度。我能想到的唯一方法是在不播放文件的情况下快速传输文件,但如果我有数百首歌曲,这显然是不切实际的。另一种方法是将文件的长度存储在某种属性文件中,但我想避免这种情况。我知道必须有一些方法来做到这一点,因为大多数音乐播放器可以告诉你一首歌的长度。
答案 0 :(得分:5)
最快捷的方法是找到文件的末尾,然后返回到你找到的最后一个Ogg页面标题并读取它的granulePosition(这是文件中每个通道的样本总数)。这不是万无一失的(你可能正在查看一个链式文件,在这种情况下你只能获得最后一个流的长度),但应该适用于绝大多数的Ogg文件。
如果您在阅读Ogg页面标题时需要帮助,可以阅读Jorbis源代码...简短版本是查找" OggS",读取一个字节(应为0),读取一个字节(只应设置第3位),然后读取64位小端值。
答案 1 :(得分:2)
我实现了ioctlLR描述的解决方案,它似乎有效:
double calculateDuration(final File oggFile) throws IOException {
int rate = -1;
int length = -1;
int size = (int) oggFile.length();
byte[] t = new byte[size];
FileInputStream stream = new FileInputStream(oggFile);
stream.read(t);
for (int i = size-1-8-2-4; i>=0 && length<0; i--) { //4 bytes for "OggS", 2 unused bytes, 8 bytes for length
// Looking for length (value after last "OggS")
if (
t[i]==(byte)'O'
&& t[i+1]==(byte)'g'
&& t[i+2]==(byte)'g'
&& t[i+3]==(byte)'S'
) {
byte[] byteArray = new byte[]{t[i+6],t[i+7],t[i+8],t[i+9],t[i+10],t[i+11],t[i+12],t[i+13]};
ByteBuffer bb = ByteBuffer.wrap(byteArray);
bb.order(ByteOrder.LITTLE_ENDIAN);
length = bb.getInt(0);
}
}
for (int i = 0; i<size-8-2-4 && rate<0; i++) {
// Looking for rate (first value after "vorbis")
if (
t[i]==(byte)'v'
&& t[i+1]==(byte)'o'
&& t[i+2]==(byte)'r'
&& t[i+3]==(byte)'b'
&& t[i+4]==(byte)'i'
&& t[i+5]==(byte)'s'
) {
byte[] byteArray = new byte[]{t[i+11],t[i+12],t[i+13],t[i+14]};
ByteBuffer bb = ByteBuffer.wrap(byteArray);
bb.order(ByteOrder.LITTLE_ENDIAN);
rate = bb.getInt(0);
}
}
stream.close();
double duration = (double) (length*1000) / (double) rate;
return duration;
}
请注意,以这种方式查找费率仅适用于 vorbis OGG!
随意编辑我的答案,它可能不完美。