更新记录

1.0.0(2026-10-02) 下载此版本

1.0.0(2026-10-02)

  • Android、iOS、鸿蒙三端统一的投屏接口。
  • DLNA 设备搜索、连接、推流播放与全套控制。
  • iOS 支持 AirPlay 接收端。
  • 支持网络地址与手机本地文件投屏。
  • 统一回调结构与错误码。

平台兼容性

uni-app(5.26)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
× √ × × √ - - - -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × × × × × × ×

其他

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

msh-dlna-cast 投屏插件

把手机上的媒体推到同一局域网内的电视、盒子或音箱播放的 uni-app 原生插件。

手机负责发现设备、下发播放地址和控制指令,画面与声音都在接收端输出。同一套 JavaScript 接口,同时支持 Android、iOS、鸿蒙 NEXT。

  • 插件 ID:msh-dlna-cast(单个 UTS 插件,同时覆盖 Android、iOS、鸿蒙)
  • 支持协议:DLNA(UPnP AV);iOS 额外支持 AirPlay
  • 媒体来源:网络地址(http / https)、手机本地文件
  • 当前版本:1.0.0

概述

功能

能力 说明
设备发现 SSDP 组播搜索局域网内的投屏设备,持续上报;iOS 同时搜索 AirPlay 接收端
设备连接 同一时间保持一台设备,可随时切换
推流播放 推送 http / https 地址,或手机本地文件(插件自建临时 HTTP 服务)
播放控制 播放、暂停、继续、从头播放、停止
进度控制 查询时长与进度,拖动跳转,快进 / 快退 10 秒
音量控制 绝对音量 0–100、静音与取消静音
状态上报 状态、进度、时长、音量变化实时回调
网络信息 读取本机 Wi-Fi IPv4

特点

  • 三端一套 API。方法名、参数、回调结构、错误码完全一致,业务代码只写一份,用设备的 protocol 字段区分 DLNA 与 AIRPLAY,不需要按平台写分支。
  • 统一回调格式。所有方法都返回 { code, message, data },成功 code === 0,失败给出中文 message 和明确的错误码。
  • 本地文件不用先上传。插件在手机上起临时 HTTP 服务,把终端可访问的地址推给设备,播放结束或页面销毁时自动停止。
  • iOS 同时覆盖两类设备。DLNA 设备插件直连;AirPlay 设备通过系统投屏路由连接。

不支持

  • 不支持屏幕镜像、投屏画面录制。
  • 不支持 Miracast、Chromecast。
  • 不支持字幕、播放列表、多设备同时播放、倍速播放。
  • 不支持小程序、H5、uni-app x。

平台兼容性

平台 最低版本 引擎实现 协议
Android Android 5.0(API 21)及以上 纯 Java,无 native 库,不区分 CPU 架构 DLNA
iOS iOS 15.0 及以上 Swift(arm64 真机,不含模拟器切片) DLNA、AirPlay
鸿蒙 NEXT API 12 及以上 ArkTS(arm64) DLNA

三端共用同一个 uni_modules/msh-dlna-cast 插件。Android 侧引擎编译为 jar、iOS 与鸿蒙侧为源码混编,对使用者都是透明的。

  • 仅支持 App(Vue 3 的 App-vue)。小程序、H5、nvue、uni-app x 不适用。需要 HBuilderX 4.25 及以上。
  • 插件带原生配置(Android 权限、iOS 本地网络说明、引擎依赖),必须制作自定义调试基座,标准基座不含这些。HBuilderX 编译时会提示。
  • 模拟器收不到局域网组播,请用真机调试。

快速开始

1. 引入插件

把 uni_modules/msh-dlna-cast 整个目录放入你的工程(三端都用这一个目录,插件内部按平台分目录)。无需在 manifest.json 里勾选本地插件,uni_modules 会被自动识别。

引入模块:

// common/dlna.js —— 三端统一入口
import * as DlnaCast from '@/uni_modules/msh-dlna-cast'

let cached = null

export function getDlnaPlugin() {
  if (cached) return cached
  cached = DlnaCast
  return cached
}

插件导出的是顶层函数(startSearch、play、onDeviceFound 等),用 import * as 整体引入。三端方法名相同,不需要按平台写条件编译。也可以不经这层封装,直接 import * as dlna from '@/uni_modules/msh-dlna-cast'。

2. 制作自定义基座

  1. 在 HBuilderX 中打开工程。
  2. 运行 → 运行到手机或模拟器 → 制作自定义调试基座。
  3. 制作完成后,用该基座真机运行。

3. 最小可用流程

import { getDlnaPlugin } from '@/common/dlna.js'

const dlna = getDlnaPlugin()

// 先注册事件
dlna.onDeviceFound((res) => {
  if (res.code !== 0) return
  console.log('发现设备', res.data.name, res.data.protocol)
})

dlna.onStateChange((res) => {
  if (res.code !== 0) return
  console.log(res.data.state, res.data.position, res.data.duration)
})

// 搜索
dlna.startSearch({ timeout: 8000 }, (res) => {
  if (res.code === 0) console.log('搜索已启动')
  if (res.code === 1001) console.log('未发现设备')
})

// 选择设备(id 来自 onDeviceFound)
dlna.selectDevice({ id: deviceId }, (res) => {
  if (res.code !== 0) return

  // 播放
  dlna.play({
    url: 'https://player.alicdn.com/video/aliyunmedia.mp4',
    title: '示例视频',
    mimeType: 'video/mp4'
  }, (playRes) => {
    console.log('播放', playRes.code, playRes.data.url)
  })
})

// 控制
dlna.pause((res) => {})
dlna.resume((res) => {})
dlna.seek({ position: 30000 }, (res) => {})
dlna.setVolume({ volume: 30 }, (res) => {})
dlna.stop((res) => {})

4. 使用前检查

手机与电视必须在同一个 Wi-Fi,路由器未开启 AP 隔离,电视已开启 DLNA / 媒体渲染功能。

Android 需在 manifest.json 中允许明文 HTTP。电视的描述地址和控制地址是 HTTP,未开启时选设备或播放会失败:

{
  "app-plus": {
    "android": {
      "usesCleartextTraffic": true
    }
  }
}

iOS 访问 HTTP 所需的 ATS 已写在插件的 Info.plist 里,打包时会合并进 App。

完整的环境配置、权限配置、Demo 验证清单和排查方法,见 docs/使用手册.md。


接口

回调统一为 { code, message, data }。code === 0 表示成功;成功时 message 为 "";无数据时 data 为 {}。

普通方法为 method(options, callback);无参数时为 method(callback);事件监听为 onXxx(callback),后一次注册覆盖前一次。

除搜索、getLocalIp 和事件注册外,未连接设备时返回 1003。

startSearch 的 callback 保持不释放,最多回调两次:搜索启动成功立刻返回 { code: 0, message: "", data: {} };本次搜索在 timeout 内一台设备都没有时,再返回 { code: 1001, message: "未发现设备", data: {} }。已经发现过至少一台,则超时不再返回 1001。搜索进行中再次调用 startSearch,重新计时并继续搜索。

方法 作用 参数 成功时 data
startSearch 开始搜索,设备只从 onDeviceFound 上报 { timeout } 可空,毫秒,默认 8000 {}
stopSearch 停止搜索。停止后不再回调本次的 1001 无 {}
onDeviceFound 发现设备 回调 设备对象
onDeviceLost 设备离线 回调 { id }
selectDevice 连接设备,替换当前连接 { id } 必填 设备对象
play 开始播放,替换当前媒体 { url } 必填,{ title, mimeType } 可空 { url }
pause 暂停,保留进度 无 {}
resume 从当前进度继续 无 {}
replay 从头播放 无 {}
stop 结束播放,设备选择保留 无 {}
getState 播放状态 无 { state }
getDuration 总时长 无 { duration } 毫秒
getPosition 当前进度 无 { position, duration } 毫秒
getVolume 当前音量 无 { volume, mute }
setVolume 音量调节 { volume } 必填,0–100 整数 { volume }
setMute 静音开关 { mute } 必填,布尔 { mute, volume }
seek 跳到指定位置 { position } 必填,毫秒 { position }
fastForward 快进 10 秒 无 { position }
rewind 快退 10 秒 无 { position }
getLocalIp 本机 Wi-Fi IPv4 无 { ip }
onStateChange 状态、进度、音量变化 回调 { state, position, duration, volume, mute }

设备对象

onDeviceFound 与 selectDevice 成功时的 data:

{
  "id": "设备 UUID",
  "name": "客厅电视",
  "protocol": "DLNA",
  "location": "设备描述地址",
  "manufacturer": "厂商",
  "model": "型号"
}
字段 类型 说明
id string 设备唯一标识,selectDevice 用它连接。AirPlay 设备的 id 以 airplay: 开头
name string 显示名
protocol string DLNA 或 AIRPLAY。Android / 鸿蒙只有 DLNA
location string 设备描述地址,AirPlay 设备可能为空
manufacturer string 厂商,取不到为 ""
model string 型号,取不到为 ""

播放状态

PLAYING、PAUSED、STOPPED、TRANSITIONING、NO_MEDIA、ERROR。

错误码

code 含义
0 成功
1001 搜索超时,未发现设备
1002 设备离线或无响应
1003 尚未选择设备
1004 播放失败
1005 缺少必填字段、地址无效,或本地文件不存在
1007 未连接 Wi-Fi,无法取得 IPv4
1008 缺少本地网络或组播权限
1009 当前媒体不支持该操作
1010 网络已切换,连接失效

本地文件投屏

play 的 url 传手机本地文件的绝对路径(以 / 开头,不带 file://)。插件会在手机上启动临时 HTTP 服务,回调里的 data.url 是终端可访问的 http 地址。

uni.chooseVideo({
  sourceType: ['album'],
  compressed: false,
  success: (res) => {
    let path = res.tempFilePath || ''
    if (typeof plus !== 'undefined' && plus.io && plus.io.convertLocalFileSystemURL) {
      path = plus.io.convertLocalFileSystemURL(path)
    }
    if (path.indexOf('file://') === 0) path = path.substring(7)

    dlna.play({ url: path, title: '本地视频' }, (playRes) => {
      if (playRes.code === 0) console.log('终端地址', playRes.data.url)
    })
  }
})

content:// 和其他相对路径返回 1005。


iOS 说明

两类设备的用法差异

DLNA 设备 AIRPLAY 设备
protocol DLNA AIRPLAY
选中后 插件直接连接 只完成选中,画面不出本机。插件会在当前窗口底部挂上系统投屏按钮
播放前 直接 play 先点该按钮,在系统列表里选定接收端,再 play
音量 可用 setVolume 走系统音量,接收端不跟随时返回 1009

AirPlay 是把视频推到接收端,不是屏幕镜像。投到 Mac 等不跟随系统音量的接收端时,中间音量返回 1009,请用接收端自身的音量控制;0 音量仍通过播放器静音保证生效。

权限与配置

插件已在 uni_modules/msh-dlna-cast/utssdk/app-ios/Info.plist 写入本地网络说明、Bonjour 和 ATS。云端打包时合并进 App,不用在 manifest.json 里再写一遍。

键 作用
NSLocalNetworkUsageDescription 本地网络用途说明。缺少时系统不弹授权框,搜索失败
NSBonjourServices(_airplay._tcp) 发现 AirPlay 接收端
NSAppTransportSecurity 允许访问 HTTP 地址

首次运行会弹出「允许访问本地网络」,必须点允许。若曾拒绝,到系统设置里重新打开。

组播权限 com.apple.developer.networking.multicast 当前没有写进 UTS.entitlements。插件把 M-SEARCH 发往 239.255.255.250:1900,iOS 16 及以上搜不到设备时,常见原因是描述文件里没有这项能力。描述文件尚未包含它时写进 entitlements,制作基座或云打包会因签名不一致失败。

本机 IPv4 用 getifaddrs 读取,不使用 com.apple.developer.networking.wifi-info(该权限只用于读取 SSID/BSSID,需另行申请)。

组播权限配置步骤

第 1 步:申请组播权限(一次性,需 Apple 审核)

用开发者主账号访问 Apple 组播权限申请页 提交申请。审核通过后(通常约 3 天):

  1. 进入 Apple Developer 账号的 Certificates, Identifiers & Profiles;
  2. 账号下会多出一项 Additional Capabilities,勾选 Multicast Networking;
  3. 重新生成并下载描述文件(Provisioning Profile)。

第 2 步:描述文件包含该能力后,再写入插件

在 uni_modules/msh-dlna-cast/utssdk/app-ios/UTS.entitlements 中加入:

<key>com.apple.developer.networking.multicast</key>
<true/>

插件的 entitlements 必须与 App 描述文件一致。描述文件里还没有这项时,保持 UTS.entitlements 为空。

第 3 步:重新制作自定义调试基座

旧基座不带新的描述文件。改完后重新制作基座,再用该基座真机运行。


隐私、权限声明

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

Android

权限 用途
android.permission.INTERNET 发现设备、下发播放地址、发送控制指令
android.permission.ACCESS_NETWORK_STATE 判断当前网络状态
android.permission.ACCESS_WIFI_STATE 读取 Wi-Fi 信息与本机 IPv4
android.permission.CHANGE_WIFI_MULTICAST_STATE SSDP 组播搜索设备

iOS

权限 用途
本地网络(NSLocalNetworkUsageDescription) 搜索并连接局域网内的投屏设备
组播(com.apple.developer.networking.multicast) 收发 SSDP 组播。需向 Apple 申请;描述文件包含该能力后再写入 UTS.entitlements,当前插件文件里没有这项
Bonjour(NSBonjourServices: _airplay._tcp) 发现 AirPlay 接收端

不使用 com.apple.developer.networking.wifi-info:本插件用 getifaddrs 读取本机 IPv4,不读取 SSID/BSSID。

鸿蒙

权限 用途
ohos.permission.INTERNET 网络访问
ohos.permission.GET_NETWORK_INFO 网络状态
ohos.permission.GET_WIFI_INFO Wi-Fi 信息与本机 IPv4

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

无。插件不采集、不上传任何个人数据,不包含统计或埋点。所有网络行为都发生在局域网内:向局域网发送 SSDP 搜索广播、访问设备描述地址、向设备下发播放与控制指令。播放本地文件时,插件在本机启动的临时 HTTP 服务只监听局域网,并随页面销毁停止。

3. 本插件是否包含广告

否。不包含任何广告。


常见问题

现象 原因与处理
搜索一台都没有,返回 1001 手机与电视不在同一 Wi-Fi,或路由器开启了 AP 隔离
列表里没有电视 电视未开启 DLNA / 媒体渲染
提示「基座里没有 msh-dlna-cast 方法」或「插件未生效」 使用了标准基座。插件带原生配置,必须制作自定义调试基座
返回 1008 iOS 未授权本地网络,或未申请组播权限;Android 缺组播权限
iOS 上 startSearch 返回成功,但始终搜不到设备 先确认已允许本地网络;仍不行再按 iOS 权限与配置 补组播 entitlement 并重做基座
云打包报错,提示与某个原生 SDK 冲突 逐个取消勾选可能冲突的第三方原生插件或广告联盟 SDK 后重新打包,定位具体冲突项
返回 1007 手机未连接 Wi-Fi
选设备或播放失败,提示未允许明文 HTTP Android 需开启 usesCleartextTraffic
部分电视静音返回 1009 该电视不支持静音指令
AirPlay 调中间音量返回 1009 接收端不跟随手机音量,请用接收端音量控制
播放中切换 Wi-Fi 后失效 返回 1010,需重新搜索并选择设备
切到后台不再发现设备 搜索只在应用前台进行,回到前台需重新搜索
本地文件投屏后电视打不开 手机与电视不在同一 Wi-Fi,电视访问不到临时 HTTP 服务
离线打包后启动报「未配置appkey或配置错误」 离线SDK 强制要求 AppKey(云打包不需要)。到 dev.dcloud.net.cn → 应用管理 → 各平台信息 → 创建 Android 离线AppKey,填包名 + 签名SHA1;AppKey 绑定 appid + 包名 + 签名SHA1 三者,换证书或换包名都要重新申请

更新记录

1.0.0

  • Android、iOS、鸿蒙三端统一的投屏接口。
  • DLNA 设备搜索、连接、推流播放与全套控制。
  • iOS 支持 AirPlay 接收端。
  • 支持网络地址与手机本地文件投屏。
  • 统一回调结构与错误码。

目录结构

目录 内容
dlna-cast-demo/uni_modules/msh-dlna-cast/ UTS 插件本体(三端实现都在这)
android/ Android 引擎源码,编译出引擎 jar 供插件调用
ios/ iOS 引擎源码(Swift),直接混编进插件
harmony/ 鸿蒙 NEXT 引擎源码(ArkTS),直接混编进插件
dlna-cast-demo/ uni-app 接入示例
docs/ 使用手册与接入文档
scripts/ 源码同步与本地构建脚本

三端源码的真源在 android/、ios/、harmony/,用 ./scripts/sync-demo-plugins.sh 同步进插件。改原生代码改源码目录,不要直接改插件里的副本。


相关文档

  • 使用手册:完整的环境配置、权限配置、接口详解、Demo 运行步骤与排查方法。

技术支持

使用中遇到问题,请一并提供以下信息,便于快速定位:

  • 平台与系统版本(Android / iOS / 鸿蒙的具体版本)
  • 手机型号与接收端设备的品牌型号
  • 调用方法与完整的 { code, message, data } 回调内容
  • 是否已确认手机与电视处于同一 Wi-Fi

隐私、权限声明

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

Android: - android.permission.INTERNET(发现设备、下发播放地址、发送控制指令) - android.permission.ACCESS_NETWORK_STATE(判断网络状态) - android.permission.ACCESS_WIFI_STATE(读取 Wi-Fi 信息与本机 IPv4) - android.permission.CHANGE_WIFI_MULTICAST_STATE(SSDP 组播搜索设备) - android.permission.WAKE_LOCK(播放期间保持必要唤醒) iOS: - 本地网络 NSLocalNetworkUsageDescription(搜索并连接局域网内的投屏设备) - Bonjour NSBonjourServices:_airplay._tcp(发现 AirPlay 接收端) - 组播 com.apple.developer.networking.multicast(收发 SSDP 组播。需向 Apple 申请,描述文件包含该能力后才写入插件;当前插件未声明此项) 鸿蒙: - ohos.permission.INTERNET(网络访问) - ohos.permission.GET_NETWORK_INFO(网络状态) - ohos.permission.GET_WIFI_INFO(Wi-Fi 信息与本机 IPv4)

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

无。插件不采集、不上传任何个人数据,不含统计与埋点。 所有网络行为均发生在用户所在局域网内:向局域网发送 SSDP 搜索广播、 访问设备描述地址、向投屏设备下发播放与控制指令。播放本地文件时, 本机临时 HTTP 服务仅监听局域网,并随页面销毁停止。

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

无

许可协议

MIT协议

暂无用户评论。