【问题标题】:Seek backwards to deal with invalid MP3 header?向后寻求处理无效的 MP3 标头?
【发布时间】:2014-03-23 18:21:36
【问题描述】:

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

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

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

当使用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;
        }

}

【问题讨论】:

    标签: java loops mp3 inputstream seek


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      我最终编写了一个函数来移动无效标头的字节,以便可以重新解析它。我在一个循环中调用该函数,其中我基本上 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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-03
        • 2019-04-23
        • 2016-03-26
        相关资源
        最近更新 更多