Проблемы с объединением субтитров SRT с MP4 (HEVC/x265/mov_text), перекодированным для iOS: «pts не имеет значения» и «вне диапазона для формата mov/mp4»

Проблемы с объединением субтитров SRT с MP4 (HEVC/x265/mov_text), перекодированным для iOS: «pts не имеет значения» и «вне диапазона для формата mov/mp4»

Я узнал, как перекодировать видео MKV в MP4 с помощью видеокодирования HEVC (x265), чтобы уменьшить размер файлов и сделать их совместимыми с iOS. Но хотя процесс отличный — и сжатие отличное при небольшом размере файла — когда я пытаюсь объединить субтитры в одно конкретное видео, в результате видео MP4 я получаю кучу ошибок от FFmpeg, например:

[mp4 @ 0x7facb9002000] pts has no value
[mp4 @ 0x7facb9002000] Application provided duration: 3152805999 / timestamp: 3154741000 is out of range for mov/mp4 format

Я использую macOS Mojave (10.15.2) с FFmpeg 4.2.1, установленным через Homebrew, но проблема сохраняется, даже если я загружаюночная сборка(ffmpeg-4.2.1-macos64-static, 20191215-9fe0790) и использовать этот двоичный файл вместо установленной версии Homebrew.

Проблема в том, что у меня есть одно видео, которое я успешно конвертировал в MP4 с видео x264 и звуком AAC в прошлом и смог объединить субтитры SRT в полученный файл без проблем. Но когда я создаю MP4 с использованием видео HEVC (x265) сегодня из того же самого источника, объединение субтитров SRT завершается ошибкой с этими ошибками «pts has no value» и «is out of range for mov/mp4 format».

Это команда, которую я использую для создания видео HEVC (x265) MP4 из источника MKV:

ffmpeg -i input.mkv \
       -map_metadata -1 \
       -vf scale=-1:720 \
       -c:v libx265 -crf 20 -c:a aac -b:a 128k \
       -threads 4 \
       -tag:v hvc1 -sn \
       -map 0:0 -map 0:1 output_hevc.mp4 \
       ;

А вот команда, которая успешно использовалась в прошлом для объединения субтитров SRT в существующий MP4 без перекодирования:

ffmpeg -i output_hevc.mp4 \
       -i input.srt \
       -c:v copy -c:a copy \
       -c:s mov_text -metadata:s:s:0 language=eng \
       output_final.mp4 \
       ;

Я думаю, проблема может быть в том, что примерно в 50% видео нет субтитров; только во второй половине видео субтитры необходимы.

Видео длится около 2 часов. И первые 50 минут или около того нет необходимости в английских субтитрах. Но примерно через 50 минут, вот когда начинаются субтитры.

Итак, субтитры в SRT начинаются так:

1
00:52:33,123 --> 00:52:50,123
It was a dark and stormy night…

Но когда я запускаю указанную выше команду FFmpeg, вывод получается примерно таким; местами немного искаженным для цели примера:

Stream mapping:
  Stream #0:0 -> #0:0 (copy)
  Stream #0:1 -> #0:1 (copy)
  Stream #1:0 -> #0:2 (subrip (srt) -> mov_text (native))
Press [q] to stop, [?] for help
frame=25560 fps=0.0 q=-1.0 size=  304640kB time=00:52:00.00 bitrate= 791.7kbits/frame=50730 fps=50726 q=-1.0 size=  681984kB time=time=00:52:00.00 bitrate=1772.4kbit[mp4 @ 0x7facb9002000] Application provided duration: 3152137000 / timestamp: 3152137000 is out of range for mov/mp4 format
[mp4 @ 0x7facb9002000] pts has no value
[mp4 @ 0x7facb9002000] Application provided duration: 3152805999 / timestamp: 3154741000 is out of range for mov/mp4 format
[mp4 @ 0x7facb9002000] pts has no value
[mp4 @ 0x7facb9002000] Application provided duration: 3153246998 / timestamp: 3156809000 is out of range for mov/mp4 format
[mp4 @ 0x7facb9002000] pts has no value
[mp4 @ 0x7facb9002000] Application provided duration: 3154051997 / timestamp: 3159013000 is out of range for mov/mp4 format
[mp4 @ 0x7facb9002000] pts has no value
[mp4 @ 0x7facb9002000] Application provided duration: 3155556996 / timestamp: 3163817000 is out of range for mov/mp4 format

И тонны подобных сообщений, пока, et voila! Объединение заканчивается, субтитры не видны и все.

Это сводит меня с ума! Я имею в виду, что если я использую ту же команду, но указываю время поиска, которое начинается примерно с того момента, когда появляются субтитры, я фактически вижу субтитры в 50% видео, где они нужны:

ffmpeg -I output_hevc.mp4 \
       -i input.srt \
       -c:v copy -c:a copy \
       -c:s mov_text -metadata:s:s:0 language=eng \
       -ss 3120 \
       output_final.mp4 \
       ;

Но мне нужно больше 50% видео, конечно. Черт, я даже только что попробовал эту команду; я могу заставить субтитры объединиться с MP4, если я установлю перемотку на любое значение, равное или большее 1005 секунд:

ffmpeg -i output_hevc.mp4 \
       -i input.srt \
       -c:v copy -c:a copy \
       -c:s mov_text -metadata:s:s:0 language=eng \
       -ss 1005 \
       output_final.mp4 \
       ;

Но что такого магического в 16,75 минутах (1005 секундах) в данном контексте?

Почему я могу объединить субтитры, если я выбираю только те моменты, когда субтитры появляются во второй половине видео, но не могу, если запускаю команду для объединения всего видео?

Кстати, если я запускаю аналогичную команду для создания MKV из видео, то все нормально!

ffmpeg -i output_hevc.mp4 \
       -i input.srt \
       -c:v copy -c:a copy -c:s copy \
       output.mkv \
       ;

Просто каким-то образом это слияние, mov_textпохоже, делает процесс неудачным.

А если я добавлю фиктивный субтитр в начало файла, например так:

0
00:00:00,000 --> 00:16:75,000
Foo!

1
00:52:33,123 --> 00:52:50,123
It was a dark and stormy night…

Все работает как надо! За исключением слова «Фу!», которое появляется на 50% видео. Очевидно, что не идеально.

Есть ли способ обойти это? Это ошибка FFmpeg или, возможно, проблема с видео HEVC (x265) с объединенными субтитрами?

решение1

Похоже, что добавление фиктивных субтитров в начало файла SRT, которые охватывают от начала видео примерно до того места, где начинаются субтитры, решило эту проблему.

Это решение, безусловно, «хак», но оно работает.

Исходя из своей идеи добавить фиктивные субтитры в начало файла SRT, я понял, что субтитры SRT — согласноспецификации SRT— разрешить HTML-теги. Зная это, я добавил следующий фиктивный подзаголовок, и все работает!

0
00:00:00,000 --> 00:16:75,000
<b></b>

1
00:52:33,123 --> 00:52:50,123
It was a dark and stormy night…

Вот и все! Простое добавление пустого жирного тега позволяет всему этому работать и объединять субтитры…


Но — как было сказано в начале — это явно хак, и я открыт для того, чтобы услышать больше от других, кто знает больше о FFmpeg. Я могу только предположить, что ни одна из этих проблем не отражает желаемого поведения, и должен быть более элегантный способ решения таких случаев. Или это ошибка (не функция) и о ней следует сообщить?

решение2

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

Я работаю на Windows 11, используя двоичные файлы, скомпилированные для Windows gyan.dev, однако я также пробовал те же команды через WSL (подсистема Windows для Linux), работающую под управлением Ubuntu и устанавливаемую через apt.

При вставке пустых тегов я продолжал получать те же ошибки:

0
00:00:00,000 --> 00:18:00,000
<b></b>

1
00:43:21,472 --> 00:43:24,933
Mysterious translated text here

[mp4 @ 0000023630f5cf80] Packet duration: 2601472000 / dts: 2601472000 is out of range
[mp4 @ 0000023630f5cf80] pts has no value
[mp4 @ 0000023630f5cf80] Packet duration: 2601681999 / dts: 2605143000 is out of range
[mp4 @ 0000023630f5cf80] pts has no value
[mp4 @ 0000023630f5cf80] Packet duration: 2602474998 / dts: 2608605000 is out of range
[mp4 @ 0000023630f5cf80] pts has no value
[mp4 @ 0000023630f5cf80] Packet duration: 2603642997 / dts: 2611441000 is out of range
[mp4 @ 0000023630f5cf80] pts has no value

Если бы я вместо этого вставил один видимый символ, например, одну точку, то файл srt объединился бы без проблем. Конечно, тогда я бы застрял с точкой на экране на первые 40 минут моего видео.

0
00:00:00,000 --> 00:18:00,000
.

1
00:43:21,472 --> 00:43:24,933
Mysterious translated text here

Я пробовал использовать тег bold, тег italics и любой другой тег, который мог придумать, даже тег paragraph, который не распознается. Любые пустые теги не запускали сохранение времени и приводили к ошибкам 'pts has no value'.

В отчаянии я решил использовать подход количества вместо качества, добавив 10 записей с интервалом в 2 минуты с очень короткой продолжительностью, так что вместо сплошного видимого символа точки у меня был бы прерывистый, и это сработало. Поэтому по прихоти я попытался установить время начала и время окончания первых 10 записей одинаковыми, и это привело к тому, что символ точки был скрыт, а субтитры влились без ошибок:

1
00:02:00,000 --> 00:02:00,000
.

2
00:04:00,000 --> 00:04:00,000
.

3
00:06:00,000 --> 00:06:00,000
.

4
00:08:00,000 --> 00:08:00,000
.

5
00:10:00,000 --> 00:10:00,000
.

6
00:12:00,000 --> 00:12:00,000
.

7
00:14:00,000 --> 00:14:00,000
.

8
00:16:00,000 --> 00:16:00,000
.

9
00:18:00,000 --> 00:18:00,000
.

10
00:20:01,000 --> 00:20:01,000
.

11
00:43:21,472 --> 00:43:24,933
Mysterious translated text here

Как и в оригинальном посте здесь, это хакерский обходной путь, а не правильное решение, но также как и в оригинальном посте здесь, я не смог найти правильного решения. Надеюсь, это поможет всем, у кого есть эта проблема и кто наткнется на этот пост, как и я.

Если у вас большой srt, который вы боитесь увеличивать вручную, я попросил GPT написать для меня этот небольшой скрипт на Python (я мог бы написать его сам, но на тот момент я уже почти закончил бороться с этой проблемой и решил, что GPT справится с ней достаточно просто).

def add_entries_to_srt(existing_file_path, new_file_path, num_entries=10, duration=0.1, buffer_time=120):
    with open(existing_file_path, 'r', encoding='utf-8') as existing_file:
        existing_content = existing_file.read()

    # Parse existing entries
    entries = existing_content.strip().split('\n\n')
    existing_entries_count = len(entries)

    # Generate new entries
    new_entries = []
    for i in range(1, num_entries + 1):
        start_time = i * (duration + buffer_time)
        end_time = start_time + duration
        entry_text = f"{i}\n{format_time(start_time)} --> {format_time(end_time)}\n<i>.</i>"
        new_entries.append(entry_text)

    # Increment entry numbers of existing entries
    for i in range(existing_entries_count):
        entry_lines = entries[i].split('\n')
        entry_number = int(entry_lines[0])
        entry_lines[0] = str(entry_number + num_entries)
        entries[i] = '\n'.join(entry_lines)

    # Combine new and existing entries
    combined_entries = '\n\n'.join(new_entries + entries)

    # Write to the new file
    with open(new_file_path, 'w', encoding='utf-8') as new_file:
        new_file.write(combined_entries)

def format_time(seconds):
    minutes, seconds = divmod(seconds, 60)
    hours, minutes = divmod(minutes, 60)
    return f"{int(hours):02d}:{int(minutes):02d}:{int(seconds):02d},000"

# Replace 'existing_subtitle.srt' with your actual file path
existing_file_path = r'C:\Temp\ffmpeg\Subtitles.srt'
new_file_path = r'C:\Temp\ffmpeg\Subtitles.EDIT.srt'

add_entries_to_srt(existing_file_path, new_file_path)

РЕДАКТИРОВАТЬ: Оказывается, в зависимости от вашего проигрывателя, установка начального и конечного времени первых 10 записей одинаковыми приводит к тому, что период либо скрывается, либо отображается в течение 5 секунд. Установка смещения в 1 миллисекунду, по-видимому, гораздо более надежна для сохранения периода скрытым.

1
00:02:00,000 --> 00:02:00,001
.

2
00:04:00,000 --> 00:04:00,001
.

3
00:06:00,000 --> 00:06:00,001
.

4
00:08:00,000 --> 00:08:00,001
.

5
00:10:00,000 --> 00:10:00,001
.

6
00:12:00,000 --> 00:12:00,001
.

7
00:14:00,000 --> 00:14:00,001
.

8
00:16:00,000 --> 00:16:00,001
.

9
00:18:00,000 --> 00:18:00,001
.

10
00:20:01,000 --> 00:20:01,001
.

11
00:43:21,472 --> 00:43:24,933
Mysterious translated text here

Связанный контент