一、环境信息
| 项目 |
详情 |
| 设备型号 |
荣耀 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 资源重建操作(eglCreateContext、eglCreateWindowSurface、makeCurrent)耗时极长,出现黑屏卡顿数秒的体验问题。
自定义 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 中修复的问题:
-
eglUninstall():makeCurrent 失败时继续执行 releaseEgl()
- 修复前:
makeCurrent 失败直接 return,mEGLDisplay/mEGLSurfaceEncoder/mEGLContextEncoder 变成孤儿对象永不释放
- 修复后:失败时只跳过
deleteTexture(),releaseEgl() 无论如何都执行
-
refreshRender():循环 30 次改为只渲染 1 次
- 修复前:无论
isForcePullFrame 参数值,始终循环 RE_DRAW_COUNT=30 次调用 eglSwapBuffers
- 修复后:直接调用一次
startDrawSurface(isForcePullFrame)
-
startDrawSurface():isNewFrame=false 时跳过 swapBuffers()
- 修复前:无论是否有新帧,都调用
eglSwapBuffers,每次都产生 sync_fence fd
- 修复后:只有
updateTexImage() 成功获取新帧后才调用 swapBuffers()
-
onSurfaceTextureAvailable():先完成 EGL 初始化,再调用 resumeRender()
- 修复前:
resumeRender() 先于 EGL 初始化执行,导致 onFrameAvailable 回调积压,EGL 绑定后集中批量执行 swap
- 修复后:
applySurfaceConfig()(EGL 初始化)先于 resumeRender() 执行
-
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 团队解答的问题
-
是否有 API 可以通知 liteAV 内部渲染线程停止对已废弃 Surface 的 dequeueBuffer 轮询? 目前观察到该线程在 BufferQueue has been abandoned 后仍持续循环 200+ 次,是「视频切换 + 旋转」导致 ANR 的核心原因。
-
~45 fd/s 的 anon:sync_fence 产生来源是什么? 我们的调查显示这些 FD 由 SDK 内部解码/渲染线程(TPDecoder)直接操作 Surface 产生,而非来自我们的 Flutter EGL 层。这是已知问题吗?在高通骁龙 7s Gen 3 / Android 15 上是否有对应的修复或 workaround?
-
在「视频切换 + 横竖屏旋转」同时发生时,是否有推荐的 Surface 生命周期处理方式? 例如,如何确保旧播放器的渲染线程在新播放器绑定新 Surface 之前完全停止?
-
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
一、环境信息
二、问题描述
在上述设备上使用
TXVodPlayer+ Flutter 播放视频时,存在以下两个相关但独立的 FD 泄漏问题:问题 1:进程 FD 耗尽导致 ANR
当用户快速切换视频的同时进行横竖屏切换,进程 FD 数量迅速增长并耗尽 Linux 系统限制(~1024),最终触发 ANR。
现象:
GPU fence dup() failed: Too many open files (errno=24)Input dispatching timed out(5000ms 未响应触摸事件)根本原因:视频切换与横竖屏旋转几乎同时发生时,旧
SurfaceTexture被 disconnect(BufferQueue 标记为 abandoned),但 liteAV 内部渲染线程(TPDecoder)仍持续对废弃的 Surface 调用dequeueBuffer,循环 200+ 次无法退出。与此同时,旋转触发新 GraphicBuffer 分配,但旧 buffer 因渲染线程持有引用无法释放,FD 从 ~150 在约 30 秒内增长至 1024+。相关日志:
问题 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 资源重建操作(
eglCreateContext、eglCreateWindowSurface、makeCurrent)耗时极长,出现黑屏卡顿数秒的体验问题。自定义 FD 监控日志(2 秒采样):
每次批量回收:峰值 10,000~14,000 → 基线 ~430,间隔约 4 分钟。
峰值时 FD 类型快照(来自
/proc/{pid}/fd遍历):三、关键日志
ANR 完整时间线(2026-03-17)
sync_fence FD 监控日志(2026-03-18,持续观测 1.5 小时)
四、已尝试的解决方案
已在
FTXEGLRender.java中修复的问题:eglUninstall():makeCurrent失败时继续执行releaseEgl()makeCurrent失败直接return,mEGLDisplay/mEGLSurfaceEncoder/mEGLContextEncoder变成孤儿对象永不释放deleteTexture(),releaseEgl()无论如何都执行refreshRender():循环 30 次改为只渲染 1 次isForcePullFrame参数值,始终循环RE_DRAW_COUNT=30次调用eglSwapBuffersstartDrawSurface(isForcePullFrame)startDrawSurface():isNewFrame=false时跳过swapBuffers()eglSwapBuffers,每次都产生 sync_fence fdupdateTexImage()成功获取新帧后才调用swapBuffers()onSurfaceTextureAvailable():先完成 EGL 初始化,再调用resumeRender()resumeRender()先于 EGL 初始化执行,导致onFrameAvailable回调积压,EGL 绑定后集中批量执行 swapapplySurfaceConfig()(EGL 初始化)先于resumeRender()执行bindEGLToDrawThread():makeCurrent失败后最多重试 3 次mEGLBoundToDrawThread永远为 false,播放器黑屏已尝试但无效的方案:
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 控制,无效五、当前状态
releaseEgl()时序 +stopRender顺序修复后,Java 层 FD 积累减少5 分钟批量回收,峰值 1000014000六、希望 SDK 团队解答的问题
是否有 API 可以通知 liteAV 内部渲染线程停止对已废弃 Surface 的
dequeueBuffer轮询? 目前观察到该线程在BufferQueue has been abandoned后仍持续循环 200+ 次,是「视频切换 + 旋转」导致 ANR 的核心原因。~45 fd/s 的
anon:sync_fence产生来源是什么? 我们的调查显示这些 FD 由 SDK 内部解码/渲染线程(TPDecoder)直接操作 Surface 产生,而非来自我们的 Flutter EGL 层。这是已知问题吗?在高通骁龙 7s Gen 3 / Android 15 上是否有对应的修复或 workaround?在「视频切换 + 横竖屏旋转」同时发生时,是否有推荐的 Surface 生命周期处理方式? 例如,如何确保旧播放器的渲染线程在新播放器绑定新 Surface 之前完全停止?
player_flutter 13.0.0 在骁龙 7s Gen 3 / Android 15(API 35)上是否存在已知兼容性问题? 我们注意到
Valid fail, house does not authorize 128错误在开发环境下会加速 FD 耗尽。附注
anon:sync_fenceFD 积压行为(积累到 ~14000 后批量回收至 ~430,间隔约 4 分钟)似乎是高通 Adreno GPU 驱动在骁龙 7s Gen 3 / Android 15 上的特定行为,在其他设备上未能复现相同模式fd监控日志.txt
anr.txt