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.pswp.cn/news/42481.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/42481.shtml
英文地址,請注明出處:http://en.pswp.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、安…