再エンコードせずにffmpegでフレームレートを変更する

再エンコードせずにffmpegでフレームレートを変更する

23.976 fps (24000/1001) の mkv (h264) ビデオがありますが、再エンコードせずに品質を落とさずに 25fps に変換したいと考えています。mkvmerge でそれができることは知っていますが (オプション --default-duration '0:25fps' を使用)、可能であれば ffmpeg から直接実行したいと思います。ドキュメントによると、これは機能するはずです。

ffmpeg -i input.mkv -r 25 -vcodec copy output.mkv

しかし、実行すると、同じビデオ fps しか得られません。ffmpeg でこれを行う正しい方法 (存在する場合) は何ですか?

答え1

現在のバージョンの FFmpeg を使用する方法は次のとおりです。これは、連結デマルチプレクサが最初のファイルの後の入力の PTS を再スケーリングするのではなく、固定オフセットを適用するだけであることに依存しています。タイムスケールが15360(FFmpeg 出力の典型) の 30 fps ストリームがあるとします。つまり、フレーム0に PTS があり0、フレーム30に PTS があるということです。PTS 値に影響を与えずに15360タイムスケールを に変更できれば、これは 45 fps ストリームになります。23040

基本的に、以下のメソッドはそれを実行します。

1ソースのプロパティを識別します。

Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1171 kb/s,
       30 fps, 30 tbr, 15360 tbn (default)

ソースのプロパティ、特に解像度とに注目してくださいtbn


2a(オプション) 計算を簡単にするために、タイムスケールを便利なものに変更します。

ffmpeg -i in.mp4 -c copy -an -video_track_timescale 30 in-v30.mp4

これで私たちは

Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1171 kb/s, \
       30 fps, 30 tbr, 30 tbn (default

この手順を実行すると、新しいタイムスケールは元のフレームレートと同じか、その整数倍になります。

2b必要なタイムスケールを計算します。ターゲット フレームレート の場合x、ソースのフレーム番号の PTS はx新しい と同じ値になりますtbn。手順 2a を実行した場合、これは非常に簡単で、単に新しいフレームレートになります。したがって、ターゲット fps の場合45、新しいtbnは になります45


3ダミービデオを生成します。

ffmpeg -f lavfi -i color=s=1280x720:r=45:d=1 -profile:v main -video_track_timescale 45 0.mp4

最良の結果を得るには、解像度、H.264 プロファイル、ピクセル形式、参照数などのすべてのプロパティが同じである必要があります。


4ビデオを連結します。

まずテキストファイルを作成する

file '0.mp4'
file 'in-v30.mp4'

そして、連結

ffmpeg -f concat -i list.txt -c copy -video_track_timescale 45 45fps.mp4

出力ファイルには、2 番目のビデオが 45 fps で再生されます。

5ダミーのプレロールを切り取ります

ffmpeg -ss 1.1 -i 45fps.mp4 -c copy -avoid_negative_ts make_zero in45.mp4

そしてあなたは

Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1757 kb/s, \
       45 fps, 45 tbr, 11520 tbn (default)

これは複雑だと言ったじゃないか!

答え2

ffmpeg -itsscale 1.0427083 -i input.mp4 -codec copy output.mp4

これは、Handbrake によって PAL DVD ソースから作成された 25 fps mp4 を 23.974 fps に正しく減速します。元の番組は NTSC です。オーディオは、現在 47 分間の再生時間中ずっと同期しています。デコード/エンコードが行われていないため、非常に高速です。ただし、約 3 秒間隔でオーディオの不具合 (ドロップアウト) が全体にわたって発生します。vcodecコーデックを置き換えた場合も同じ結果になりますが、ビデオは再エンコードされませんが、オーディオは元のビットレートの半分で再エンコードされ、ドロップアウトの不具合が依然として発生します。

ffmpeg -itsscale 1.0427083 -i input.mp4 -vcodec copy -filter:a "atempo=0.959041" output.mp4

これにより、オーディオのドロップアウトがなくなりますが、オーディオは再エンコードされます。これは、ビデオを再エンコードするよりもはるかに高速です。残りの欠点は、デフォルトで元のオーディオ ビット レートの半分に設定されることです。再エンコードのオーディオ ビット レートを設定する方法を理解する必要があります。

答え3

使用-itsscale入力ビデオに適用して、効果的なフレームレートの変更を実現します。 で問題なく動作します-vcodec copy

答え4

これは、ビットストリーム フィルタを使用して行うことができますsetts。これにより、生のストリームをファイルに書き出してから再多重化する手間も省けます。これが機能するのは、コーデック コピー ( -c:v copy) では通常のフィルタはデコードされたビデオ ストリームで機能するため使用できませんが、エンコードされたストリームで機能するビットストリーム フィルタを使用できるためです。

例えばフレームレートを60fpsに変更するには、

-bsf:v setts=ts=STARTPTS+N/TB_OUT/60

これはストリームをデコードせずに両方を設定する必要があります。可変フレームレートのビデオの場合は、代わりに次のようなものが必要になる場合がありますptsdts

-bsf:v setts=ts='if(PREV_OUTPTS+9223372036854775808\,PREV_OUTPTS\,STARTPTS)+PREV_OUTDURATION*2'

これは、各フレームの継続時間が一定であると想定するのではなく、フレームの継続時間を変更します。奇妙に見えるif式は、最初のフレームが正しくオフセットされていることを確認するためのものです。最初のフレームでは が (64 ビット int の最小値)PREV_OUTPTSに設定されており-9223372036854775808、これをゼロに置き換える必要があります。

Bフレームがある場合、 をdtsより小さくする必要があるため、デコードがうまくいかない可能性がありますが、私の場合は問題なく動作するようです。問題が発生した場合はを にpts置き換えてみてください。setts=tssetts=ptshttps://ffmpeg.org/ffmpeg-bitstream-filters.html#setts詳細については

関連情報