首页 > 解决方案 > 用 c++ 解包位域会产生错误的结果

问题描述

我正在尝试使用位域解压缩 mp3 帧。

mp3 帧的标头以同步字 0xFFF 开始,后跟 20 位标头数据。头的结构表示如下:

struct Mp3FrameRaw {
  unsigned short fff:12; // Should always be 0xFFF = 4095
  unsigned short mpeg_standard : 1;
  unsigned short layer : 2;
  unsigned short error_protection : 1;
  unsigned short bitrate : 4;
  unsigned short frequency : 2;
  unsigned short pad_bit : 1;
  unsigned short  : 1;
  unsigned short mode :2;
  unsigned short mode_extension :2;
  unsigned short copyrighted : 1;
  unsigned short original: 1;
  unsigned short emphasis: 2;
};

标题总共有 32 位长。

我的程序首先找到同步字:

size_t find_sync_word(std::vector<unsigned char> & input) {
  bool previous_was_ff = false;

  for (size_t offset = 0; offset < input.size(); ++offset) {
    if (previous_was_ff && (input[offset] & 0xF0 == 0xF0))
      return offset - 1;
    previous_was_ff = 0xFF == input[offset];
  }
  return -1;
}

然后尝试解压第一个标头:

int parse(std::vector<unsigned char> & input) {
  size_t offset = find_sync_word(input);
  if (offset < 0) {
    std::cerr << "Not a valid Mp3 file" << std::endl;
    return -1;
  }

  Mp3FrameRaw *frame_ptr = reinterpret_cast<Mp3FrameRaw * >(input.data() + offset);
  std::cout << frame_ptr->fff << " (Should always be 4095)" << std::endl;
  std::cout << frame_ptr->layer << " (Should be 1 )" << std::endl;
  std::cout << frame_ptr->bitrate << " (Should be 1-14)" << std::endl;
  return 0;
}

内容main.cpp如下:

 int main() {
  std::ifstream mp3_file("/path/to/file.mp3", std::ios::binary);
  std::vector<unsigned char> file_contents((std::istreambuf_iterator<char>(mp3_file)),
                                           std::istreambuf_iterator<char>());

  return parse(file_contents);
 }

结果显示:

3071 (Should always be 4095)
3 (Should be 1 )
0 (Should be 1 - 14)

相反,如果我一点一点地手动解压缩字段,一切都会按预期工作。例如

{
    size_t offset;
    Mp3FrameRaw frame;        
    ...

    frame.fff = input[offset++];
    frame.fff = (frame.fff << 4) | (input[offset] >> 4);

    frame.mpeg_standard = (input[offset] >> 3) & 1;
    frame.layer = (input[offset] >> 1) & 0x3;
    frame.error_protection = (input[offset++]) & 0x1;
    frame.bitrate = input[offset] >> 4;
    ...

}

我假设位域的位置不是他们直觉上应该做的。我究竟做错了什么?

我在 Ubuntu 18.04 上使用 gcc。

标签: c++bit-fields

解决方案


推荐阅读