Skip to content

视频播放 + 横竖屏切换时触发 ANR 及 anon:sync_fence FD 积压问题(骁龙 7s Gen 3 / Android 15) #152

@jingle729

Description

@jingle729

一、环境信息

项目 详情
设备型号 荣耀 200(HONOR ELI-AN00)
处理器 高通骁龙 7s Gen 3(平台代号:crow,厂商:QTI)
GPU Adreno(高通)
Android 版本 Android 15(API 35)
Flutter 版本 3.35.1
Dart 版本 3.9.0
player_flutter 版本 13.0.0
渲染模式 TextureView(该设备强制软解码)

二、问题描述

在上述设备上使用 TXVodPlayer + Flutter 播放视频时,存在以下两个相关但独立的 FD 泄漏问题:

问题 1:进程 FD 耗尽导致 ANR

当用户快速切换视频的同时进行横竖屏切换,进程 FD 数量迅速增长并耗尽 Linux 系统限制(~1024),最终触发 ANR。

现象

  • GPU fence dup() failed: Too many open files (errno=24)
  • HWUI 渲染循环卡死
  • 主线程 ANR:Input dispatching timed out(5000ms 未响应触摸事件)

根本原因:视频切换与横竖屏旋转几乎同时发生时,旧 SurfaceTexture 被 disconnect(BufferQueue 标记为 abandoned),但 liteAV 内部渲染线程(TPDecoder)仍持续对废弃的 Surface 调用 dequeueBuffer,循环 200+ 次无法退出。与此同时,旋转触发新 GraphicBuffer 分配,但旧 buffer 因渲染线程持有引用无法释放,FD 从 ~150 在约 30 秒内增长至 1024+。

相关日志

E/BufferQueueProducer: [SurfaceTexture-1-{pid}-12] dequeueBuffer: BufferQueue has been abandoned
I/Adreno: DequeueBuffer: dequeueBuffer failed
...(以上循环出现 200+ 次)
E/vulkan: dup(fence) failed, stalling until signalled: Too many open files (24)
E/Fence: merge: sync_merge(...) returned an error: Too many open files (-24)
E/Parcel: fcntl(F_DUPFD_CLOEXEC) failed: Too many open files
W/HWUI: dequeueBuffer failed, error = -110; switching to fallback
W/HWUI: swapBuffers encountered EGL error 12301, halting rendering...
D/CrashReport: ANR Input dispatching timed out
  (MainActivity is not responding. Waited 5000ms for MotionEvent)

问题 2:anon:sync_fence FD 积压导致横竖屏切换卡顿

即使没有触发 ANR,正常播放视频期间,anon:sync_fence 类型 FD 以 ~45 个/秒(约 170,000 个/小时) 的速度持续积累。

在骁龙 7s Gen 3 / Adreno GPU / Android 15 上,这些 FD 不会被立即回收,而是由 GPU 驱动大约每 4~5 分钟批量回收一次,峰值可达 10,000~14,000 个 FD,回收后降回基线 ~430。

虽然进程不会崩溃(系统 FD 上限 32768 未被触及),但在 FD 高积压期间进行横竖屏切换时,EGL 资源重建操作(eglCreateContexteglCreateWindowSurfacemakeCurrent)耗时极长,出现黑屏卡顿数秒的体验问题。

自定义 FD 监控日志(2 秒采样)

[FdMonitor] TREND elapsed=20093ms current=1631 delta=946 rate=169491fd/h
[FdMonitor] ⚡ FD BATCH RECLAIM #1 fd: 10134 → 490 (dropped=9644) elapsed=199372ms
[FdMonitor] ⚡ FD BATCH RECLAIM #2 fd: 11223 → 487 (dropped=10736) elapsed=42245ms
[FdMonitor] ⚡ FD BATCH RECLAIM #3 fd: 13284 → 502 (dropped=12782) elapsed=292499ms
[FdMonitor] ⚡ FD BATCH RECLAIM #4 fd: 14024 → 496 (dropped=13528) elapsed=556822ms
[FdMonitor] ⚡ FD BATCH RECLAIM #5 fd: 14185 → 477 (dropped=13708) elapsed=825512ms

每次批量回收:峰值 10,000~14,000 → 基线 ~430,间隔约 4 分钟。

峰值时 FD 类型快照(来自 /proc/{pid}/fd 遍历):

type=anon:sync_fence  count=13500+  ← 主要类型,线性增长
type=system:lib       count=60      ← 稳定不变
type=socket           count=31      ← 稳定不变

三、关键日志

ANR 完整时间线(2026-03-17)

[10:17:21] 进入学习页,加载 VOD 视频(view_id=7,强制软解码)
[10:17:22] liteAV: Valid fail, house does not authorize 128.
[10:17:27] 用户 seek 操作 → pause/resume 循环
[10:30:01] 用户切换到另一个视频
[10:30:02] 新播放器创建(view_id=8)+ 屏幕旋转(竖屏→横屏)同时发生
[10:30:02] SurfaceTexture-1-{pid}-12 被 disconnect
           → Adreno 渲染线程进入 dequeueBuffer 循环(200+ 次)
[10:30:02] GraphicBuffer 分配压力:已分配 ~116MB,新分配失败
[10:30:44] FD 接近上限:liteAV HTTP session_id 变为 null
[10:30:44] GPU fence dup() 失败:Too many open files (errno=24)
[10:30:44] HWUI 渲染失败:dequeueBuffer error=-110, EGL error 12301
           GraphicBuffer 总量升至 ~210MB
[10:36:15] ANR:Input dispatching timed out(等待 5000ms,MotionEvent)
[10:36:28] Bugly 上报失败:EMFILE(logcat、zip、DNS 解析均报错)

ANR 主线程调用栈:
  android.os.MessageQueue.nativePollOnce(Native Method)
  android.os.MessageQueue.next(MessageQueue.java:381)
  android.os.Looper.loopOnce(Looper.java:191)
  android.app.ActivityThread.main(ActivityThread.java:10404)

ANR 瞬间 CPU 使用率:
  132% 总计:102% user + 30% kernel
  15%  TPDecoder#0#2
  15%  HttpClient 线程

sync_fence FD 监控日志(2026-03-18,持续观测 1.5 小时)

[13:42:15] monitor_start fd=293(anon:sync_fence=~30,正常基线)
[13:42:26] TREND elapsed=4018ms current=843 rate=217720fd/h
[13:43:26] TREND elapsed=20119ms current=3896 rate=181977fd/h rotations=1
[13:44:39] ⚡ FD BATCH RECLAIM #1 fd: 7258 → 491 (dropped=6767)
[13:45:34] TREND elapsed=24403ms current=3121 rate=193254fd/h
...(持续以约 53 fd/s 速度积累)

[更早的会话 — 连续运行 1.5 小时]
⚡ FD BATCH RECLAIM #1  fd: 10134 → 490   elapsed=199372ms
⚡ FD BATCH RECLAIM #2  fd: 11223 → 487   elapsed=42245ms
⚡ FD BATCH RECLAIM #3  fd: 13284 → 502   elapsed=292499ms
⚡ FD BATCH RECLAIM #4  fd: 14024 → 496   elapsed=556822ms
⚡ FD BATCH RECLAIM #5  fd: 14185 → 477   elapsed=825512ms
⚡ FD BATCH RECLAIM #6  fd: 10787 → 426   elapsed=1988908ms
⚡ FD BATCH RECLAIM #10 fd: 9773  → 426   elapsed=3338335ms
→ 进程连续运行 55+ 分钟无崩溃
→ 每次回收:峰值 10000~14000 → 基线 ~430,间隔约 4~5 分钟

四、已尝试的解决方案

已在 FTXEGLRender.java 中修复的问题:

  1. eglUninstall()makeCurrent 失败时继续执行 releaseEgl()

    • 修复前:makeCurrent 失败直接 returnmEGLDisplay/mEGLSurfaceEncoder/mEGLContextEncoder 变成孤儿对象永不释放
    • 修复后:失败时只跳过 deleteTexture()releaseEgl() 无论如何都执行
  2. refreshRender():循环 30 次改为只渲染 1 次

    • 修复前:无论 isForcePullFrame 参数值,始终循环 RE_DRAW_COUNT=30 次调用 eglSwapBuffers
    • 修复后:直接调用一次 startDrawSurface(isForcePullFrame)
  3. startDrawSurface()isNewFrame=false 时跳过 swapBuffers()

    • 修复前:无论是否有新帧,都调用 eglSwapBuffers,每次都产生 sync_fence fd
    • 修复后:只有 updateTexImage() 成功获取新帧后才调用 swapBuffers()
  4. onSurfaceTextureAvailable():先完成 EGL 初始化,再调用 resumeRender()

    • 修复前:resumeRender() 先于 EGL 初始化执行,导致 onFrameAvailable 回调积压,EGL 绑定后集中批量执行 swap
    • 修复后:applySurfaceConfig()(EGL 初始化)先于 resumeRender() 执行
  5. bindEGLToDrawThread()makeCurrent 失败后最多重试 3 次

    • 修复前:单次失败后 mEGLBoundToDrawThread 永远为 false,播放器黑屏
    • 修复后:失败后每隔 16ms 重试,最多 3 次

已尝试但无效的方案:

  • 在 draw thread 执行 GLES20.glFinish() 主动触发 GPU 回收:FD 高压时 makeCurrent 失败(EGL_BAD_ALLOC),glFinish 无法执行;即使能执行,sync_fence FD 由 liteAV 内部 TPDecoder 线程直接操作 Surface 产生,完全绕过我们的 EGL context,glFinish 无效
  • setSurface(null) + 重连:重连后 SDK 立即恢复向新 Surface 写入,FD 产生速率不变,且重连过程造成短暂黑屏
  • EGL14.eglSwapInterval(display, 1) 开启垂直同步:sync_fence FD 由 SDK 内部线程产生,不受我们 EGL context 的 swap interval 控制,无效

五、当前状态

问题 状态
ANR(问题 1) ✅ 显著改善 — 正确的 releaseEgl() 时序 + stopRender 顺序修复后,Java 层 FD 积累减少
anon:sync_fence 积压(问题 2) ⚠️ 未解决 — 积累速率仍约 45 fd/s,每 45 分钟批量回收,峰值 1000014000
高 FD 时横竖屏切换卡顿 ⚠️ 未解决 — EGL 资源重建在高 FD 压力下耗时极长,黑屏数秒

六、希望 SDK 团队解答的问题

  1. 是否有 API 可以通知 liteAV 内部渲染线程停止对已废弃 Surface 的 dequeueBuffer 轮询? 目前观察到该线程在 BufferQueue has been abandoned 后仍持续循环 200+ 次,是「视频切换 + 旋转」导致 ANR 的核心原因。

  2. ~45 fd/s 的 anon:sync_fence 产生来源是什么? 我们的调查显示这些 FD 由 SDK 内部解码/渲染线程(TPDecoder)直接操作 Surface 产生,而非来自我们的 Flutter EGL 层。这是已知问题吗?在高通骁龙 7s Gen 3 / Android 15 上是否有对应的修复或 workaround?

  3. 在「视频切换 + 横竖屏旋转」同时发生时,是否有推荐的 Surface 生命周期处理方式? 例如,如何确保旧播放器的渲染线程在新播放器绑定新 Surface 之前完全停止?

  4. player_flutter 13.0.0 在骁龙 7s Gen 3 / Android 15(API 35)上是否存在已知兼容性问题? 我们注意到 Valid fail, house does not authorize 128 错误在开发环境下会加速 FD 耗尽。


附注

  • anon:sync_fence FD 积压行为(积累到 ~14000 后批量回收至 ~430,间隔约 4 分钟)似乎是高通 Adreno GPU 驱动在骁龙 7s Gen 3 / Android 15 上的特定行为,在其他设备上未能复现相同模式
  • 进程在 55+ 分钟的持续运行中未出现崩溃(FD 上限 32768 未触及),主要用户体验问题是 FD 积压期间横竖屏切换的卡顿黑屏
  • 如有需要,可以提供完整 logcat 输出、ANR trace 文件或调试版测试包

fd监控日志.txt

anr.txt

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions