2

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

根据ID3v2 官方文档

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

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

%11111111 111xxxxx

并且应该替换为:

%11111111 00000000 111xxxx

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

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

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

我的问题是:

  1. 如何搜索和替换这个位%11111111 111xxxxx模式%11111111 00000000 111xxxxx
  2. 反之亦然,如何搜索和替换这个位%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,非常感谢!

4

1 回答 1

1

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

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

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

你只是错过了$这里。

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

对这些位串使用序列FF和。E0

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

在替换字符串中使用 $2 此处,因为您搜索变量字节。否则,更简单的 str_replace 将起作用。

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

同样的伎俩。

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

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

于 2011-04-19T07:56:51.400 回答