highlight: a11y-light
theme: juejin
摘要:最近在做一个freeSwitch项目,前端需要通过sip协议完成音视频通话,把一些关键的核心api记录一下;因为网上找的一部分资料不一定准确,这个是实际操作过得具有一定的参考性;基本复制粘贴可快速完成直连freeSwitch的目的;更新日期2022-10-19;
前端sip这块使用的是jssip(版本3.9.1),详细api可以去官网查看;freeSwitch(1.10.7)这个我们只做参考;
注册
js let ws = '你的freeSwitch直连地址'; let socket = new JsSIP.WebSocketInterface(ws); let configuration = { sockets: [socket], uri: `sip:${username}@${serverip}`, password: '', authorizationUser: '', sessionTimersExpires: 5000 } let UA = new JsSIP.UA(configuration); // 存储当前session和connection let currentSession = null; let currentConnection = null;
绑定事件
```js // 开始尝试连接 UA.on('connecting', () => {}); // 连接完毕 UA.on('connected', () => {}); // 主动取消注册或注册后定期重新注册失败 UA.on('unregistered', () => {}); // 注册失败 UA.on('registrationFailed', () => {}); // 注册成功 UA.on('registered', () => {}); // websocket 连接失败 UA.on('disconnected', () => {}); // 新消息接收和发送消息 UA.on('newMessage', (res) => { console.log('接收和发送消息', res, '消息字段', res.data); if (res.originator === 'remote') { // 远程消息 } else if ( res.originator === 'local') { // 本地消息 }; }); // 这一块处理webRTC音视频逻辑 UA.on('newRTCSession', (res) => { let { session, originator } = res; // 远程来电 if (originator === 'remote') { // 处理接听逻辑 handleAnswerWebRTCSession(session); } else if (originator === 'local') { // 处理呼叫逻辑 handleCallWebRTCSession(session); }; });
```
newRTCSession处理
```js // 处理接听newRTCSession function handleAnswerWebRTCSession(session) { /* session 要单独存下,后面接听挂断需要 挂断: session.terminate(); 接听:session.answer({'mediaConstraints': { 'audio': true, 'video': true }}) */ let {connection} = session; let currentSession = session; let currentConnection = connection; // 来电-被接听了 session.on("accepted", () => {
handleStreamsSrcObject(connection); }); session.on("peerconnection", () => {}); // 来电=>自定义来电弹窗,让用户选择接听和挂断 session.on("progress", () =>{}); // 挂断-来电已挂断 session.on("ended", () => {}); // 当会话无法建立时触发 session.on("failed", () => {}); } // 处理呼叫newRTCSession function handleCallWebRTCSession(session){ /* session 要单独存下挂断需要 挂断: session.terminate(); connection 如果后面音视频做禁音,或者解绑轨道,或共享桌面需要 */ let {connection} = session; let currentSession = session; let currentConnection = connection; session.on('confirmed', () => { handleStreamsSrcObject(connection); }); } // 处理媒体流 handleStreamsSrcObject(connection) { if (connection.getRemoteStreams().length > 0) { // 获取远程媒体流 let srcObject = connection.getRemoteStreams()[0];
};
if (connection.getLocalStreams().length > 0) {
// 获取本地媒体流
let srcObject = connection.getLocalStreams()[0];
};
} ```
挂断/接听/呼叫
js hangUp() { currentSession.terminate(); }, answer(){ currentSession.answer({'mediaConstraints': { 'audio': true, 'video': true }}) } call(target, options = {isAudio: true, isVideo: true}, callback) { let url = `sip:${target}@${serverip}`; var eventHandlers = { // 在接收或生成对INVITE请求的1XX SIP类响应(> 100)时触发 'progress': function(data){ callback('呼叫中'); }, // 会议无法建立时触发 'failed': function(data){ callback('无法建立', data); }, // 通话确认(ACK收/发)时触发 'confirmed': function(data){ callback('已接听', data); }, // 当已建立的通话结束时触发 'ended': function(){ callback('通话结束了'); } } this.UA.call(url, { 'eventHandlers': eventHandlers, 'mediaConstraints': { 'audio': options.isAudio, 'video': options.isVideo}, }); }
屏幕共享-结束共享后切换原摄像头
js // 共享桌面 desktopSharing(){ let peerConnection = currentConnection; navigator.mediaDevices.getDisplayMedia({video: true, audio: true}).then((disStream) => { let srcObject = disStream; peerConnection.getSenders().forEach((sender) => { if (sender.track.kind == 'video') { var res = sender.replaceTrack(disStream.getVideoTracks()[0]); console.log(res); }; }); // 监听屏幕共享 disStream.getVideoTracks()[0].addEventListener('ended', () => { // '屏幕共享结束, 准备切换为本地摄像头 this.switchLocalCamera(peerConnection); }); }).catch((error) => { console.error('屏幕共享失败,失败原因:', error); }); } // 手动结束屏幕共享 endDesktopSharing() { let disStream = currentConnection.getLocalStreams()[0]; disStream.getVideoTracks().forEach((track) => { track.stop(); }); } // 切换为本地摄像头 switchLocalCamera() { var constraints = { audio: { autoGainControl: true, // 噪音消除 noiseSuppression: true, // 设置降噪 echoCancellation: true }, video: true } navigator.mediaDevices.getUserMedia(constraints).then((stream) => { let srcObject = stream; peerConnection.getSenders().forEach((sender) => { if (sender.track.kind == 'video') { sender.replaceTrack(stream.getVideoTracks()[0]) } }); }).catch((error) => { console.error('切换摄像头失败,失败原因:', error); }); }
视频上传分辨率
js // 视频上传分辨率 settingsScreenSize(constraints = { width: {exact: 50}, height: {exact: 50}}) { let tmpLocal = currentConnection.getLocalStreams()[0]; const videoTrack = tmpLocal.getVideoTracks()[0]; videoTrack.applyConstraints(constraints).then(() => { console.log('动态改变分辨率'); }); }
开启/关闭音频
js // 禁音 mute(){ let pc = currentConnection; if (pc.getSenders) { pc.getSenders().forEach((sender) => { // 如果是视频的话 kind === 'video' if (sender.track.kind === 'audio') { sender.track.enabled = false } }) } else { pc.getLocalStreams().forEach((stream) => { stream.getAudioTracks().forEach((track) => { // 如果是视频的话 kind === 'video' if (track.kind === 'audio') { track.enabled = false; } }) }); } } // 解除禁音 unmute(){ var pc = currentConnection; if (pc.getSenders) { pc.getSenders().forEach(function (sender) { // 如果是视频的话 kind === 'video' if (sender.track.kind === 'audio') { sender.track.enabled = true; } }); } else { pc.getLocalStreams().forEach(function (stream) { stream.getAudioTracks().forEach(function (track) { // 如果是视频的话 kind === 'video' if (track.kind === 'audio') { track.enabled = true; } }); }); } }