惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
人人都是产品经理
人人都是产品经理
IT之家
IT之家
Cyberwarzone
Cyberwarzone
T
Troy Hunt's Blog
有赞技术团队
有赞技术团队
阮一峰的网络日志
阮一峰的网络日志
T
Threat Research - Cisco Blogs
S
SegmentFault 最新的问题
Apple Machine Learning Research
Apple Machine Learning Research
G
GRAHAM CLULEY
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
博客园 - 叶小钗
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes
The Hacker News
The Hacker News
Jina AI
Jina AI
T
Tor Project blog
V
Vulnerabilities – Threatpost
酷 壳 – CoolShell
酷 壳 – CoolShell
Spread Privacy
Spread Privacy
博客园_首页
C
Cybersecurity and Infrastructure Security Agency CISA
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Simon Willison's Weblog
Simon Willison's Weblog
Security Latest
Security Latest
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
博客园 - 司徒正美
V2EX - 技术
V2EX - 技术
I
Intezer
The Cloudflare Blog
Cisco Talos Blog
Cisco Talos Blog
SecWiki News
SecWiki News
博客园 - 【当耐特】
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
L
Lohrmann on Cybersecurity
Scott Helme
Scott Helme
Google Online Security Blog
Google Online Security Blog
量子位
The Last Watchdog
The Last Watchdog
AI
AI
Application and Cybersecurity Blog
Application and Cybersecurity Blog
S
Security Affairs
P
Palo Alto Networks Blog
S
Secure Thoughts
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Attack and Defense Labs
Attack and Defense Labs

Yesterday17's Blog

2026 新年解密红包 / Melody Flag | Yesterday17's Blog 谈谈 Iori 的设计思路(二):如何实现一个 Showroom 录制工具? | Yesterday17's Blog 谈谈 Iori 的设计思路(一):从 Nico Timeshift 说起 | Yesterday17's Blog Iori Minyami 0.1.0 发布 | Yesterday17's Blog 2025 新年解密红包 / Melody Flag | Yesterday17's Blog 使用 Cloudflare Warp 解决罗森票务的海外登录问题 | Yesterday17's Blog How To Blog 04: The Astro v5 Era | Yesterday17's Blog 谈谈 tokio::select! 的公平性 | Yesterday17's Blog Learning Pingora 05 - Connect with TLS | Yesterday17's Blog Leaving Bytedance | Yesterday17's Blog 大橋彩香 AsiaTour「Reflection」上海公演 个人向记录 & Repo | Yesterday17's Blog Recoving from burnout - What happened? | Yesterday17 Yubikey 重建手册 | Yesterday17's Blog How To Blog 03: Heimus | Yesterday17's Blog 🪧 Blog Migration Accouncement | Yesterday17's Blog Learn Your IDE - VSCode 是如何仅重启插件的? | Yesterday17's Blog How To Blog 02: Astro❤️Password | Yesterday17's Blog How To Blog 01: Why, How, and the Future | Yesterday17's Blog Learning Pingora 04 - Establish L4 Connection | Yesterday17's Blog Learning Pingora 03 - Upstreams and Peers | Yesterday17's Blog Learning Pingora 02 - A Simple HTTP Server | Yesterday17's Blog Learning Pingora 01 - Getting Started | Yesterday17's Blog 2024 新年解密红包 / Melody Flag | Yesterday17's Blog 向新的一年飞驰——记录 2023 | Yesterday17's Blog 「サクラノ刻」对话选摘(2) | Yesterday17's Blog PGP Key Revocation 注销声明 | Yesterday17's Blog 「サクラノ刻」对话选摘(1) | Yesterday17's Blog 2023 新年解密红包 / Melody Flag | Yesterday17's Blog 『蒼の彼方のフォーリズム』通关感想 | Yesterday17's Blog 单显卡直通教程 | Yesterday17's Blog 对博客与笔记的思考 | Yesterday17's Blog Project Anni 之旅(3)自动化 Flutter 应用 CI/CD 上架流程 | Yesterday17's Blog AsobiStage 直接播放链接 | Yesterday17 如何在后分P时代进行投稿——sswa使用详解 | Yesterday17's Blog JSON RPC 与 LSP 协议基础 | Yesterday17's Blog Grajapa Shueisha / BookEnd 加密方式调查 | Yesterday17's Blog 【2022篇+WriteUp】如何再收一个新年红包? | Yesterday17's Blog 如何将良心云的良心功能清理干净 | Yesterday17's Blog 【油猴脚本】bilibili 投稿页面返回旧版+旧版页面强制允许分P上传 | Yesterday17's Blog Cloudr1v1 授权方式分析 | Yesterday17 Typora 1.0.2 逆向实录 | Yesterday17's Blog Project Anni 之旅(2)ValueAfterTable——toml-rs的实现与限制 | Yesterday17's Blog IPv4透明代理+IPv6 Passthrough——树莓派单臂软路由折腾记 | Yesterday17's Blog Chaos; Child 汉化补丁 神秘编码探索 | Yesterday17's Blog 镣铐与舞蹈——个性与共性之迷思 | Yesterday17's Blog Go 学习笔记 02 - 找准 io 之道 | Yesterday17's Blog NAT Slipstreaming v1 原理浅析 | Yesterday17's Blog 绕过「9-nine-」的 CDKEY 验证——KrkrPlugin 正(?)向实录 | Yesterday17's Blog 静流的青春纪念册——「サクラノ刻 -櫻の森の下を歩む-」体验版感言 | Yesterday17's Blog Project Anni 之旅 01 - 从 clap-builder 到 derive | Yesterday17's Blog [Google CTF 2021] CPP WriteUp | Yesterday17's Blog 获取 アソビステージ 的实际播放链接 | Yesterday17's Blog 90 行 Rust 代码实现 AsyncTeeReader | Yesterday17's Blog 或许还算有价值一读的文章列表 | Yesterday17's Blog 从零开始的 Seedbox 之旅 | Yesterday17's Blog [随笔]技术型博客行文迷思(1) | Yesterday17's Blog 浅谈 git fetch 的工作方式 | Yesterday17's Blog 『ソーサレス*アライヴ! ~the World's End Fallen Star~』通关感想" | Yesterday17's Blog Rust std::fmt 格式语法简述 | Yesterday17's Blog 日亚修改居住国的解决方案 | Yesterday17's Blog [Windows/Linux] GC553 的 Switch 完美采集之路 | Yesterday17's Blog 【翻译】Subtyping and Variance / 子类型与变型 | Yesterday17's Blog Berd's Red Envelope 2021 WriteUp | Yesterday17's Blog 【中英对照】ALSA 音频 API 使用教程/A Tutorial on Using the ALSA Audio API | Yesterday17's Blog 从 cue_scanner.l 看 CUE Sheet 的词法单元 | Yesterday17's Blog Postman 历史记录导出的解决方案 | Yesterday17's Blog 《恋爱绮谭 不存在的夏天》通关感想 | Yesterday17's Blog [微机实验/TD-PITE] 微机接口综合实验 | Yesterday17's Blog [微机实验/TD-PITE] 键盘扫描及数码管显示实验 | Yesterday17's Blog [微机实验/TD-PITE] 数码管显示实验 | Yesterday17's Blog Airsonic Advanced+Google Drive+Caddy 部署纪实 | Yesterday17's Blog X-NUCA 2020 - hellowasm 题解 | Yesterday17's Blog [微机实验/TD-PITE] 8251 串行接口实验 | Yesterday17's Blog Node.js child_process.fork 与 env 污染 RCE | Yesterday17's Blog EP.01 「夜の向日葵」 | Yesterday17's Blog [微机实验/TD-PITE] 8254 定时/计数器实验+选做实验 | Yesterday17's Blog [JLU CTF/2020] babywasm WriteUp | Yesterday17's Blog PHP 反序列化与经典利用 | Yesterday17's Blog WebAssembly 逆向简述 | Yesterday17's Blog 『彼女、お借りします』一期完结点评 | Yesterday17's Blog [微机实验/TD-PITE] D/A 转换实验+选做实验 | Yesterday17's Blog [微机实验/TD-PITE] A/D 转换实验+选做实验 | Yesterday17's Blog 开源项目申请 JetBrains Open Source License 简单流程 | Yesterday17's Blog 微软拼音与 JetBrains 搜索快捷键冲突的解决方案 | Yesterday17's Blog [微机实验/TD-PITE] 8259 中断优先级实验+选做实验 | Yesterday17's Blog IFTTT 测试(续) | Yesterday17 IFTTT 测试 | Yesterday17's Blog [微机实验/TD-PITE] 存储器扩展实验+选做实验 | Yesterday17's Blog 新版 GCC 针对 -fdump-translation-unit 的替代方案 | Yesterday17's Blog 一次 HSTS 策略配置的排错之旅 | Yesterday17's Blog YukiNative 踩坑记——Windows 的消息队列 | Yesterday17's Blog 我是我自己——论获取 HTTPS 证书时的验证步骤 | Yesterday17's Blog 【设计文档】对 PUG 的大规模设计修订(1.1) | Yesterday17's Blog GS65 折腾记(2)加装固态,分区,Grub2 引导 Manjaro LiveCD | Yesterday17's Blog 「さくら、もゆ。」的空白字体列表——一次逆向问题定位过程实录 | Yesterday17's Blog GSuite 探索篇(1)使用 Service Account 向 Google Drive 传输文件 | Yesterday17's Blog 『サクラノ詩 -櫻の森の上を舞う-』通关感想 | Yesterday17's Blog 《ATRI -My Dear Moments-》通关感想 | Yesterday17's Blog [工具][VSCode 扩展] AegiKit——方便 Aegisub 使用的工具箱 | Yesterday17's Blog 贝塞尔曲线、字体矢量化与曲线运算 | Yesterday17's Blog
libass 源码阅读 00 - 开始 | Yesterday17's Blog
Yesterday17 · 2020-06-27 · via Yesterday17's Blog

ToC

  • 前言
  • 基本流程
  • 字体
  • ASS Library
    • 消息回调(msg_callback
  • ASS Renderer
  • Ass Track
  • 小结
  • 参考

前言

在字幕渲染领域,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: 将代码修改为图表形式

struct ass_library {

char *fonts_dir;

int extract_fonts;

char **style_overrides;

ASS_Fontdata *fontdata;

int num_fontdata;

void (*msg_callback)(int, const char *, va_list, void *);

void *msg_callback_data;

};

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)