向后寻求处理无效的MP3标题?

时间:2014-03-23 18:21:36

标签: java loops mp3 inputstream seek

我正在编写一个解码MP3帧的应用程序。我很难找到标题。

MP3标头为32位,以签名开头:11111111 111

在下面的内部循环中,我寻找这个签名。找到此签名后,我将检索接下来的两个字节,然后将标头的后三个字节传递给自定义MpegFrame()类。该类验证标头的完整性并从中解析信息。 MpegFrame.isValid()返回一个布尔值,表示帧头的有效性/完整性。如果标头无效,则再次执行外循环,并再次查找签名。

使用CBR MP3执行程序时,只找到部分帧。该应用程序报告许多无效的框架。

我认为无效帧可能是跳过位的结果。标头长度为4个字节。当标头被确定为无效时,我跳过所有4个字节并开始从接下来的四个字节中寻找签名。在类似于以下情况的情况下:11111111 11101101 11111111 11101001,在前两个字节中找到标头签名,但是第三个字节包含使标头无效的错误。如果我跳过所有字节,因为我已经确定从第一个字节开始的头是无效的,我会错过从第三个字节开始的有效标头(因为第三个和第四个字节包含一个签名)。

我无法在InputStream中向后搜索,所以我的问题如下:当我确定以字节1和2开头的标头无效时,如何运行我的签名查找循环字节2,而不是字节5?

在下面的代码中,b是正在考虑的可能标头的第一个字节,b1是第二个字节,b2是第三个字节,b3是第四个字节。

int bytesRead = 0;

//10 bytes of Tagv2
int j = 0;

byte[] tagv2h = new byte[10];
j = fis.read(tagv2h);
bytesRead += j;

ByteBuffer bb = ByteBuffer.wrap(new byte[]{tagv2h[6], tagv2h[7],tagv2h[8], tagv2h[9]});
bb.order(ByteOrder.BIG_ENDIAN);
int tagSize = bb.getInt();

byte[] tagv2 = new byte[tagSize];
j = fis.read(tagv2);
bytesRead += j;

while (bytesRead < MPEG_FILE.length()) {

        boolean foundHeader = false;

        // Seek frame
        int b = 0;
        int b1 = 0;
        while ((b = fis.read()) > -1) {
            bytesRead++;
            if (b == 255) {
                b1 = fis.read();
                if (b1 > -1) {
                    bytesRead++;
                    if (((b1 >> 5) & 0x7) == 0x7) {
                        System.out.println("Found header.");
                        foundHeader = true;
                        break;
                    }
                }
            }
        }

        if (!foundHeader) {
            continue;
        }

        int b2 = fis.read();
        int b3 = fis.read();

        MpegFrame frame = new MpegFrame(b1, b2, b3, false);
        if (!frame.isValid()) {
            System.out.println("Invalid header @ " + (bytesRead-4));
            continue;
        }

}

2 个答案:

答案 0 :(得分:0)

您可以将输入流包装在PushbackInputStream中,以便可以推回一些字节并重新解析它们。

答案 1 :(得分:0)

我最终编写了一个函数来移动无效头的字节,以便可以重新解析它。我在一个循环中调用该函数,其中我基本上Seek表示有效帧。

找到有效帧时,

Seek()返回true(存储调用Seek()找到的最后一帧的其他位置)。 CheckHeader()验证标头的完整性。 SkipAudioData()读取帧的所有音频数据,将流标记放在帧的末尾。

private boolean Seek() throws IOException {
    while(!(CheckHeader() && SkipAudioData())){
        if(!ShiftHeader()){
            return false;
        }
    }
    return true;
}


private boolean ShiftHeader() {
    try {
        if (bytesRead >= MPEG_FILE.length()) {
            return false;
        }
    } catch (Exception ex) {
        ex.printStackTrace();
        return false;
    }

    header[0] = header[1];
    header[1] = header[2];
    header[2] = header[3];

    try {
        int b = fis.read();
        if (b > -1) {
            header[3] = b;
            return true;
        }
    } catch (IOException ex) {
        return false;
    } catch (Exception ex) {
        return false;
    }

    return false;
}