我有一个nodejs 脚本,我想在其中解析MP3 帧。这些帧很容易检测,因为每个帧都以两个字节开始
0xff 0xfb
。
我正在使用
Uint8Array
来访问该文件的字节。使用 [].indexOf.call(data, 0xff)
我可以轻松搜索单个字节,但不能搜索两个字节。显然我可以手动检查第二个字节,但我想知道是否有一种干净的方法来获取特定字节序列的索引。
显然,如果不编写自定义代码,就没有什么好的方法可以做到这一点,所以这就是我所做的:
Uint8Array.prototype.indexOfMulti = function(searchElements, fromIndex) {
fromIndex = fromIndex || 0;
var index = Array.prototype.indexOf.call(this, searchElements[0], fromIndex);
if(searchElements.length === 1 || index === -1) {
// Not found or no other elements to check
return index;
}
for(var i = index, j = 0; j < searchElements.length && i < this.length; i++, j++) {
if(this[i] !== searchElements[j]) {
return this.indexOfMulti(searchElements, index + 1);
}
}
return(i === index + searchElements.length) ? index : -1;
};
我知道这是一个 11 年前的问题,你找到了一些对你有用的东西并回答了你自己的问题,但我一直在寻找这个基本上相同的原因(解析 mpeg 音频帧)并意识到这并不总是为此而努力。
MP3 帧通常以
0xff 0xfb
开头,但其他 mpeg 音频则不然(请参阅有关 MPEG 音频帧标头的信息)。
MPEG 音频帧的前两个字节分解如下表所示(从上面的链接逐字复制,但包括在内,以防文章出现问题)。
位置 | 长度(位) | 意义 | 示例 |
---|---|---|---|
0 | 11 | 帧同步以查找标头(所有位始终设置) | 1111 1111 111 |
11 | 2 | 音频版本 ID(另请参见表 3.2) 00 - MPEG 版本 2.5(MPEG 2 的非官方扩展) 01 - 保留 10 - MPEG 版本 2 (ISO/IEC 13818-3) 11 - MPEG 版本 1 (ISO/ IEC 11172-3) |
11 |
13 | 2 | 层索引 00 - 保留 01 - 第 III 层 10 - 第 II 层 11 - 第 I 层 |
01 |
15 | 1 | 保护位 0 - 由标头后面的 16 位 CRC 保护 1 - 无 CRC |
1 |
鉴于这些信息,您最终得到的解决方案感觉有点缺乏,因为如果我遇到一个带有 CRC 位设置的罕见 mp3(
0xfa
而不是 0xfb
),我的代码可以轻松跳过 CRC 并正常工作,但前提是它首先找到 mp3 帧 - 使用 indexOfMulti
,我可能必须多次循环遍历(至少部分)整个缓冲区/数组,以支持解析可能的 MP3 帧的变化(indexOfMulti([0xff, 0xfb])
会得到我-1
,所以我必须在之后运行indexOfMulti([0xff, 0xfa])
),或者更有可能的是它会在我没有意识到的情况下跳到下一帧。
如上表所示,MPEG 音频以
1111 1111 111
开头,因此为了搜索它,我最终做了类似下面的代码的操作。
let offset = 0;
while (offset < srcBuffer.length) {
let buffer = srcBuffer.read(10) // length of ID3 frame header
// ... (skipped code where I parse ID3 tags and
// increase the offset and
// read more data into the buffer)
// this is the key line,
// I'm not searching for the byte `0xe0`,
// I'm searching for the bit pattern fff* ****
if (
buffer[offset] === 0xff &&
(buffer[offset + 1] & 0xe0) === 0xe0
) {
// ... (skipped parsing the mp3 frame header)
}
// ... (skipped rest of code for brevity)
}