ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

基于jssip的简单封装

2021-03-15 09:35:46  阅读:272  来源: 互联网

标签:基于 封装 log jssip .# agent state session evt


import {UA as Agent, WebSocketInterface as Socket, debug} from 'jssip';
import EventEmitter from "./eventEmitter";

debug('JsSIP:RTCSession:DTMF');

export default class SipClient extends EventEmitter {
    #debug = false;
    #state = 'unknown';
    #agent = null;
    #session = null;
    #player = null;
    #server = '';

    constructor(opts = {}) {
        super();
        this.#debug = opts.debug || false;
        const player = document.createElement('audio');
        player.autoplay = true;
        this.#player = player;
    }

    register(opts = {server: '', aor: '', displayName: ''}) {
        this.#state = 'unknown';
        this.#agent = this.#session = null;
        this.#server = opts.server;

        this.#initAgent({
            sockets: [new Socket(this.#server)],
            uri: 'sip:' + opts.aor,
            password: opts.password,
            display_name: opts.displayName,
            no_answer_timeout: 50, // 电话呼入无人应答超时
            register: true, // 自动注册
            session_timers: false//启用会话计时器(根据RFC 4028)
        });
        this.#agent.start();
    }

    get debug() {
        return this.#debug;
    }

    set debug(val) {
        this.#debug = val;
    }

    get state() {
        return this.#state;
    }

    get session() {
        return this.#session;
    }

    /**
     * 呼叫
     * @param {string} aor 对方的SIP号码
     */
    call(aor) {
        this.#log('-> 呼叫', aor);
        const opts = {
            mediaConstraints: {audio: true, video: false},
            pcConfig: {iceServers: []},
            eventHandlers: {
                peerconnection: evt => this.#peerConnectionEventHandler(evt),
            }
        }
        this.localSession = this.#agent.call(`sip:${aor}`, opts);
    }

    /**
     * 取消呼叫
     */
    cancel() {
        this.#log('-> 取消')
        if (this.#session) {
            this.#session.terminate();
        } else {
            this.#log('呼出的会话不存在');
        }
    }

    /**
     * 拒绝对方
     */
    decline() {
        this.#log('-> 拒绝')
        if (this.#session) {
            this.#session.terminate();
        } else {
            this.#log('呼入的会话不存在');
        }
    }

    /**
     * 接受对方
     */
    accept() {
        this.#log('-> 接受')
        if (this.#session) {
            this.#session.answer({
                mediaConstraints: {audio: true, video: false},
                pcConfig: {iceServers: []},
            });
        } else {
            this.#log('呼入的会话不存在');
        }
    }

    /**
     * 挂断
     */
    hangup() {
        this.#log('-> 挂断')
        if (this.#session) {
            this.#session.terminate();
        } else {
            this.#log('会话不存在')
        }
    }

    sendDtmf() {

    }

    /**
     * 设置状态
     * @param {string} state
     */
    #setState(state) {
        this.#log('setState', state);
        if(state === 'idle'){
            this.#session = null;
            this.#player.srcObject = null;
        }
        if (this.#state !== state) {
            this.emit('state', state);
        }
        this.#state = state;
    }
    #sessionEventHandler(evt) {
        const {originator, session} = evt;
        if (originator === 'remote') {
            this.remoteSession = session;
            // 如果正在通话中, 回复忙
            if(this.#session){
                session.terminate({
                    status_code: 486
                });
                return;
            }
            const {uri, display_name} = session.remote_identity;
            const remote = {user: uri.user, name: display_name || uri.user, host: uri.host};
            this.emit('callIn', remote);
        }
        session.on('peerconnection', evt => {
            this.#peerConnectionEventHandler(evt);
        }).on('connecting', evt => {
        }).on('progress', evt => {
            this.#log(evt.originator === 'remote' ? '等待对方接听' : '等待自己接听', evt);
            this.#setState('waiting');
        }).on('accepted', evt => {
        }).on('confirmed', evt => {// 确认呼叫后触发
            this.#log(evt.originator === 'remote' ? '自己已接受' : '对方已接受', evt);
            this.#setState('calling');
        }).on('sdp', evt => {
            this.#log(evt.originator === 'remote' ? '对方SDP' : '自己SDP', evt);
        }).on('newDTMF', evt => {
            this.#log('收到DTMF', evt);
        }).on('ended', evt => {
            this.#log(evt.originator === 'remote' ? '对方挂断' : '自己挂断', evt);
            this.#setState('idle');
        }).on('failed', evt => this.#failedEventHandler(evt));
        this.#session = session;
    }

    #peerConnectionEventHandler(evt) {
        this.#log('======peerconnection', evt);
        evt.peerconnection.onaddstream = evt => {
            this.#log('onAddStream', evt.stream.getTracks());
            this.#player.srcObject = evt.stream;
        }
    }

    #failedEventHandler(evt) {
        this.#log('failed', evt.cause, evt);
        const {originator, cause} = evt;
        const isRemote = originator === 'remote';
        switch (cause) {
            case 'Canceled':
                this.#log(isRemote ? '对方已取消' : '自己已取消');
                this.emit('canceled', originator);
                break;
            case 'Unavailable':
                this.#log(isRemote ? '对方不可用' : '自己不可用');
                break;
            case 'No Answer':
                this.#log(isRemote ? '对方无应答' : '自己无应答');
                this.emit('noAnswer');
                break;
            case 'Rejected':
                this.#log(isRemote ? '对方拒绝' : '自己拒绝');
                break;
            case 'SIP Failure Code':
                this.#log(isRemote ? '对方呼叫失败' : '自己呼叫失败');
                break;
            default:
                this.#log('failedEventHandler', cause, originator);
                break;
        }
        this.#setState('idle');
    }

    /**
     * 初始化
     */
    #initAgent(opts = {}) {
        this.#log('createAgent', opts);
        const agent = new Agent(opts);
        agent.on('connected', _ => this.#setState('connected'));
        agent.on('disconnected', _ => this.#setState('disconnected'));
        // 注册成功,data:Response JsSIP.IncomingResponse收到的SIP 2XX响应的实例
        agent.on('registered', _ => this.#setState('idle'));
        agent.on('unregistered', _ => this.#setState('unregistered'));
        //注册失败而被解雇,data:Response JsSIP.IncomingResponse接收到的SIP否定响应的实例,如果失败是由这样的响应的接收产生的,否则为空
        agent.on('registrationFailed', evt => {
            this.#setState('registrationFailed');
        });
        //1.在注册到期之前发射几秒钟。如果应用程序没有为这个事件设置任何监听器,JsSIP将像往常一样重新注册。
        //2.如果应用程序订阅了这个事件,它负责ua.register()在registrationExpiring事件中调用(否则注册将过期)。
        //3.此事件使应用程序有机会在重新注册之前执行异步操作。对于那些在REGISTER请求中的自定义SIP头中使用外部获得的“令牌”的环境很有用。
        agent.on('registrationExpiring', evt => {

        });
        agent.on('newRTCSession', evt => {
            this.#log('newRTCSession', evt);
            this.#sessionEventHandler(evt);
        });
        this.#agent = agent;
    }

    #log(...args) {
        this.#debug && console.log('SipClient', new Date().toLocaleTimeString(), ...args);
    }
}

标签:基于,封装,log,jssip,.#,agent,state,session,evt
来源: https://www.cnblogs.com/zh33gl/p/14535640.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有