更新记录
1.0.0(2026-09-10)
首个正式版本,提供可上架售卖的核心播放能力。
新增
- 跨端架构:采用 UTS uni-app 兼容模式组件(
utssdk/app-android/index.vue、utssdk/app-ios/index.vue),面向传统 uni-app(vue2/vue3)的 nvue 页面,同时兼容 uni-app x 的 VDOM 模式。 - 播放内核:
- Android:Media3 / ExoPlayer(Apache-2.0),支持 mp4、HLS(m3u8)、DASH、SmoothStreaming。
- iOS:系统 AVPlayer / AVPlayerLayer(AVFoundation),支持 mp4、HLS(m3u8)。
- 媒体源:在线点播(mp4)、在线/直播流(m3u8)、本地沙盒视频文件。
- 播放控制方法:
play()/pause()/stop()/seekTo(ms)/setRate(rate)/getCurrentTime()/getDuration()/setMute(bool)/enterFullScreen()/exitFullScreen()。 - Props:
src/title/poster/autoplay/loop/muted/isLive/playRate/showControl/objectFit/controlAutoHide/showTitle/showPoster/showFullscreenBtn。 - 事件:
onReady/onPlay/onPause/onEnded/onBuffering/onError(code,message)/onProgress(current,duration)/onFullscreenChange/onRateChange。 - 内置 UI 控制面板:进度条(可 seek)、播放/暂停、全屏、倍速(0.5~2.0)、静音、标题栏、封面图、控件自动隐藏、可单独隐藏任意控件。
- 画面缩放:
objectFit支持 contain / cover / fill。 - 音频焦点:Android 通过 AudioManager 音频焦点 + 前后台生命周期,iOS 通过 AVAudioSession + 中断/前后台通知,实现切后台、来电自动暂停,切回前台自动恢复。
- 健壮性:页面销毁时释放播放器、移除监听器/定时器/通知,防止内存泄漏;播放异常统一走
onError回调,不崩溃。 - 共享契约:
utssdk/interface.uts统一双端事件名、错误码、负载字段,保证两端行为一致。 - 示例与文档:提供
demo/下 nvue 示例(入口导航、mp4 点播、m3u8 直播)、完整readme.md、license.md、独立测试文档TEST.md。
修复与优化(真机测试期)
- 页面经
$refs调用的全部组件方法改为派发原生主线程执行,修复按钮失灵、全屏卡死、返回后页面导航异常。 play()自愈:IDLE 态(stop()或播放出错后)自动重新prepare(),ENDED 态(播完)自动回起点重播;iOS 端 AVPlayerItem 处于 failed 时自动重建媒体项。修复“播放按钮间歇性失灵”。- Android 本地无协议头绝对路径自动补
file://;demo 新增「选本地视频」(uni.chooseVideo+plus.io路径转换)。 - 直播 demo 更换为 Apple 官方 HLS / Mux / Akamai 三源(国内更可达)。
- demo 点播页不再放置「停止」「取进度」按钮(对应 API
stop()/getCurrentTime()/getDuration()保留)。
已知限制
- 仅 App 端(Android / iOS)可用;小程序、H5、快应用、鸿蒙不支持。
- 兼容模式组件须在 nvue 页面中使用;不支持组合式 API(仅选项式 API)。
- H.265/HEVC 硬解依赖设备硬件,老旧机型可能无声或无画面。
- 普通授权仅支持云打包并绑定 AppID;离线打包需源码授权;试用仅限自定义基座。
后续阶段规划(本版本不含)
- 阶段二:选集切换、边播边缓存(最大缓存设置 +
clearCache)、全屏横竖屏完善。 - 阶段三:弹幕、截屏保存相册、画中画悬浮小窗。
- 阶段四:DLNA 局域网投屏。
平台兼容性
uni-app(5.24)
| Vue2 | Vue2插件版本 | Vue3 | Vue3插件版本 | Chrome | Safari | app-vue | app-nvue | app-nvue插件版本 | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | 1.0.0 | √ | 1.0.0 | × | × | × | √ | 1.0.0 | 5.0 | 1.0.0 | 14 | 1.0.0 | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | × | × | × | × | × | × |
全能视频播放器 omni-media-player
纯 UTS 兼容模式视频播放器组件 · Android 基于 Media3-ExoPlayer(Apache-2.0) · iOS 基于系统 AVPlayer · 无 GPL 风险 · 可安全用于商业闭源 App
omni-media-player 是一款面向 uni-app 的原生视频播放器组件,使用 UTS(uni type script)编写,编译为纯原生代码(Android→Kotlin,iOS→Swift)。它内置了完整的播放控制面板,支持点播、直播、本地视频、倍速、全屏、音频焦点等能力,开箱即用。
✨ 核心优势
| 优势 | 说明 |
|---|---|
| 无 GPL 风险 | Android 采用 Media3-ExoPlayer(Apache-2.0),iOS 采用系统 AVPlayer,均可闭源商用,区别于使用 ijkplayer(GPL)的方案 |
| 🚀 纯 UTS 原生 | 编译为原生代码,性能接近纯原生播放器,非 webview 模拟 |
| 🎛️ 内置控制面板 | 进度条、播放/暂停、全屏、倍速、静音、标题、封面、自动隐藏,全部可配置、可单独隐藏 |
| 📡 点播 + 直播 | 支持 mp4 点播、m3u8(HLS)点播/直播、本地沙盒视频 |
| 🔊 音频焦点 | 切后台、来电自动暂停,切回自动恢复(双端一致) |
| 🧩 easycom 零配置 | 导入后在 nvue 页面直接写 <omni-media-player> 标签即可 |
| 🛡️ 健壮防泄漏 | 页面销毁自动释放资源;异常统一走 onError,不崩溃 |
⚠️ 平台兼容性(务必先读)
本插件为 UTS 兼容模式组件,仅在 App 端可用:
| 平台 | 支持 | 说明 |
|---|---|---|
| App-Android | ✅ | 基于 Media3-ExoPlayer,minSdkVersion 21(Android 5.0)+ |
| App-iOS | ✅ | 基于 AVPlayer,deploymentTarget iOS 14.0+ |
| App-鸿蒙(harmony) | ❌ | 暂不支持 |
| H5 | ❌ | 不支持 |
| 小程序(微信/支付宝等) | ❌ | 不支持 |
| 快应用 | ❌ | 不支持 |
Vue 版本:兼容传统 uni-app 的 vue2 与 vue3。
页面类型(关键约束):
- 本组件必须在 nvue 页面中使用(
.nvue文件)。nvue 是原生渲染页面,兼容模式 UTS 组件依赖原生 View 承载。 - 不支持在 vue 页面(webview 渲染)中使用。
- 组件仅支持选项式 API(
export default {}),不支持组合式 API(<script setup>/setup())。 - 同时兼容 uni-app x 的 VDOM 模式(如在 uni-app x 中使用,请参考 DCloud UTS 组件文档的 uni-app x 调用方式)。
🔐 授权与打包说明
| 授权类型 | 是否可查看源码 | 打包方式 | 适用场景 |
|---|---|---|---|
| 普通授权 | 否(DCloud 平台加密) | 仅云打包,绑定购买者 AppID | 大多数用户,价格低 |
| 源码授权 | 是 | 云打包 / 离线打包均可 | 需二次开发、需离线打包 |
- 试用:仅支持运行到自定义基座进行试用,不能用于打正式包。
- 普通授权购买后,插件源码由 DCloud 市场平台机制自动加密,绑定你的 AppID,无需自行处理加密。
- 若需离线打包(本地集成 Android Studio / Xcode 工程),请购买源码授权。
🚀 快速开始
📖 详细操作手册(demo 逐按钮说明、本地视频播放、全屏机制、FAQ)见 docs/OPERATION.md。
1. 导入插件
在 HBuilderX 中,将本插件(omni-media-player)导入到你的项目 uni_modules/ 目录下。导入后目录结构:
你的项目/
├─ pages/
│ └─ index.nvue
├─ uni_modules/
│ └─ omni-media-player/ ← 插件本体
├─ pages.json
└─ manifest.json
uni_modules 组件支持 easycom 自动扫描,导入后无需手动 import 组件,直接在 nvue 页面使用标签即可。
2. 在 nvue 页面中使用
新建一个 .nvue 页面(注意不是 .vue),直接使用 <omni-media-player> 标签:
<template>
<view class="page">
<!-- 播放器组件:必须放在 nvue 页面中 -->
<omni-media-player
ref="player"
class="player"
:src="videoSrc"
:title="视频标题"
:autoplay="true"
:loop="false"
:muted="false"
:playRate="1.0"
:showControl="true"
objectFit="contain"
@ready="onReady"
@play="onPlay"
@pause="onPause"
@ended=""
@buffering="onBuffering"
@error="onError"
@progress=""
@fullscreenchange=""
@ratechange=""
></omni-media-player>
<view class="btns">
<text class="btn" @click="doPlay">播放</text>
<text class="btn" @click="doPause">暂停</text>
<text class="btn" @click="doSeek">跳到30秒</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
videoSrc: 'https://example.com/test.mp4'
}
},
methods: {
// 通过 this.$refs.player 调用组件方法
doPlay() {
this.$refs.player.play();
},
doPause() {
this.$refs.player.pause();
},
doSeek() {
this.$refs.player.seekTo(30000); // 单位:毫秒
},
// 事件回调(注:负载字段的读取存在跨端差异,详见下文「事件负载的读取方式」,推荐用 pv(e,key) 防御式读取)
onReady(e) {
console.log('准备完成,时长(ms):', e.duration);
},
onPlay() { console.log('开始播放'); },
onPause() { console.log('暂停'); },
() { console.log('播放结束'); },
onBuffering(e) { console.log('缓冲中:', e.buffering); },
onError(e) { console.log('播放错误:', e.code, e.message); },
(e) { console.log('进度(ms):', e.current, '/', e.duration); },
(e) { console.log('全屏:', e.isFullscreen); },
(e) { console.log('倍速:', e.rate); }
}
}
</script>
<style>
.page { flex: 1; background-color: #000000; }
/* nvue 中组件需显式设置宽高 */
.player { width: 750rpx; height: 422rpx; }
.btns { flex-direction: row; padding: 20rpx; }
.btn { color: #ffffff; background-color: #2979ff; padding: 12rpx 24rpx; margin-right: 16rpx; border-radius: 8rpx; }
</style>
nvue 布局提示:nvue 使用 weex/flex 布局,组件必须显式设置
width/height,否则可能不显示。rpx 在 nvue 中同样可用。
📋 Props 属性
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
src |
String | "" |
视频地址。支持:http(s):// 在线 mp4/m3u8、file:// 或沙盒路径的本地视频 |
title |
String | "" |
标题栏文字 |
poster |
String | "" |
封面图地址(http(s):// 或本地路径),播放前显示 |
autoplay |
Boolean | false |
是否自动播放 |
loop |
Boolean | false |
是否循环播放 |
muted |
Boolean | false |
是否静音 |
isLive |
Boolean | false |
是否直播。直播场景建议设为 true(作为直播语义标识;直播下进度条/seek 的精细呈现将于后续阶段完善) |
playRate |
Number | 1.0 |
播放倍速,范围 0.5 ~ 2.0 |
showControl |
Boolean | true |
是否显示内置控制条 |
objectFit |
String | "contain" |
画面缩放模式:contain(等比完整,可能黑边) / cover(等比铺满裁剪) / fill(拉伸铺满,可能变形) |
controlAutoHide |
Number | 3000 |
控制条自动隐藏时间(毫秒) |
showTitle |
Boolean | true |
是否显示标题栏 |
showPoster |
Boolean | true |
是否显示封面 |
showFullscreenBtn |
Boolean | true |
是否显示全屏按钮 |
所有 Props 均支持运行时动态修改(组件内部
watch实时生效)。例如动态改变src即可切换播放源。
🛠️ 方法(通过 this.$refs.xxx 调用)
| 方法 | 参数 | 返回 | 说明 |
|---|---|---|---|
play() |
无 | 无 | 播放 / 继续播放 |
pause() |
无 | 无 | 暂停 |
stop() |
无 | 无 | 停止(回到起点并暂停,重新显示封面) |
seekTo(positionMs) |
positionMs: Number(毫秒) |
无 | 跳转到指定播放位置 |
setRate(rate) |
rate: Number(0.5~2.0) |
无 | 设置播放倍速 |
getCurrentTime(callback) |
callback: (ms: Number) => void |
无(结果经 callback 回传) | 获取当前播放位置(毫秒) |
getDuration(callback) |
callback: (ms: Number) => void |
无(结果经 callback 回传) | 获取总时长(毫秒,直播或未知回传 0) |
setMute(muted) |
muted: Boolean |
无 | 设置静音 |
enterFullScreen() |
无 | 无 | 进入全屏 |
exitFullScreen() |
无 | 无 | 退出全屏 |
命名说明:倍速/静音的方法名为
setRate/setMute(而非setPlayRate/setMuted),因为 prop 已占用playRate/muted;UTS 兼容模式组件会为每个 prop 自动生成同名 setter,方法若与之同名会产生 JVM 签名冲突。声明式改倍速/静音也可直接改 prop(:playRate/:muted),二者等效。
调用示例:
⚠️ nvue 重要约束:nvue(weex 引擎)不支持组件方法的同步返回值。因此
getCurrentTime/getDuration采用 callback 回调形式返回结果,而非return。此外,兼容模式组件不支持在组件标签上绑定 touch/click/tap 等事件(组件内部的按钮点击由原生层处理,不受影响)。
// 播放
this.$refs.player.play();
// 跳转到第 60 秒
this.$refs.player.seekTo(60000);
// 设置 1.5 倍速
this.$refs.player.setRate(1.5);
// 获取当前进度(nvue 不支持同步返回值,必须用 callback 形式)
this.$refs.player.getCurrentTime((cur) => {
console.log('当前位置(ms):', cur);
});
this.$refs.player.getDuration((dur) => {
console.log('总时长(ms):', dur);
});
// 静音
this.$refs.player.setMute(true);
// 全屏
this.$refs.player.enterFullScreen();
📡 事件
事件负载统一为对象(内部以 Map<string, any> 传递)。
| 事件名 | 回调参数 | 说明 |
|---|---|---|
@ready |
{ duration } |
播放器准备完成,可获取时长(毫秒) |
@play |
无 | 开始播放 / 继续播放 |
@pause |
无 | 暂停 |
@ended |
无 | 播放结束(非循环时触发) |
@buffering |
{ buffering } |
缓冲状态变化,buffering 为 true/false |
@error |
{ code, message } |
播放错误,见下方错误码 |
@progress |
{ current, duration } |
播放进度(单位毫秒),约每 0.5 秒触发一次 |
@fullscreenchange |
{ isFullscreen } |
全屏状态变化 |
@ratechange |
{ rate } |
倍速变化 |
错误码(error 事件的 code)
| code | 含义 | 常见原因 |
|---|---|---|
1001 |
网络错误 | 断网、超时、拉流失败 |
1002 |
无效地址 | src 为空、非法 URL、文件不存在 |
1003 |
解码/编码不支持 | 设备硬件不支持该编码(如部分机型不支持 H.265) |
1999 |
未知错误 | 其他未归类异常 |
事件名均为全小写(如
fullscreenchange、ratechange),以兼容早期 HBuilderX 对事件名的限制。
❗ 事件负载的读取方式(跨端差异,务必阅读)
事件负载在原生层统一以 Map<string, any> 发送。但在传统 uni-app(vue2/vue3)的 nvue 页面中,负载到达 JS 层的位置在不同平台/版本上可能不一致:
- 部分情况下字段挂在事件对象本身:
e.code、e.current; - 部分情况下(尤其 iOS)字段挂在
e.detail下:e.detail.code、e.detail.current。
为保证双端一致,推荐用一个防御式小函数读取负载(demo 中已采用):
methods: {
// 优先 e.detail[key],回退 e[key]
pv(e, key) {
if (e == null) return undefined;
const d = e.detail;
if (d != null && d[key] !== undefined) return d[key];
if (e[key] !== undefined) return e[key];
return undefined;
},
onError(e) {
const code = this.pv(e, 'code');
const message = this.pv(e, 'message');
console.log('错误:', code, message);
},
(e) {
const current = this.pv(e, 'current');
const duration = this.pv(e, 'duration');
console.log('进度:', current, '/', duration);
}
}
负载
Map的 value 仅支持string/number/Map等基础类型(iOS 限制),本插件所有负载字段均为string/number/boolean,符合规范。若你使用 uni-app x,请参考 uni-app x 的事件接收方式(iOS 用UniEvent,Android 用Map)。
📺 支持的媒体格式
| 类型 | Android(Media3) | iOS(AVPlayer) |
|---|---|---|
| MP4 (H.264) | ✅ | ✅ |
| HLS / m3u8(点播+直播) | ✅ | ✅ |
| DASH | ✅ | ⚠️ 视系统而定 |
| SmoothStreaming | ✅ | ❌ |
| 本地沙盒视频 | ✅ | ✅ |
| H.265 / HEVC | ⚠️ 依赖硬件 | ⚠️ 依赖硬件 |
实际可播放的编码/分辨率取决于设备硬件与系统解码能力,本插件不承诺支持全部格式。建议优先使用 H.264 + AAC 的 MP4 或标准 HLS 流以获得最佳兼容性。
❗ 使用限制与注意事项
- 仅 App 端:小程序、H5、快应用、鸿蒙均不支持。
- 仅 nvue 页面:必须在
.nvue页面中使用,vue 页面(webview)无效。 - 仅选项式 API:不支持
<script setup>/ 组合式 API。 - nvue 布局:组件必须显式设置宽高(flex 布局),否则可能不显示。
- 全屏说明:全屏通过将播放器视图临时挂载到窗口/DecorView 并切换横竖屏实现;退出全屏会恢复原布局。
- 音频焦点:组件会自动申请音频焦点,切后台/来电时暂停,回到前台恢复;这是预期行为。
- 打包限制:普通授权仅云打包并绑定 AppID;离线打包需源码授权;试用仅自定义基座。
- HBuilderX 版本:建议使用较新版本 HBuilderX(含较新 UTS 编译器),以确保 UTS 语法与 Media3 依赖正常编译。
❓ 常见问题(FAQ)
Q:为什么组件不显示 / 是黑屏?
A:① 确认页面是 .nvue 而非 .vue;② 确认给组件设置了 width/height;③ 确认 src 地址可访问;④ 真机运行(模拟器可能无法解码)。
Q:为什么提示需要自定义基座? A:UTS 插件包含原生代码,标准基座不含本插件,必须制作自定义基座或云打包后才能运行。
Q:进度回调频率是多少?
A:约每 0.5 秒一次(@progress)。如需更精细可自行调整。
Q:能播放 H.265 吗? A:取决于设备硬件解码能力。老旧机型可能无声或无画面,建议提供 H.264 源作为兜底。
Q:支持 DRM 加密视频吗? A:第一阶段暂不支持 DRM。
🗺️ 功能规划
- ✅ 阶段一(当前版本):核心播放 + UI 控制面板 + 音频焦点 + 全屏。
- ⏳ 阶段二:选集切换、边播边缓存(最大缓存设置 +
clearCache)、全屏横竖屏完善。 - ⏳ 阶段三:弹幕、截屏保存相册、画中画悬浮小窗。
- ⏳ 阶段四:DLNA 局域网投屏。
后续阶段将单独交付并同步更新本 readme 与
TEST.md。购买用户可获得对应阶段的更新。
📜 许可与免责
- 本插件 Android 端使用 Media3-ExoPlayer(Apache License 2.0),iOS 端使用 Apple 系统 AVFoundation/AVPlayer,均可闭源商用,无 GPL 传染风险。详见
license.md。 - 本插件代码为独立实现,未抄袭任何第三方商业插件源码。
- 本插件不采集、不上传任何用户隐私数据,播放与缓存等逻辑全部在设备本地执行。
- 媒体内容的版权与合法性由使用者自行负责,本插件仅提供播放能力。
- 作者不对使用者因使用本插件产生的任何业务损失承担责任。
📮 联系与支持
- 使用问题、Bug 反馈、功能建议:请通过 DCloud 插件市场本插件页面的「联系作者」或评论区反馈。
- 反馈时请附上:HBuilderX 版本、手机型号与系统版本、复现步骤、错误日志(
@error回调的code/message)。
祝使用愉快 🎬

收藏人数:
购买源码授权版(
试用
赞赏(0)
下载 4
赞赏 0
下载 12586883
赞赏 1949
赞赏
京公网安备:11010802035340号