更新记录

1.2.1(2026-09-18)

  • 【鸿蒙 seek 防抖】seekTo 指令立即把 UI 进度定位到目标位置,并设 1.2s 保护窗口(seekGuardMs/seekTargetMs)忽略原生回传的旧位置,消除拖动后进度条回跳抖动
  • 【鸿蒙 overlay slot】模板新增 <slot> 自定义覆盖层,支持第三方 overlay UI 挂载,与 Web 端对齐
  • 【安卓挂载层置顶】bringCustomLayerToFront 增加重复挂载检测:已是子视图时先 removeView 再 addView,确保自定义层始终位于最前
  • 【iOS 编译依赖】DPlayerEngine.swift 补充 import DCloudUTSFoundation,修复编译期模块解析失败
  • 【插件类型】package.json dcloudext.type 由 uts-vue-component 更正为 component-uts
  • 【鸿蒙进度条样式】拖动滑块 thumb 位置微调(top: -4px → -3px)
  • 增加 自定义视频上层 内容 如 控制栏,弹幕,字幕等

1.1.0(2026-09-13)

  • 【Web 支持】新增 Web 平台实现(utssdk/web/index.vue):采用 uni-h5 内置 <video> 组件,解决 Web 端此前渲染为空(easycom 回退到空占位组件)的问题
  • 【Web 接口对齐】props、emits、command#key 指令协议与原生平台对齐(play/pause/stop/seekTo/setVolume/setPlaybackRate),事件载荷统一为 Map<string, any>
  • 【Web 控制能力】支持播放/暂停/停止/跳转/静音/倍速(含 0.5x/1.25x/2x,uni-h5 对非预设倍率回退 1x)、资源切换、全屏(浏览器原生控器)
  • 【Web 进度同步】timeupdate 驱动 progress/percent,引入 seek 保护窗口(1.2s ±500ms)避免拖动后进度回跳
  • 【Web 直播】直播流加载失败自动重连(最多 3 次,触发 livereconnect);

1.0.9(2026-09-13)

  • 【iOS 崩溃修复】修复真机启动闪退问题:resources prop 编译为 [Any?] 无法 KVC 桥接,改用 PropType<Array<any>> 生成 Array<Any>,修复 viewDidLoad 反射崩溃
  • 【iOS 加载动画】修复 loading 图标位于左上角的问题,layoutControls() 中按 intrinsicContentSize 居中显示
  • 【iOS 字体图标】修复打开新播放页面后字体图标不显示的问题,loadIconFont() 增加对已注册字体(fontNames(forFamilyName:))的判断
  • 【鸿蒙多页面】修复第一个页面播放控制失效的问题:新增 onHide/onShow 页面生命周期处理,页面隐藏自动暂停、返回自动恢复
  • 【鸿蒙多会话】getPlayerForSession 不再创建幽灵会话,会话缺失时回退 createPlayer() 并输出错误日志
  • 【鸿蒙状态同步】修复返回页面后状态显示暂停但视频仍在播放的问题,onNativeState 仅在 mpv 确认 paused=true 时才切换为暂停态
  • 【鸿蒙进度条】自研触控进度条替换原生 Slider:解决全屏模式进度条宽度不自动铺满(改用 100% 自适应布局)以及拖动无反应(getBoundingClientRect() 精确定位 + isSeeking 防轮询回跳 + 触摸热区加大 + 事件冒泡拦截)问题
  • 【安卓暂停修复】修复暂停按钮无效问题,pause() 仅停止主线程进度刷新,不再清空 mpv 线程的查询队列
  • 【安卓全屏播放】修复暂停状态下切换全屏后自动播放的问题,进入全屏前同步 mr wantPlay 状态
  • 【安卓多页面】修复第二个页面不播放的问题,loadMedia() 将 wantPlay 同步提前到 Surface 就绪判断之前
查看更多

平台兼容性

uni-app x(5.0)

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

其他

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

DPlayer 视频播放器

基于 libmpv 的跨平台视频播放器 uni_modules 插件,支持 H.265 解码和直播拉流。

功能特性

  • H.265/HEVC 支持:Android/iOS 硬件解码 + FFmpeg 软件解码
  • 直播拉流:HLS、HTTP-FLV、RTMP、RTSP 协议
  • 多平台:uni-app X (UTS)、uni-app NVue、Web、微信小程序
  • 弹幕系统:滚动弹幕、顶部弹幕、底部弹幕
  • 清晰度切换:通过 resources prop 传入多码率列表,点击循环切换
  • 自动重连:直播断流自动重连
  • B站风格 UI:底部控制栏采用 B 站两层布局设计
  • 鸿蒙适配:支持 HarmonyOS NEXT,ArkUI 原生渲染

安装

  1. 下载本插件到项目的 uni_modules/ 目录
  2. 在 pages.json 中配置页面
  3. 在页面中使用 <d-player-video> 组件

iOS 依赖配置

iOS 平台通过 CocoaPods 自动依赖 Libmpv 框架,无需手动下载。首次编译时会自动从 Gitee 下载 libmpv-1.0.0.xcframework.zip。

基础用法

<template>
  <d-player-video
    ref="player"
    src="https://example.com/video.mp4"
    :autoplay="true"
    :live="false"
    :resources="qualityList"
    @statechange="onStateChange"
    @progress=""
    @prepared="onPrepared"
    @error="onError"
  />
</template>

<script lang="uts">
export default {
  data() {
    return {
      qualityList: [
        { name: '超清', url: 'https://example.com/1080p.mp4' },
        { name: '高清', url: 'https://example.com/720p.mp4' }
      ]
    }
  },
  methods: {
    onStateChange(e: any) {
      console.log('状态变化:', e.state)
    },
    (e: any) {
      console.log('进度:', e.position, '/', e.duration)
    },
    onPrepared(e: any) {
      console.log('时长:', e.duration)
    },
    onError(e: any) {
      console.error('错误:', e.code, e.message)
    }
  }
}
</script>

Props

属性 类型 默认值 说明
src String "" 视频/直播流地址
autoplay Boolean false 自动播放
loop Boolean false 循环播放
live Boolean false 是否为直播流
muted Boolean false 静音
volume Number 1.0 音量 (0-1)
playbackRate Number 1.0 播放速度
decodeMode String "auto" 解码模式: auto/hw/sw
showControls Boolean true 显示控制栏
showDanmaku Boolean false 显示弹幕
objectFit String "contain" 视频填充模式
command String "" 外部命令(如 "play"/"pause"/"seek:10000")
resources Array [] 清晰度列表,每项 { name: string, url: string }
headers Object {} HTTP 鉴权头,如 { Authorization: "Bearer xxx", Referer: "https://example.com" }

API 使用方法

通过 ref 调用组件方法:

播放控制

// 开始播放(如果未加载媒体则自动加载)
this.$refs.player.play()

// 暂停播放
this.$refs.player.pause()

// 停止播放(重置到开头)
this.$refs.player.stop()

// 跳转到指定位置(单位:毫秒)
this.$refs.player.seek(10000)  // 跳转到 10 秒

音量控制

// 设置音量(0-1 之间)
this.$refs.player.applyVolume(0.5)  // 50% 音量
this.$refs.player.applyVolume(0.0)  // 静音
this.$refs.player.applyVolume(1.0)  // 最大音量

// 切换静音状态(通过 UI 按钮或外部调用)
this.$refs.player.toggleMute()

播放速度

// 设置播放速度
this.$refs.player.setSpeed(0.5)   // 0.5 倍速
this.$refs.player.setSpeed(1.0)   // 正常速度
this.$refs.player.setSpeed(1.5)   // 1.5 倍速
this.$refs.player.setSpeed(2.0)   // 2 倍速

// 循环切换倍速(UI 按钮自动循环:1.0→1.25→1.5→2.0→0.75)
this.$refs.player.cycleSpeed()

获取播放信息

播放状态通过事件回调获取,组件内部状态通过 data 绑定:

// 状态变化通过 statechange 事件
// 状态值: "idle" | "opening" | "ready" | "playing" | "paused" | "completed" | "error"
this.$on('statechange', (e) => {
  console.log('状态:', e.state)
})

// 进度通过 progress 事件
this.$on('progress', (e) => {
  console.log('位置:', e.position, '时长:', e.duration)
})

弹幕控制

// 通过 UI 按钮切换弹幕显示/隐藏
// 弹幕功能在内置控制栏中操作

清晰度切换

通过 resources prop 传入清晰度列表,用户点击 UI 上的清晰度按钮自动循环切换:

<d-player-video
  :resources="qualityList"
  @qualitychange="onQualityChange"
/>
const qualityList = [
  { name: '超清', url: 'https://example.com/1080p.mp4' },
  { name: '高清', url: 'https://example.com/720p.mp4' },
  { name: '标清', url: 'https://example.com/480p.mp4' }
]

注:qualitychange 事件目前仅在鸿蒙平台触发,Android/iOS 待实现。

全屏控制

全屏通过内置 UI 按钮触发,会自动切换页面方向(横屏/竖屏)和导航栏样式:

// 全屏状态通过 statechange 事件获取
// isFullscreen 状态在组件内部维护

释放资源

// 停止播放并释放所有资源(组件销毁前调用)
this.$refs.player.releasePlayer()

完整示例

<template>
  <view>
    <d-player-video
      ref="player"
      src="https://example.com/video.mp4"
      :autoplay="true"
      :loop="false"
      :live="false"
      :resources="qualityList"
      @statechange="onStateChange"
      @progress=""
      @prepared="onPrepared"
      @completion="onCompletion"
      @error="onError"
    />
    <view class="controls">
      <button @click="togglePlay">播放/暂停</button>
      <button @click="seekForward">快进 10 秒</button>
      <button @click="seekBackward">快退 10 秒</button>
      <button @click="changeSpeed">切换倍速</button>
    </view>
  </view>
</template>

<script lang="uts">
export default {
  data() {
    return {
      isPlaying: false,
      qualityList: [
        { name: '超清', url: 'https://example.com/1080p.mp4' },
        { name: '高清', url: 'https://example.com/720p.mp4' }
      ]
    }
  },
  methods: {
    togglePlay() {
      this.$refs.player.togglePlay()
    },

    seekForward() {
      this.$refs.player.seek(this.$refs.player.getPosition() + 10000)
    },

    seekBackward() {
      this.$refs.player.seek(Math.max(0, this.$refs.player.getPosition() - 10000))
    },

    changeSpeed() {
      this.$refs.player.cycleSpeed()
    },

    onStateChange(e) {
      this.isPlaying = (e.state === "playing")
      console.log('状态变化:', e.state)
    },

    (e) {
      console.log('位置:', e.position, '时长:', e.duration)
    },

    onPrepared(e) {
      console.log('媒体加载完成,时长:', e.duration)
    },

    onCompletion() {
      console.log('播放完成')
    },

    onError(e) {
      console.error('播放错误:', e.code, e.message)
    }
  },

  onUnmounted() {
    this.$refs.player.releasePlayer()
  }
}
</script>

事件

事件名 参数 说明
statechange { state } 播放状态变化(idle/opening/ready/playing/paused/completed/error)
progress { position, duration } 播放进度更新
prepared { duration } 媒体加载完成
buffering { percent } 缓冲状态
error { code, message } 播放错误
videosizechanged { width, height } 视频尺寸变化
seekcomplete {} 跳转完成
completion {} 播放完成
livereconnect { attempt } 直播重连
qualitychange { index, name, url } 清晰度切换(鸿蒙平台)

直播拉流

<template>
  <d-player-video
    src="rtmp://live.example.com/stream"
    :live="true"
    :autoplay="true"
    @livereconnect="onReconnect"
  />
</template>

支持的直播协议:

  • HLS (m3u8)
  • HTTP-FLV
  • RTMP
  • RTSP

H.265 支持

组件自动检测 H.265 硬件解码能力:

  • Android:MediaCodec 硬解 + FFmpeg 软解
  • iOS:VideoToolbox 硬解 + FFmpeg 软解
  • 鸿蒙:FFmpeg 软解
  • Web:ffmpeg.wasm 软解(实验性)
  • 小程序:不支持 H.265

平台支持

平台 状态 说明
uni-app X (Android) 完整支持 libmpv + MediaCodec
uni-app X (iOS) 完整支持 libmpv + VideoToolbox
uni-app X (鸿蒙) 完整支持 mpv-ohos ArkUI 原生渲染
uni-app NVue 完整支持 同 uni-app X
Web 完整支持 mpegts.js + hls.js
微信小程序 基础支持 原生 video 组件

UI 特性

B站风格控制栏

底部控制栏采用 B 站两层布局设计:

  • 上层:细红色进度条,支持拖动 seek
  • 下层:播放/暂停按钮、时间显示、倍速、音量、全屏

图标样式

  • 播放按钮:饱满三角形(B站风格)
  • 暂停按钮:圆角双竖杠(B站风格)
  • 全屏按钮:四角向外/向内图标
  • 音量按钮:喇叭+声波图标

全屏适配

  • 全屏模式下控制栏自动放大 1.4 倍
  • 保持 B 站风格布局一致
  • 鸿蒙平台通过 uni.getWindowInfo().safeArea 动态适配安全区域
  • 鸿蒙暂停状态下全屏切换通过 frame-step 强制渲染修复画面不铺满问题

自定义控制栏 / 挂载点

native-view 组件不支持子组件,所以自定义 UI 不能通过 <slot> 塞进原生容器(Web 端例外,见下)。App 端提供挂载层:一个透明的原生容器,始终位于视频与内置控制栏之上,且随全屏自动迁移,自定义 UI 在全屏下依然可见可控。

平台行为

平台 挂载层 全屏跟随
Android FrameLayout,全屏时由 moveControlsTo 迁移进独立全屏窗口 自动
iOS UIView,随 containerView 整体进出全屏 UIWindow 自动
鸿蒙 同视图树,全屏用 position: fixed 覆盖 第二批支持
Web <slot> 直接写模板 容器整体 requestFullscreen(),自动

App 端用法

// 1. 拿到挂载层(NVLoad 之后可用)
const layer = this.$refs.player.playerCustomLayer()

// 2. 把自建原生 View 挂上去
const btn = new View(this.$androidContext!)
btn.setLayoutParams(new FrameLayout.LayoutParams(48, 48))
this.$refs.player.addCustomView(btn)

// 3. 移除
this.$refs.player.removeCustomView(btn)

// 4. 清空全部自定义视图
this.$refs.player.clearCustomViews()

addCustomView 内部会切到主线程执行,可安全地在任意线程调用。

状态读取(拉取式)

自定义原生 UI 可用定时器轮询,不必等事件:

playerPosition() : number     // 当前播放位置,毫秒
playerDuration() : number     // 总时长,毫秒
playerState() : string        // "idle"|"opening"|"ready"|"playing"|"paused"|"completed"|"error"
playerIsPlaying() : boolean
playerIsMuted() : boolean
playerIsFullscreen() : boolean
playerSpeed() : number

统一 player* 前缀:Android 侧原生字段(duration、isFullscreen 等)会自动生成同名 getter,方法名冲突会编译失败。

状态推送(订阅式)

不想轮询可订阅回调,返回的 id 用于注销:

const pid = this.$refs.player.addProgressListener((position, duration) => {
  // 约 250ms 一次,仅在已准备好后触发
  this.updateBar(position, duration)
})

const sid = this.$refs.player.addStateListener((state, isPlaying) => {
  this.updatePlayIcon(isPlaying)
})

// 注销(务必在 onUnload / 卸载时调用,避免闭包持有 this 造成泄漏)
this.$refs.player.removeProgressListener(pid)
this.$refs.player.removeStateListener(sid)

Web 端用法

Web 实现是普通 Vue 组件,直接支持 <slot>,覆盖层置于视频之上:

<d-player-video ref="player" :src="src">
  <view class="my-badge">LIVE</view>
</d-player-video>

覆盖层容器 pointer-events: none 不拦截事件,直接子元素自动恢复可交互,空白区域点击会穿透到下方原生 video 控制条。

设计取舍

  • 不走 <slot>:native-view 组件不支持子组件(官方文档);UTS 的 <slot> 自动转容器组件仅兼容模式支持,且不支持具名插槽、不支持 touch/click/tap 事件。本插件是标准模式 uts-vue-component。
  • 不做全局 createDPlayerContext(id):跨平台原生 view 类型不统一,且本插件是 NVLoad() 返回原生 view,模板里没有 native-view 节点,官方 iterateElement 递归范式无法套用。故以 ref 调组件方法交付;第三方插件跨组件访问留作后续。
  • Android 层叠:SurfaceView 未设 setZOrderMediaOverlay,默认 surface 在窗口之下,同 FrameLayout 内后加的 View 天然在上层,与内置控制栏可用原理一致。

注意事项

  1. H.265 专利风险:H.265 编解码器存在专利许可问题,商业使用需注意授权
  2. Web 端 H.265:依赖 ffmpeg.wasm,性能可能受影响
  3. 小程序限制:微信小程序原生 video 组件不支持 H.265
  4. 直播重连:默认最多重试 3 次,间隔 3 秒
  5. iOS 依赖:首次编译需联网下载 Libmpv 框架(约 10MB)
  6. 鸿蒙平台:不支持 calc()/env() CSS 函数,安全区域通过 JS 动态读取;position: absolute/fixed 必须使用 width/height 而非 left/right/top/bottom 撑开

开发

目录结构

d-player-video/
├── index.uts                    # 全局入口
├── index.vue                    # 根组件
├── package.json                 # uni_modules 配置
├── common/
│   ├── types.uts                # 类型定义
│   ├── events.uts               # 事件常量
│   ├── state-machine.uts        # 状态机
│   ├── player-config.uts        # 配置管理
│   ├── danmaku-types.uts        # 弹幕类型
│   ├── quality-types.uts        # 清晰度类型
│   └── event-utils.uts          # 事件工具
├── components/
│   ├── DPlayerControl.vue       # 控制栏组件
│   ├── DPlayerProgress.vue      # 进度条组件
│   ├── DPlayerDanmaku.vue       # 弹幕组件
│   └── DPlayerQuality.vue       # 清晰度组件
└── utssdk/
    ├── app-android/
    │   ├── index.vue            # Android UTS 组件
    │   ├── index.uts            # Android 平台函数
    │   ├── config.json          # Android 配置
    │   └── DPlayerNative.kt     # Native 桥接
    ├── app-ios/
    │   ├── index.vue            # iOS UTS 组件
    │   ├── index.uts            # iOS 平台函数
    │   ├── config.json          # iOS 配置
    │   ├── Libmpv.podspec       # CocoaPods 配置
    │   └── DPlayerEngine.swift  # 引擎实现
    ├── app-harmony/
    │   ├── index.vue            # 鸿蒙 UTS 组件
    │   ├── index.uts            # 鸿蒙平台函数
    │   ├── builder.ets          # ArkTS 构建函数 + mpv 命令
    │   └── config.json          # 鸿蒙配置
    ├── web/
    │   └── index.vue            # Web 平台实现(uni-h5 内建 video + slot 覆盖层)
    └── mp-weixin/
        └── index.vue            # 微信小程序实现

更新日志

详见 changelog.md

许可

MIT License

隐私、权限声明

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

无

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

无

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

无

暂无用户评论。