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


下载或者安装 sip.js 到 uniapp 项目,APP 端在 menifest.json 中配置麦克风权限

menifest.json 中 app 权限配置选中:

android.permission.RECORD_AUDIO

android.permission.MODIFY_AUDIO_SETTINGS

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

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

注册
            取消注册

            呼叫

                {{isInOut?inComingNumber:outGoingNumber}}

                    通话中...

                        呼入...

                        呼出...

    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 = false
                    console.log(resp, '语音登录失败')
                })
                ua.on('unregistered', (response, cause) => {
                    this.showTishi('【' + this.user.number + '】取消语音登录成功')
                    console.log(response, cause, '取消语音登录')
                })
                ua.on('invite', (session) => {
                    this.currentSession = session
                    this.inComingNumber = session.remoteIdentity.uri.user
                    this.isInOut = true
                    this.$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.popup.close()
                })
                session.on('accepted', (resp) => {
                    this.$refs.bell.$refs.audio.pause()
                    this.isConnected = true
                    console.log(resp, '接受了')
                })
                session.on('trackAdded', () => {
                    const pc = session.sessionDescriptionHandler.peerConnection
                    const remoteStream = new MediaStream()
                    pc.getReceivers().forEach((receiver) => {
                        if (receiver.track) {
                            remoteStream.addTrack(receiver.track)
                            this.$refs.remoteAudio.$refs.audio.srcObject = remoteStream
                            this.$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 = false
                        this.$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.outGoingNumber
                if (this.isRegistered) {
                    this.isInOut = false
                    const 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'
                })
            },
        }
    }

    .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;
    } 

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

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 = invitation
            this.inComingNumber = invitation.remoteIdentity.uri.user
            this.$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 = true
                this.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 = remoteStream
        this.$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 = null
        this.$refs.remoteAudio.$refs.audio.pause()
        this.endCall()
    }

    handleCall(){
        this.isCallIn = false
        this.$refs.popup.open()
        const inviterOptions = {
            sessionDescriptionHandlerOptions: {
                constraints: { audio: true, video: false }
            }
        }
        if(target){
            const inviter = new Inviter(userAgent, target, inviterOptions)
            this.currentSession = inviter

            inviter.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 = false
            registerer.unregister()
        }
    }

    sendMessage(){
        if(target && this.isRegistered){
            const messager = new Messager(userAgent, target, '你好')
            messager.message()
        }
    }
    endCall(){
        this.isConnected = false
        this.$refs.popup.close()
        switch(this.currentSession.state){
            case SessionState.Initial:
            case SessionState.Establishing:
                if(this.currentSession instanceof Inviter){
                    // incoming session
                    this.currentSession.cancel()
                }else{
                    // outgoing session
                    this.currentSession.reject()
                }
                break;
            case SessionState.Established:
                this.currentSession.bye()
                break;
            case SessionState.Terminating:
                break;
            case SessionState.Terminated:
                console.log(SessionState,'Terminated-endCall')
                break;     
        }
    }
} 

APP模式下检测麦克风权限,permission.js可从插件市场直接下载,使用页面import引入。

getPermission(){
    let env = uni.getSystemInfoSync().platform
    if (env === 'android') {permission.requestAndroidPermission('android.permission.RECORD_AUDIO').then((e) => {
            if (e === -1) {
                uni.showToast({
                    title: '您已经拒绝录音权限,请在应用设置中手动打开',
                    icon: 'none',
                })
            } else if (e === 0) {
                uni.showToast({
                    title: '您拒绝了录音授权',
                    icon: 'none',
                })
            } else if (e === 1) {
                console.log('已授权')
            } else {
                uni.showToast({
                    title: '授权返回值错误',
                    icon: 'none',
                })
            }
        }).catch((err) => {
            uni.showToast({
                title: '拉起录音授权失败',
                icon: 'none',
            })
        })
    } else if (env === 'ios') {
        if (permission.judgeIosPermission("record")){
            console.log('已授权')
        }else{
            uni.showToast({
                title: '您拒绝了录音授权,请在应用设置中手动打开',
                icon: 'none',
            })
        }   
    }
}

4月5日,星期五简报,60秒知晓天下事

【MySQL】数据库排查慢查询、死锁进程排查、预防以及解决方法

获取更多资讯请加入交流群


    协助本站SEO优化一下,谢谢!
    关键词不能为空
评 论
更换验证码