Русский

Видео и субтитры · Инструментарий субтитров

Как браузер анализирует файл SRT: блоки, индексы, тайм-коды и текст

· Как это работает

субтитры СРТ форматы файлов

Два блока меток SRT, разделенных пустой строкой, каждый из которых содержит индексную строку, строку тайм-кода и текстовые строки.
Оригинальная векторная иллюстрация ToolAcre

SRT выглядит тривиально, пока вы не встретите реальные файлы. В этом посте рассказывается, как парсер разбивает блоки, считывает индексы и тайм-коды, обрабатывает многострочный текст и восстанавливает данные из некорректных блоков, содержащихся в реальных файлах.

Файл «выглядит нормально», но половина реплик отсутствует — как снисходительный формат скрывает строгие ожидания

SRT не имеет тела спецификации, регистрации MIME и валидатора, который поставляется с игроками. Вместо этого существует форма, с которой соглашается большинство программ: число, строка тайм-кода, одна или несколько строк текста, затем пустая строка. Поскольку форма является условной, а не заданной, два файла могут выглядеть корректно в текстовом редакторе, хотя загружается только один из них, и ошибка обычно не возникает. Игрок, который не может прочитать реплику, скорее пропустит ее, чем сообщит о ней, поэтому файл с битым блоком воспроизводится с пробелом, а не с ошибкой.

Таким образом, у синтаксического анализатора есть две задачи, которые тянут в противоположные стороны. Ему приходится принимать вариации, которые содержатся в реальных файлах, поскольку файлы создаются службами транскрипции, ручным редактированием и преобразователями форматов, каждый из которых делает разные предположения. Ему также приходится отказываться от показаний, которые могут указывать на неподходящее время, потому что молчаливая неправильная временная метка хуже, чем сообщение о сбое.

Разделение на блоки — пустые строки в качестве разделителей и проблема с случайными пробелами и CRLF.

Разделение происходит по пустым строкам, а не по номерам индексов. Анализатор сначала нормализует окончания строк, заменяя как пару CRLF, так и одиночный CR одним символом новой строки, поскольку файл, созданный в Windows и отредактированный в Unix, может содержать и то, и другое. Затем он разбивается на две или более новых строк, обрезает каждый результирующий блок и отбрасывает пустые. Этот порядок имеет значение: разделение перед нормализацией оставит случайный возврат каретки в конце строки тайм-кода, и тогда тайм-код не сможет совпадать.

Перед этим удаляется знак порядка байтов. UTF-8 BOM в начале файла представляет собой три байта, которые простой анализатор видит как часть первого индексного номера, чего достаточно, чтобы сделать первый сигнал нечитаемым во время анализа каждого последующего сигнала. Конечные пробелы в пустой разделительной строке обрабатываются обрезкой, поэтому файл, пустые строки которого содержат пробел, по-прежнему разбивается правильно.

Индексная строка — почему числа часто неверны, дублируются или отсутствуют и почему парсерам не стоит им доверять

Индексный номер считывается, а затем игнорируется. Реальные файлы нумеруют сигналы с нуля, перезапускают нумерацию после слияния, дублируют номер после редактирования вручную или полностью опускают строку при записи файла конвертером. Доверие к этим числам означает наследование каждой из этих ошибок, поэтому вместо этого синтаксический анализатор присваивает свой собственный порядковый номер, подсчитывая сигналы, которые он уже успешно построил.

Этот выбор также объясняет, почему синтаксический анализатор никогда не требует присутствия индексной строки. Он находит строку тайм-кода, выполняя поиск в блоке первой строки, содержащей стрелку, вместо того, чтобы предполагать, что тайм-код является второй строкой. Блок без индексной строки анализируется нормально, а блок с двумя случайными строками перед тайм-кодом все еще анализируется, поскольку позиция не является тем, что идентифицирует тайм-код.

Строка таймкода — ЧЧ:ММ:СС,ммм --> ЧЧ:ММ:СС,ммм, допустимые варианты и те, которые ломают игроков.

Строка тайм-кода сопоставляется с одним регулярным выражением, и допуски в ней являются преднамеренными. Часы не являются обязательными, поскольку WebVTT допускает считывание по двум полям, а преобразователи его выдают. В качестве разделителя миллисекунд принимается либо запятая, либо точка, независимо от того, в каком формате заявлен файл, поскольку смешанные разделители достаточно распространены, и их отклонение приведет к сбою более хороших файлов, чем плохих. Дробные цифры дополняются справа, поэтому сигнал, заканчивающийся одной цифрой, читается как сотни миллисекунд, а не как единицы.

В двух чтениях отказано. Поле минут или секунд выше пятидесяти девяти скорее отклоняется, чем переносится, поскольку девяносто секунд не являются показаниями часов и обычно указывают на поврежденный или неправильно преобразованный файл; молчаливая нормализация сдвинула бы реплику. Строка, начало или конец которой не удалось проанализировать, создает зарегистрированную проблему с указанием некорректного текста и ожидаемой формы, и блок пропускается, а не угадывается.

Текстовые строки — многострочные подсказки, теги форматирования и место, где на самом деле заканчивается блок.

Все, что находится после строки тайм-кода, представляет собой текстовую подсказку, соединенную вместе с помощью символов новой строки. Ограничения по строкам и попыток перекомпоновки нет, поэтому трехстрочный сигнал сохраняется как три строки. Это причина, по которой пустая строка является несущей: это единственное, что сообщает синтаксическому анализатору, что текст закончился, поэтому сигнал, собственный текст которого содержит пустую строку, будет читаться как два блока, а вторая половина будет сообщена как не имеющая тайм-кода.

Настройки сигнала отделяются от метки времени окончания двумя или более пробелами. WebVTT позволяет директивам позиционирования, таким как выравнивание и размещение строк, следовать времени окончания одной и той же строки, поэтому синтаксический анализатор разделяет их до анализа временной метки и сохраняет их рядом с сигналом. Одиночный пробел не является разделителем, который предохраняет просто неопрятную строку таймкода от потери времени окончания.

Рабочий пример: анализ файла с пятью репликами с двумя преднамеренными ошибками — что восстанавливает надежный анализатор и что он помечает

Возьмите файл из пяти блоков, в котором строка тайм-кода третьего блока повреждена и читается 00:01:75,000 --> 00:01:78,000, а четвертый блок полностью потерял строку тайм-кода во время копирования и вставки. Синтаксический анализатор нормально читает первый и второй блоки и нумерует их один и два. Третий блок соответствует форме тайм-кода, но содержит поле секунд длиной семьдесят пять, поэтому он отклоняется и записывается как неверная временная метка, обозначающая строку, которую он не смог прочитать.

В четвертом блоке вообще нет стрелки, поэтому он записывается как не имеющий метки времени, при этом первые сорок символов блока заключаются в кавычки, чтобы строку можно было найти в исходном файле. Блок пять анализирует и становится сигналом три, а не сигналом пять, потому что нумерация учитывает успешные сигналы. В результате получается три полезных сигнала и две конкретные локализованные жалобы, а не исключение по первой неисправности и отсутствие информации о второй.

Что здесь не распространяется — ASS/SSA коды стиля, позиционирования и текст без субтитров, сбрасываемые в SRT

Здесь описывается SRT и части WebVTT, которые разделяют его форму сигнала. Он не распространяется на ASS и SSA, которые содержат заголовок сценария, определения стилей и ссылки на стили для каждого события и которые невозможно прочитать путем разделения на пустые строки. Тайминг караоке, команды рисования и встроенные теги переопределения, используемые в этих форматах, выходят за рамки моделей анализатора сигналов и тайм-кода.

Он также не восстанавливает текст. Транскрипт, вставленный в файл без тайм-кодов, создает список блоков без временной метки, который сообщается точно, но не может быть преобразован в субтитры без отсутствующей информации о времени. Ошибки кодирования представляют собой отдельную проблему: файл, декодированный с неправильным набором символов, анализируется на совершенно допустимые сигналы с неправильным текстом, и никакая структурная проверка этого не обнаружит.

Вывод: анализируйте снисходительно, пишите строго — как Subtitle Toolkit читает грязный SRT и записывает обратно чистый

Рабочее правило — разбирать снисходительно и писать строго. По пути примите необязательные часы, разделитель, отсутствующие индексные строки, смешанные окончания строк и начальный знак порядка байтов, а также записывайте каждую ошибку как обнаруженную проблему, а не выбрасывайте первую, чтобы файл можно было исправить за один проход. На выходе излучаем одну каноническую форму.

Именно это делает Subtitle Toolkit при преобразовании. Нумерация реплик меняется с одной и сохраняется непрерывной, временные метки повторно выдаются с запятой для SRT и точкой для WebVTT, а возвращаемый файл представляет собой форму, которую игроки ожидают, независимо от того, насколько нерегулярным был входной сигнал. Вставьте файл, который игрок отклонил, в конвертер и сначала прочитайте сообщения о проблемах; они называют реплику и цитируют строку, чего обычно достаточно, чтобы найти ошибку в оригинале.