当前位置: 首页 > 工具软件 > SIP.js > 使用案例 >

Freeswitch+Sip.js实现软电话功能

裴姚石
2023-12-01

Freeswitch+Sip.js实现软电话功能

版本:
sip.js : 0.13.8
freeswitch: 1.6


freeswitch相关配置

  • 修改 acl.conf.xml(笔者之前有遇到分机呼不通的情况,后来修改了此配置)
     <list name="lan" default="allow">
     <node type="deny" cidr="192.168.42.0/24"/>
     <node type="allow" cidr="192.168.42.42/32"/>
     <!-- use cidr= if you wish to allow ip ranges to this domains acl. -->
     <node type="allow" cidr="0.0.0.0/0"/>   <!-- 允许所有ip 此处根据自己的需要设置 -->
    </list>
    
    
  • 修改 internal.xml
    修改,如没有则添加
    <param name="apply-candidate-acl" value="lan"/>
    
    

Sip.js相关代码及说明

	<!DOCTYPE html>
	<html>
	<head>
	    <meta charset="utf-8">
	    <title>sipjstest</title>
	    <link rel="stylesheet" href="css/style.css"/>
	</head>
	<body>
	<div>
	    <input id="number" value="1000" type="text"/>
	    <button id="startCall">Start Call</button>
	    <button id="endCall">End Call</button>
	    <button id="transfer">transfer</button>
	    <button id="hold">hold</button>
	    <button id="unhold">unhold</button>
	    <button id="mute">mute</button>
	    <button id="unmute">unmute</button>
	</div>
	<br>
	<div>
	    <input id="msg" value="你好" type="text"/>
	    <button id="sendMessage">Send Message</button>
	
	    <audio id="ringtone" loop src="./sounds/ringtone.wav"></audio>            <!--//来电提示音-->
	    <audio id="ringbacktone" loop src="./sounds/ringbacktone.wav"></audio>    <!--//电话呼叫后声音-->
	    <audio id="dtmfTone" src="./sounds/dtmf.wav"></audio>
	
	    <video id="remoteVideo" autoplay="autoplay"></video>
	    <video id="localVideo" autoplay="autoplay" muted="muted"></video>
	    <!-- DTMF -->
	    <div id="dtmf-buttons">
	        <button onclick="sendDtmf('1');">1</button>
	        <button onclick="sendDtmf('2');">2</button>
	        <button onclick="sendDtmf('3');">3</button>
	        <button onclick="sendDtmf('4');">4</button>
	        <button onclick="sendDtmf('5');">5</button>
	        <button onclick="sendDtmf('6');">6</button>
	        <button onclick="sendDtmf('7');">7</button>
	        <button onclick="sendDtmf('8');">8</button>
	        <button onclick="sendDtmf('9');">9</button>
	        <button onclick="sendDtmf('0');">0</button>
	        <button onclick="sendDtmf('#');">#</button>
	        <button onclick="sendDtmf('*');">*</button>
	    </div>
	</div>
	
	<script src="./js/sip-0.13.8.js"></script>
	<script>
	
	    var extCode = "1012"; //分机号
	    var extPass = "1012"; //分机密码
	    var config = {
	        uri: "sip:" +extCode + '@192.168.0.253:5066',
	        authorizationUser: extCode,
	        password: extPass,
	        displayName: extCode,
	        log: {
	            builtinEnabled: true,
	            level: 3 // log日志级别
	        },
	        transportOptions: {
	          //  wsServers: ['ws://192.168.0.253:5066'], // ws协议
	             wsServers: ['wss://192.168.0.253:7443'], //wss协议
	            traceSip: true  //开启sip日志,用于排查问题
	        },
	        allowLegacyNotifications: true,
	        hackWssInTransport: true, // 设置为true 则注册时 transport=wss; false:transport=ws;
	        /**
	         * sip.js生成的随机contact字符串 设置后会以此为后缀 可以搭配 cusContactName使用,根据自身业务进行使用
	         *  设置前: sofia/internal/sip:admin10@df7jal23ls0d.invalid;
	         *  设置后: sofia/internal/sip:admin10@192.168.0.253;
	         *
	         */
	        hackIpInContact: "192.168.0.253",
	        userAgentString: "myAwesomeApp",
	        registerOptions: {
	            expires: 300,
	            registrar: 'sip:registrar.mydomain.com',
	        },
	        /**
	         * 此处是笔者自定义的参数,因为注册到fs时,sip.js会随机生成contact字符串,如:sofia/internal/sip:admin10@df7jal23ls0d.invalid;
	         *  笔者自己添加了一个参数,对sip.js的源码进行了修改 ,修改后效果  sofia/internal/sip:1012@192.168.0.253 不需要的可以不理会此参数
	         * 此处纠正问题,官方提供了一个参数[contactName],使用此参数即可,sip.js使用官方的即可
	         * contactName:"1012"
	         */
	        // cusContactName: "1012"
	    };
	    var ua = new SIP.UA(config);
	
	    var sessionall;
	    var startCall = document.getElementById('startCall');
	    var endCall = document.getElementById('endCall');
	    var transfer = document.getElementById('transfer');
	    var hold = document.getElementById('hold');
	    var unhold = document.getElementById('unhold');
	    var sendMessage = document.getElementById('sendMessage');
	    var mute = document.getElementById('mute');
	    var unmute = document.getElementById('unmute');
	
	    var Ring={
	        /**
	         * 振铃
	         */
	        startRingTone: function () {
	            let play = document.getElementById("ringtone").play();
	            play.then(()=>{
	
	            }).catch((err)=>{
	
	            });
	
	        },
	        /**
	         * 停止振铃
	         */
	        stopRingTone: function () {
	            document.getElementById("ringtone").pause();
	        },
	
	        startRingbackTone: function () {
	            let play = document.getElementById("ringbacktone").play();
	            play.then(()=>{
	
	            }).catch((err)=>{
	
	
	            });
	        },
	        stopRingbackTone: function () {
	            document.getElementById("ringbacktone").pause();
	        }
	
	
	    };
	
	    /**
	     *  绑定方法
	     * @param obj 要绑定事件的元素
	     * @param ev 绑定的事件
	     * @param fn  绑定事件的函数
	     */
	    function bindEvent(obj, ev, fn) {
	        if (obj.attachEvent) {
	            obj.attachEvent("on" + ev, fn);
	        }
	        else {
	            obj.addEventListener(ev, fn, false);
	        }
	    }
	
	    /**
	     *  发送DTMF
	     * @param num 按键
	     */
	    function sendDtmf(num) {
	        sessionall.dtmf(num);
	    }
	
	    /**
	     * 拨打电话
	     */
	    bindEvent(startCall, 'click', function () {
	
	
	        var number = document.getElementById("number").value;
	        //创建一个新的出站(用户代理客户端)会话
	        sessionall = ua.invite(number, {
	            sessionDescriptionHandlerOptions: {
	                constraints: {
	                    audio: true,
	                    video: false
	                },
	                alwaysAcquireMediaFirst: true // 此参数是sip.js官方修复在firefox遇到的bug所设置
	            }
	        });
	
	        //开始嘟嘟嘟
	        Ring.startRingbackTone();
	
	
	        var remoteVideo = document.getElementById('remoteVideo');
	        var localVideo = document.getElementById('localVideo');
	
	
	        //每次收到成功的最终(200-299)响应时都会触发。
	        sessionall.on("accepted", function (response, cause) {
	            console.log(response);
	            Ring.stopRingbackTone();
	            var pc = this.sessionDescriptionHandler.peerConnection;
	            var remoteStream;
	
	            if (pc.getReceivers) {
	                remoteStream = new window.MediaStream();
	                pc.getReceivers().forEach(function (receiver) {
	                    var track = receiver.track;
	                    if (track) {
	                        remoteStream.addTrack(track);
	                    }
	                });
	            } else {
	                remoteStream = pc.getRemoteStreams()[0];
	            }
	            remoteVideo.srcObject = remoteStream;
	
	
	            var localStream_1;
	            if (pc.getSenders) {
	                localStream_1 = new window.MediaStream();
	                pc.getSenders().forEach(function (sender) {
	                    var track = sender.track;
	                    if (track && track.kind === "video") {
	                        localStream_1.addTrack(track);
	                    }
	                });
	            }
	            else {
	                localStream_1 = pc.getLocalStreams()[0];
	            }
	            localVideo.srcObject = localStream_1;
	        })
	
	        //挂机时会触发
	        sessionall.on("bye", function (response, cause) {
	            Ring.stopRingbackTone();
	            console.log(response);
	        })
	
	        //请求失败时触发,无论是由于最终响应失败,还是由于超时,传输或其他错误。
	        sessionall.on("failed", function (response, cause) {
	            Ring.stopRingbackTone();
	            console.log(response);
	        })
	
	        /**
	         *
	         */
	        sessionall.on("terminated", function (message, cause) {
	            Ring.stopRingbackTone();
	        })
	
	        /**
	         * 对方拒绝
	         */
	        sessionall.on('rejected', function (response, cause) {
	            Ring.stopRingbackTone();
	        })
	    })
	
	
	    //挂断事件
	    bindEvent(endCall, 'click', function () {
	        if (!sessionall) {
	            return;
	        } else if (sessionall.startTime) { // Connected
	            sessionall.bye();
	        } else if (sessionall.reject) { // Incoming
	            sessionall.reject();
	        } else if (sessionall.cancel) { // Outbound
	            sessionall.cancel();
	        }
	    })
	
	
	
	    //电话转移盲转
	    bindEvent(transfer, 'click', function () {
	        if (!sessionall) {
	            return;
	        }
	        var number = document.getElementById("number").value;
	        sessionall.refer(number, {
	            receiveResponse: function (msg, ss) {
	                console.error(msg);
	                console.error(ss);
	            }
	        });
	    })
	
	
	    //电话保持
	    bindEvent(hold, 'click', function () {
	        if (!sessionall) {
	            return;
	        }
	        sessionall.hold();
	    })
	
	
	    //恢复电话保持
	    bindEvent(unhold, 'click', function () {
	        if (!sessionall) {
	            return;
	        }
	        sessionall.unhold();
	    })
	
	    //发送消息
	    bindEvent(sendMessage, 'click', function () {
	        // Sends a new message
	        var number = document.getElementById("number").value;
	        var message = document.getElementById("msg").value;
	        ua.message(number, message);
	        // When receiving a message, prints it out
	        ua.on('message', function (message) {
	            console.log(message.body);
	        });
	    })
	
	    // 静音
	    bindEvent(mute, 'click', function () {
	        if (!sessionall) {
	            console.warn("No session to toggle mute");
	            return;
	        }
	
	        var pc = sessionall.sessionDescriptionHandler.peerConnection;
	        if (pc.getSenders) {
	            pc.getSenders().forEach(function (sender) {
	                if (sender.track) {
	                    sender.track.enabled = false;
	                }
	            });
	        } else {
	            pc.getLocalStreams().forEach(function (stream) {
	                stream.getAudioTracks().forEach(function (track) {
	                    track.enabled = false;
	                });
	                stream.getVideoTracks().forEach(function (track) {
	                    track.enabled = false;
	                });
	            });
	        }
	    })
	
	    // 解除静音
	    bindEvent(unmute, 'click', function () {
	        if (!sessionall) {
	            console.warn("No session to toggle mute");
	            return;
	        }
	
	        var pc = sessionall.sessionDescriptionHandler.peerConnection;
	        if (pc.getSenders) {
	            pc.getSenders().forEach(function (sender) {
	                if (sender.track) {
	                    sender.track.enabled = true;
	                }
	            });
	        } else {
	            pc.getLocalStreams().forEach(function (stream) {
	                stream.getAudioTracks().forEach(function (track) {
	                    track.enabled = true;
	                });
	                stream.getVideoTracks().forEach(function (track) {
	                    track.enabled = true;
	                });
	            });
	        }
	    })
	
	
	    // 接受入站(用户代理服务器)会话
	    ua.on('invite', function (session) {
	        // 开始振铃
	        Ring.startRingTone();
	        var url = session.remoteIdentity.uri.toString() + "来电了,是否接听";
	
	        var remoteVideo = document.getElementById('remoteVideo');
	        var localVideo = document.getElementById('localVideo');
	
	
	        session.on("terminated", function (message, cause) {
	            Ring.stopRingTone();
	            console.error(message);
	
	        })
	
	        /**
	         *
	         */
	        session.on('accepted', function (response, cause) {
	            console.error(response);
	            console.error(session);
	            Ring.stopRingTone();
	            // If there is a video track, it will attach the video and audio to the same element
	            var pc = this.sessionDescriptionHandler.peerConnection;
	            console.error(this.sessionDescriptionHandler);
	            var remoteStream;
	
	            if (pc.getReceivers) {
	                remoteStream = new window.MediaStream();
	                pc.getReceivers().forEach(function (receiver) {
	                    var track = receiver.track;
	                    if (track) {
	                        remoteStream.addTrack(track);
	                    }
	                });
	            } else {
	                remoteStream = pc.getRemoteStreams()[0];
	            }
	            remoteVideo.srcObject = remoteStream;
	
	
	            var localStream_1;
	            if (pc.getSenders) {
	                localStream_1 = new window.MediaStream();
	                pc.getSenders().forEach(function (sender) {
	                    var track = sender.track;
	                    if (track && track.kind === "video") {
	                        localStream_1.addTrack(track);
	                    }
	                });
	            }
	            else {
	                localStream_1 = pc.getLocalStreams()[0];
	            }
	            localVideo.srcObject = localStream_1;
	            localVideo.volume = 0;
	
	
	        })
	
	        session.on('bye', function (resp, cause) {
	            Ring.stopRingTone();
	        });
	
	        var isaccept = confirm(url);
	        if (isaccept) {
	            //接受来电
	            session.accept({
	                sessionDescriptionHandlerOptions: {
	                    constraints: {
	                        audio: true,
	                        video: false
	                    }
	                }
	            });
	            sessionall = session;
	        }
	        else {
	            //拒绝来电
	            session.reject();
	        }
	
	    });
	
	</script>
	
	</body>
	</html>

##参考资料: https://www.sipjs.com/
##参考资料: https://freeswitch.org/confluence/display/FREESWITCH/ACL
##参考资料: https://freeswitch.org/confluence/display/FREESWITCH/WebRTC#WebRTC-InstallCertificates
##源码连接: https://download.csdn.net/download/weixin_42275389/12919279
##码云地址:
如果文章帮到了你,请点个赞给笔者一个鼓励。如果有疑问,欢迎留言

 类似资料: