更新记录

1.0.0(2026-10-18)

首个正式版。三端(Android / iOS / HarmonyOS)全屏体验与进度显示统一收口。

  • 全屏进出不流畅(三端共享层):
    • 全屏状态改为点击瞬间乐观置位,success 只校准、fail 回滚。此前要等 success 回调(它在旋转动画结束后才回来)才置位,控制栏 padding、标题栏、全屏图标会比动画晚一拍"跳"一下
    • 暂停帧重绘从「每实例两次微 seek」降为「一次」:全局广播由 300ms + 600ms 两次改为请求瞬间一次,接收端加防抖(SURFACE_REFRESH_DELAY,默认 500ms,只此一个可调常量)
    • 移除 onResize 兜底:它是页面生命周期、组件内不生效(uni-app x 亦无 uni.onWindowResize API),跨实例通知统一走 uni.$emit,并在注释中写明原因
    • 新增 @fullscreenchange / @fullscreenerror 兜底:系统手势、返回键退出全屏时 page.exitFullscreen 的 success 根本不会触发,改由 page.fullscreenElement(Web 用 document.fullscreenElement)校准;带可靠性开关,属性未实现时不会把刚进全屏的 UI 误判成"未全屏"
    • 进度条与播放时间改本地线性插值(displayPosition):不再按原生轮询频率(安卓 400ms / 鸿蒙 500ms)2~2.5Hz 跳格;插值系数封顶 1,不会超前于最近一次真实采样;seek / 切源等跳变直接吸附
    • 清理死代码:refreshSafeTop()(全注释空函数)、safeTop ref、标题栏残留的空 :style
  • 安卓:全屏时页面 TitleBar 由 View.GONE 改为 View.INVISIBLE,修复 GONE 释放布局空间导致页面内容上移铺满、退出全屏后顶部被遮挡的问题(与代码内注释及 issue #33146 对齐)
  • 修复中央播放按钮在默认配置下基本不出现:初始化被 && !autoplay 置否(默认 autoplay 为真,故开局即为 false),且状态到 READY/PLAYING 后被单向置 false 再无恢复路径,图标还写死为播放键。现改为随控制栏显隐、图标跟随播放状态切换(播放中显示暂停键);并把原来 100%×100% 的覆盖层收成图标大小的居中按钮,避免它连"点画面任意位置切换控制栏"一起吃掉;顺带删掉随之失效的 controlsState

0.3.2(2026-09-30)

  • 鸿蒙多实例视频状态读取:改用 getProperty 直读替代 player.state 回调,修复多实例场景下只有最后一个实例能更新状态的问题
  • autoplay=false 未真正暂停:doOpen 后紧跟 ['set', 'pause', 'yes'],mpv 打开文件默认自动播放
  • 暂停状态推导:用 wantPlay 标志位替代不可信的 getPropertyBool('pause')(mpv 暂停后仍返回 false)
  • 打开队列串行化:等待当前实例 duration > 0 才放行下一路,加 8s 看门狗防死锁
  • getPlayerForSession 不再复用其他 session 的 player,避免 surface 冲突
  • emitTimeupdate 每 tick 无条件触发(此前仅在 playing 状态下发送,导致暂停/结束不更新)
  • 全屏退出后暂停视频画面压扁:页面旋转导致 XComponent surface 重建,暂停视频不自动重渲染。新增全局事件总线 + 微 seek 强制重配 VO 渲染一帧
  • loading 语义修正:暂停和已播完不显示 loading,仅播放中缓冲才显示(buffering = playing && paused-for-cache)
  • 缓冲状态边沿触发:仅在 loading 状态翻转时发事件,避免每 500ms 重复发送 waiting/buffering
  • Android 内存泄漏:destroy() 置空 surfaceCallback / surfaceHolder / surfaceView / nativeContainer / $element / params,切断 View → Activity 引用链

0.3.1(2026-09-29)

Added

  • README 新增「注意事项」章节:蒸汽模式不支持 rpx、退出全屏字体变大、watch 不触发、@keyframes 不可用、Math.random() 返回 0、Element.animate() 不支持、iOS 事件对象兼容说明

Fixed

  • iOS 自定义 UI 时间/状态不更新:getEventValue 先尝试 (e as any)[key] 属性访问,失败再 fallback 到 (e as Map<string, any>).get(key)
  • 所有 Demo 页面 rpx → px 转换(d-video/vapor/live),2rpx=1px
查看更多

平台兼容性

uni-app x(5.0)

Chrome Safari Android iOS 鸿蒙 微信小程序
√ √ 8.0 16 √ -

其他

多语言 暗黑模式 宽屏模式 蒸汽模式
× × × √

d-video · 蒸汽模式视频播放器

对齐官方 uni-video 架构,UI 直接写 view、native-view 桥接 libmpv 引擎,支持蒸汽(Vapor)模式与直播/倍速/弹幕/全屏。

特性

  • 蒸汽模式 — 视图层直出 bytecode/nativecode,无 VDOM diff
  • native-view 桥接 — Android 端通过 native-view 桥接 libmpv,Web 端回退内置 <video>
  • 跨平台 — Android / iOS / 鸿蒙 / H5 四端覆盖
  • 直播支持 — HLS/FLV/RTMP/RTSP 直播流,断流自动重连,直播模式自动隐藏进度条与倍速
  • 弹幕引擎 — Vapor 视图层 v-for + setInterval 推进,不经过 VDOM diff
  • 图标字体 — CSS @font-face 全局加载,模板用 HTML 实体 &#xe612; 引用
  • 全屏 — Android 沉浸式 + requestedOrientation,iOS/鸿蒙 $setPageStyle 横屏,H5 原生 DOM Fullscreen API
  • 自定义 UI — controls="false" 隐藏默认 UI,通过 <slot> 插槽注入自定义控制栏
  • 多实例 — 支持同页多个 d-video 实例,各自独立引擎

快速开始

# 安卓
cli launch app-android --project <path> --playground custom --customType local --cleanCache true

# H5
cli launch web --project <path>

使用

<d-video
  ref="playerRef"
  :src="videoUrl"
  :autoplay="false"
  :live="false"
  :muted="false"
  :danmuBtn="true"
  :enableDanmu="true"
  :danmuList="danmuList"
  :showMuteBtn="true"
  @statechange="onStateChange"
  @progress="onProgress"
  @prepared="onPrepared"
  @fullscreenchange=""
/>

自定义 UI(Slot 插槽)

传 controls="false" 隐藏默认 UI,通过 <slot> 注入自定义控制栏:

<d-video ref="playerRef" :src="videoUrl" :controls="false" :showCenterPlayBtn="false" :showLoading="false">
  <!-- 自定义播放按钮 -->
  <view class="my-overlay" v-if="!playing">
    <text class="my-play-btn" @click="playerRef?.play()">▶</text>
  </view>
  <!-- 自定义控制栏 -->
  <view class="my-controls">
    <text>{{ formatTime(position) }} / {{ formatTime(duration) }}</text>
    <text @click="playerRef?.toggle()">{{ playing ? '⏸' : '▶' }}</text>
    <text @click="playerRef?.toggleFullscreen()">⛶</text>
  </view>
</d-video>

多实例视频列表

<scroll-view>
  <view v-for="(item, idx) in videoList" :key="idx">
    <d-video
      :ref="(el: any) => onPlayerRef(el, idx)"
      :src="item.src"
      :controls="false"
      :danmuBtn="false"
      @statechange="(e: any) => onStateChange(e, idx)"
      @progress="(e: any) => onProgress(e, idx)"
    ></d-video>
    <text>{{ item.title }}</text>
  </view>
</scroll-view>

直播模式

<d-video
  :src="hlsUrl"
  :live="true"
  :autoplay="true"
  @livereconnect="onReconnect"
/>

直播模式下自动隐藏进度条、倍速控制;断流触发 livereconnect 事件。

支持协议

基于 libmpv 引擎,支持以下协议:

协议 示例
RTMP rtmp://192.168.0.182:1936/test
RTSP rtsp://192.168.0.182:8554/test
HLS https://192.168.0.182:8888/test/index.m3u8
FLV https://example.com/live.flv
HTTP/HTTPS https://example.com/video.mp4

libmpv 支持格式

类别 格式
容器 MP4, MKV, AVI, MOV, FLV, HLS (m3u8), TS, WebM
视频编码 H.264, H.265/HEVC, VP9, AV1, MPEG-2, MPEG-4, Theora
音频编码 AAC, MP3, FLAC, Vorbis, Opus, AC3, DTS
字幕 SRT, ASS/SSA, VobSub, WebVTT

组件 API

Props

属性 类型 默认值 说明
src string — 视频 URL
controls boolean true 显示默认控制栏
autoplay boolean false 自动播放
live boolean false 直播模式
muted boolean false 静音
loop boolean false 循环
playbackRate number 1.0 倍速
objectFit string 'contain' 填充模式
title string '' 全屏标题
danmuBtn boolean false 显示弹幕开关
enableDanmu boolean false 开启弹幕
danmuList Danmu[] [] 弹幕列表
showMuteBtn boolean false 显示静音按钮
showFullscreenBtn boolean true 显示全屏按钮
showPlayBtn boolean true 显示播放按钮
showCenterPlayBtn boolean true 显示中央播放按钮
showLoading boolean true 显示加载指示

Methods

方法 说明
play() 播放
pause() 暂停
stop() 停止
seek(ms) 跳转(毫秒)
toggle() 播放/暂停切换
mute(muted) 静音/取消
playbackRate(rate) 设置倍速
requestFullscreen() 进入全屏
exitFullscreen() 退出全屏
toggleFullscreen() 全屏切换
getCurrentState() 当前状态
getPosition() 当前位置(ms)
getDuration() 总时长(ms)
switchQuality(index) 切换清晰度
switchSource(url) 切换视频源(Vapor 模式需直接调用)

Events

事件 说明
statechange 播放状态变化
progress 播放进度
prepared 就绪
end 播放结束
error 错误
fullscreenchange 全屏变化
livereconnect 直播重连

Iconfont 用法

/* App.uvue 全局加载 */
@font-face {
  font-family: 'dplayer-iconfont';
  src: url('/static/dplayer-iconfont.ttf');
}
<!-- 模板中用 HTML 实体,不要用 JS 转义 -->
<text style="font-family: 'dplayer-iconfont';">&#xe612;</text>

<!-- 动态切换可用 JS 字符串 -->
<text>{{ playing ? '\ue60f' : '\ue612' }}</text>
图标 码点 HTML 实体
播放 U+E612 &#xe612;
暂停 U+E60F &#xe60f;
全屏进入 U+E610 &#xe610;
全屏退出 U+E611 &#xe611;
音量 U+E615 &#xe615;
静音 U+E617 &#xe617;

注意:字体文件不含 U+E601,返回按钮使用 Unicode 箭头 ← 代替。

Demo 页面

页面 路径 说明
蒸汽模式 Demo pages/vapor/index 多实例视频列表 + 弹幕 + 全屏
官方式 Demo pages/d-video/index 对齐 uni-video
HLS 直播 Demo pages/live/index 直播流 + 断流重连
自定义 UI Demo pages/custom-ui/index slot 插槽自定义控制栏

平台支持

平台 引擎 全屏方式
Android libmpv (native-view) 沉浸式 + requestedOrientation
iOS libmpv (native-view) $setPageStyle 横屏
鸿蒙 libmpv (native-view) $setPageStyle 横屏
H5 内置 <video> DOM Fullscreen API

注意事项

  1. 蒸汽模式不支持 rpx — Vapor 模式下 rpx 会被当作固定像素值,不随屏幕适配缩放,请统一使用 px(2rpx = 1px)
  2. 退出全屏字体变大 — 全屏状态下 100vw/100vh 计算会因方向切换导致 UI 异常,建议全屏/非全屏使用不同的控制栏样式,或通过 fullscreenchange 事件监听后主动切换
  3. Vapor 模式 watch 不触发 — watch(() => props.src) 在 Vapor 模式下不会触发,需通过 switchSource(url) 方法直接切换视频源
  4. CSS @keyframes 不可用 — Vapor 模式下 CSS 动画不生效,弹幕等动效请使用 v-for + setInterval 推进
  5. Math.random() 不生效 — Vapor 模式下返回 0,需使用自定义伪随机(如 LCG)
  6. Element.animate() 不支持 — Vapor 模式下不可用,需手动操作样式
  7. iOS 事件对象 — 自定义 UI 中读取事件数据时,需兼容属性访问和 Map.get() 两种方式
  8. Vapor 不跟踪 ref 深层属性写入 — states.value[idx].position = v 这类深层赋值不会触发视图更新,必须整体重新赋值(states.value = list)或改用扁平 ref<number> / ref<string>
  9. Vapor 慎用 Record + 动态下标 — ref<Record<number, X>>({}) 配合 v-for 动态 key 时更新易丢失,建议改用 ref<X[]>([]) 并按索引整体替换元素
  10. getPropertyBool('pause') 不可信 — mpv 的 pause 属性在暂停后可能仍返回 false,必须用 wantPlay 标志位推导暂停状态
  11. autoplay=false 需显式暂停 — mpv 打开文件后默认自动播放,doOpen 中必须紧跟 ['set', 'pause', 'yes'] 才能真正暂停
  12. 全屏退出后暂停视频画面变形 — 页面旋转导致 XComponent surface 重建,暂停视频不会自动重渲染。需在旋转结束后通过全局事件通知所有实例,微 seek 强制重配 VO 渲染一帧
  13. Android 内存泄漏:DVideo 持有 View/Activity 引用 — destroy() 必须置空 surfaceCallback / surfaceHolder / surfaceView / nativeContainer / $element / params,否则已销毁的 Activity 无法回收

许可

MIT

隐私、权限声明

1. 本插件需要申请的系统权限列表:

无

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

无

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

暂无用户评论。