本文主要记录 Android 上 Media3 ExoPlayer 播放 PGS / SUP 时字幕整体被竖直压扁的现象、根因与修复方向。问题出在 构图平面与视频显示宽高比不一致时的非等比布局,不是 surface 算错,也不是位图像素被竖向压缩。

以 remux 进库的 2.39:1 片源为例:视频约 3840×1608,内嵌 PGS 的 composition plane 仍是蓝光常见的 1920×1080。libmpv 同片正常;Media3 路径(内嵌与外置)在修复前字形都「扁」。

问题现象

内容
片源scope 宽画幅 + BD 系 PGS plane(约 16:9)
Exo + 内嵌 PGS(修复前)有字,整体竖直压扁 / 水平拉宽
Exo + 外置 .sup overlay(修复前)同样压扁
libmpv 同片横竖屏字形正常
视频 surfacecontain 已与片源比例一致

需要先分清两条渲染栈:

路径解码绘制
HTTPFILE 内嵌原生 PGSMedia3 PgsParser历史上走 SubtitleView / SubtitlePainter
外置 / complete-file .sup应用层 windowed decoder自绘 OverlayView

外置栈在修复前与 Media3 内嵌栈 几何模型同构(宽、高各自按视口归一化),因此两条都会扁。

排查过程

量化变形

对样例 object、视口贴视频 2.39:1 时,显示宽高比相对位图固有比约等于:

视口 AR / plane AR ≈ 2.39 / 1.78 ≈ 1.34

即相对正确字形约 宽 34%、高只剩约 74%,与观感一致。

为何有的播放器「横屏看起来还行」

部分开源播放器把字幕层铺满全屏,只在「屏比视频更宽」时加左右 margin。横屏手机 / 平板屏比接近 16:9 时,字幕视口碰巧接近 plane,变形轻;竖屏时视口与 plane 差极大,变形立刻离谱。

结论:那是 碰运气,没有修 plane ≠ video 的几何。

libmpv / Kodi 对照

  • libmpv:参考系是视频 content 区域;HD PGS 强制 letterbox,plane 等比映射进视频框并居中。
  • Kodi:plane 与视频比接近时对齐视频;不一致时走保宽高比的 screen AR 对齐,避免字幕变形。

Media3 几何模型

PgsParser 把 object 的宽高分别按 plane 宽、高归一化到「相对视口宽 / 相对视口高」;SubtitlePainter 再乘父布局宽高。plane 按 16:9 假定、视频 2.39 时,等价于 非等比 stretch

官方 issue androidx/media#2849 在 demo 上复现同一类片源;维护者说明分属视口高/宽的 fractional 字段在 AR 不一致时会错,并表示 不计划 在现有字幕管线里专门处理 plane AR ≠ video AR。后续 RELEASENOTES 有 PGS 解码修复, 本布局问题修复。因此 不能 指望「只升级 Media3」解决内嵌压扁。

原因分析

根因(确定)

  1. PGS composition plane 与视频显示宽高比不一致(典型 plane 16:9,视频 scope 2.39)。
  2. Media3(及修复前的应用 overlay)把 plane 坐标 分别 映射到视口宽与高。
  3. 内嵌路径与外置路径曾是 同一几何错误的两套实现

非根因

  • 某一台平板的硬件 / DV 输出本身
  • 视频 surface contain 算错
  • PGS 位图像素被竖向压缩(像素比正常;扁在布局)
  • 必须 full-cache 抽轨才能正确显示

产品约束

  • 禁止 把 HTTPFILE 内嵌默认改成 full-cache track.sup 再 overlay:会推迟首字,破坏「开播即显」。
  • UI 上显示 native / Embedded 只表示 轨目录来源,不表示绘制仍走 SubtitleView

解决方法

外置 / complete-file overlay

对 OverlayView 使用与 libmpv / Kodi 一致的 contain 几何:

scale = min(viewW / planeW, viewH / planeH)
offsetX/Y = 居中
全部 object 坐标 × 同一 scale

HTTPFILE 内嵌

策略要点:

内嵌轨仍 selected(in-band demux,保马上出字)
解码:继续 Media3 PgsParser(禁止双解 / 禁止 full-cache)
绘制:Cue 映射到已修好的 OverlayView;SubtitleView 不画 bitmap

已否决:仅清 bitmapHeight 的「轻改」;内嵌改走 full-cache windowed session。

验证要点

路径期望
Exo + 外置/HLS PGS等比正常;可有 windowed session 日志
Exo + HTTPFILE 内嵌等比正常;服务端 该会话 track.sup / subtitle-resource
libmpv 同片对照正常

复验时应用日志与服务端活动原因交叉:内嵌修复应来自 本地绘制接管,不是服务端抽轨。

相关阅读

参考