更新记录

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 播放控制 iOS 播放控制 鸿蒙 播放控制

设备列表

Android 15 · API 35 iOS 26.5 · iPhone 17 鸿蒙 · Pura 90 Pro
Android 设备列表 iOS 设备列表 鸿蒙 设备列表

选集

Android 15 · API 35 iOS 26.5 · iPhone 17 鸿蒙 · Pura 90 Pro
Android 选集 iOS 选集 鸿蒙 选集

classic 浅色主题

Android 15 · API 35 iOS 26.5 · iPhone 17 鸿蒙 · Pura 90 Pro
Android classic 浅色主题 iOS classic 浅色主题 鸿蒙 classic 浅色主题

平台

平台 实现 内置面板 备注
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 描述地址受合法域名配置限制。

隐私、权限声明

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

Android 网络/Wi-Fi/组播;iOS 本地网络/组播签名;Harmony INTERNET/GET_NETWORK_INFO。

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

不上传业务数据;本地保存断点进度;投屏 URL 发送至用户选择的接收端。

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

无广告

暂无用户评论。