Skip to content

fix(player): 修复 libc++ 打包冲突导致 libmpv/libass 无法加载、ASS 字幕不渲染 - #89

Merged
ModerRAS merged 2 commits into
masterfrom
codex/fix-subtitle-libass-libcxx
Sep 16, 2026
Merged

ModerRAS merged 2 commits into
masterfrom
codex/fix-subtitle-libass-libcxx

Conversation

@ModerRAS

@ModerRAS ModerRAS commented Sep 16, 2026 •

Copy link
Copy Markdown
Owner

问题

ASS 字幕完全不渲染(LV999 等内嵌 ASS 的剧集)。根因是 #71 引入的 native libc++ 打包冲突:

  1. feat(audio): neon FIR 32/64-bit with pre-allocated arena + FFT design — 完整变更 #71 增加 audio-dsp-native 模块时,在 app 级加了 pickFirsts += "**/libc++_shared.so"。
  2. 打包合并时选中了 audio-dsp-native 的 NDK libc++_shared.so(1,330,832 B,不导出 __from_chars_floating_point),而不是 player-mpv-android 预编译 libmpv.so 所匹配的那份(1,374,336 B,导出该符号)。
  3. libmpv.so(libass 宿主)dlopen 失败:
MiruLibass: Could not open libmpv.so: dlopen failed: cannot locate symbol
_ZNSt6__ndk127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE
  1. NativeAssRenderer.isAvailable() 变 false → 内嵌 ASS 样本回落到 Media3 SsaParser;这些样本是 zlib 转义数据,SsaParser 把每条 Dialogue 当畸形行丢弃:
SsaParser: Skipping dialogue line with fewer columns than format: Dialogue: 0:00:00:00,...

修复前同一文件(LV999-S01E03-ASSx2.mkv)logcat 里完全没有 NativeAssTextRenderer 日志,只有满屏 SsaParser 丢弃行,字幕一条都不显示。

修复

  • audio-dsp-native:ANDROID_STL=c++_shared → c++_static,模块不再产出第二份 libc++_shared.so(JNI 边界只有 POD,静态链 libc++ 安全;模块自带 instrumented test 仍可独立运行)。
  • app:移除 pickFirsts += "**/libc++_shared.so"。以后若再出现第二份 libc++,native lib merge 会直接失败,而不是静默选错。
  • docs/agents/pre-pr-checklist.md:新增该失败类(native runtime packaging)+ libass native host / native DSP ABI 交叉检查项。

真机验证(HK1 RBOX K8,debug 构建,全部为实际按键操作 + 观察)

项 操作 观察
内嵌 ASS(核心修复) 播放 LV999-S01E03-ASSx2.mkv(ASSx2 = 双语双轨) logcat:MiruLibass: Resolved libass API from packaged libmpv.so、5 个 Matroska 字体附件、NativeAssTextRenderer: Activated raw ASS track;HDMI 实拍双语对白「鏡浩二… 999 級!? / 鏡浩二… レベル999!?」+ 角落 ASS 注释框正常渲染;OP 特效字幕同样正常
无字幕视频 播放 probe_30s_1080p_hdr_h264_high10.mp4 Video first frame rendered,无 Player error / AndroidRuntime:E
IJK 后端 SDR 测试图 tmp_sdr_bars.mp4(IJK 对 HDR 会按设计回退 Exo) active_backend=EXPERIMENTAL_IJKPLAYER、IjkPlayback: IJK player prepared、HDMI 正常出画
native DSP ABI ./gradlew :audio-dsp-native:connectedDebugAndroidTest 3/3 通过;isAvailable=true neon=true,native FIR design 2755x,streaming maxErr 0.0
硬门槛 ./gradlew test lint BUILD SUCCESSFUL

未设备验证:外挂 .ass 单独选取路径。调试 hook 注入的外挂轨语言固定为 und,与当前偏好 zh 不匹配,不会被自动选中;其渲染路径与内嵌轨完全相同(同一个 NativeAssTextRenderer + libass),本次修复只影响 libass 可用性本身。

版本

Patch 级(bug fix),不改 baseAppVersionName;线上最高稳定版本 v2.14.766,本地 base 2.14.0 已对齐。

附带修复:CI 卡在 Setup Android SDK

本 PR 第一次跑 CI 时 build/lint 在 20 秒内就失败,原因与本次改动无关:
android-actions/setup-android@v3 的默认 packages 是 'tools platform-tools',
而 Google 已经从 SDK 仓库移除了 legacy tools 包:

Setup Android SDK  packages: tools platform-tools
[command]sdkmanager tools
Warning: Failed to find package 'tools'
Error: The process '.../sdkmanager' failed with exit code 1

master 的 push 流程(含 alpha 自动发版)同样会死在这一步。第二个 commit 在三个
job(build / build-release / lint)都显式指定 packages: 'platform-tools'。

#71 引入 audio-dsp-native 时在 app 级加了 pickFirsts += "**/libc++_shared.so"。
打包时选中了 audio-dsp-native 的 NDK libc++_shared.so,而 player-mpv-android
预编译的 libmpv.so(libass 宿主)需要 __from_chars_floating_point,NDK 版
libc++ 不导出该符号,导致 libmpv.so dlopen 失败:

  MiruLibass: Could not open libmpv.so: dlopen failed: cannot locate symbol
  _ZNSt6__ndk127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE

libass 原生渲染器因此不可用,内嵌 ASS 样本回落到 Media3 SsaParser;这些样本
是 zlib 转义数据,SsaParser 把每一条 Dialogue 都当畸形行丢弃(logcat 刷
"Skipping dialogue line with fewer columns than format"),字幕完全不渲染。

修复:
- audio-dsp-native 改用 ANDROID_STL=c++_static:模块不再产出第二份
  libc++_shared.so,APK 里只剩 player-mpv-android 那份与 libmpv.so 匹配的副本
- app 移除 pickFirsts:若将来再出现第二份 libc++,native lib merge 会直接
  失败而不是静默选错
- docs/agents/pre-pr-checklist.md 增加该失败类、libass native host 与 native
  DSP ABI 交叉检查项

真机验证(HK1 RBOX K8 / 192.168.63.237,debug 构建)见 PR 描述。
@coderabbitai

coderabbitai Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 6ebd3f91-93d8-4bea-8564-46a6adba94f2


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

android-actions/setup-android@v3 的默认 packages 是 'tools platform-tools',
Google 已从 SDK 仓库移除 legacy 'tools',CI 的 Setup Android SDK 步骤因此直接
失败(Warning: Failed to find package 'tools'),master 的 alpha 发布同样被卡住。
显式指定 packages: 'platform-tools' 即可。三个 job(build / build-release /
lint)都已修复。
@ModerRAS
ModerRAS merged commit 61f8631 into master Sep 16, 2026
9 checks passed
@ModerRAS
ModerRAS deleted the codex/fix-subtitle-libass-libcxx branch September 16, 2026 23:16
ModerRAS added a commit that referenced this pull request Sep 17, 2026
内嵌 mpv 退场或 EOF 时会先 detach Surface,而窗口型 VO(GLES2 设备默认的
mediacodec_embed)紧接着做 video-reconfig,mpv 随即在
vo_mediacodec_embed.c:40 断言失败,vo 线程 SIGABRT 掉整个进程:

  F/libc: ../video/out/vo_mediacodec_embed.c:40 create_mediacodec_device_ref():
    assertion "vo->opts->WinID != 0 && WinID != -1" failed
  F/libc: Fatal signal 6 (SIGABRT) in tid (vo), pid com.miruplay.tv

mpv 日志证据(时间顺序):video EOF reached → detaching surface →
event: video-reconfig → abort。

修复:
- surfaceDestroyed / releasePlayerAfterBegin 在 detachSurface / destroy 之前先把
  vo 切到 "null"(音频继续播),surfaceCreated 再按 voInUse 恢复真实 vo
- applyRuntimeOptions 只在 surface 已附着时重建 vo,避免无窗口时重建
- BaseMPVViewTest 覆盖 destroy 动作顺序与 vo 应用条件

注:真机复现/验证依赖 #89 的 libc++ 修复(否则 libmpv.so 根本加载不了)。
真机验证见 PR 描述。
ModerRAS added a commit that referenced this pull request Sep 17, 2026
内嵌 mpv 退场或 EOF 时会先 detach Surface,而窗口型 VO(GLES2 设备默认的
mediacodec_embed)紧接着做 video-reconfig,mpv 随即在
vo_mediacodec_embed.c:40 断言失败,vo 线程 SIGABRT 掉整个进程:

  F/libc: ../video/out/vo_mediacodec_embed.c:40 create_mediacodec_device_ref():
    assertion "vo->opts->WinID != 0 && WinID != -1" failed
  F/libc: Fatal signal 6 (SIGABRT) in tid (vo), pid com.miruplay.tv

mpv 日志证据(时间顺序):video EOF reached → detaching surface →
event: video-reconfig → abort。

修复:
- surfaceDestroyed / releasePlayerAfterBegin 在 detachSurface / destroy 之前先把
  vo 切到 "null"(音频继续播),surfaceCreated 再按 voInUse 恢复真实 vo
- applyRuntimeOptions 只在 surface 已附着时重建 vo,避免无窗口时重建
- BaseMPVViewTest 覆盖 destroy 动作顺序与 vo 应用条件

注:真机复现/验证依赖 #89 的 libc++ 修复(否则 libmpv.so 根本加载不了)。
真机验证见 PR 描述。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant