ToC
前言
在字幕渲染领域,libass
和 xy-VSFilter
可以说是两架马车。libass
使用 C
语言编写,根据 Google Code
存档的说法,在大多数情况下 libass
的效率比 xy-VSFilter
快 50%
。从支持的角度来看,ffmpeg
支持 libass
而非 xy-VSFilter
,而 Linux
版本的 Aegisub
也只有 libass
可以选用。
最初对 libass
源码的兴趣源于 vsfiltermod
中的 \fsc
标签。根据描述,其功能近似于同时设置 \fscx
和 \fscy
,而在实验中,我发现 libass
也会解析 \fsc
,但其只会将字体大小复位,和 vsfiltermod
的效果不同。由此,我开始了对 libass
源码的探索之旅。
本系列使用的 libass
源码的 commit hash
为 `d149636`
[1],对应 libass
版本号为正式版 0.15.0
。
基本流程
我们的样例是 test/test.c
。这个样例的功能是调用 libass
,读取字幕文件并输出某一秒的渲染结果为 png
图片。通过梳理源码,我们可以整理出简单的程序执行流程,如图 1 所示:
图中实心箭头表示这个样例中主干部分的执行流程,而空心箭头则表示了详细的内部调用过程。这里我们只列出了大致的调用流程,省略了错误处理等情况。
简单梳理一下,图 1 中相对重要的部分有:**font_provider**
、**ass_library**
、**ass_renderer**
、**ass_read_file**
和 **ass_render_frame**
,分别对应了 libass
中的字体、**ASS Library**
、**ASS Renderer**
、**ASS Track**
和实际渲染部分。暂时忽略实际渲染,我们来看一看剩下的四个概念。
字体
字幕字幕,核心是字。既然有字那就必然存在字体。libass
中字体相关的架构如图 2 所示:
可以看到,字体的来源有两种:内嵌字体(Embbed Font
)和外部字体。内嵌字体位于 ASS
文件的字体行中;而外部字体则和不同的操作系统有着密切的联系,libass
把对这部分的抽象命名为 Font Provider
。
不同的操作系统对外部字体的支持方式不尽相同。如 Apple
的 Core Text
[2],Win32
的 DirectWrite
[3],Linux
的 fontconfig
[4] 等,各自都是互不相同的字体实现方案。
样例通过 ass_get_available_font_providers
函数获取了系统中可用的字体实现方案。
ASS Library
从功能角度而言,ASS_Library
的职责并不单一:主要负责内嵌字体和样式相关的内容,同时也兼任消息回调的执行。
TODO: 将代码修改为图表形式
在 libass
中,ASS_Library
还算常见。当你遇到它但又不记得具体功能时,回来看看就行了。
消息回调(msg_callback
)
libass
通过消息回调向外界输出日志。为使这个过程更加灵活,libass
使用了消息回调的方式,调用用户定义的 msg_callback
函数进行输出。样例在渲染米粒垃圾 393 回 omake
时的日志输出如图 3 所示:
ASS Renderer
作为字幕渲染库,libass
的本质工作终究还是渲染。ASS_Renderer
就是 libass
中负责渲染的核心组件。
ASS Renderer
中记录了渲染所需的全部信息。由于这个部件过于复杂,我们之后单独论述。
Ass Track
正确读入 ass
字幕文件是渲染的前提,libass
中通过 ASS_Track
存储字幕,其基本结构如图 4 所示:
可以看到,ASS Track
包含了一个字幕文件的全部信息,而 ASS ParserPriv
则包含一些解析时会用到的非公开信息。
小结
通过 test/test.c
的示例,我们简单梳理了 libass
中的一些基本概念。本文也是继技术型博客行文迷思(1)之后的第一篇博客,期待各位在可读性、流畅性等方面提出宝贵意见(笑)。