iOS 用にトランスコードされた MP4 (HEVC/x265/mov_text) と SRT 字幕を結合する際の問題: 「pts に値がありません」および「mov/mp4 形式の範囲外です」

iOS 用にトランスコードされた MP4 (HEVC/x265/mov_text) と SRT 字幕を結合する際の問題: 「pts に値がありません」および「mov/mp4 形式の範囲外です」

私は、MKV ビデオを HEVC (x265) ビデオ エンコーディングで MP4 にトランスコードして、iOS と互換性のある方法でファイルを小さくする方法を学びました。しかし、プロセスは素晴らしく、圧縮もファイル サイズを小さくして優れていますが、字幕を 1 つの特定のビデオに結合しようとすると、結果として得られる 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 に正常に変換したビデオが 1 つあり、結果のファイルに 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% で字幕が表示される時間だけを選択すると字幕を結​​合できるのに、ビデオ全体を結合するコマンドを実行すると結合できないのはなぜですか?

ちなみに、同様のコマンドを実行してビデオの 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 を再構築するさまざまな方法を試して 1 日の大半を費やしましたが、すべて無駄でした。最終的にこの投稿にたどり着きました。非常に残念なことに、ここで提案されている回避策は私には機能しませんでした。

私は、gyan.dev によって Windows 用にコンパイルされたバイナリを使用して Windows 11 上で実行していますが、Ubuntu を実行し、apt 経由でインストールする WSL (Windows Subsystem for Linux) を通じて同じコマンドも試しました。

空のタグを挿入すると、同じエラーが引き続き発生しました。

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

代わりに、単一のピリオドなどの表示可能な文字を 1 つ挿入すると、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 に値がありません」というエラーが発生します。

仕方なく、質より量を重視することにしました。2 分間隔で非常に短い継続時間で 10 個のエントリを追加して、ピリオド文字がはっきり表示される代わりに、ピリオド文字が断続的に表示されるようにしたところ、うまくいきました。そこで、思いつきで最初の 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

関連情報