Регулярное выражение против схемы асинхронизации ID3v2 в mp3-файле? - PullRequest
2 голосов
/ 19 апреля 2011

Я создаю фрагмент кода для проверки mp3-файлов на моем сервере и получения результата, если некоторые из них имеют ложную синхронизацию или нет. Короче говоря, я загружаю эти файлы в PHP с помощью функции fread () и получаю поток в переменную. После разделения этого потока, чтобы получить отдельные потоки для id3v1 (не обязательно, это не предмет синхронизации), id3v2 (основная проблема) и аудио, я должен реализовать эту схему для потока id3v2.

Согласно официальной документации ID3v2 :

Единственная цель «схемы несинхронизации» - сделать тег ID3v2 как можно более совместимым с существующим программным обеспечением. В «несинхронизированных» тегах нет смысла, если файл должен обрабатываться только новым программным обеспечением. Несинхронизация может выполняться только с файлами MPEG 2 layer I, II и III и MPEG 2.5.

Всякий раз, когда в теге обнаруживается ложная синхронизация, один нулевой байт вставляется после первого байта ложной синхронизации. Формат правильной синхронизации, который должен быть изменен кодерами ID3, выглядит следующим образом:

% 11111111 111xxxxx

И следует заменить на:

% 11111111 00000000 111xxxxx

У этого побочного эффекта есть необходимость изменить все комбинации $ FF 00, чтобы на них не влиял процесс декодирования. Поэтому все комбинации $ FF 00 должны быть заменены комбинацией $ FF 00 00 во время несинхронизации.

Чтобы указать использование несинхронизации, должен быть установлен первый бит в «флагах ID3» (примечание: я нашел этот бит). Этот бит следует устанавливать только в том случае, если тег содержит, теперь исправленную, ложную синхронизацию. Этот бит должен быть очищен только в том случае, если тег не содержит ложных синхронизаций.

Имейте в виду, что, если кодер использует схему сжатия, схема несинхронизации должна применяться после . При декодировании сжатого «несинхронизированного» файла сначала необходимо проанализировать «схему несинхронизации», а затем декомпрессию.

Мои вопросы:

  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 () работать?

P.S. Я знаю, как это делать, читая байты за байтами в каком-то цикле, но я уверен, что это возможно с помощью регулярных выражений (кстати, если честно, я впитываю регулярные выражения).

Дайте мне знать, если вам нужно больше деталей.


Еще одна вещь ...

В данный момент я использую этот шаблон

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

отрезать часть строки, начиная с первого нулевого байта и до конца. Это правильный способ сделать это?


Обновление

( @ mario's answer ).

В первой паре тестов ... этот код вернул правильный результат.

  // 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, большое спасибо!

1 Ответ

1 голос
/ 19 апреля 2011

Двоичные строки не совсем похожи на регулярные выражения. Но у вас уже был правильный подход с использованием \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");

Я бы только следил за тем, чтобы всегда использовать обратную косую черту \ double, поэтому PCRE интерпретирует шестнадцатеричные последовательности \x00, а не парсер PHP. (В конечном итоге он станет терминатором строки C, прежде чем достигнет libpcre.)

...