簡體   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