niuma_player 0.5.2 copy "niuma_player: ^0.5.2" to clipboard
niuma_player: ^0.5.2 copied to clipboard

Flutter video player SDK with unified API across iOS / Android / Web, plus automatic ExoPlayer to IJK fallback on Android.

Changelog #

All notable changes to this project will be documented in this file.

Format follows Keep a Changelog; versioning follows Semantic Versioning.

0.5.2 - 2026-07-27 #

Changed #

  • IJK 产物改用 c++_static 链接:不再随包分发 libc++_shared.so。 实测五个 ijk .so 里只有 libijksoundtouch 真用 C++ 运行时(0.1 → 0.4 MB), 其余体积不变,不存在「每个 so 各带一份 libc++」的膨胀。 装机体积 arm64-v8a 9.9 → 8.5 MB、armeabi-v7a 7.5 → 6.4 MB, aar 7.7 → 6.9 MB。相对 GSY 官方产物的净增从 +2.7 MB 压到 +1.3 MB。

Fixed #

  • example 的默认测试流误提交成了内部签名地址(0.5.1 随包发出), 改回公开示例源。

0.5.1 - 2026-07-27 #

Fixed #

  • Android IJK 软解兜底重建:改用自编 FFmpeg 8.1,修好 fMP4 分片 HLS 的三处硬伤。 产物从 GSY 官方(FFmpeg 4.x)换回自编 ijkplayer-0.8.8-ff8.1-20260727 (arm64-v8a + armeabi-v7a,minSdk 保持 21)。fork 自带的私货与旧假设逐条修掉:
    • fMP4 位置恒 0 / seek 后永久卡 bufferingffp_get_current_position_l()ic->start_time 的假设不适用于 fMP4(start_time 取自分片的 baseMediaDecodeTime,是个很大的值,而 master clock 不含此偏移), pos < start_diff 恒成立直接返回 0。画面其实一直在正常播,坏的只是位置上报。
    • 起播黑屏 2.4~5.7 秒:fork 在 ffp_check_buffering_l() 里为「逼出首帧」 做 seek_to(0),而 show_first_frame 只有首帧显示后才清零、缓冲期间又 显示不出来 → 每轮缓冲检查都 seek 回 0 → 分片反复重拉。实测起播要读 2326 个分片(约 140 秒内容);移除后降到 12 片。
    • H.264 有声无画:fork 在 stream_component_open() 里无条件优先取 *_mediacodec 解码器,无视 mediacodec 选项。niuma 显式设了 mediacodec=0 仍会走硬解 wrapper,H.264 上 avcodec_open2 失败 (EINVAL)导致视频组件根本没打开。改为尊重选项。
    • PlayerSession 心跳补一道 buffering 出口兜底:位置在推进即视为帧在流, snap 回 playing——不再依赖 BUFFERING_END 事件(fMP4 上它不发)。
    • 编译链、补丁(patches/0001~0003)、VERSIONS.lockmodule-niuma-ff8-slim.sh 全部回归本仓;踩坑记录见 doc/ijk-fmp4-hls.md
  • 播完后拖回进度可能永远转圈:video_player 在 completed 事件里调的是 自己的 pause(),绕过 VideoPlayerBackend.pause(),播放意图停在 true; 而拖动进度会把 isCompleted 翻回 false,底层重新缓冲就被推导成 buffering——spinner 不灭、按钮显示正在播、实际没播。现在观察到 completed 即作废播放意图。触发要求 seek 目标处发生重缓冲,真机上表现为 间歇(HLS / 长视频 / 弱网更易命中)。

0.5.0 - 2026-07-25 #

Changed #

  • Android 主内核切换为官方 video_player(2.0 架构):三端主路径统一走 官方 video_player(iOS AVPlayer / Android ExoPlayer / Web <video>),由 Flutter 官方维护、可随意升级;niuma 退居「编排壳」——多线路 failover / retry / middleware / 手势 / 全屏 / wakelock / PiP 编排 + Android IJK 软解兜底(video_player 初始化失败时当次会话自动接管,forceIjkOnAndroid 可显式强制)。自研 ExoPlayer 会话(ExoPlayerSession.kt)与 media3 直接 依赖删除——ExoPlayer 升级、Android 新版本适配从此归官方 video_player。
    • 多线路 auto-failover 逻辑对 Android 主路径同样生效(此前仅 iOS / Web)
    • vp 初始化 wall-clock 超时补发 FallbackTriggered(timeout)(对齐原 native 路径的排障信号)
    • BackendSelected.kind:Android 主路径成功时为 videoPlayer(原为 native);IJK 兜底成功时仍为 native
    • useAndroidPlatformView: true 在主路径映射为 video_player 的 VideoViewType.platformView(0.3.3 根治有声黑屏的渲染路径在 vp 主路径 等价保留;IJK 兜底路径仍走自家 Hybrid Composition)
  • 新增 Dependabot(pub / gradle / actions):video_player 等依赖出新版 自动提 PR 并由 CI(analyze + test + example apk 构建)验证兼容性。
  • BREAKING:环境门槛提升——video_player >=2.10(viewType)要求 Flutter >=3.27 / Dart >=3.6,pubspec environment 同步收紧。

Fixed #

  • 暂停被误报成 buffering:vp 的 pause() 只翻 isPlaying、不动 isBuffering,而 ExoPlayer 暂停只改 playWhenReady、不改 playbackState (收不到 bufferingEnd)——在缓冲窗口内按暂停会永久停在 bufferingeffectivelyPlaying 恒 true 导致播放/暂停按钮不翻转,spinner 也不灭。 VideoPlayerBackend 现在自己记录播放意图,derivePhaseFor 据此推导, 不变量是无播放意图时永不产出 buffering——与 PlayerPhase.buffering 的既有文档契约(「播放意图为真但解码器拿不到数据」)一致,也与 1.x native 路径的 PlayerSession.userWantsPlay 同语义。web 路径按 <video>paused 真值推导,本就不受影响。
  • Android platformView 退出全屏后画面冻结useAndroidPlatformView: true 时 vp 的平台视图构造即 setVideoSurfaceView(自己) 抢走 ExoPlayer 的独占 输出 surface,销毁时只释放自己的 surface 不归还。宿主用共享 controller 实现无缝全屏(inline 那份全屏期间仍挂在树上)时,退全屏后 player 绑在已 释放的 surface 上——声音和进度正常,画面停在最后一帧。现在同一 controller 下只有最后 mount 的 NiumaPlayerView 构造 vp 渲染 widget,它 unmount 时 所有权回退给仍存活的上一个并重建,绑定自然恢复(与 1.x 自研 native 路径 PlayerSession.surfaceStack 同语义)。接入方零改动;texture 路径与 iOS 不受影响。
  • Android 9(API 28)上 vp 主路径自动降级 textureView:该版本 video_player 的平台视图走 setupSurfaceWithCallback——surfaceCreated 里无条件 seekTo(1)(surface 每次重建都跳回开头,切后台再回来也会)、 surfaceDestroyed 里无条件 setVideoSurface(null)(不判断当前绑的是不是 自己)。多 surface 交接必然黑屏,useAndroidPlatformView 在 API 28 上因此 自动失效。IJK 兜底路径走自家 PlayerSession.surfaceStack,不受此限。
  • 倍速状态不再被打回 1.0:vp / IJK 两后端的 value 映射漏带 playbackSpeedsetPlaybackSpeed(2.0) 后下一条底层通知会把公开 value.playbackSpeed 覆盖回 1.0(实际播放仍 2x,UI 倍速指示闪回)。
  • switchLine + forceIjkOnAndroid 播错线路:IJK 路径此前固定拉默认 线路,目标线路被忽略(事件却报 LineSwitched 成功);现按目标线路拉起。 vp 多线路全失败落 IJK 兜底后 activeLineId 也同步校正为实际播放的默认线。
  • switchLine 重试改为重建式:原实现在同一个已失败的 backend 上反复 initialize()(vp 失败后不重建必然再失败);现与 init 路径共用同一 拉起序列(dispose → middleware → 重建 → init)。
  • 三处生命周期竞态:dispose 后仍在退避重试中的 init 链会把新建的平台 播放器 attach 到已销毁的 controller 上(feed 快滑泄漏源);init 未完成时 load() 换源会出现双活 init 链互相拆台;NativeBackend.dispose() 不 settle 等待中的 initialize(只能干等 initTimeout)。现分别以代际号 / attach 前置检查 / completeError 处理。
  • 播放器池并发窗口preload 未 await 时立刻 acquire 会拿到未 初始化的半成品 controller(或在 evict 窗口重复建导致泄漏)——entry 插入与 pending 赋值改为同一同步段完成,附回归测试。
  • 手势亮度/音量起手闪谷底:锁定方向后基准值异步读回期间的指针事件 以 0.0 为基准直接把系统亮度/音量设到谷底;现读回完成前丢弃。 水平 seek 结束改为提交拖动中算好的目标,不再从 HUD 进度反算(丢精度)。
  • Android PlatformView 会话 id 与 texture id 可能撞号:两套分配器 空间重叠,混用两种渲染模式时后建会话会顶掉先建的(泄漏);PV id 起点 抬到 1e12 隔离。
  • web 后端:移除必然 MissingPluginException 的 PiP 总线订阅(每会话 一条控制台红错);timeupdate 双 listener 合并为一次 value 广播。

Removed #

  • BREAKING:PlatformBridge.deviceFingerprint() 删除——设备记忆策略 (0.4.0 移除)的最后残留,SDK 内已无任何消费方;自定义 PlatformBridge 实现删掉该 override 即可。native create 协议同步移除 fingerprint / fromMemory 返回字段与 deviceFingerprint handler。
  • BREAKING:PlayerBackend.webFullscreenState 删除——从未被写入 (恒 false)也无消费方;web 全屏状态由 webFullscreenRouteCountListenable 承载。
  • BREAKING:投屏(Cast)整体移出核——值类型(CastDevice / CastSession / CastConnectionState / CastEndReason)、controller 的 connectCast / disconnectCast / castSession、事件 CastStarted / CastEnded / CastError 全部删除(协议实现本就在 git 历史参考皮)。 核收敛为"干净的播放":需要投屏的业务自持会话对象,播放侧只需 pause() 本地播放器。
  • BREAKING:danmakuVisibility 删除——弹幕引擎 0.1.0 已随参考皮出核, 该遗留字段核内零消费;业务自持一个 ValueNotifier<bool> 即可。
  • BREAKING:BackendFactory.createNative 移除 forceIjk 参数(2.0 起 native 只有 IJK 会话,参数恒 true 无意义);NativeBackend 构造与 create 协议同步移除。NiumaPlayerValue.copyWith 移除零调用的 clearOpeningStageGestureFeedbackState 移除零调用的 copyWith
  • 全库死代码清理:backend 级 FallbackTriggered 发射(controller 一律 丢弃,权威版本由 controller 自发)、native 全局 channel 的 play/pause/seek/setSpeed/setVolume 死分支与 forward()NativeBackend.selectedVariant 公开 getter、PlayerSession 的 Exo 时代钩子(onHeartbeatTick 等)、iOS/Android 的 getPlatformVersion 模板残留、gradle 的 no-op abiFilters / packagingOptions。

0.4.0 - 2026-07-22 #

Changed #

  • Android IJK 产物切换为 GSY 官方 MavenCentral 分发io.github.carguo:gsyvideoplayer-java/-ex_so:11.3.0,bilibili ijkplayer 0.8.8 血统 + FFmpeg 4.3 全量:h264 / h265 / mp4 / HLS 全支持,minSdk 21)。 替换原自编 ShikinChen ff7.1 slim aar——后者存在 fork 私货(缓冲期反复 seek(0) 的 show_first_frame hack)与高码率流软解不出帧的问题,真机表现 为「有声无画 / 无限 buffering」。换装后同一条 2568×1440 加密 HLS 在 IJK 软解下正常出画(OPPO 真机验证)。
  • 移除设备记忆策略(Try-Fail-Remember):一次 ExoPlayer 失败不再被持久 化、不再影响后续会话的内核选择——SDK / 源侧问题修复后设备立即回到硬解 快路径。内核选择完全由 forceIjkOnAndroid 显式决定;会话内 Exo→IJK 的 单次兜底重试保留(不落盘)。
    • BREAKING:删除 NiumaPlayerController.clearDeviceMemory()BackendSelected.fromMemory 字段保留但恒为 false(废弃语义)。
  • IJK framedrop 固定为 0:软解跟不上实时的临界场景下 framedrop>0 会把 所有帧 early-drop 掉(有声无画黑屏);0 的代价是可能渐进失步,两害取轻。

Added #

  • EngineFallbackFailure:Android 双内核(Exo + IJK 兜底)都失败时抛出 的组合异常,同时携带两段原始错误——修复「只报最后一环 IJK 错误、把 Exo 的根因(如 HTTP 403)掩盖」的排障陷阱。
  • example 新增「内核切换测试」页:运行时切 ExoPlayer / IJK 对比同一条流。

Fixed #

  • IJK probesize 曾被设为 100KB「加速 prepare」——对 6Mbps+ 高码率 TS 连 一个视频关键帧都探不齐 → 视频流缺失(-10000 / 无限 buffering)。改回 FFmpeg 默认(probesize 是上限而非必读量,调大零成本)。
  • IJK 移除 reconnect_streamed=1:它把 HLS 分片的正常 EOF 当断连,无限重 连同一分片导致播放卡死。

Removed #

  • android/localmaven/ 自编 ijkplayer aar 与 android/scripts/ FFmpeg 编译链整体退役(GSY 产物上线后不再需要;git 历史可寻)。

0.3.4 - 2026-07-21 #

Fixed #

  • 初始化失败「无限转圈、无失败提示」initialize() 失败(如 IJK prepare 卡死触发 30s initTimeout)时,错误此前只经 initialize() 返回的 future 传播,value.phase 从不进入 error 态——调用方若未 catch future, 靠 value 驱动的错误层永远收不到失败,用户只见无限 buffering(真机实测)。 现失败同步落入 valuephase=error + value.error 按既有规则分类), 错误 UI 自然显示,接入方零改动受益。

Added #

  • H.265 / HEVC 全链路支持
    • 新增 NiumaCapabilities.supportsHevc() 能力检测——Android 查 MediaCodecList 是否存在 video/hevc 硬件解码器(纯软解不算), iOS 恒 true(系统级支持),web 走 MediaSource.isTypeSupported + canPlayType 双探测。典型用途:源协商——业务据此决定向服务端请求 H.265 还是 H.264 源(协议字段业务自定,SDK 不自动附加任何请求头)。 结果进程内缓存。
    • IJK 软解兜底重新支持 HEVC(撤销 0.2.1 的裁剪):重编 ijkplayer aar 加回 hevc decoder / parser / hevc_mp4toannexb bsf / hevc_mediacodec。 此前兜底链对 H.265 是断的——硬解失败切 IJK 后彻底播不了;现在闭环。 体积代价:aar 7.37 → 7.67 MB(arm64 libijkplayer.so 6.5 → 6.9 MiB)。
    • 播放侧无需任何配置:流是 h264 还是 h265 由解封装自动识别。
    • vendored hls.js 1.5.20 → 1.6.16:补齐 web(Chrome / Edge 等 MSE 浏览器)的 HLS H.265——TS 分片里的 HEVC 是 hls.js 1.6.0 才支持的, 1.5.x 下即使浏览器能解也播不了。升级后 NiumaCapabilities.supportsHevc() 的检测结果与实际播放链路对齐。 已回归:h264 TS-HLS(mux 测试流)正常;HEVC fmp4 HLS(bitmovin 测试流)Chrome 实测出画。API 面(isSupported / attachMedia / loadSource / destroy / xhrSetup / hlsError)1.6 全兼容。

0.3.3 - 2026-06-16 #

Fixed #

  • Android PlatformView「有声黑屏」根治useAndroidPlatformView = true): 渲染从裸 AndroidView 改为真正的 Hybrid CompositionPlatformViewLink
    • PlatformViewsService.initExpensiveAndroidView)。裸 AndroidView 走 TLHC / Virtual Display——靠把原生 view 拷进 Flutter 纹理再合成,而 SurfaceView 的像素画在独立 Surface 上、纹理拷贝抓不到 → 合成出来是黑的 (声音/状态机正常但画面全黑,进/退全屏、锁屏解锁、切后台最易触发; flutter#128920 / #172641 / #144219)。HC 把 SurfaceView 插进原生视图层级 直接渲染,画面正常显示,原生缩放 / HDR 保留,Flutter 控件仍可叠层。
    • initExpensiveAndroidView(纯 HC,不回退 VD),不用 initAndroidView / initSurfaceAndroidView(不兼容场景会回退 Virtual Display 又黑回去,flutter#107313)
    • Kotlin 侧零改动;surface 栈 / PlayerSurfaceView / PlayerSession 全部 保留(它们管退全屏 codec error,与黑屏正交)
    • OPPO Android 16 真机验证:进 / 退全屏画面正常
    • 注:Hybrid Composition 在多实例 + 频繁创建销毁(feed)下开销大于单 播放器;feed 等场景如性能吃紧可继续用默认 Texture 路径

0.3.2 - 2026-06-14 #

Fixed #

  • Android IJK 软解音画失步framedrop0 改为 1。被钉死走 IJK 软解 的机型上,高码率 / 高帧率 / 倍速视频解不过实时,framedrop=0 让落后的视频 帧一帧不丢 → 相对音频主钟渐进失步(画面比声音慢、对不上嘴,越看越脱节)。 改为允许丢晚帧让视频追上音频主钟(解不动时偶尔跳帧,但保持同步)。
  • Android ExoPlayer 开启解码器回退DefaultRenderersFactory .setEnableDecoderFallback(true)。硬解 codec 初始化失败时同会话内自动尝试 下一个(含软件)解码器,而非直接抛错绕 Dart 重试 → IJK(少一次错误闪)。

Changed #

  • codec 失败记忆 NO_EXPIRY → 7 天 TTL:此前首帧前硬解 codec 失败会把该 设备永久钉死走 IJK 软解(哪怕只是单条视频 / 单次 codec 抽风),导致本可 硬解的视频长期走软解(卡 / 发热 / 音画失步)。改为 7 天有效期:近期重试仍直 接走 IJK 避免反复撞坏 codec,到期后回 ExoPlayer 硬解重试。读取判定与到期清理 Dart 侧早已就绪。

0.3.1 - 2026-06-12 #

Added #

  • 播放中自动保持屏幕常亮(wakelock):修复「播到一半自动熄屏」。 NiumaPlayerController 在 playing 边沿自动保持 / 释放亮屏(暂停、结束、 出错、dispose 都会释放),多实例(feed / 池)以进程级计数归并——任一在播 即亮屏,全部停了才释放。Android 走 FLAG_KEEP_SCREEN_ON(窗口级、无需 权限、退后台自动失效),iOS 走 isIdleTimerDisabled,web 无操作(浏览器 播 <video> 自身防熄屏)。
    • 新增 NiumaPlayerOptions.manageScreenWakelock(默认 true;音频类 业务想允许熄屏可置 false
    • PlatformBridge 接口新增 setKeepScreenOn(bool)——自定义 PlatformBridge 实现方需补该方法(0.x 下随 minor 发布)

0.3.0 - 2026-06-12 #

Added #

  • Android PlatformView(SurfaceView)渲染路径(opt-in)NiumaPlayerOptions.useAndroidPlatformView = true 时,Android 视频改由 PlatformView(SurfaceView 原生缩放)渲染,替代 Flutter Texture——从根 上解决 Texture 路径的画质模糊,且不吃 filterQuality 的每帧采样开销。 默认 false(行为与 0.2.x 完全一致),iOS / web 忽略此选项。
    • 原生侧新增 PlayerSurfaceView / PlayerSurfaceViewFactory(viewType cn.niuma/player_surface);NiumaPlayerView 自动按 backend 选 AndroidView 渲染分支
    • surface 栈:全屏路由 push 时第二个 SurfaceView 抢绑定、销毁时自动 回退绑定到 inline 仍存活的 surface——退全屏不再输出到 dead Surface 导致 codec 报错(OPPO Android 16 真机验证)
    • prepare 不等 surface:ExoPlayer / IJK 无 surface 即可 prepare (音频与状态机照常推进),surface 何时到画面何时出——feed 类 「initialize → play → 才渲染激活页」的 mount 顺序不会死锁
    • 接入建议:详情页 / 单播放器开启收益最大;feed 每滑一条重建 SurfaceView 有黑闪,建议维持默认 Texture。NiumaPlayerView 外请保持 松约束(如包 Center),全屏路由建议快淡/瞬切(参考 example)
  • PlayerBackend.androidPlatformViewId getter(默认 null)。

Changed #

  • BackendFactory.createNative 增加 useAndroidPlatformView 可选参数—— 自定义 BackendFactory 实现方需同步该签名(0.x 下随 minor 发布)。

0.2.3 - 2026-06-07 #

Changed #

  • Android Texture 路径默认提一档画质NiumaPlayerView 新增 filterQuality 参数,默认 FilterQuality.medium(双三次插值),替代 Flutter Texture 硬编码的默认 FilterQuality.low(双线性)。修复反馈 「视频有点花、不够高清」(多见于小米15 等大屏 / 高 DPI 现代机型,原默认 low 在拉伸时糊感明显)。medium 在 2020+ 中端机以上无可感性能开销。极致性能场景 (feed 多实例 + 低端机)可显式传 FilterQuality.low 降回旧默认。 iOS 不受影响(VideoPlayer widget 内走 AVPlayer 原生 scaling);web 同样不受影响(浏览器直接缩放)。
  • 下一步预告:长期方案是把 Android 渲染改为 PlatformView(SurfaceView 原生缩放),从根上去掉 Texture 路径的每帧 filterQuality 开销。已起 feat/android-platform-view 分支,待真机回归后发 0.3.0。

0.2.2 - 2026-06-07 #

Fixed #

  • web rapid seek 卡 buffering(Chrome/Firefox/Edge + hls.js):WebVideoBackend.seekTo 改为合并模式(latest-wins)——已有 seek 在路上时新调用只更新目标,待 'seeked' 事件后再 fire 最新值,避免反复 seek 把 hls.js 的 SourceBuffer 卡进 updating=true 永不释放、'playing' 永不来。配 3s 安全 timer 兜底(极端 case 浏览器漏发 'seeked' 时也能解锁)。
  • Safari + hls.js 在已 buffered 区间 seek 后 phase 卡 buffering:新增 'seeked' 事件监听,按 video 真值兜底校准 phase(仅当 phase 为 buffering 时纠正,playing/paused/ended/error 等明确状态保持不动)。修复多次快进快退 后 UI spinner 不消失、底栏图标错乱、点击屏幕才恢复的 quirk。
  • load() 换源时残留 seek 状态:换源即作废 _isSeeking/_pendingSeek/ _seekSafetyTimer,避免锁跨源残留导致换源后第一次 seek 被吞。

0.2.1 - 2026-06-07 #

Fixed #

  • web HLS:hls.js xhrSetup 跳过浏览器 forbidden request headersWebVideoBackend 给 hls.js 配 xhrSetup 时把 dataSource.headers 里的 referer / host / origin / user-agent / cookie 等浏览器禁止 JS 设置 的请求头跳过,避免 hls.js 抛 Refused to set unsafe header "referer" 并中断 HLS 加载。鉴权 token 等正常 header 仍透传。

Changed #

  • Android ijkplayer aar 砍掉 HEVC(H.265)软解兜底,进一步精简: libijkplayer.so arm64 6.9→6.5 MiB / armv7 5.5→5.1 MiB,aar 7.6→7.0 MiB, 对应 APK 单 arm64 ABI 省 ~400KB。decoder 仅留 h264 / aac / aac_latm / mp3* + h264_mediacodec / mp3_mediacodec;HEVC 解封装/parser/bsf 同步移除。 影响:IJK 软解兜底路径不再支持 H.265;Android ExoPlayer 主路径仍可硬解 H.265,仅在「ExoPlayer 翻车 + 视频是 H.265」双重场景才会播不了。点播 mp4+HLS(主流 H.264 编码)不受影响。编译配置见 android/scripts/compile/modules/module-niuma-ff7-slim.sh

0.2.0 - 2026-06-05 #

Added #

  • NiumaPlayerController.load(NiumaMediaSource) 原地换源(+ PlayerBackendsupportsSourceSwap / load):复用当前 backend 换到新源。web 后端复用 同一个 <video> 元素换 src(supportsSourceSwap=true),保住 iOS Safari 的 有声播放激活——"滑到才知道下一条 URL"的 feed 可用一个 controller 反复换源, 而非每条新建 controller、每条丢激活导致只能静音。backend 不支持换源时自动 dispose + 重建兜底。
  • web 全屏分流原语NiumaWebFullscreenMode / webFullscreenMode / requestBrowserFullscreen / exitBrowserFullscreen / onBrowserFullscreenChange, 自 web_fullscreen_coordination):把「浏览器全屏能力检测(安全读 fullscreenEnabled、绕开 iOS Safari undefinedTypeError)+ 画布真全屏 进出 + 全屏状态监听」收进核,接入方据 webFullscreenMode 分流即可、不必碰 DOM:nativeVideoElement(iOS Safari,走系统 player 全屏)/ browserElement (Chrome 等,走画布真全屏 + 自家全屏页)/ notWeb
  • NiumaPlayerController.setWebNativeControls(bool)(+ PlayerBackend 同名 方法):web-only,开 / 关底层 <video> 的浏览器原生控件。iOS Safari 上 Flutter 自定义控件叠在 <video> 上会被浏览器吞、点不动,接入方可开原生控件兜底 (播放 / 进度 / 全屏交给浏览器)。非 web backend 为空操作。

Fixed #

  • web video 被 DOM reparent 后自动续播WebVideoBackend 维护「播放意图」, video 因全屏搬迁等被浏览器自发暂停时,只要意图仍在播就自动续播——修复 「Chrome 进全屏 / 退全屏后视频卡停」。用户主动 pause() 不受影响。
  • iOS Safari 退出原生全屏后自动恢复播放WebVideoBackend.enterNativeFullscreen()webkitEnterFullscreen 时记住进全屏前的播放态,监听 webkitendfullscreen, 退出系统 player 后自动 play()(iOS 退出系统 player 默认会把 video 暂停)。
  • web enterNativeFullscreen 回归「浏览器原生全屏」语义WebVideoBackend 此前把 enterNativeFullscreen() 实现成只翻一个 NiumaPlayerView 并不读取的 内部 flag(等于 web 上调用它没有任何可见效果);现在按 PlayerBackend 接口 文档契约真正调用 <video>.webkitEnterFullscreen()(iOS Safari,进系统原生 video player UI)/ requestFullscreen()(桌面 Safari / Chrome / Firefox / Android Chrome)。exitNativeFullscreen() 同步走 webkitExitFullscreen / document.exitFullscreen()

0.1.0 #

BREAKING CHANGE: 重定位为 headless 播放内核 #

niuma_player 现在是纯 headless 视频播放内核——只导出 NiumaPlayerController + NiumaPlayerView(无样式渲染面)+ 全部纯 Dart 编排 逻辑(多线路 / auto-failover / retry policy / source middleware)+ 手势 / 全屏 的 headless controllerNiumaGestureController / NiumaFullscreenController)+ cast 值类型。接入方监听 controller.valueValueNotifier<NiumaPlayerValue>)自己拼 UI,或让 AI 按需 生成。

所有 UI 全部出核,移入 git 历史(曾经的 88 文件 niuma_ui 参考皮):

  • 一体化播放器壳 NiumaPlayer + 22 个原子控件 + 控件条(NiumaControlBar / ControlBarConfig / ButtonOverride)+ 全屏页 NiumaFullscreenPage + 三态 反馈 UI + 主题 NiumaPlayerTheme
  • 弹幕引擎与 UI(NiumaDanmakuController / overlay / painter / settings panel / DanmakuTrackAllocator)。
  • 广告调度(NiumaAdSchedule / AdSchedulerOrchestrator / NiumaAdOverlay + analytics 事件模型)。
  • 缩略图取帧逻辑与 widget(ThumbnailFrame / WebVttParser / ThumbnailResolver / NiumaThumbnailView / NiumaScrubPreview)。
  • 投屏协议实现与 UI(DLNA SSDP/SOAP、AirPlay RoutePicker、 NiumaCastRegistry / CastService SPI、cast 按钮 / picker 面板)。
  • 短视频整套(5 个 NiumaShortVideo* widget + NiumaShortVideoTheme)。
  • 本地续播(resume position)。

需要参考实现:git log --all -- 'example/lib/niuma_ui/**' 定位 commit, git show <sha>:example/lib/niuma_ui/... 取文件,或喂给 AI 当参考。

核仍保留的 cast 值类型CastDevice / CastSession / CastConnectionState / CastEndReason——controller.connectCast(session) / disconnectCast(...) + castSession getter + CastStarted / CastEnded 事件依赖它们。具体协议由接入方实现 CastService 产出 CastSession 交给核。

Removed (依赖瘦身) #

  • 三方依赖从重定位前的一大堆砍到 5 个。移除 shared_preferences (Android 设备记忆改走 Kotlin 侧 SharedPreferences,Dart 不再依赖)/ flutter_svg(UI 出核)/ http(核不再 fetch VTT,缩略图取帧出核)。

Changed (平台引擎) #

  • Web 后端从已废弃的 dart:html 迁到 package:web + dart:js_interopwasm-ready——可随 flutter build web --wasm 编译。
  • Android IJK 升级到 FFmpeg 7.1.1 slim 重编(vendored .aar), ExoPlayer ↔ IJK 自动回退路径不变。

Changed (example) #

  • example/ 精简为 100 行最小 demoexample/lib/main.dart): NiumaPlayerController.dataSource + NiumaPlayerView + ValueListenableBuilder<NiumaPlayerValue> 自拼 play/pause + 进度 + 时间。 原 8 个 demo 页随参考皮移入 git 历史。

0.0.4 - 2026-05-25 #

Fixed #

  • Vendored the custom-compiled ijkplayer .aar (13 MB) into git and the published package. It was previously git-ignored and fetched by a download script whose release URL no longer exists, so neither git nor pub.dev consumers received the binary and every Android build failed to resolve tv.danmaku.ijk:ijkplayer. The aar now ships under android/localmaven/, so Android builds work out of the box. Removed the dead download script.

0.0.3 - 2026-05-09 #

Fixed #

  • Bumped video_player lower bound to >=2.10.0. The 2.8.0 lower bound failed pana downgrade analysis because VideoPlayerController.playerId (used by the iOS PiP bridge to map a Flutter texture id to its native AVPlayer instance) was only added in video_player 2.10.0.
  • Declared web platform support in pubspec.yaml plugin manifest with a no-op NiumaPlayerWebRegistrar stub. Web behavior is implemented in pure Dart via conditional imports (WebVideoBackend); the stub exists only so Flutter's web plugin discovery can satisfy the platform declaration.
  • Trimmed CHANGELOG.md to public 0.0.x entries only. The full internal-preview history (0.1.0 through 0.9.1) — which is mostly Chinese prose and was tripping pub.dev's non-ASCII content check — moved to doc/CHANGELOG_zh_internal_preview.md.

0.0.2 - 2026-05-09 #

Fixed #

  • Replaced dart:js_util (removed in Dart SDK 3.11) with dart:js_interop / dart:js_interop_unsafe in web_video_backend.dart. Fixes pub.dev pana static analysis failure that previously zeroed out platform support score.
  • Shortened pubspec.yaml description to fit pub.dev 60-180 char limit.
  • Added English-language summaries to CHANGELOG.md.

0.0.1 - 2026-05-09 #

First public pub.dev release. Version reset from internal-preview 0.9.x to 0.0.1 as the inaugural public SDK version. Feature set equivalent to internal 0.9.1, including:

  • 3-tier backend abstraction (VideoPlayerBackend for iOS/Web, NativeBackend for Android) plus Android Try-Fail-Remember device memory.
  • Orchestration layer (multi-line, retry policy, source middleware, resume position, WebVTT thumbnails, danmaku bucket loader, auto-failover).
  • All-in-one NiumaPlayer widget plus 22 atomic control widgets and a configurable NiumaControlBar.
  • Picture-in-Picture (iOS via reflection bridge, Android native).
  • Cast: DLNA and AirPlay auto-registration via NiumaCastRegistry.
  • Feedback UI builder slots: loadingBuilder, errorBuilder, endedBuilder.
  • Short-video player with TikTok-style gestures, scrubber, speed control.
  • Web fullscreen, cross-backend swap coordination, iOS Safari quirk fixes.

For the detailed history of internal-preview iterations leading up to this release (0.1.x through 0.9.1), see doc/CHANGELOG_zh_internal_preview.md (Chinese).

1
likes
145
points
317
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Flutter video player SDK with unified API across iOS / Android / Web, plus automatic ExoPlayer to IJK fallback on Android.

Repository (GitHub)
View/report issues
Contributing

Topics

#video #player #hls #exoplayer #ijkplayer

License

Apache-2.0 (license)

Dependencies

clock, flutter, flutter_web_plugins, meta, plugin_platform_interface, video_player, web

More

Packages that depend on niuma_player

Packages that implement niuma_player