uniapp Vue 使用 sip.js进行语音通话视频通话

下载或者安装 sip.js 到 uniapp 项目,APP 端在 menifest.json 中配置麦克风权限
menifest.json 中 app 权限配置选中:
android.permission.RECORD_AUDIO
android.permission.MODIFY_AUDIO_SETTINGS

sip.js 低版本 如 V0.13.0 版本的写法

<template><view class="container"><view class="top-box"><button type="primary" @click="handleRegister">注册</button><button type="primary" plain @click="handleUnRegister">取消注册</button></view><view class="top-box"><uni-easyinput class="margr" v-model="outGoingNumber" placeholder="SIP URL"></uni-easyinput><button type="primary" @click="handleCall">呼叫</button></view><view class="top-box"><audio ref="remoteAudio" id="remoteAudio"></audio><audio ref="localAudio" id="localAudio"></audio><audio ref="bell" id="bell" src="~/static/music.mp3"></audio></view><uni-popup ref="popup" type="center"><view class="popup-box"><view class="flex-box fs-36 fw-600">{{isInOut?inComingNumber:outGoingNumber}}</view><template v-if="isConnected"><view class="flex-box">通话中...</view><view class="flex-box"><uni-tag text="挂断" type="error" @click="handleCacel"></uni-tag></view></template><template v-else><view v-if="isInOut"><view class="flex-box">呼入...</view><view class="flex-box"><uni-tag text="接听" type="success" @click="handleAccept"></uni-tag><uni-tag text="拒绝" type="error" @click="handleTerminate"></uni-tag></view></view><view v-else><view class="flex-box">呼出...</view><view class="flex-box"><uni-tag text="挂断" type="error" @click="handleTerminate"></uni-tag></view></view></template></view></uni-popup></view></template><script>import * as sip from "@/common/js/sip-0.13.0.min.js"let ua;export default {name: "VoiceIntercom",props: {},data() {return {configuration: {},outGoingNumber: '02700002',baseUrl: 'web.domain.com', // sipurl格式: "sip:02700001@web.domain.com:7065",port: '7065',user: {number: '02700001',name: 'test',password: '123456'},server: 'wss://web.domain.com:7067',currentSession: null,inComingNumber: null,isRegistered: false,isConnected: false, // 是否接通isInOut: true, // true 被呼, false 呼出}},onLoad() {this.handleRegister()},beforeDestroy() {this.handleUnRegister()},methods: {// 登录handleRegister() {this.configuration = {uri: `sip:${this.user.number}@${this.baseUrl}:${this.port}`,displayName: this.user.name,password: this.user.password,transportOptions: {wsServers: [this.server],traceSip: true},}ua = new sip.UA(this.configuration)ua.on('registered', (resp) => {this.showTishi('【' + this.user.number + '】语音登录成功')this.isRegistered = true})ua.on('registrationFailed', (resp) => {if (resp.statusCode == 503) {this.showTishi('【' + this.user.number + '】服务不可用')} else {this.showTishi('【' + this.user.number + '】语音登录失败:' + resp.reasonPhrase)}this.isRegistered = falseconsole.log(resp, '语音登录失败')})ua.on('unregistered', (response, cause) => {this.showTishi('【' + this.user.number + '】取消语音登录成功')console.log(response, cause, '取消语音登录')})ua.on('invite', (session) => {this.currentSession = sessionthis.inComingNumber = session.remoteIdentity.uri.userthis.isInOut = truethis.$refs.popup.open()this.$nextTick(() => {this.$refs.bell.$refs.audio.play()this.$refs.bell.$refs.audio.currentTime = 0})this.sessionEvent(session)})ua.on('message', (message)=>{console.log(message,'ua-message')})ua.start()},// 退出登录handleUnRegister() {if (ua) {ua.unregister()this.isRegistered = false}},// session 处理sessionEvent(session) {session.on('rejected', () => {console.log('inComing挂断')})session.on('cancel', () => {console.log('outgoing挂断')})session.on('terminated', (message, cause) => {console.log(message, cause, 'session-terminated')if (cause == 'Rejected') {if (message.reasonPhrase == 'Decline') {this.showTishi('您的拨号暂时无人接听!')} else {this.showTishi('对方拒接了!')}} else if (cause == 'BYE') {this.showTishi('对方已挂机!')} else if (cause == 'Canceled') {this.showTishi('对方已取消!')}this.$refs.bell.$refs.audio.pause()this.$refs.bell.$refs.audio.pause()this.$refs.popup.close()})session.on('accepted', (resp) => {this.$refs.bell.$refs.audio.pause()this.isConnected = trueconsole.log(resp, '接受了')})session.on('trackAdded', () => {const pc = session.sessionDescriptionHandler.peerConnectionconst remoteStream = new MediaStream()pc.getReceivers().forEach((receiver) => {if (receiver.track) {remoteStream.addTrack(receiver.track)this.$refs.remoteAudio.$refs.audio.srcObject = remoteStreamthis.$refs.remoteAudio.$refs.audio.play()}})})session.on('bye', (resp, cause) => {console.log(resp, cause, 'session-bye')if ((resp && resp.method == 'BYE') || cause == 'BYE') {this.isConnected = falsethis.$refs.popup.close()this.$refs.remoteAudio.$refs.audio.pause()this.showTishi('【' + this.user.number + '】通话已结束!')}})session.on('failed', () => {console.log('session-failed')})},// 接听handleAccept() {const option = {sessionDescriptionHandlerOptions: {constraints: {audio: true,video: false}}}this.currentSession.accept(option)},// 拒接handleTerminate() {this.currentSession.terminate()this.$refs.popup.close()this.isConnected = false},// 挂断handleCacel() {if (this.isInOut) {this.currentSession.reject()} else {this.currentSession.terminate()}this.$refs.popup.close()this.isConnected = false},// 拨打handleCall(number) {number = this.outGoingNumberif (this.isRegistered) {this.isInOut = falseconst sipUrl = `sip:${number}@${this.baseUrl}:${this.port}`this.currentSession = ua.invite(sipUrl, {sessionDescriptionHandlerOptions: {constraints: {audio: true,video: false}}})this.$refs.popup.open()this.sessionEvent(this.currentSession)} else {this.showTishi('请先登录语音用户')}},showTishi(title){uni.showToast({title: title,icon: 'none'})},}}
</script><style scoped lang="scss">.container {font-size: 30rpx;}.top-box {padding: 30rpx;display: flex;}.popup-box {background: #ffffff;width: 80vw;padding: 40rpx;border-radius: 20rpx;line-height: 80rpx;}.flex-box {display: flex;justify-content: space-around;.uni-tag {font-size: 30rpx;padding: 16rpx 28rpx;}}.fs-36{font-size: 36rpx;font-weight: bold;}
</style>

sip.js 高版本如 V0.21.2 用法(参数同上,只列出 methods 里的部分)

<script>
import { UserAgentOptions, UserAgent, Registerer, Invitation, Inviter, Session, SessionState, InvitationAcceptOptions, InviterOptions, Messager, URI, RegistererState,  } from '@/common/js/sip-0.21.2.min.js'let userAgent, registerer;
const target = UserAgent.makeURI(`sip:${this.outGoingNumber}@${this.baseUrl}:${this.port}`);methods: {handleRegister() {const uri = UserAgent.makeURI(`sip:${this.user.number}@${this.baseUrl}:${this.port}`)if(!uri){console.log('创建URI失败')}const transportOptions = {server: this.server}const userAgentOptions = {authorizationUsername: this.user.number,authorizationPassword: this.user.password,displayName: this.user.name,transportOptions,uri,delegate: {onInvite}} userAgent = new UserAgent(userAgentOptions) registerer = new Registerer(userAgent)userAgent.start().then(()=>{registerer.register({requestDelegate: {onReject: (resp)=>{console.log(resp, 'onReject')},onAccept: (resp)=>{console.log(resp, 'onAccept')},onProgress: (resp) => {console.log(resp, 'onProgress')},onRedirect: (resp) => {console.log(resp, 'onRedirect')},onTrying: (resp) => {console.log(resp, 'onTrying')},}}).catch((e: Error) => {console.log(e, "Register failed")})}).catch((err:any)=>{console.log(err,'start-err')}) registerer.stateChange.addListener((newState)=>{switch (newState) {case RegistererState.Unregistered:console.log('退出登录')break;case RegistererState.Registered :break;case RegistererState.Initial :console.log('语音用户登录Initial')break;case RegistererState.Terminated :console.log('语音用户登录Terminated')break;}})function onInvite(invitation){this.currentSession = invitationthis.inComingNumber = invitation.remoteIdentity.uri.userthis.$refs.popup.open()invitation.stateChange.addListener((state)=>{this.sessionStateEvent(state, invitation)})}}handleAccept(){let constrainsDefault = {audio: true,video: false,}const options = {sessionDescriptionHandlerOptions: {constraints: constrainsDefault}}this.currentSession.accept(options)this.isConnected = true}handleReject(){this.currentSession.reject()this.$refs.popup.close()}sessionStateEvent(state, session){switch(state){case SessionState.Initial:console.log('SessionState.Initial')case SessionState.Establishing:console.log('SessionState.Establishing')break;case SessionState.Established:console.log('SessionState.Established')this.isConnected = truethis.setupRemoteMedia(session)break;case SessionState.Terminating:console.log('SessionState.Terminating')break;case SessionState.Terminated:console.log('SessionState.Terminated')this.clearupMedia(session)break;     } }setupRemoteMedia(session){const remoteStream = new MediaStream()// console.log(session.sessionDescriptionHandler, 'sessionDescriptionHandler')session.sessionDescriptionHandler.peerConnection.getReceiver().forEach((receiver)=>{if(receiver.track){remoteStream.addTrack(receiver.track)}})this.$refs.remoteAudio.$refs.audio.srcObject = remoteStreamthis.$refs.remoteAudio.$refs.audio.play()}clearupMedia(session){if(this.isCallIn){if(session.isCanceled){console.log('对方已挂机')}}else{if(!session.isCanceled){// console.log('对方已挂机')}}this.$refs.remoteAudio.$refs.audio.srcObject = nullthis.$refs.remoteAudio.$refs.audio.pause()this.endCall()}handleCall(){this.isCallIn = falsethis.$refs.popup.open()const inviterOptions = {sessionDescriptionHandlerOptions: {constraints: { audio: true, video: false }}}if(target){const inviter = new Inviter(userAgent, target, inviterOptions)this.currentSession = inviterinviter.invite({requestDelegate: {onReject: (resp)=>{console.log(resp, 'inviter-onReject')if(resp.statusCode == 500){console.log('对方不在线')}else{console.log('对方拒接了')}},onAccept: (resp)=>{console.log(resp, 'inviter-onAccept')},onProgress: (resp) => {console.log(resp, 'inviter-onProgress')},onRedirect: (resp) => {console.log(resp, 'inviter-onRedirect')},onTrying: (resp) => {console.log(resp, 'inviter-onTrying')},}})inviter.stateChange.addListener((state)=>{this.sessionStateEvent(state, inviter)})} }handleURegister(){if(userAgent){this.isRegistered = falseregisterer.unregister()}}sendMessage(){if(target && this.isRegistered){const messager = new Messager(userAgent, target, '你好')messager.message()}}endCall(){this.isConnected = falsethis.$refs.popup.close()switch(this.currentSession.state){case SessionState.Initial:case SessionState.Establishing:if(this.currentSession instanceof Inviter){// incoming sessionthis.currentSession.cancel()}else{// outgoing sessionthis.currentSession.reject()}break;case SessionState.Established:this.currentSession.bye()break;case SessionState.Terminating:break;case SessionState.Terminated:console.log(SessionState,'Terminated-endCall')break;     }}
}</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/42481.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

latex 笔记:cs论文需要的排版格式

主要针对英文文献 1 基本环境 连字符 不同长度的"-"表示不同含义。 一个"-"长度的连字符用于词中两个"-"长度的连字符常用于制定范围三个"-"长度的连字符是破折号数学中的负数要用数学环境下的-得到 强调 在正式文章中, 通常不…

神经网络基础-神经网络补充概念-48-rmsprop

概念## 标题 RMSProp&#xff08;Root Mean Square Propagation&#xff09;是一种优化算法&#xff0c;用于在训练神经网络等机器学习模型时自适应地调整学习率&#xff0c;以加速收敛并提高性能。RMSProp可以有效地处理不同特征尺度和梯度变化&#xff0c;对于处理稀疏数据和…

Open3D点云数据处理(二十):最小二乘直线拟合(三维)

文章目录 1 最小二乘三维直线拟合原理2 代码实现3 直线拟合的评估指标4 计算拟合的评估指标5 np.linalg.lstsq() 函数详解专栏目录:Open3D点云数据处理(Python) 1 最小二乘三维直线拟合原理 最小二乘三维直线拟合的原理是通过最小化数据点到直线距离的平方和,找到最优的直…

ARM64 程序调用标准

ARM64 程序调用标准 1 Machine Registers1.1 General-purpose Registers1.2 SIMD and Floating-Point Registers 2 Processes, Memory and the Stack2.1 Memory Addresses2.2 The Stack2.2.1 Universal stack constraints2.2.2 Stack constraints at a public interface 2.3 Th…

【C语言】字符串和内存函数的介绍 -- 详解

重点介绍处理字符和字符串的库函数的使用和注意事项。 C语言中对字符和字符串的处理很是频繁&#xff0c;但是C语言本身是没有字符串类型的&#xff0c;字符串通常放在常量字符串中或者字符数组中。字符串常量适用于那些对它不做修改的字符串函数。 一、求字符串长度⚪strlen …

python的requests库使用

安装 pip install requests方法 requests.get() 发起get请求调用 查询 requests.post() 发起post请求调用 报错 requests.put() 发起put请求调用 修改 requests.delete() 发起delete请求调用 删除 requests.session() 获取requests的session对象 requests.session().request(…

【Rust】Rust学习 第十四章进一步认识 Cargo 和 Crates.io

本章会讨论 Cargo 其他一些更为高级的功能&#xff0c;我们将展示如何&#xff1a; 使用发布配置来自定义构建将库发布到 crates.io使用工作空间来组织更大的项目从 crates.io 安装二进制文件使用自定义的命令来扩展 Cargo Cargo 的功能不止本章所介绍的&#xff0c;关于其全…

云积天赫|AIGC+营销的排头兵

AIGC生成式人工智能&#xff0c;正逐渐成为人们关注的焦点。AIGC的出现&#xff0c;标志着人工智能已经进入了一个全新的时代。AIGC的出现&#xff0c;也为营销行业带来了新的活力。那么企业该怎么利用这次AIGC浪潮&#xff0c;成为AIGC营销的排头兵呢&#xff1f;      “…

【JavaScript】使用js实现滑块验证码功能与浏览器打印

滑块验证码 效果图&#xff1a; 实现思路&#xff1a; 根据滑块的最左侧点跟最右侧点&#xff0c; 是否在规定的距离内【页面最左侧为原点】&#xff0c;来判断是否通过 html代码&#xff1a; <!DOCTYPE html> <html><head><title>滑动图片验证码&…

Python爬虫常用:谷歌浏览器驱动——Chromedriver 插件安装教程

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 我们在做爬虫的时候经常要使用谷歌浏览器驱动&#xff0c;今天分享下这个Chromedriver 插件的安装方法。 话不多说&#xff0c;直接开搞&#xff0c;如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 第一步、打开谷…

使用Dockker创建vwas容器时报错的解决方法

执行命令 docker run -it -d -p 13443:3443 --cap-add LINUX_IMMUTABLE secfa/docker-awvs没有详细看报错之前找了各种各样的解决办法&#xff0c;都无法解决。因此以后在看报错提示的时候耐心一点看关键词Error 后来才发现启动vwas时docker报了这个错&#xff1a; OSError: …

CANoe自动化工程的搭建

基于XMLCAPL建立自动化工程 1、导入ini文件2、新建 Test Environment3、报告类型4、代码编写 1、导入ini文件 工程的配置的文件&#xff0c;配置DUT相关信息&#xff0c;具体视工程而编写内容。 2、新建 Test Environment 1、新建XML测试用例环境 2、导入XML测试用例文件 …

Jmeter常用功能-参数化介绍

JMeter也有像LR中的参数化&#xff0c;本篇就来介绍下JMeter的参数化如何去实现。 参数化&#xff1a;录制脚本中有登录操作&#xff0c;需要输入用户名和密码&#xff0c;假如系统不允许相同的用户名和密码同时登录&#xff0c;或者想更好的模拟多个用户来登录系统。 这个时…

mac M1安装opencv方法及类型报错解决

安装opencv: pip install opencv-python pip install --user opencv-contrib-python pip install opencv-python 4.5.2.54 numpy 1.25.2 安装过程中报错如下&#xff1a; python-类型错误&#xff1a;“numpy._DTypeMeta”对象不可下标 TypeError: ‘numpy._DTypeMeta’ obje…

虚拟机/双系统Ubuntu扩容

虚拟机Ubuntu扩容 1.需要删除所有的快照 2.扩展虚拟机磁盘大小 虚拟机(M)→设置(s)→硬盘(SCSI)→扩展磁盘容量 3.Ubuntu内调整分区大小 安装gparted分区工具&#xff1a;sudo apt-get install gparted 启动gparted并resize分区 4.最后最好建一个快照&#xff0c;不然gg了…

WinPlan经营大脑垂直大模型行业报告

一、引言 在当前高度信息化的时代,企业经营管理决策的重要性已经得到了广泛的认可。然而,在实际操作中,许多企业仍然在凭经验、拍脑袋进行经营决策,缺乏数据工具与专职分析团队,导致决策难、效率低等问题。针对这一问题,近年来,一种名为“WinPlan”的经营决策产品逐渐崭…

[测试报告] 爱搜Blog 自动化测试报告

目录 项目背景 项目功能 测试详情 一、设计测试用例 二、功能测试步骤结果 1. 登录页面 2. 个人博客页面 3. 博客详情页 4. 博客编辑页 三、自动化测试及测试结果 1. 测试环境 2. 登录测试用例&#xff1a; 3. 个人详情页测试用例&#xff1a; 4. 写博客并发布测试…

Android免打包多渠道统计如何实现

摘要&#xff1a; 实际上只要完成1-2步即可实现多渠道打包&#xff0c;这也意味着&#xff0c;只要每次更新App时给出一个原始包&#xff0c;运营人员就能在后台自己进行操作管理&#xff0c;简单快捷到全程无需开发人员参与。 我们都知道&#xff0c;Android 市场被分割成几十…

Go学习笔记之数据类型

文章目录 GO数据类型数组array切片slice集合map结构体make和new GO数据类型 在go语言中&#xff0c;定义的全局数据结构不使用不会报错&#xff0c;定义的局部数据结构必须使用&#xff0c;否则报错&#xff1b;建议定义的数据类型就要使用&#xff0c;要么不定义。 数组array …

使用Alien对.deb包与.rpm包相互转换

目录 1、切换到root 2、更新yum&#xff08;更新比较耗时&#xff0c;不更新没试行不&#xff0c;自行斟酌是否跳过这一步&#xff09; 3、卸载ibus 4、安装Alien及其依赖包 5、安装Alien 6、将.deb转换成.rpm包 7、安装RPM包 8、如果报错 9、将.rpm转换成.deb包 10、安…