
朋友,今天我想和你谈电话。 对于某些人来说,这是一个全新的话题。 对于其他人来说,这纯粹是“可以将Skype发送给我吗?” 第三,突然需要生活。 最后一个选项是我们的选项。
在本文中,我将向您展示一个很小但非常可行的实现示例,该示例将使您能够真正地制作自己的WEB拨号器,并直接从浏览器中调用数十个javascript行的膝盖来直接呼叫朋友。
关于技术和协议

院子里有2019年,令我们高兴的是,已经有一个现成的工具,用于实现Web的实时通讯(RTC),即WebRTC 。 几年前,他正在积极发展。 该API仍在最终确定中,但是该技术已成为事实上的标准,并且在大多数流行的浏览器中都受支持。 在本文中,我们不会只讨论技术本身,您可以在开发人员网站上阅读更多内容,或在中心上查找文章。 例如, 这里 。
但是在我们开始之前,我想澄清几点。
- 首先,WebRTC在一系列协议的基础上运行,甚至对于p2p通信,您都将需要某种服务器,客户端可以通过该服务器找到彼此的朋友。 我们的示例将使用SIP协议,您可以在此处了解更多信息。
- 您将需要一台支持以上所有内容的服务器-例如FreeSwitch或Asterisk。
这些内容不在本文讨论范围之内。 我们将假定您和我们一样幸运,并且您已经可以配置VoIP电话。
好吧,本文最长的部分在后面,让我们编写代码!
页面布局

首先,我们需要一个用于调用的页面,用于输入用户名,密码,电话号码和几个按钮的字段。 在最简单的版本中,它将如下所示:
<div class="container"> <div class="input-group mb-6"> <div class="input-group-prepend"> <span class="input-group-text">Login</span> </div> <input id="loginText" type="text" class="form-control"> <div class="input-group-prepend"> <span class="input-group-text">Password</span> </div> <input id="passwordText" type="password" class="form-control"> <button id="loginButton" type="button" class="btn btn-primary" onclick="login()">Login</button> <button id="logOutButton" type="button" class="btn btn-primary d-none" onclick="logout()">LogOut</button> </div> <div class="input-group mb-6 d-none" id="callPanel"> <input id="callNumberText" type="text" class="form-control" placeholder="Call number"> <button id="callNumberButton" type="button" class="btn btn-success" onclick="call()">Call</button> <button id="hangUpButton" type="button" class="btn btn-danger d-none" onclick="hangUp()">Hang Up</button> </div> <audio id="localAudio" autoPlay muted></audio> <audio id="remoteAudio" autoPlay></audio> <audio id="sounds" autoPlay></audio> </div>
音频元素将“发送”和“接收”声音,并通过声音播放拨号声音以达到美丽的目的。
用户界面已准备就绪,您无法找到UX的问题,让它正常工作。
我们紧固JSSIP

我们将使用一个库,其中已实现了所需的所有内容-JSSIP 。 您可以查看文档:一切都已详细描述,甚至有现成的实现示例。 也就是说,我们实际上不需要执行任何操作-只需尽可能简化所有操作并弄清楚是什么。
输入登录名/密码(必须在电话服务器上注册)后,您需要登录到服务器。 我们这样做:
socket = new JsSIP.WebSocketInterface("wss://webrtcserver:port/ws"); _ua = new JsSIP.UA( { uri: "sip:" + this.loginText.val() + "@webrtcserver", password: this.passwordText.val(), display_name: this.loginText.val(), sockets: [socket] });
在此过程中,您可以订阅连接事件和连接事件,并在那里做一些有用的事情。 但是,让我们继续进行注册事件:
his._ua.on('registered', () => { console.log("UA registered"); this.loginButton.addClass('d-none'); this.logOutButton.removeClass('d-none'); this.loginText.prop('disabled', true); this.passwordText.prop('disabled', true); $("#callPanel").removeClass('d-none'); });
在这里,我们只需要更改按钮的状态:显示必要的内容,隐藏不必要的内容。 如果登录突然出现问题,我们将错误记录在日志中:
this._ua.on('registrationFailed', (data) => { console.error("UA registrationFailed", data.cause); });
这足以登录。 仍然需要用
this._ua.start();
如果正确指定了服务器并且接受了您的用户名/密码,则会出现用于输入电话和“呼叫”按钮的字段。
对于日志记录,您需要调用this._ua.stop(),一切都很简单。
打个电话
现在-最重要的是:您需要拨打输入的号码。
this.session = this._ua.call(number, { pcConfig: { hackStripTcp: true,
请注意:我们明确启用了多路复用,还必须在您的服务器上启用此设置。 对于星号,在sip.conf设置中为rtcp_mux = yes。
进一步的交互是基于回调的,在回调中,我们必须确保音频视频流指向相应的页面元素的方向,并以正确的顺序将必要的消息发送到服务器。
总的来说,一切都很合乎逻辑。 拨打['progress']时-播放拨号声音。 在我们的示例中,我们播放自己的声音,但正如pvsur正确指出的那样,您也可以从被叫方获取声音,并听到自动通知者的响应,例如“在哔声后留下消息”(如果有)。
一旦我通过['accepted']-播放已回答的声音。 订户拿起电话后,我们将获取其声音流并将其放入remoteAudio ['connecting'和'addstream']元素中。
在调用结束时,请执行closeMediaStream。 你可以放松一下
关于操作的一点点
在测试期间,发现了两件事。
- 在Chrome中,拨号开始时会延迟几秒钟,这很烦人。 我们从日志中发现他去了冰服务器,因为我们有自己的服务器,所以根本不需要冰服务器。 因此,在JSSIP的配置中,我们只是删除了它们,并立即变得更漂亮。 请参阅pcConfig.iceServers和pcConfig.hackStripTcp。
- 我们的星号具有WSS协议,该协议具有用于SIP的加密。 浏览器在使用HTTPS网站时需要这样做。 但是星号使用基于联系参数的WS,其中JSSIP库包含一个硬编码的WS描述符。 图书馆的开发人员同时指出了实际上对此标准没有要求的标准。 翠菊的同事们一直不愿修复任何东西。 一般来说,死胡同。 好吧,这时我们在源代码中找到了this._configuration.contact_uri = new URI(...)行,将transport:'ws'更改为transport:'wss'并继续享受生活。
通常,该示例已准备就绪,您可以接听并致电。 无需放置任何软件电话或自行开发。 无需担心将此软件部署到客户端汽车上。 只需打开浏览器并致电。
另一个库允许您在音频模式下拨打其他号码。 也就是说,您可以很好地致电例如银行的呼叫中心,然后转到语音菜单上的所需项目。 为此,只需运行以下命令:
this._call.sendDTMF('. ')
关于法卡皮

有几点确实让我感到紧张。
我将这一部分放在本文的讨论范围之外,但是除了拨出电话以外,我们还需要接受拨入电话。 有一段时间,我不得不蹲下一个打来的电话,但后来打断了。 一切都由上面提到的rtcpMuxPolicy设置决定,并在星号上启用了多路复用,但是我们在相当长的一段时间里都很愚蠢。
而且,在同一台计算机上拨打电话和拨打电话时,仍然存在拨号自己的问题。 我记不清了,但是连接已成功建立,也没有错误,也没有声音:)时间用完了,所以我们在这种特殊效果上得分。 但是请记住,在单独的汽车上测试来电更好。
结论
最后,我想指出的是,我们在呼叫中心测试了JSSIP + Asterisk捆绑包,一切正常,至少在chrome中可以正常使用,这完全适合我们。 最主要的是允许浏览器访问媒体设备并在拨号服务器上注册用户。
您可以在github上看到完成的示例。
有用的链接
关于威宝
关于SIP: tyts , tyts
关于星号
Jssip库