该文章只说明前端代码逻辑,如有错误,感谢指出。
jssip官方网站
jssip中文文档api
html部分代码
主要通过ontrack事件监听媒体流,实现语音实时通话
<template>
<el-card id="sip">
<header>
<h1>SIP呼叫</h1>
</header>
<el-divider></el-divider>
<main>
<div class="btns">
<el-input
type="text"
placeholder="输入号码"
v-model="phoneNum"
size="small"
style="width: 15%; margin-right: 10px"
></el-input>
<el-button type="success" size="small" @click="call()"
>SIP拨号</el-button
>
<el-button type="danger" size="small" @click="hangup()">挂断</el-button>
</div>
<!-- //挂断、接通、执行的录音 -->
<audio id="audio" autoplay="autoplay" src="" controls></audio>
</main>
</el-card>
</template>
/
/
js代码部分
<script>
export default {
data() {
return {
ua: null,
socket: null,
eventHandlers: null,
phoneNum: null,
config: {},
isRegister: false, // 是否已注册
registerState: false,
};
},
mounted() {},
methods: {
// 挂断
hangup() {
if (this.ua) {
this.ua.stop();
}
},
// 拨号
call() {
this.$nextTick(async () => {
await this.getSipInfo();
console.log("获取接口");
if (!this.registerState) {
this.callPhone(); // 注册
console.log("注册事件");
}
await this.testCall();
console.log("判断是否注册");
});
},
testCall(sip_phone_number_) {
console.log("sip3");
// Register callbacks to desired call events
// 判断是否注册
if (this.isRegister) {
}
let options = {
eventHandlers: this.eventHandlers,
mediaConstraints: {
audio: true,
video: false,
mandatory: { maxWidth: 640, maxHeight: 360 },
},
};
// 后端请求返回的参数格式
// let obj = {
// sip: "47.98.172.37:5960",
// wsip: "wss://h5.zzz888.cn:7443",
// username: "26ac631ac14213882738650",
// password: "28ca40e86b27c4a8f1b606587eec80f6",
// uri: `sip:26ac631ac14215171505290@47.98.172.37:5960`,
// };
// console.log("this.config", this.config);
let obj = {
sip: this.config.sip,
wsip: this.config.wssip,
username: this.config.ws_user,
password: this.config.ws_pass,
uri: `sip:${this.config.ws_user}@${this.config.sip}`,
};
this.ua.call("sip:fs" + this.phoneNum + "@" + obj["sip"], options);
},
//sip 配置
getUa() {},
// 调用接口,获取sip信息
async getSipInfo(phone) {
console.log("sip11111111");
// this.phoneNum = "13882738650";
// 获取主叫号码
let activePhone = localStorage.getItem("ms_username");
let parentId = localStorage.getItem("parentId");
let params = {
activePhone: activePhone,
passivePhone: this.phoneNum,
parentId,
};
let res = await callAppBind(params);
if (res.data.statusCode == "00000") {
Message.success("sip拨打成功");
} else if (res.data.statusCode != "00000") {
Message.error(res.data.message);
} else {
Message.error(res.data.data);
}
this.config = res.data.data;
},
// 注册sip
callPhone() {
console.log("sip2");
// debugger;
// 拨打号码
// this.getSipInfo()
JsSIP.C.SESSION_EXPIRES = 120;
JsSIP.C.MIN_SESSION_EXPIRES = 120;
let obj = {
sip: this.config.sip,
wsip: this.config.wssip,
username: this.config.ws_user,
password: this.config.ws_pass,
uri: `sip:${this.config.ws_user}@${this.config.sip}`,
};
this.eventHandlers = {
progress: function (e) {
console.log("call is in progress");
},
failed: function (e) {
console.log("call failed: ", e);
},
ended: function (e) {
console.log("call ended : ", e);
},
confirmed: function (e) {
console.log("call confirmed");
},
};
// 配置信息
if (this.ua) {
this.ua.stop();
}
this.socket = new JsSIP.WebSocketInterface(obj.wsip);
// const socket = new this.JsSIP.WebSocketInterface(obj.wsip);
const configuration = {
sockets: [this.socket],
uri: obj.uri,
password: obj.password,
register: false, //指示JsSIP用户代理是否应在启动时自动注册
//utbound_proxy_set: obj.wsip,
contact_uri:
"sip:" +
obj["username"] +
"@" +
obj["sip"] +
";transport=" +
(obj["wsip"].substr(0, 3) == "wss" ? "wss" : "ws"),
};
console.log("configuration", configuration);
// 创建UA
let options = {
eventHandlers: this.eventHandlers,
mediaConstraints: { audio: true, video: false },
};
this.ua = new JsSIP.UA(configuration);
console.log("call号码", "sip:bob" + "18398754423" + "@" + obj["sip"]);
// this.ua.call("sip:fs" + "17381586338" + "@" + obj["sip"], options);
// 状态回调
this.ua.on("connected", function (e) {
console.log("--------已连接---------");
});
this.ua.on("disconnected", function (e) {
console.log("-------未连接--------");
});
this.ua.on("registered", function (e) {
console.log("--------已注册-------");
// this.isRegister = true;
// this.ua.unregister((options = null));
this.registerState = true;
});
this.ua.on("unregistered", function (e) {
console.log("------未注册--------");
this.registerState = false;
});
this.ua.on("registrationFailed", (e) => {
// this.$message.error("SIP注册失败,请联系管理员");
});
//客户接到了电话才会走这里
this.ua.on("newRTCSession", (e) => {
let audio = document.getElementById("audio");
let session = e.session;
let peerconnection = session.connection;
if (e.originator === "local") {
// addstream方法已被淘汰,所以现在不适用了,但还是可以正常使用
// peerconnection.addEventListener("addstream", (event) => {
// // try {
// audio.srcObject = event.stream;
// console.log("打电话", event.stream);
// // } catch (e) {
// // console.log(e);
// // }
// });
peerconnection.ontrack = (event) => {
audio.srcObject = event.streams[0];
// console.log("打电话", event.stream);
};
} else {
let callers = session.remote_identity.uri.user;
//emitter.setCallinStatus.call(true, callers);
}
// 接听失败
session.on("failed", (mdata) => {
//emitter.setCallinStatus.call(false);
// console.log("来电的时候 拒接或者 还没接听对方自己就挂断了");
});
// 接听成功
session.on("accepted", (response, cause) => {
console.log("接听成功");
// 可以在这里执行媒体流的操作
});
// 接听成功后 挂断
session.on("ended", () => {
console.log("接听结束");
});
// 通话被挂起
session.on("hold", (data) => {
let org = data.originator;
if (org === "local") {
// console.log("通话被本地挂起:", org);
} else {
// console.log("通话被远程挂起:", org);
}
});
// 通话被继续
session.on("unhold", (data) => {
let org = data.originator;
if (org === "local") {
console.log("通话被本地继续:", org);
} else {
console.log("通话被远程继续:", org);
}
});
e.session.on("confirmed", (e) => {
console.log("--------------------------电话接通,开始通话");
});
e.session.on("ended", (e) => {
console.log("---------------------------电话已挂断,开始问卷");
});
//绑定回调后先给坐席‘滴’一声
// e.session.answer();
});
// 登陆
this.ua.start();
// 登出
// ua.stop();
},
},
};
</script>