我使用两种不同的方式通过 ffmpeg 为视频添加字幕。第一种方法是使用
drawtext
命令,这样一切都会完美运行。这是命令
ffmpeg -i ./input.mp4 -vf "drawtext=text='reise':fontfile=../fonts/Audiowide-Regular.ttf:fontsize=55:fontcolor=white:x=0:y=683" -codec:a copy ./output.mp4
第二种方法是使用ass字幕文件。这样我得到了较小的字母和错误的文本 y 位置。以下是屁股字幕文件内容
[Script Info]
Title: Advanced Highlighted Subtitle Example
ScriptType: v4.00+
WrapStyle: 0
PlayResX: 1048
PlayResY: 750
[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,Audiowide Regular,55,&HFFFFFF,&H00FFFFFF,&H00000000,&H00000000,1,0,0,0,100,100,0,0,0,0,0,2,10,10,10,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,00:00:0.00,00:00:2.38,Default,,0,0,0,,{\pos(0,683)\an4}reise
以及第二种方法的命令
ffmpeg -i ./input.mp4 -vf "ass=../subtitles.ass:fontsdir=../fonts/Audiowide-Regular.ttf" ../output.mp4
所以他们都得到相同的视频、相同的字体和相同的文本。问题是,如果使用 ass 文件,文本会小得多并且错位
轴上的数字表示以像素为单位的大小。 正如您在第二张图片中看到的那样,它小得多并且 y 坐标错误。似乎它的缩放数字错误。我的 ass 文件配置有什么问题?
我尝试了 [universalmediaserver] 的解决方案来删除 PlayResX/Y,但它不起作用。 (https://www.universalmediaserver.com/forum/viewtopic.php?t=5907)。我还尝试以许多其他方式测量文本宽度(例如在浏览器、画布中渲染的 html 中),所以我非常确定
drawtext
确实给出了正确渲染的宽度。该问题与 ass 字幕文件有关。另外,如果我使用像 Arial 这样的流行字体,偏差就会小得多。
FFmpeg 的
drawtext
过滤器对文本字体大小的解释与 ASS 渲染器(如 Libass)不同。 drawtext
过滤器使用字体的标称大小(以像素为单位),根据每个 EM 的单位进行缩放。相比之下,ASS 渲染器使用字体的实际尺寸进行缩放,这是通过对字体表(例如 OS/2 和 hhea)中的 ascender
字段求和并减去 descender
字段的值来确定的。
因此,为了匹配 FFmpeg 的
drawtext
和 ASS 之间的大小,我们需要找到从标称大小 (drawtext
's) 计算字体实际尺寸大小 (ASS's) 的方法。因此,我们首先计算字体的基本尺寸,然后用于缩放它。
对于标称尺寸,我们需要从
字体标题表中读取
unitsPerEm
,如果是Audiowide字体,则为2048。
对于实际尺寸大小,我们需要获取 ascender
和 descender
字段值,可以在 hhea 表 中找到,对于 Audiowide 字体,其上升部分为 2027,下降部分为 -584。
那么: 标称尺寸 =
unitsPerEm
= 2048
实际尺寸大小 = ascender
- descender
= 2027 - (-584) = 2611
那么实际尺寸会大一些。
比例=实际尺寸/标称尺寸=2611 / 2048 ≈ 1.279
所以我们需要将原始字体大小(55)乘以比例因子:55 * 1.279 ≈ 70.345
其次,请注意,drawtext 过滤器默认使用左下对齐,而您使用的 ASS 中的 n4 对齐标签对应于左中对齐。为了匹配位置,您应该在 ASS 中使用 n7(左上对齐)。
第三,
drawtext
将文本对齐到最高字形(出于历史原因),而不是基线加上升(通常是如何完成的),但您可以通过在y_align=font
过滤器中设置drawtext
来更改此设置。
这里是更正后的 ASSv4+ 脚本文件:
[Script Info]
Title: Advanced Highlighted Subtitle Example
ScriptType: v4.00+
WrapStyle: 0
PlayResX: 1048
PlayResY: 750
[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,Audiowide,70.345,&H00FFFFFF,&H00FFFFFF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,0,0,2,10,10,10,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,00:00:0.00,00:00:2.38,Default,,0,0,0,,{\pos(0,683)\an7}reise
并更正了 FFmpeg 命令:
ffmpeg -i ./input.mp4 -vf "drawtext=text='reise':fontfile=../fonts/Audiowide-Regular.ttf:fontsize=55:fontcolor=white:x=0:y=683:y_align=font" -codec:a copy ./output.mp4
下面是如何在 Python 中使用 Freetype(这是
drawtext
和 ASS 渲染器 Libass 在渲染字体时使用的)从字体读取度量值的示例:
import freetype
face = freetype.Face('path/to/your/fontfile.ttf')
units_per_em = face.units_per_EM
ascender = face.ascender
descender = face.descender
print(f"Units per EM: {units_per_em}")
print(f"Ascender: {ascender}")
print(f"Descender: {descender}")
Audiowide 字体的结果 应该是 :
Units per EM: 2048
Ascender: 2027
Descender: -584
您可以使用以下方法安装所需的库:
python -m pip install freetype-py