將 SRT 字幕與針對 iOS 轉碼的 MP4 (HEVC/x265/mov_text) 合併時出現問題:“pts 沒有值”和“超出 mov/mp4 格式的範圍”

將 SRT 字幕與針對 iOS 轉碼的 MP4 (HEVC/x265/mov_text) 合併時出現問題:“pts 沒有值”和“超出 mov/mp4 格式的範圍”

我學習如何使用 HEVC (x265) 視訊編碼將 MKV 視訊轉碼為 MP4,從而使檔案更小,從而使其與 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),並透過 Homebrew 安裝了 FFmpeg 4.2.1,但即使我下載了每晚建構(ffmpeg-4.2.1-macos64-static,20191215-9fe0790)並使用該二進位檔案而不是 Homebrew 安裝的版本。

問題是我已經成功地將這個影片轉換為具有 x264 視訊和 AAC 音訊的 MP4,並且能夠將 SRT 字幕合併到生成的檔案中,沒有出現任何問題。但是,當我今天使用來自相同來源的 HEVC (x265) 視訊建立 MP4 時,SRT 字幕合併失敗,並出現「pts 無值」和「超出 mov/mp4 格式範圍」錯誤。

這是我用來從 MKV 來源建立 HEVC (x265) MP4 影片的指令:

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% 的影片沒有字幕;只有影片的後 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

還有大量類似的消息,直到,瞧!合併結束,看不到字幕,就是這樣。

這讓我抓狂!我的意思是,如果我使用相同的命令,但指定從字幕進入點附近開始的搜尋時間,我實際上會在需要字幕的影片的 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% 的影片。哎呀,我什至剛剛嘗試過這個命令;如果我將搜尋設定為等於或大於 1005 秒的任何值,我可以將字幕與 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 \
       -ss 1005 \
       output_final.mp4 \
       ;

但在此背景下,16.75 分鐘(1005 秒)有何神奇之處?

為什麼我只選擇字幕出現在影片後 50% 的次數就可以合併字幕,而如果運行命令合併整個影片就不能合併字幕?

FWIW,如果我運行類似的命令來創建視頻的 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…

一切都按預期進行!除了「Foo!」這個字之外出現在影片的 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

我正在使用的媒體檔案遇到了這個問題,我花了一天的大部分時間嘗試不同的命令以及重建 pts 的不同方法,但在我最終偶然發現這篇文章之前都無濟於事。令我非常悲傷的是,這裡提出的解決方法對我來說不起作用。

我使用 gyan.dev 為 Windows 編譯的二進位檔案在 Windows 11 上運行,但是我也透過執行 ubuntu 的 WSL(Linux 的 Windows 子系統)嘗試了相同的命令並透過 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

我嘗試使用粗體標籤、斜體標籤以及我能想到的任何其他標籤,甚至是無法識別的段落標籤。任何空標籤都不會觸發保留時間並導致“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

就像這裡的原始帖子一樣,這是一個 hacky 解決方法,而不是一個正確的解決方案,但也像這裡的原始帖子一樣,我找不到合適的解決方案。希望這對遇到此問題並像我一樣偶然發現這篇文章的其他人有所幫助。

如果你有一個大的 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

相關內容