Skip to content

libass 源码阅读 00 - 开始

Published: at 22:56

ToC

前言

在字幕渲染领域,libassxy-VSFilter 可以说是两架马车。libass 使用 C 语言编写,根据 Google Code 存档的说法,在大多数情况下 libass 的效率比 xy-VSFilter50%。从支持的角度来看,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 - libass 测试样例的执行流程图
图 1 - libass 测试样例的执行流程图

图中实心箭头表示这个样例中主干部分的执行流程,而空心箭头则表示了详细的内部调用过程。这里我们只列出了大致的调用流程,省略了错误处理等情况。

简单梳理一下,图 1 中相对重要的部分有:**font_provider****ass_library****ass_renderer****ass_read_file****ass_render_frame**,分别对应了 libass 中的字体**ASS Library****ASS Renderer****ASS Track**实际渲染部分。暂时忽略实际渲染,我们来看一看剩下的四个概念。

字体

字幕字幕,核心是。既然有字那就必然存在字体libass 中字体相关的架构如图 2 所示:

图 2 - 字体相关功能在 Library 和 Renderer 的分布
图 2 - 字体相关功能在 Library 和 Renderer 的分布

可以看到,字体的来源有两种:内嵌字体(Embbed Font)和外部字体。内嵌字体位于 ASS 文件的字体行中;而外部字体则和不同的操作系统有着密切的联系,libass 把对这部分的抽象命名为 Font Provider

不同的操作系统对外部字体的支持方式不尽相同。如 AppleCore Text[2],Win32DirectWrite[3],Linuxfontconfig[4] 等,各自都是互不相同的字体实现方案。

样例通过 ass_get_available_font_providers 函数获取了系统中可用的字体实现方案。

ASS Library

从功能角度而言,ASS_Library 的职责并不单一:主要负责内嵌字体样式相关的内容,同时也兼任消息回调的执行。

TODO: 将代码修改为图表形式

1
struct ass_library {
2
char *fonts_dir;
3
int extract_fonts;
4
char **style_overrides;
5
6
ASS_Fontdata *fontdata;
7
int num_fontdata;
8
void (*msg_callback)(int, const char *, va_list, void *);
9
void *msg_callback_data;
10
};
11
12
typedef struct ass_library ASS_Library;

libass 中,ASS_Library 还算常见。当你遇到它但又不记得具体功能时,回来看看就行了。

消息回调(msg_callback

libass 通过消息回调向外界输出日志。为使这个过程更加灵活,libass 使用了消息回调的方式,调用用户定义的 msg_callback 函数进行输出。样例在渲染米粒垃圾 393 回 omake 时的日志输出如图 3 所示:

图 3 - test 渲染 ミリラジ 393 回 omake 第 10 秒时的日志输出
图 3 - test 渲染 ミリラジ 393 回 omake 第 10 秒时的日志输出

ASS Renderer

作为字幕渲染库,libass 的本质工作终究还是渲染。ASS_Renderer 就是 libass 中负责渲染的核心组件。

ASS Renderer 中记录了渲染所需的全部信息。由于这个部件过于复杂,我们之后单独论述。

Ass Track

正确读入 ass 字幕文件是渲染的前提,libass 中通过 ASS_Track 存储字幕,其基本结构如图 4 所示:

图 4 - 字幕解析存储格式
图 4 - 字幕解析存储格式

可以看到,ASS Track 包含了一个字幕文件的全部信息,而 ASS ParserPriv 则包含一些解析时会用到的非公开信息。

小结

通过 test/test.c 的示例,我们简单梳理了 libass 中的一些基本概念。本文也是继技术型博客行文迷思(1)之后的第一篇博客,期待各位在可读性、流畅性等方面提出宝贵意见(笑)。

参考

  1. https://github.com/libass/libass/releases/tag/0.15.0
  2. https://developer.apple.com/documentation/coretext
  3. https://docs.microsoft.com/en-us/windows/win32/directwrite/direct-write-portal
  4. https://wiki.archlinux.org/index.php/Font_configuration_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)