更新记录

1.0.1(2026-11-13)

  1. 增加设置倍速、设置起播位置
  2. 增加获取接收端能力接口
  3. 增加支持歌词、字幕接口

1.0.0(2026-11-13)

  • 支持视频、图片、文件等,主要取决于接收端支持的格式
  • 支持本地视频图片文件和远程视频图片文件
  • 支持暂停、继续、停止、快进、音量(静音或具体某个音量)
  • 支持接收端投屏信息(包含媒体信息、设备能力、播放进度、音量大小、静音状态)
  • 支持苹果、Android、鸿蒙
  • 支持vue2、vue3、uniapp、uniapp x(包含蒸汽模式)

平台兼容性

uni-app(3.8.11)

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

uni-app x(3.8.11)

Chrome Safari Android Android插件版本 iOS iOS插件版本 鸿蒙 鸿蒙插件版本 微信小程序
- - 5.0 1.0.1 √ 1.0.1 √ 1.0.1 -

其他

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

dlna投屏,本地和远程视频图片文件投屏

功能介绍

  • 支持视频、图片、文件等,主要取决于接收端支持的格式
  • 支持本地视频图片文件和远程视频图片文件
  • 支持暂停、继续、停止、快进、音量(静音或具体某个音量)、倍速、起播位置
  • 支持获取接收端投屏信息(包含媒体信息、设备能力、播放进度、音量大小、静音状态)、获取接收端支持能力
  • 支持歌词字母
  • 支持苹果、Android、鸿蒙
  • 支持vue2、vue3、uniapp、uniapp x(包含蒸汽模式)

集成步骤

  1. 插件详细集成步骤可以参考https://www.jianshu.com/p/c1615a7880a7
  2. 拷贝demo里的nativeResources、AndroidManifest.xml、harmony-configs、Info.plist文件到项目根目录
  3. iOS打包的provisioining profile文件需要开通组播/广播权限,参考https://www.jianshu.com/p/c2f6e6aa41a3?v=1682211826634,单播可以不用
  4. 如需定制或增加功能请点击插件标题下方的"进入交流群"私聊作者

接口


import * as sp from "@/uni_modules/wrs-uts-scrproj"
  • 设置回调

sp.onSetCallback((resp) => {
    // console.log("onSetCallback resp:" + JSON.stringify(resp))
    const opt = resp.opt
    switch (opt) {
        // 扫描到设备
        case "onDeviceFound": {
            // console.log("device:" + JSON.stringify(resp.device))
            this.upsertDevice(resp.device)
        }
        break
        // 设备断开连接
        case "onDeviceDelete": {
            this.removeDevice(resp.device.address, resp.device.location)
        }
        break
        default:
            break
    }
})
  • 开始扫描设备

sp.startSearch()
  • 停止扫描设备

sp.stopSearch()
  • 投屏视频、图片、文件等

let url = "" // 视频/图片/文件的地址,可以是本地或远程地址,如远程地址https://www.baxxx/aa.mp4、本地地址/ss/xx/xx/aa/mp4
let location = "" // 从扫描到的设备里获取
const start = 0 // 从第几秒开始播放
const speed = '1' // 播放速度,需要接收端支持
const tracks = this.buildTextTracks()
const preferredId = tracks != null ? tracks[0].id : null

const media = {
    url: url,
    mediaKind: this.mediaKind,
    mimeType: null,
    title: null,
    textTracks: tracks,
    preferredTextTrackId: preferredId,
    textTrackFailurePolicy: this.textTrackFailurePolicy
}

const startText = start > 0 ? `,从 ${this.formatTime(start)} 开始` : ''
const speedText = this.supportsSpeed ? `,${speed}x` : ''
const trackText = tracks != null ? `,${tracks.length} 个字幕轨` : ''
const kindText = this.mediaKind === 'auto' ? '' : `,类型 ${this.mediaKind}`
this.pushLog(`投屏请求: ${this.mediaUrl}${kindText}${startText}${speedText}${trackText}`)

sp.startCast(location, media, start, speed, (ok, msg) => {
    this.casting = false
    if (ok) {
        this.pushLog(msg && msg !== 'ok' ? msg : '投屏成功')
        uni.showToast({
            title: '投屏成功',
            icon: 'success'
        })
        setTimeout(() => {
            this.refreshTransportState()
            this.refreshPosition()
            this.refreshMediaInfo()
        }, 500)
    } else {
        let errText = msg
        try {
            const err = JSON.parse(msg)
            errText = `[${err.errCode}] ${err.errMsg}`
        } catch (e) { }
        this.pushLog(`投屏失败: ${errText}`)
        uni.showToast({
            title: errText || '投屏失败',
            icon: 'none'
        })
    }
})
  • 暂停播放

sp.pause(location, (ok, msg) => {
    if (ok) {
        this.pushLog('暂停请求已发送')
        setTimeout(() => this.refreshTransportState(), 500)
    } else {
        this.pushLog(`暂停失败: ${msg}`)
    }
})
  • 继续播放

sp.resume(location, (ok, msg) => {
    if (ok) {
        this.pushLog('继续请求已发送')
        setTimeout(() => this.refreshTransportState(), 500)
    } else {
        this.pushLog(`继续失败: ${msg}`)
    }
})
  • 停止投屏

sp.stop(location, (ok, msg) => {
    if (ok) {
        this.pushLog('停止请求已发送')
        this.stopProgressTimer()
        this.currentSec = 0
        setTimeout(() => this.refreshTransportState(), 500)
    } else {
        this.pushLog(`停止失败: ${msg}`)
    }
})
  • 获取传输状态

sp.getTransportInfo(location, (state, msg) => {
    this.transportState = state
    this.playing = state === 'PLAYING'
    this.paused = state === 'PAUSED_PLAYBACK'
    if (state === 'PLAYING') {
        this.startProgressTimer()
    } else {
        this.stopProgressTimer()
    }
    if (state === 'UNKNOWN') this.pushLog(`查询状态失败: ${msg}`)
    // 状态变化后同步动作能力
    this.refreshActions()
})
  • 获取接收端设备能力

sp.getCurrentTransportActions(location, (actions, msg) => {
    if (actions && actions.length > 0) {
        this.actions = actions
    }
})
  • 获取当前接收端媒体信息

sp.getMediaInfo(location, (meta, msg) => {
    if (meta == null) return
    this.mediaTitle = meta.title || ''
    this.mediaProtocol = meta.protocolInfo || ''
})
  • 获取当前播放进度

sp.getPositionInfo(location, (info, msg) => {
    if (info == null) return
    this.durationSec = info.durationSec
    this.currentSec = info.currentSec
})
  • 快进到第几秒

sp.seek(location, target, (ok, msg) => {
    this.seeking = false
    if (!ok) this.pushLog(`跳转失败: ${msg}`)
    setTimeout(() => this.refreshPosition(), 300)
})
  • 获取当前音量

sp.getVolume(location, (v, msg) => {
    if (v >= 0) this.volume = v
})
  • 设置具体音量

sp.setVolume(location, v, (ok, msg) => {
    this.volumeChanging = false
    if (!ok) this.pushLog(`设置音量失败: ${msg}`)
})
  • 获取当前接收端是否是静音状态

sp.getMute(location, (mute, msg) => {
    this.muted = mute
})
  • 切换静音状态

const target = !this.muted
sp.setMute(location, target, (ok, msg) => {
    if (ok) {
        this.muted = target
        this.pushLog(target ? '已静音' : '已取消静音')
    } else {
        this.pushLog(`静音切换失败: ${msg}`)
    }
})
  • 获取接收端支持的能力

sp.getDeviceCapabilities(location, (caps, msg) => {
    if (caps == null) {
        this.pushLog(`能力探测失败: ${msg}`)
        this.deviceCaps = null
        return
    }
    this.deviceCaps = caps
    const speedText = caps.supportsSpeed ? `支持倍速(${caps.speeds.join('/')})` : '不支持倍速'
    const seekText = caps.supportsSeek ? '支持定位' : '不支持定位'
    this.pushLog(`设备能力: ${speedText},${seekText}`)
})
  • 切换播放倍速

let speed = "1"
sp.setPlaySpeed(location, speed, (ok, msg) => {
    if (ok) {
        this.currentSpeed = speed
        this.pushLog(`倍速已切换为 ${speed}x`)
        setTimeout(() => this.refreshTransportState(), 300)
    } else {
        this.pushLog(`倍速切换失败: ${msg}`)
        uni.showToast({
            title: msg || '设备不支持该倍速',
            icon: 'none'
        })
    }
})

隐私、权限声明

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

1. udp广播权限

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

插件不采集任何数据

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

无