• 基于jssip的简单封装


    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);
        }
    }
    
  • 相关阅读:
    【十五分钟Talkshow】fmplan(十五分钟计划)的初步想法
    #ifdef,#ifndef,#define,#endif解析
    mass Framework menu插件
    软件随想
    gwt+smartgwt framework网站开发
    .NET异步
    CentOS6
    【十五分钟Talkshow】谈谈HTML 5及其对Web开发人员的挑战和机遇
    MongoDB源码概述——日志
    TWaver在FTTX设备网管系统中的应用
  • 原文地址:https://www.cnblogs.com/zh33gl/p/14535640.html
Copyright © 2020-2023  润新知