本文主要对照 Kodi 对 VobSub(idx/sub)的客户端原生路径,与媒体库产品把 VobSub 统一转成 Blu-ray PGS(.sup) 再按既有 bitmap 合同分发的做法。目标是讲清架构差异与历史演进,不是承诺恢复 pair 数据面或各端自带 VobSub 渲染器。
媒体库字幕主线通常是:主视频不烧录;HLS 主输出固定去字幕流;客户端只提交 subtitleTrackId;服务端返回 presentation descriptor,客户端走唯一呈现路径。VobSub 一旦进入多端(Web / libmpv / Media3),「谁 demux、谁渲染、时间轴以谁为准」会迅速分叉。
Kodi:什么叫「原生 VobSub」
文件模型
| 文件 | 内容 |
|---|---|
.idx | 文本索引:语言轨、timestamp + filepos、palette / size 等 |
.sub | 二进制 MPEG PS 中的 DVD SPU 包(常见 00 00 01 BA) |
完整路径需要 pair;孤立 .sub 不会被当成完整 VobSub 主路径。
发现与 demux
播放本地片源时扫描媒体旁路目录(subs / subtitles / vobsubs 等),对 .idx 找配对 .sub。专用 demux 大致:
- 文本读
.idx - 用 FFmpeg 以
video/x-vobsub→ mpeg 打开.sub - 用 idx 的 pts 覆盖 packet 时间戳
- 解码走 FFmpeg
AV_CODEC_ID_DVD_SUBTITLE,渲染进自有 overlay
观察: Kodi 的「原生」= 自研 idx 时间轴 demux + 文件发现,解码主力仍是 FFmpeg dvdsub,不是从零写一套与 FFmpeg 无关的 DVD 解码器。碟片 SPU 菜单高亮路径与外挂 VobSub 也不完全同一条。
这对「我们自己做一个 VobSub 组件」的估工作量有直接含义:至少要覆盖发现、pair、时间轴、多语言轨、overlay 几何,而不是「调一个解码 API」。
媒体库侧:统一转 PGS 主线
当前合同(转 SUP 后)
| 来源 | 产品主线 |
|---|---|
| 内嵌 VobSub | 服务端转 单语言 complete-file .sup |
| 外置 idx/sub | catalog 按语言拆轨后同样转 SUP |
| 各端呈现 | 复用既有 PGS / bitmap-overlay 路径 |
典型端侧消费:
| 客户端 / 内核 | HTTPFILE | HLS |
|---|---|---|
| Web | bitmap-overlay + complete-file track.sup | 同左 + timeline offset |
| Windows / Android libmpv | 不走 VobSub native-embedded;sidecar / 与 PGS 同类 | 主输出 -sn + sidecar |
| Android Media3 | app-layer PGS overlay;不是 Exo 原生 VobSub | complete-file overlay |
转换侧质量合同往往包括:外置时间优先 IDX timestamp;画布保留源 size;颜色映射避免整字发黑;几何安全边距等。这些规则是「转一次、各端一致」的基础——若客户端各自 demux/渲染,必须在某处复刻,否则端间观感分叉。
为何收敛到转 SUP
历史演进(摘要):
- 外置 pair 阶段:Web 有
loadVobSub+ index/data URL;Media3 基本不支持;内嵌长期缺口。 - native-embedded 窗口:HTTPFILE 上曾允许内嵌 bitmap in-band 策略,但内嵌 VobSub 产品上仍常因路由 / 资源缺失不可播——策略允许 ≠ 全端可用。
- 统一转 PGS:删除 pair 端点与 Web loadVobSub;presentation 显式禁止 VobSub in-band native-embedded;各端只消费
track.sup。
动机可以压成三条:
- 各端 VobSub 能力不一致,矩阵维护成本高
- PGS 路径已在 Web / libmpv / Media3 overlay 打通
- 服务端可集中落实时间轴与颜色合同
「客户端自己处理」要按三维矩阵想
讨论「不走服务端转 SUP」时,至少拆开:
| 轴 | 取值 |
|---|---|
| 内核 | Web / Windows libmpv / Android libmpv / Android Media3 |
| 交付 | HTTPFILE 完整源文件 / HLS 无字幕主输出 |
| 来源 | 内嵌 dvd_subtitle / 外置 idx+sub |
再叠加身份命名空间:
UI / API: subtitleTrackId
Catalog: 外置索引 / 内嵌 ffmpegStreamIndex
Kernel: mpv sid | Media3 track | Web overlay session
libmpv 侧还有既有铁律:禁止把业务 track id 或源 ffmpegStreamIndex 直接当 sid;HLS 更禁止用源 index 映射当前 playlist 的 sid。这些约束与「能不能解 dvdsub」无关,却决定远程场景能不能做对。
可行性观察(非产品决议)
| 方向 | 观察 |
|---|---|
| 本机外挂 + 单内核 | 最接近 Kodi;与媒体库「远端 session」模型仍不同 |
| HTTPFILE 内嵌 + libmpv | 技术上可能讨论 in-band;要重做选轨与禁止误映射 |
| HLS + 任意内核 | 主输出无字幕时,客户端仍需要 独立字幕资源;等价于另一种 sidecar 合同 |
| Media3 原生 VobSub | 不能假设 Exo 开箱等于 Kodi pair 体验 |
| 每端一个组件 | 可能让部分格子亮起来;时间轴 / 颜色 / 多端一致要付四次税 |
有可能性 ≠ 值得做。 统一转 SUP 用一次服务端复杂度换四端合同收敛,是媒体库多端场景里更常见的取舍。
和 PGS 压扁问题的衔接
转 SUP 之后,客户端仍可能踩 PGS plane AR ≠ 视频 AR 的绘制问题(尤其 Media3)。那是 bitmap 呈现层的几何问题,见 Android Media3 PGS字幕画幅排障。转格式解决的是 格式分裂,不自动解决 overlay 几何。
相关阅读
参考与边界
- Kodi 开源树中外挂字幕扫描、
DVDDemuxVobsub、FFmpeg overlay codec 路径 - 媒体库侧以当前 presentation / route 策略与已归档「VobSub→PGS」计划为准
- 本文不做「是否恢复 pair」的产品决策,也不给改动清单