更新记录
1.0.0(2026-10-23)
- 初版:SSDP 发现、描述 XML 解析、DLNA SOAP 控制与跨端能力查询。
- Android / iOS / Harmony 原生网络适配与 classic / minimal 双面板。
- 视频、MP3、图片、文本轨兼容元数据、真实音量与倍速确认。
- 选集、上下集、自然结束自动连播、断点续播、清晰度与主题。
- Android 受控交接、会话归属核验、单调采样与终态保护。
- 微信 UDP 搜索、TCP HTTP(含 chunked、多字节内容)控制。
- 媒体预检、能力判断、事件、脱敏环形日志和支持包。
- 补齐首次交接面板真实选设备起播测试,阻止起播后重新连接破坏交接归属。
- 切集失败时保留原会话自然结束检测,避免丢失后续自动连播。
平台兼容性
uni-app(5.0.0)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | - | - | - | - | - | 4.4 | 12 | √ |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(5.0.0)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| - | - | 5.0 | 12 | √ | - |
其他
| 多语言 | 暗黑模式 | 宽屏模式 | 蒸汽模式 |
|---|---|---|---|
| × | × | × | √ |
uni-app x 的局域网 DLNA 媒体投屏插件。独立实现设备发现、UPnP 控制、双风格面板和会话管理,无商业投屏 SDK 依赖。
三端运行截图
Android、iOS、鸿蒙运行的系统截图。
播放控制
| Android 15 · API 35 | iOS 26.5 · iPhone 17 | 鸿蒙 · Pura 90 Pro |
|---|---|---|
![]() |
![]() |
![]() |
设备列表
| Android 15 · API 35 | iOS 26.5 · iPhone 17 | 鸿蒙 · Pura 90 Pro |
|---|---|---|
![]() |
![]() |
![]() |
选集
| Android 15 · API 35 | iOS 26.5 · iPhone 17 | 鸿蒙 · Pura 90 Pro |
|---|---|---|
![]() |
![]() |
![]() |
classic 浅色主题
| Android 15 · API 35 | iOS 26.5 · iPhone 17 | 鸿蒙 · Pura 90 Pro |
|---|---|---|
![]() |
![]() |
![]() |
平台
| 平台 | 实现 | 内置面板 | 备注 |
|---|---|---|---|
| Android 6+ | Kotlin UDP / HTTP + UTS 状态机 | classic / minimal | 需要包含插件的原生构建;支持视频交接 |
| iOS 13+ | Swift UDP / URLSession + UTS 状态机 | classic / minimal | 自动发现需要本地网络授权与 Apple 组播权限;另有系统 AirPlay 播放器入口 |
| HarmonyOS | ArkTS Socket / HTTP + UTS 状态机 | classic / minimal | 需要包含插件的 HAP |
| 微信小程序 | UDP 发现 + TCP HTTP 控制 | 业务自行实现 | 需要真机基础库同时支持 UDP/TCP Socket;HTTPS 设备地址仍受 request 域名规则约束 |
| Web / 其他小程序 | 统一降级 API | 无 | getCastCapabilities().supported 为 false;不会伪造设备或投屏成功 |
建议使用 HBuilderX 5.0+,并为项目配置自己的 AppID。当前工程使用 Vue3;插件可被普通 uni-app 和 uni-app x 调用。
快速接入
将 uni_modules/ai-touping 整个文件夹复制到项目同名目录,入口统一从插件根目录导入:
import * as Cast from '@/uni_modules/ai-touping'
Cast.initCast({
debug: true,
success() {
Cast.showCastPanel({
panelStyle: 'minimal',
theme: 'auto',
media: {
url: '',
title: '示例视频',
mediaKind: 'video',
mimeType: 'video/mp4',
resumeKey: 'course:1001:episode:1'
},
fail: console.error
})
},
fail: console.error
})
省略 panelStyle 使用 classic,minimal 使用简洁面板。关闭面板保留电视播放与稳定播放列表的自动下一集检测。普通面板在起播/切换过程中关闭会取消该事务的本地提交;网络请求可能已到达电视,必要时主动停止并核验电视。
App 面板由原生 WebView / WKWebView / ArkWeb 展示本地 HTML,不依赖远程网页、注册业务页面或额外 Vue 组件。网络访问仅由插件的原生协议层执行,面板本身禁用联网。
自定义界面与微信小程序
const listener = event => {
if (event.name === 'deviceListChange') {
console.log(Cast.getDiscoveredDevices())
}
if (event.name === 'error') console.error(event.payload)
}
const listenerId = Cast.onCastEvent('*', listener)
Cast.initCast({
success() { Cast.startDiscovery({ timeoutMs: 10000, clearBeforeStart: true }) }
})
function selectDevice(deviceId) {
Cast.connectDevice({
deviceId,
success() {
Cast.startCast({ media: { url: 'https://example.com/movie.mp4' } })
}
})
}
// 页面卸载时使用订阅编号可靠释放;offCastEvent 也接受原回调引用。
function cleanup() {
Cast.offCastEventById(listenerId)
Cast.stopDiscovery()
}
搜索结果通过事件取得,startDiscovery.success 仅表示搜索启动。manualDeviceDescriptionUrl 可以直接加载已知电视的设备描述 XML,便于在没有组播权限时调试。
选集、续播、清晰度
Cast.showCastPanel({
panelStyle: 'minimal',
media: { url: 'https://example.com/1.mp4', title: '第 1 集', resumeKey: 'series:1:ep:1' },
episodes: [
{ id: 'series:1:ep:1', title: '第 1 集', url: 'https://example.com/1.mp4' },
{ id: 'series:1:ep:2', title: '第 2 集', url: 'https://example.com/2.mp4' }
],
currentEpisodeIndex: 0,
rememberProgress: true,
qualities: [
{ id: '720p', label: '720P', url: 'https://example.com/1-720.mp4' },
{ id: '1080p', label: '1080P', url: 'https://example.com/1-1080.mp4' }
],
currentQuality: '720p'
})
currentTime 或 media.startPosition 用于从手机实际进度接着播放。省略时按稳定的 resumeKey 恢复;不要把包含签名的媒体 URL 用作断点键。断点保存在设备本地,最多保留 100 项;初始化时忽略 30 天以前的数据。
只有接收端确认起播后才更新选集与画质。已确认时长显示位置达到片尾时清除过期断点并从头开始。自然播完需要连续两次 STOPPED、之前实际播放且接近真实片尾;暂停、主动停止和跳转不会直接触发切集,最后一集不循环。
图片没有时间轴。MP3 使用 mediaKind: 'audio', mimeType: 'audio/mpeg'。图片设置 mediaKind: 'image' 和正确 MIME。字幕/歌词用 textTracks,格式支持 embedded / srt / vtt / ass / lrc,优先轨道使用 preferredTextTrackId。插件通过 DIDL-Lite / Samsung CaptionInfoEx 发送兼容元数据,最终显示能力由电视决定。严格 textTrackFailurePolicy: 'fail-cast' 会明确失败,因为通用 DLNA 没有确认字幕已显示的机制。
弹幕需要自己的电视画面层。设置 danmakuSupported: true、onDanmakuToggle(enabled, done),业务切换成功后 done(true)。插件不会把弹幕写到不支持的电视屏幕上。
面板配置
支持 theme: 'dark' | 'light' | 'auto'、标题与副标题、播放/暂停、前后 15 秒、拖动、音量/静音、选集、上下集、清晰度、倍速、换设备、停止、断开。
minimal 面板支持:
appearance.progressColor / progressTrackColor / episodeSelectedColor:#RRGGBB。appearance.progressThumbIcon:{ type: 'tv' | 'circle' | 'image', dataUri? };自定义 PNG/JPEG Base64 最大约 256KB。minimalQuickActions:最多三个可见项,kind为 volume / playbackRate / quality / danmaku / custom;支持 title、value、visible、disabled。showPlaybackRate / showEpisodeControls / showQualityControls / showThemeToggle / showRefresh:显示开关。playbackRateLabel / playbackRateDetectingText / playbackRateUnsupportedText:倍速文案。showCustomAction / customActionText:业务入口,操作通过customAction事件回传。
音量只展示接收端确认的数据;未知显示 --%。读取设备 SCPD 的 TransportPlaySpeed 声明前倍速保持“检测中”;未声明可用倍率时不展示伪造的 720P 或倍速能力。API 可以直接尝试倍速,仅在 GetTransportInfo 回报目标速度后提交状态。Android 与 HarmonyOS 面板支持硬件音量键。
Android 视频交接
const requestId = 'video:' + Date.now()
let sessionId = ''
Cast.showCastHandoffPanel({
requestId,
identity: 'episode:1:1080p',
media: {
url: 'https://example.com/movie.mp4',
mediaKind: 'video',
duration: 300,
startPosition: 65 // 必须来自手机播放器确认暂停后的采样
},
success(state) { sessionId = state.sessionId },
onPanelAction(event) {
if (event.action === 'hide') return
// 配置了回调时由业务负责停止;返回手机与保持暂停由业务分别处理。
Cast.stopCastHandoff({
requestId, expectedSessionId: sessionId,
success(state) {
if (state.stopConfirmed && (state.positionConfirmed || state.neverStarted)) {
console.log('可以按业务策略恢复手机', state.position)
}
}
})
}
})
支持 show / getState / reopen / hide / stop 和 getCastHandoffClock(),完整方法名见 API 表。首次打开交接面板的 success 不代表电视已播放。每次交接使用唯一 requestId,后续操作核对 expectedSessionId、identity、sampleId。sampledAt 与 getCastHandoffClock() 同源,不能混用 Date.now()。
已结束请求保护两分钟;重复停止返回原证据,不会控制后来的媒体。终态读取保留原采样时间,不会把旧证据刷新成“新”。接收端不返回可辨认 TrackURI 时保守拒绝确认归属;外部应用重投完全相同地址仍无法可靠识别。停止事实未知时让手机保持暂停。其他平台交接 API 返回 9030001,时钟返回 -1。
API
所有异步控制支持 success / fail / complete,成功与失败互斥、complete 调用一次。查询方法同步返回。
| 方法 | 参数/返回 |
|---|---|
| initCast | debug、autoStartDiscovery、discoveryTimeoutMs、sessionSyncIntervalMs、autoRediscoverOnNetworkChange |
| getCastCapabilities | 当前平台的真实能力与降级原因 |
| startDiscovery / stopDiscovery | timeoutMs、clearBeforeStart、manualDeviceDescriptionUrl |
| getDiscoveredDevices | 设备快照 |
| connectDevice | deviceId |
| disconnectDevice | stopRemote;true 时确认远端停止后断开 |
| startCast | media |
| pauseCast / resumeCast / stopCast | 控制后查询接收端真实状态 |
| seekCast | position,秒;需接收端确认目标位置 |
| setCastVolume | volume,0–1;设置后读取确认 |
| setCastPlaybackRate | rate;不支持时明确失败 |
| getCastSessionState | 设备、媒体、状态、进度、音量、volumeKnown、倍速、最近错误 |
| showCastPanel / hideCastPanel | 上述面板配置;useBuiltinUI: false 时仅派发面板事件 |
| selectCastEpisode / selectCastQuality | index / id;提供给业务自定义界面 |
| inspectCastMediaSource | { media },静态检查,不联网验证电视 |
| getCastDiagnostics | 会话与自动下一集门禁信息 |
| exportCastSupportBundle | includeDevices、includeRecentEvents、maxEvents |
| exportCastDebugLog | maxEntries、printToConsole、clearAfterRead、includeRecentEvents |
| onCastEvent / offCastEvent / offCastEventById | name、回调;on 返回订阅编号,推荐 offCastEventById(id) 注销;* 监听全部事件 |
| showCastHandoffPanel | Android:requestId、identity、media、panel、onPanelAction |
| getCastHandoffState / stopCastHandoff | Android:requestId、expectedSessionId |
| reopenCastHandoffPanel / hideCastHandoffPanel | Android:原请求重开/收起,不重复投送 |
| getCastHandoffClock | Android 单调时钟,毫秒 |
事件:ready、discoveryStart、discoveryStop、deviceFound、deviceLost、deviceListChange、panelShow、panelHide、connectStart、connected、disconnected、castStart、stateChange、progress、volumeChange、customAction、error、debugLog。事件对象包含 name / payload / timestamp。
错误码:9030001 不支持,9030002 参数错误,9030003 环境不可用,9030004 搜索失败,9030005 没有目标设备,9030006 连接失败,9030007 媒体不适合投屏,9030008 起播失败,9030009 控制失败,9030010 面板失败,9030011 非 MP3 音频,9030012 文本轨参数错误,9030013 文本轨确认不支持,9030014 文本轨发送失败。
平台配置
Android 插件已声明 INTERNET、ACCESS_NETWORK_STATE、ACCESS_WIFI_STATE、CHANGE_WIFI_MULTICAST_STATE,并在发现期间持有组播锁。DLNA 任意局域网主机使用明文 HTTP,插件包含网络安全配置;宿主已使用自己的 networkSecurityConfig 时请合并规则。
iOS 插件提供 Info.plist 本地网络用途说明、NSAllowsLocalNetworking 与 UTS.entitlements。自动发现需要 Apple 批准 Multicast Networking entitlement,更新 App ID 能力与 provisioning profile 后重新构建。没有该权限时可用已知描述 URL 调试,不能据此宣称自动发现可用。系统 AirPlay 使用独立 AVPlayerViewController,播放控制由系统播放器提供,DLNA 会话状态不代表其外部路由状态。
HarmonyOS 声明 INTERNET、GET_NETWORK_INFO。微信需要 createUDPSocket/createTCPSocket 和局域网可互访环境;开发者工具不等于真机,HTTPS 描述地址受合法域名配置限制。

收藏人数:
购买源码授权版(
试用
使用 HBuilder 导入示例项目
赞赏(0)












下载 0
赞赏 0
下载 12709441
赞赏 1957
赞赏
京公网安备:11010802035340号