更改 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

但當我執行它時,我只能得到相同的視訊幀率。在 ffmpeg 中執行此操作的正確方法是什麼(如果存在)?

答案1

這是使用目前版本的 FFmpeg 的方法。它依賴 concat 解復用器,不會在第一個檔案之後重新調整輸入的 PTS,而只是應用固定偏移量。假設您有一個 30 fps 的流,時間尺度為15360(典型的 FFmpeg 輸出)。這意味著幀0有 PTS0並且幀30有 PTS 15360。如果我們可以在23040不影響 PTS 值的情況下更改時間刻度,這將成為 45 fps 流。

本質上,這就是下面的方法的作用。

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應該具有與新的相同的值。如果您執行了步驟 2a,這非常簡單,這只是新的幀速率。因此,對於目標 fps , new應該是。xtbn45tbn45


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

輸出檔案將以 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) 一起使用,因為它們適用於解碼的視訊串流,但您可以使用適用於編碼流的位元流過濾器。

例如,若要將幀速率變更為 60 fps,請插入

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

這應該設置pts並且dts不解碼流。如果您有可變幀速率視頻,您可能需要這樣的東西

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

這會修改幀的持續時間,而不是假設每個幀具有恆定的持續時間。看起來很奇怪的if表達式是為了確保第一幀正確偏移,因為在第一幀期間PREV_OUTPTS設定為-9223372036854775808(64位元int的最小值),我們需要將其替換為零。

如果你有 B 幀,這可能會擾亂解碼,因為dts可能需要小於pts,但無論如何它對我來說似乎運作得很好。您可以嘗試替換setts=tssetts=pts如果遇到問題,看https://ffmpeg.org/ffmpeg-bitstream-filters.html#setts欲了解詳情

相關內容