我有一個 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應該是。x
tbn
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
輸出檔案將以 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=ts
為setts=pts
如果遇到問題,看https://ffmpeg.org/ffmpeg-bitstream-filters.html#setts欲了解詳情