繁体   English   中英

针对mp3文件中ID3v2不同步方案的正则表达式?

[英]Regular expression against the ID3v2 unsynchronisation scheme in mp3 file?

我正在创建一段代码来检查服务器上的mp3文件,并获得结果,说明其中某些文件是否具有错误的同步。 简而言之,我正在使用fread()函数在PHP中加载这些文件,并在变量中获取流。 在将该流拆分为id3v1(没有必要,它不是同步主题),id3v2(主要问题)和音频以获取单独的流之后,我必须针对id3v2流实现该方案。

根据ID3v2官方文档

“非同步方案”的唯一目的是使ID3v2标签与现有软件尽可能兼容。 如果文件仅由新软件处理,则“不同步”标签中没有任何用处。 只能使用MPEG 2的I,II和III层以及MPEG 2.5文件进行不同步。

只要在标签内发现错误同步,就会在第一个错误同步字节之后插入一个清零字节。 ID3编码器应更改的正确同步的格式如下:

%11111111 111xxxxx

并应替换为:

%11111111 00000000 111xxxxx

这样做的副作用是必须更改所有$ FF 00组合,因此它们不会受到解码过程的影响。 因此,在非同步期间,所有$ FF 00 00组合都必须替换为$ FF 00 00组合。

为了指示不同步的用法,应设置“ ID3标志”中的第一位(注意:我已经找到了该位)。 仅当标签包含现已纠正的错误同步时,才应设置此位。 仅当标签不包含任何错误同步时,才应清除该位。

请记住,如果编码器使用了压缩方案,则应随后应用不同步方案。 解码压缩的“非同步”文件时,应首先解析“非同步方案”,然后再解压缩。

我的问题是:

  1. 如何搜索并用%11111111 00000000 111xxxxx替换此位模式%11111111 111xxxxx %11111111 00000000 111xxxxx
  2. 反之亦然,如何使用%11111111 111xxxxx搜索和替换此位模式%11111111 00000000 111xxxxx %11111111 111xxxxx

...使用preg_replace()

到目前为止,我已经创建的代码可以完美地工作,而且我只增加了一行(嗯,恰好是两行)。

<?php

  // some basic checkings here, such as 'does file exist'
  // and 'is it readable'

  $f = fopen('test.mp3', 'r');

  // ...rest of my code...  

  $pattern1 = '?????'; // pattern from 1st question
  $id3stream = preg_replace($pattern1, 'something1', $id3stream);

  // ...extracting frames...

  $pattern1 = '?????'; // pattern from 2nd question
  $id3stream = preg_replace($pattern2, 'something2', $id3stream);

  // ..do more job...

  fclose($f);

?>

如何使用preg_replace()函数使这两行起作用?

PS我知道如何在某种循环中逐字节读取字节,但是我敢肯定,使用正则表达式是可行的(顺便说一句,我正则表达式很烂)。

让我知道是否需要更多详细信息。


还有一件事...

目前,我正在使用此模式

$pattern0 = '/[\x00].*/';
echo preg_replace($pattern0, '', $input_string);

从第一个零字节开始截断字符串的一部分,直到结尾。 那是这样做的正确方法吗?


更新

@mario的答案 )。

在前几次测试中,此代码返回了正确的结果。

  // print original stream
  printStreamHex($stream_original, 'ORIGINAL STREAM');

  // adding zero pads on unsync scheme
  $stream_1 = preg_replace(':([\\xFF])([\\xE0-\\xFF]):', "$1\x00$2", $stream_original);
  printStreamHex($stream_1, 'AFTER ADDING ZEROS');

  // reversing process
  $stream_2 = preg_replace(':([\\xFF])([\\x00])([\\xE0-\\xFF]):', "$1$3", $stream_1);
  printStreamHex($stream_2, 'AFTER REMOVING ZEROS');


  echo "Status: <b>" . ($stream_original == $stream_2 ? "OK" : "Failed") . "</b>";

但是几分钟后,我发现了一种特殊情况,即一切看起来都像预期的结果,但流中仍然有FFE0 +对。

ORIGINAL STREAM
+-----------------------------------------------------------------+
| FF  E0  DB  49  53  BE  3B  E0  90  40  EA  2B  3A  61  FF  FA  |
| 84  E0  A9  99  1F  39  B5  E1  54  FF  E7  ED  B8  B1  3A  36  |
| 88  01  69  CA  7D  47  FA  E1  70  7C  85  34  B8  1A  FF  FF  |
| FF  F8  21  F9  2F  FF  F7  17  67  EB  2A  EB  6E  41  82  FF  |
+-----------------------------------------------------------------+

AFTER ADDING ZEROS
+-----------------------------------------------------------------+
| FF  00  E0  DB  49  53  BE  3B  E0  90  40  EA  2B  3A  61  FF  |
| 00  FA  84  E0  A9  99  1F  39  B5  E1  54  FF  00  E7  ED  B8  |
| B1  3A  36  88  01  69  CA  7D  47  FA  E1  70  7C  85  34  B8  |
| 1A  FF  00  FF  FF  00  F8  21  F9  2F  FF  00  F7  17  67  EB  |
| 2A  EB  6E  41  82  FF                                          |
+-----------------------------------------------------------------+

AFTER REMOVING ZEROS
+-----------------------------------------------------------------+
| FF  E0  DB  49  53  BE  3B  E0  90  40  EA  2B  3A  61  FF  FA  |
| 84  E0  A9  99  1F  39  B5  E1  54  FF  E7  ED  B8  B1  3A  36  |
| 88  01  69  CA  7D  47  FA  E1  70  7C  85  34  B8  1A  FF  FF  |
| FF  F8  21  F9  2F  FF  F7  17  67  EB  2A  EB  6E  41  82  FF  |
+-----------------------------------------------------------------+

Status: OK

如果流包含FF FF FF FF ,它将被FF 00 FF FF 00 FF代替,但应为FF 00 FF 00 FF 00 FF 那个FF FF对将再次错误mp3同步,因此我的任务是避免音频流之前的每个FFE0+模式(在ID3v2标签流中;因为mp3以FFE0+字节对开头,并且应该首先出现在音频数据的开头)。 我发现我可以循环使用相同的正则表达式,直到得到没有FFE0 +字节对的流。 是否有不需要循环的解决方案?

很好@mario,非常感谢!

二进制字符串并不完全是正则表达式。 但是,使用\\x00您已经有了正确的方法。

3 ..截断从第一个零字节开始的字符串部分,直到结尾

$pattern0 = '/[\\x00].*$/';

您只是在这里错过了$

1 ..如何用%11111111 00000000 111xxxxx搜索和替换此位模式%11111111 111xxxxx %11111111 00000000 111xxxxx

对这些位串使用序列FFE0

preg_replace(':([\\xFF])([\\xE0-\\xFF]):', "$1\x00$2");

由于您搜索可变字节,因此在替换字符串中使用$ 2。 否则,可以使用更简单的str_replace。

2.反之亦然,如何使用%11111111 111xxxxx搜索和替换此位模式%11111111 00000000 111xxxxx %11111111 111xxxxx

同样的把戏。

preg_replace(':([\\xFF])([\\x00])([\\xE0-\\xFF]):', "$1$3");

我只会注意始终使用\\双反斜杠,因此PCRE会解释\\x00十六进制序列,而不是PHP解析器。 (在到达libpcre之前,它将最终成为C字符串终止符。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM