影片播放與轉檔 - [FFmpeg] 烙印字幕至影像上 (Hardsub) - 電腦

前往內容


[FFmpeg] 烙印字幕至影像上 (Hardsub)

FFmpeg

簡介與其他資料:
 http://www.mobile01.com/topicdetail.php?f=510&t=3734550

其他範例:
  http://www.mobile01.com/topicdetail.php?f=510&t=4487462


參考資料

FFmpeg 官方資料:
  https://trac.ffmpeg.org/wiki/HowToBurnSubtitlesIntoVideo

匯入或匯出字幕可以參考這篇:
  http://www.mobile01.com/topicdetail.php?f=510&t=4458621


介紹

如果你的播放設備不支援字幕 (softsub, 也就是封裝在影片內的字幕) ,
你就要重新編碼視訊將字幕烙印在畫面上 (hardsub),
播放時一定有字幕,因為字幕是烙印在畫面上,有畫面就有字幕。


subtitles filter

要將字幕烙印至影像上做成 Hardsub 必須使用到 libass (subtitles filter),
ffmpeg 的 configuration 如果含有 --enable-libass 則代表 libass 是可用的。

Windows 必須要 fonts-conf 才能使 libass 正常工作,見附件。

subtitles filter 官方說明:
https://www.ffmpeg.org/ffmpeg-all.html#subtitles-1

必須要重新編碼視訊才可以將字幕烙印至影像上,
所以不能使用 "-codec:v copy" / "-c:v copy" 或 "-vcodec copy" 設定來複製視訊串流。

subtitles filter 只能指定輸入字幕檔名,無法指定路徑,
所以必須將字幕檔放置在工作目錄(%CD%)下。

將 ASS 字幕做成 Hardsub:
ffmpeg -input.mp4 -vf "subtitles=filename='sub.ass'" output.mp4

若輸入檔名為第一個可以省略選項名 (filename, f):
ffmpeg -input.mp4 -vf "subtitles='sub.srt'" output.mp4

從輸入影片內讀取第1個(#0)字幕做成 Hardsub:
ffmpeg -input.mkv -vf "subtitles=input.mkv:si=0" output.mkv


如果需要強制指定字幕的樣式,
可以先使用 Aegisub 在字幕內設定所需樣式幕並另存為 ASS 檔,再給 FFmpeg 處理。
Aegisub: http://www.mobile01.com/topicdetail.php?f=510&t=4192039

或者可以使用 subtitles filter 的 force_style 選項

強制覆蓋字幕樣式:
-vf "subtitles='sub.srt':force_style='FontName=Microsoft JhengHei,FontSize=20,PrimaryColour=&HAA00FF00'"


若 subtitles filter 輸入的影像不是正方形像素(PAR 1:1),這會導致輸出字幕寬高比例錯誤
http://www.mobile01.com/topicdetail.php?f=510&t=4038239

假設 input.mkv 為 1280x720 [PAR 1:1 DAR 16:9],

ffmpeg -i input.mkv -vf "scale='w=704:h=480',subtitles='sub.srt'" output.mkv

將解析度轉為 704x480 ,由於 scale filter 預設會使輸出 DAR 與輸入相同 (16:9),
所以 PAR = (16 / 9) / (704 / 480) = 40 / 33
由於 subtitles filter 的輸入像素寬高(PAR)比不是 1:1,輸出字幕將會得到錯誤的寬高比。

所以可以將 subtitles filter 移動到 scale filter 之前:
ffmpeg -i input.mkv -vf "subtitles='sub.srt',scale='w=704:h=480'" output.mkv

或是使用 subtitles filter 的 original_size 選項指定影像解析度讓字幕寬高正常縮放:
ffmpeg -i input.mkv -vf "scale='w=704:h=480',subtitles='sub.srt':original_size='854x480'" output.mkv

480*16/9=853.333...


以圖片為基礎的字幕

由於目前 subtitles filter 只能使用 ASS/SSA、SRT... 之類的文字字幕,若字幕為 "以圖片為基礎" 的格式,例如,Blu-ray 字幕 (hdmv_pgs_subtitle) 或 DVD 字幕 (dvdsub) 是以圖片為基礎的類型,你可以透過使用 overlay video filter 將字幕覆蓋至影像上。

將 M2TS 內的視訊與字幕串流結合為一個視訊串流:
ffmpeg -i input.m2ts -filter_complex "[0:v][0:s]overlay[v]" -map "[v]" -map 0:a -c:v libx264 -c:a copy output.mkv

假設 M2TS 檔案內有多個字幕串流,
如果要挑選第二個字幕串流可以使用 "0:s:1" 這樣的串流說明符:
ffmpeg -i input.m2ts -filter_complex "[0:v][0:s:1]overlay[v]" -map "[v]" -map 0:a -c:v libx264 -c:a copy output.mkv

串流說明符 請見以下連結:
http://www.mobile01.com/topicdetail.php?f=510&t=4200388


附件

fonts-conf
※最新版 FFmpeg 已不再需要這些額外檔案

libass 所需,fonts-conf 的用途是讓程式從系統中取用所需字型
所以系統必須要有安裝字幕用到的字型。

fontconfig 為 External Libraries,
ffmpeg 的 configuration 之中必須含有 --enable-fontconfig 才可用。

附加壓縮檔: fonts-conf.zip (字型組態檔案)

解壓縮後,將它放到 ffmpeg 所在路徑中即可
... \ffmpeg_dir\fonts

如下
[ffmpeg 所在目錄]\ffmpeg.exe
[ffmpeg 所在目錄]\fonts\fonts.conf
[ffmpeg 所在目錄]\fonts\fonts.dtd
[ffmpeg 所在目錄]\fonts\conf.d
kingdragon wrote:
假設 input.mkv 為 1280x720 [PAR 1:1 DAR 16:9],


K大 我有一個實務問題:

a.wmv 400x225 (一支MV)

替它作了一個字幕 a.ass (因為有上傳的需要,所要以壓內嵌字幕)

我採用方法如下:

ffmpeg -i a.wmv -vf ass=a.ass -s 1280x720 -crf 17 -preset veryslow -c:a aac -b:a 128k -movflags +faststart a.mp4

結果就是字幕渲染的很難看。。。馬賽克太嚴重了

所以我分成兩次

1. 以crf 17 接近無損的方式 加它放大成1280x720
2. 再一次以crf 17 將字幕渲染進來

這時字幕才接近外掛字幕的品質。。。。

1.所以我的問題是在放大視屏過程中如何避免二壓 才能把字幕壓出上述二壓所得的字幕品質??

2.-s 1280x720 與 -vf "scale=1280x720" 是一樣的效果嗎?或有差異??

笑看風雲 wrote:
K大 我有一個實務...(恕刪)

1. 你需要先做出一個適合解析度 1280x720 的 ASS 字幕
然後使用以下命令:
ffmpeg -i a.wmv -s 1280x720 -vf "subtitles=a.ass" -c:v libx264 -crf 17 -preset veryslow -refs 4 -c:a aac -b:a 128k -movflags +faststart a.mp4

建議使用 -refs 限制 ref 數量,因為 veryslow 內的 ref 值太大
http://www.mobile01.com/topicdetail.php?f=510&t=4500233


2.

A: -s 1280x720 -vf "subtitles=a.ass"

B: -vf "subtitles=a.ass,scale=1280x720"

C: -vf "scale=1280x720,subtitles=a.ass"

B 是 ... -> subtitles -> resize to 1280x720 -> ...

C 則是 ... -> resize to 1280x720 -> subtitles -> ...

要注意如果輸出影片的 PAR 不是 1:1
則 C 方法會產生寬高比失真的字幕

PAR:
http://www.mobile01.com/topicdetail.php?f=510&t=4038239

A 則與 B 相同 (映像中舊版是與 C 相同...)
-s 在 -vf 前後都沒有差別,結果都是一樣的


kingdragon wrote:
s -> resize to 1280x720 -> ...

C 則是 ... -> resize to 1280x720 -> subtitles ->




K大 你好

在你回答問題之前,我自己也試過所有的前後排列,都沒有你上述 (2) 所說的效果

針對(1) 我有猜想會不會我的ass裡沒有制定為1280x720,所以我又編了一個版本的ass 再去壓製,並認真比對 所有結果的字幕 壓製的品質皆一樣

與我上述所說的二次壓製品質還是極大的差別。。。

所以我可能放棄 一次搞定的想法 XDDD

ps.我現在用的ffmpeg的版本是最新的151224
====================

另針對K大的所述的refs 的問題,好像限不限制,其視頻的ref都是4

之前我

我觀察過youtube 下載的mp4 及韓網發佈的720p-WITH 版本的mp4
ref都是1 且都有g (GOP)的參數
另外我剪輯cut軟體 關鍵幀 之間的移動 特別準確(都能移動到場景切換的第一幀)

所以我想請教你ref=1 是否對 剪輯軟體的 關鍵幀移動 是否是準確的關鍵???

若針對剪輯軟體的後續處理 會有較大的幫助。。。

你認為需要加那些參數 才能達成 ???
笑看風雲 wrote:
所有結果的字幕 壓製的品質皆一樣
與我上述所說的二次壓製品質還是極大的差別。。。

Aegisub:

視訊 (V) > 使用空白視訊...(U)
視訊解析度選擇 『1280x720 (HD 720p)』

檔案 (F) > 配置(P)...
手動設定為 1280x720
如果有設定使用空白視訊點擊 『從視訊獲得 (V)』即可

然後到 樣式編輯器 設定合適的字幕大小
(因為你在 ASS 字幕內指定了影片解析度
所以必須重新設定字幕大小)

做出來的 ASS 大概是這樣:
[Script Info]
; Script generated by Aegisub 3.2.2
; http://www.aegisub.org/
Title: Default Aegisub file
ScriptType: v4.00+
WrapStyle: 0
ScaledBorderAndShadow: yes
YCbCr Matrix: TV.601
PlayResX: 1280
PlayResY: 720


[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Microsoft JhengHei,72,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,2,0,2,10,10,30,1

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.00,0:00:10.00,Default,,0,0,0,,字幕測試


用 nullsrc 產生一個 400x226 的 video 來測試:
ffmpeg -f lavfi -i nullsrc=s=400x226:r=24000/1001 -t 15 -vf "scale=1280x720,subtitles=1280x720.ass" -c:v libx264 -crf 17 -preset veryslow -refs 4 -movflags +faststart 1280x720.mp4

1280x720.mp4:


笑看風雲 wrote:
我自己也試過所有的前後排列,都沒有你上述 (2) 所說的效果

因為你的輸出是 1280x720 [PAR 1:1, DAR 16:9]
如果輸出是 704x480 [PAR 40:33, DAR 16:9], 就必須注意 subtitles 與 scale 的順序

例如: 1920x1080 [PAR 1:1, DAR 16:9] -> 704x480 [PAR 40:33, DAR 16:9]
ffmpeg -i 1920x1080.mkv -vf "subtitles=sub.ass,scale=704x480,setsar=r='40/33'" output.mp4


笑看風雲 wrote:
所以我想請教你ref=1 是否對 剪輯軟體的 關鍵幀移動 是否是準確的關鍵???

正常來說只有在做直接分割(複製)時 才會注意 keyframe, GOP...
http://www.mobile01.com/topicdetail.php?f=510&t=4461015

非線性剪接軟體都是重新編碼輸出影片
理論上而言應該不會有影響
K大 你好

再請教一個壓字幕的實務問題
我在ASS檔裡,因有需求
我會把字型改為 @黑體 配合ASS特效碼{\frz90} 作旋轉90度
將語句 改為"縱向"
其中我的語句中有... (因為它的位置靠下,所以轉90度後, ... 會變成靠右)
雖然無錯 但視覺上感覺很有問題

後來我將它置換為全型符號 … (w8以上 按 Ctrl + /(?) 則可直接打出)

如此aegisub 與potplayer 看起來都是 旋轉90度並置中的'…'
但是用ffmpeg 壓製後 ,該符號'…' 卻仍唯持橫向 沒有旋轉90度的 符號

不知你是否有這方面經驗,可以幫忙研判
這個問題 是否有解
(比較好奇是aegisub 及 potplayer 都能渲染正確)
為何ffmpeg 卻無法 渲染出 想要的樣子?
笑看風雲 wrote:
(比較好奇是aegisub 及 potplayer 都能渲染正確)
為何ffmpeg 卻無法 渲染出 想要的樣子?

通常常見的 subtitles filter 為 LibASS、VSFilter
ffmpeg 所支援的是 libass,所以你使用的語法必須相容於 libass...

如果你需要 VSFilter 可以使用 VapourSynth + VSFilter

然後輸出 yuv4mpeg,pipe 給 ffmpeg:
vspipe --y4m script.vpy - | ffmpeg -i - output.mkv



1頁 (共1頁)

前往