更新记录
1.0.1(2026-11-07) 下载此版本
- 修正插件市场描述:按市场 100 汉字上限精简,并把「小程序与 H5 明确降级」改为准确表述——微信小程序在基础库提供 UDP/TCP Socket 时条件支持真实链路,其余小程序与 H5 降级。
- 插件标签按市场 5 个上限收敛为:投屏、DLNA、UPnP、SSDP、uts。
- 源码契约测试新增两项市场限制守护(描述 ≤100 字符、标签 ≤5 个),避免下次发版被服务端拒绝。
1.0.0(2026-11-07) 下载此版本
首个公开版本:局域网媒体投屏 UTS 插件(DLNA / UPnP)。
- 设备发现:SSDP M-SEARCH 组播 + 单播响应解析;组播不可用时可用 describeRenderer() 直连设备描述,不伪造设备列表。
- 投送与控制:SetAVTransportURI / Play / Pause / Stop / Seek / SetVolume / SetMute,状态只在接收端回报或事件到达时更新,不把「命令已被接受」当成「播放成功」。
- 内置控制面板 up-castplay-panel:设备名册(已连接高亮)、媒体信息与封面、播放/暂停、可拖动进度条、音量条、清晰度 / 字幕 / 倍速、节目单选集与上下集、交接续播、可折叠诊断行;面板只读 session 真实状态。
- 进阶能力:ConnectionManager 能力协商、节目单选集、清晰度、字幕文本轨(DIDL-Lite 第二个 res)、倍速、交接续播凭证、本地文件与字幕的局域网 HTTP 发布(支持 Range)、系统路由选择器、GENA 事件订阅。
- 多会话:显式 session 取代全局单例,可同时管理多个接收端。
- 平台:App(Android / iOS / HarmonyOS)原生 UDP 组播 + HTTP 传输;微信小程序在基础库提供 UDP/TCP Socket 时条件支持;H5 与其他小程序明确降级(异步操作返回 30002),同步能力查询、节目单状态与媒体预检仍可用。
- 协议与会话状态机收敛在平台无关核心,原生层只负责传输;错误码 30001–30016。
- 示例工程内置可复跑的验证资产:由真实 wire.uts 生成的报文夹具、模拟 UPnP 接收端(真实视频流与播放页)、MuMu 实机往返验证,以及用无头 Edge 渲染真实面板组件得到的界面截图。
平台兼容性
UP CastPlay
独立实现的局域网媒体投屏 UTS 插件。用 SSDP 发现 DLNA/UPnP MediaRenderer,解析设备描述,再用 SOAP 把媒体地址交给接收端自行取回并控制播放。插件 ID 为 up-castplay,自带一个可直接使用的内置控制面板组件,不依赖任何商业插件或第三方 SDK。
功能对标 DCloud 插件市场插件 id=28205 的公开功能说明(DLNA 投屏、设备发现、播放控制、视频/音频/图片投送、内置面板、选集/清晰度/字幕/倍速、交接续播、诊断能力、系统路由入口、小程序条件支持)。本插件只参考公开的能力边界,未使用其源码、API 命名、组件标签、界面素材、图标或品牌。接口语义、状态模型、事件模型、工程结构均为另行设计:例如用显式 session 取代全局单例,用 requested 区分「命令被接受」与「接收端确认播放」,用 roster / link / clock 三类信号取代回调堆叠。
这是「把媒体 URL 交给电视播放」,不是屏幕镜像、录屏推流或 AirPlay 私有协议。电视必须能直接访问该 URL;手机本地文件可先用
publishLocal()发布成局域网 URL,带登录态的私有地址与 DRM 加密流不能直接投送。
能力与平台
| 平台 | 设备发现 | 媒体投送与控制 | 扩展能力 | 实现与限制 |
|---|---|---|---|---|
| App-Android 5.0+(API21) | SSDP M-SEARCH 组播 + 单播响应 | 支持 | 内置面板、本地 HTTP 服务、GENA 回调服务、系统投屏设置入口 | Kotlin DatagramSocket + HttpURLConnection + ServerSocket;只发送组播、接收单播响应,不加入组播组、不持有 MulticastLock |
| App-iOS 13+ | 同上 | 支持 | 同上,系统路由用 AVRoutePickerView |
Swift BSD UDP socket + URLSession;自动发现需本地网络权限与 Apple Multicast Networking 授权,未获批时用 describeRenderer() 直连 |
| App-HarmonyOS NEXT | 同上 | 支持 | 内置面板、本地 HTTP 服务、GENA 回调服务 | @kit.NetworkKit 的 UDP / TCP socket 与 http;受路由器/系统局域网策略影响 |
| 微信小程序 | 运行时具备 UDP/TCP Socket 时支持 | 条件支持 | 基础链路(发现/投送/控制/节目单/协商) | 依赖 wx.createUDPSocket / wx.createTCPSocket;手写 HTTP/1.1 over TCP,无本地服务与系统路由能力。未做真机联调,平台矩阵标 u |
| H5 / Web 与其他小程序 | 无 | 无 | 同步查询与媒体预检 | 浏览器/沙箱没有等价的 UDP 组播与局域网原生 socket;所有异步操作返回 30002,platformInfo() / probeMedia() 等同步查询仍可用 |
没有 AirPlay/Google Cast 私有协议、屏幕镜像、DRM 解密与 ContentDirectory 片库浏览;客观原因与尚未验证项见 docs/up-castplay-scope.md。
架构
utssdk/
wire.uts 平台无关:SSDP / 设备描述 / SOAP / DIDL-Lite / 时钟 /
静态预检 / protocolInfo / 倍速 NMTOKEN / GENA 头与事件解析
session.uts 平台无关:多会话状态机、名册、投送、轮询、节目单、交接、
能力协商、本地资源、系统路由、GENA 订阅、信号、脱敏日志
interface.uts 公共类型与 30001–30016 错误码
unierror.uts 统一错误对象
index.uts 默认降级入口(不注入传输)
web/ 降级入口,重导出默认入口
mp-weixin/ 条件入口:运行时探测 Socket 能力后注入传输
app-android/ app-ios/ app-harmony/
index.uts 注入传输实现 + 重导出共享核心
transport.uts UTS ↔ 原生桥适配
CastWireBridge.* UDP 组播 + HTTP(S)
CastPlayExtras.* 本地 HTTP 服务 / GENA 回调 / 系统路由入口
components/
up-castplay-panel/ 内置控制面板(Vue SFC,独立命名与样式)
协议与业务逻辑只有一份,各端差异被压缩成一个传输对象:{ multicast, http, now, schedule },可选 publish / unpublish / listen / exchange / openSystemRoute。因此 wire.uts / session.uts 可以直接用 Node 跑真实报文的契约测试,而各端入口只做装配。
安装
- 把整个
up-castplay目录放入项目的uni_modules,内置面板会随插件一起被 uni-app 的 easycom 识别。 - 使用 HBuilderX 4.61+,App 端必须制作自定义调试基座后运行到真机;仅热更新页面不会让原生传输生效。
- 按
docs/platform-setup.md配置三端的本地网络权限。手机与接收端必须在同一局域网,且路由器未隔离组播 / 客户端互访;GENA 订阅还要求接收端能回连手机的本地回调端口。 - 插件不引入三方依赖,各端
config.json只有系统框架与版本声明。
<!-- 最简用法:一行组件,会话由宿主页面创建并传入 -->
<up-castplay-panel :session="sessionId" />
import {
platformInfo, probeMedia, openSession, closeSession,
scan, haltScan, describeRenderer, listRenderers,
attach, detach, load, play, pause, stop, seek,
setVolume, setMute, refresh, snapshot, observe, unobserve, trace,
setProgram, programState, selectEpisode, stepEpisode,
selectVariant, setSpeed, setSubtitleTrack,
exportHandoff, restoreHandoff, negotiateRenderer, capabilities,
publishLocal, unpublishLocal, openSystemRoute,
subscribeEvents, cancelEvents
} from '@/uni_modules/up-castplay';
六个平台入口导出同名同签名的 38 个方法与 24 个公共类型。所有异步操作都是 options 回调风格:success / fail 只结算一次,complete 一定执行(即使前一个回调抛错)。errMsg 为 ${action}:fail 说明 (细节),成功结果为 ${action}:ok。
快速上手
// 1. 先确认平台与媒体地址是否值得尝试(纯计算,H5 也可用)
const info = platformInfo();
const probe = probeMedia({ url: 'https://example.com/movie.mp4', kind: 'video' });
if (!info.supported || !probe.castable) return;
// 2. 显式创建会话并订阅信号
const session = openSession({ label: '客厅', pollIntervalMs: 2000 });
observe({ session, handler: signal => console.log(signal.name, signal.text) });
// 3. 发现接收端(或 iOS 未取得组播授权时直接给描述地址)
scan({ session, clearRoster: true, timeoutMs: 6000 });
// describeRenderer({ session, descriptionUrl: 'http://192.168.1.20:8200/rootDesc.xml' });
// 4. 连接、协商能力、投送
attach({ session, rendererKey: listRenderers(session)[0].key });
negotiateRenderer({ session, mime: 'video/mp4' });
// capabilities(session).mimeSupported / timeSeek 可用于提前提示
load({
session,
media: { url: 'https://example.com/movie.mp4', title: '示例视频', kind: 'video', mime: 'video/mp4' }
});
// 5. 状态以接收端回报为准
refresh({ session, success: res => console.log(res.transportState, res.position) });
load 的 success 只返回 state: 'requested',表示 SetAVTransportURI → Play 两条 SOAP 命令被接收端接受,不代表画面已出现。真实状态由 GetTransportInfo / GetPositionInfo / GetVolume 或 GENA NOTIFY 的回报更新,可通过 refresh()、snapshot() 或 clock.tick / transport.changed / device.event 信号观察。
进阶能力
| 需求 | 做法 |
|---|---|
| 选集 / 清晰度 / 字幕 / 倍速 | setProgram({ program }) 装载 CastProgram,再用 selectEpisode / stepEpisode / selectVariant / setSubtitleTrack / setSpeed 切换;切换保留当前进度,字幕写进 DIDL-Lite 第二个 res,倍速走 Play(Speed) |
| 断点续播 / 手机→电视交接 | const h = exportHandoff(session) 持久化;换设备或重进页面后 restoreHandoff({ handoff: h }) |
| 投本地文件 / 内联字幕 | publishLocal({ sourcePath }) 或 publishLocal({ sourceText, sourceName, mime }) → 拿到局域网 http:// URL 再投;用完 unpublishLocal() |
| 系统路由 | openSystemRoute() 拉起系统公开选择器;platformInfo().systemRoute 为 false 时返回 30002,不会假装已连接 |
| 接收端主动回报 | 设备描述含 eventSubURL 时 subscribeEvents(),状态由 NOTIFY 更新;cancelEvents() 退订,关闭会话也会尽力退订 |
| 内置 UI | 直接用 <up-castplay-panel :session="sessionId" />;也可只用 API 自建界面,插件不强制绑定 UI |
内置面板
up-castplay-panel 是一个播放器级的控制面板,不是只列几个按钮的调试页。面板从 session 读取真实状态,界面分区如下:
| 区域 | 内容 |
|---|---|
| 头部 | 标题与能力徽章:真实投屏 / 降级模式(按 platformInfo().supported) |
| 接收端 | 设备名册,横向滚动的设备卡片(设备名 + 已连接 / 点击连接),点击即 attach();旁边是「扫描」「能力协商」「系统路由」入口,并显示接收端声明支持的 MIME |
| 播放器 | 当前媒体与集数、播放中徽标、可拖动跳转的进度条(左右显示 mm:ss / 总时长)、播放 / 暂停、停止、刷新、静音、音量条(0–100%,未知时置灰) |
| 画质 · 字幕 · 倍速 | 流畅 / 高清、中文字幕 / 关闭字幕、0.5x / 1x / 1.5x / 2x,当前选中项高亮 |
| 节目单 · 选集 | 装载示例、上一集 / 下一集、集数按钮网格(当前集高亮) |
| 交接 · 本地资源 · 事件 | 导出交接 / 恢复续播、发布本地字幕 / 停止本地服务、订阅事件 / 取消订阅,并显示本地服务地址 |
| 诊断 | 可折叠的动作日志行(动作名与结果码) |
面板只读状态、不自己造状态:进度与时长来自接收端回报或事件通知,播放态取 transportState === 'playing'(session.uts 的状态映射统一为小写)。未连接、未装载节目单、音量未知时都有明确文案,不会把空状态显示成有数据。
API
完整参数与返回值见 docs/api.md。
| 分组 | 方法 | 说明 |
|---|---|---|
| 查询(同步) | platformInfo() / probeMedia(media) / programState(session) / capabilities(session) / exportHandoff(session) |
能力矩阵、静态预检、节目单/能力快照、交接凭证;降级平台也可用 |
| 会话 | openSession(options) → string / closeSession(options) |
显式会话;关闭会话停止轮询、清空订阅、尽力退订 GENA |
| 发现 | scan / haltScan / describeRenderer / listRenderers(session) |
组播搜索、中止、按描述地址直连、读取名册 |
| 连接 | attach / detach |
detach({ stopRemote: true }) 才向接收端发 Stop |
| 投送与控制 | load / play / pause / stop / seek / setVolume / setMute / setSpeed |
setVolume 的 0–1 映射为 UPnP 的 0–100 |
| 节目单 | setProgram / selectEpisode / stepEpisode / selectVariant / setSubtitleTrack |
切换统一复用投送链路并保留进度 |
| 交接与协商 | restoreHandoff / negotiateRenderer |
交接凭证恢复;GetProtocolInfo 能力协商 |
| 本地与系统 | publishLocal / unpublishLocal / openSystemRoute |
局域网 HTTP 发布、停止服务、系统路由选择器 |
| 状态 | refresh / snapshot(session) |
主动采样与同步快照 |
| 事件 | subscribeEvents / cancelEvents |
GENA 订阅与退订 |
| 信号 | observe → boolean / unobserve → boolean |
注册与移除必须用同一函数引用 |
| 诊断 | trace(options) → Array<TraceEntry> |
只有动作、阶段、耗时、错误码;不含 URL、设备地址、SOAP 正文 |
状态与信号
CastTransportState:idle → requested(命令被接受)→ playing / paused / stopped / transitioning / error,后几者只在接收端回报后出现。volumeKnown: false 时 snapshot().volume 只是命令目标值,不是真实音量。
信号名:scan.opened / scan.closed、renderer.online / roster.changed、link.opened / link.closed、transport.changed、clock.tick、volume.changed、program.changed、speed.changed、capability.changed、handoff.restored、device.event / device.subscribed、fault。单个订阅者抛错不会阻断其他订阅者或状态机。
错误码
| 码 | 含义 | 常见处理 |
|---|---|---|
| 30001 | 会话不存在或已关闭 | 重新 openSession |
| 30002 | 当前平台不支持真实投屏 | 改用 App 自定义基座;H5/未适配小程序只能做预检;系统路由未注入时也返回此码 |
| 30003 | 参数不正确 | 检查 rendererKey / media / position / volume / speed / descriptionUrl |
| 30004 | 传输层不可用 | 检查本地网络权限、Wi-Fi、路由器组播隔离 |
| 30005 | 未发现可用接收端 | 换 searchTarget,或用 describeRenderer 直连 |
| 30006 | 接收端缺少必要的 UPnP 服务 | 无 AVTransport 不能投屏;无 RenderingControl 时音量动作不可用;无 ConnectionManager 无法协商;无 eventSubURL 无法订阅 |
| 30007 | 媒体地址不可投送 | 先看 probeMedia().flags 与 advice,本地文件用 publishLocal() |
| 30008 | 尚未连接接收端 | 先 attach |
| 30009 | SOAP 请求失败或超时 | 检查设备地址、HTTP 状态、局域网互访 |
| 30010 | 接收端拒绝该控制动作 | 解析 UPnP Fault 的 errorCode,常见于状态不允许跳转 |
| 30011 | 设备描述文档解析失败 | 描述地址不是有效的 UPnP root 文档 |
| 30012 | 订阅或诊断参数不正确 | 检查 handler / maxEntries |
| 30013 | 节目单状态不正确 | 未装载节目单、集数越界、档位/字幕轨不存在、交接凭证无节目也无媒体 |
| 30014 | 接收端不支持该能力 | 协议协商不匹配,或倍速被设备拒绝 |
| 30015 | 本地资源服务不可用 | 平台未注入 publish、发布失败或缺少 sourcePath / sourceText |
| 30016 | 事件订阅失败或已过期 | 本地回调端口不可用、SUBSCRIBE 失败或响应缺 SID |
测试
工程根目录执行 npm test。Node 直接加载 .uts 源码(处理条件编译与类型注解)跑契约测试,不需要 HBuilderX:
wire.test.mjs:真实 SSDP / 描述 XML / SOAP / DIDL-Lite 报文的线格式;session.test.mjs:注入假传输后的状态机、去重、投送顺序、Fault 映射、一次性回调、脱敏日志;extended.test.mjs:节目单、清晰度、字幕、倍速、交接续播、能力协商、本地资源、系统路由、GENA 订阅与30013–30016;platform-gate.test.mjs:降级入口的30002行为、同步查询与空快照;source-contract.test.mjs:六个入口导出一致性、平台无关性、权限声明、能力矩阵、错误码、独立命名、SFC 绑定;verification-contract.test.mjs:MuMu 夹具与插件源码同步、发布截图合规。
另有 node verification/check-sfc.mjs 用真实 @vue/compiler-sfc 编译示例页与内置组件(需要本地或同工作区已安装 Vue 编译器)。
发布截图分两类,都不是设计稿:一类是无头 Edge 渲染本插件真实的面板组件(设备列表、播放/暂停、进度条、音量都是面板源码的真实结构);一类是 MuMu 12 实机截图,其中包含模拟接收端真实播放视频的画面。生成流程见工程根目录 README.md 与 verification/README.md。
隐私
插件不采集、不上传任何数据。设备描述与 SOAP 控制报文只在业务手机与用户选择的局域网接收端之间传输;publishLocal() 起的 HTTP 服务只监听局域网、只提供业务显式发布的文件与字幕文本,unpublishLocal() 即关闭;诊断日志只记录动作、阶段、耗时与错误码,不含媒体地址或设备地址。

收藏人数:
下载插件并导入HBuilderX
下载示例项目ZIP
赞赏(0)
下载 163026
赞赏 508
下载 12749782
赞赏 1959
赞赏
京公网安备:11010802035340号