Тот, кто когда-либо занимался созданием субтитров для Blu-Ray, наверняка сталкивался с ситуацией, когда при воспроизведении некоторые субтитры появляются буквально на долю секунды, а некоторые - не видны вовсе.
Проблема в том, что большинство програмных (включая VLC, вплоть до текущей версии 2.05) и аппаратных (на linux-ядре, включая "народный" Pioneer BDP-140, а также разнообразные приставки) проигрывателей Blu-Ray основаны на библиотеке ffmpeg, которая при некоторых условиях некорректно обрабатывала тайминги PGS-субтитров. Эта ошибка была исправлена только в сентябре 2012 года, но вовсе не факт, что все производители оперативно обновят свои прошивки, если вообще озаботятся подобным вопросом.
Анализ фирменных дисков Blu-Ray показал, что в них минимальный интервал между соседними субтитрами никогда не бывает меньше 2х полных фреймов, а начало и конец каждого субтитра синхронизированы со сменой фрейма. При таком условии ошибка в ffmpeg не проявляется и все плееры показывают субтитры без каких либо сбоев и пропусков.
Прилагаемый ниже скрипт выравнивает начало и конец каждого субтитра по границам фрейма, а также устанавливает минимальный интервал между субтитрами в 2 фрейма, пропорционально уменьшая начало и конец соседних субтитров. Если увеличение интервала между какми-либо субтитрами невозможно из-за слишком малой их длительности, то скрипт остановит свою работу, указав номер субтитра, требующего ручной корректировки.
Скрытый текст
// Align times to frames & set minimal 2 frames gaps between subs
// Required for strict Blu-Ray compatibility for SUP/PGS
//
// v0.02b, (c) JustOff 2013, off.just.off[@]gmail.com
// 10x2: Tengo & trixter[@]oldskool.org
program BluRayCompat;
const
MinFrameGap = 2;
var
FPS :Double;
ATimeStart, AFrameStart, ATimeEnd, AFrameEnd, BTimeStart, BFrameStart, BTimeEnd, BFrameEnd :Integer;
FrameGap, Count, i :Integer;
Form :TForm;
cmbFPS :TComboBox;
begin
Form := TForm(Application.MainForm);
cmbFPS := TComboBox(Form.FindComponent('cmbFPS'));
i := Pos(',',cmbFPS.Text);
if i <> 0 then
FPS := StrToFloat(Copy(cmbFPS.Text,0,i-1) + '.' + Copy(cmbFPS.Text,i+1, Length(cmbFPS.Text)-i))
else
FPS := StrToInt(cmbFPS.Text);
Count := GetSubtitleCount;
if Count < 2 then exit;
for i := 0 to Count-2 do
begin
ATimeStart := GetSubtitleInitialTime(i);
ATimeEnd := GetSubtitleFinalTime(i);
AFrameEnd := Round(ATimeEnd*FPS/1000);
BTimeStart := GetSubtitleInitialTime(i+1);
BFrameStart := Round(BTimeStart*FPS/1000);
if i = 0 then
begin
AFrameStart := Round(ATimeStart*FPS/1000);
ATimeStart := Round(AFrameStart*1000/FPS+0.5);
SetSubtitleInitialTime(i, ATimeStart);
end;
FrameGap := BFrameStart - AFrameEnd;
if FrameGap < MinFrameGap then
begin
AFrameEnd := AFrameEnd + Round(FrameGap/2) - Round(MinFrameGap/2);
BFrameStart := AFrameEnd + MinFrameGap;
end;
ATimeEnd := Round(AFrameEnd*1000/FPS+0.5);
if ATimeEnd <= ATimeStart then
begin
MsgBox('Subtitle #' + IntToStr(i+1) + ' is too short!', '&Ok', '', '', $10);
exit;
end
else
begin
BTimeStart := Round(BFrameStart*1000/FPS+0.5);
SetSubtitleFinalTime(i, ATimeEnd);
SetSubtitleInitialTime(i+1, BTimeStart);
end;
if i = Count-2 then
begin
BTimeEnd := GetSubtitleFinalTime(i+1);
if BTimeEnd <= BTimeStart then
BTimeEnd := BTimeStart + 500;
BFrameEnd := Round(BTimeEnd*FPS/1000);
BTimeEnd := Round(BFrameEnd*1000/FPS+0.5);
SetSubtitleFinalTime(i+1, BTimeEnd);
end;
end;
MsgBox('Subtitles aligned to frames,' + #13#10 + 'minimal gap is set to ' + IntToStr(MinFrameGap) + ' frames,' + #13#10 + 'based by fps ' + cmbFPS.Text + '.', '&Ok', '', '', $40);
end.