Aucune description

mobileSip.js 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. var outgoingSession = null,
  2. timeInterval = null,
  3. incomingSession = null,
  4. currentSession = null,
  5. calState, nativeStream = null,
  6. muteSession = null,
  7. callVideoState = null,
  8. callVideoFail = null,
  9. dbCallVideoState = null;
  10. var bigVideoStyle = {
  11. "background-color": "#000000",
  12. "width": "100%",
  13. "height": "100%",
  14. "top": "0",
  15. "right": "0",
  16. "z-index": "1"
  17. }
  18. var smallVideoStyle = {
  19. "background-color": "#EEEEEE",
  20. "width": "20%",
  21. "height": "20%",
  22. "top": "10px",
  23. "right": "10px",
  24. "z-index": "2"
  25. }
  26. var localStream = null,
  27. userAgent = null,
  28. superviseMan = null,
  29. videoInputDevices = [],
  30. currentVideoIndex = 0,
  31. timer = null,
  32. oppoStream = null,
  33. peerData = null,
  34. timeOut = null;
  35. var front = true;
  36. var myVideoView = document.getElementById('myVideo'); //我的本地视频
  37. var yourVideoView = document.getElementById('yourVideo'); //对方视频信息
  38. var getWidthScale = document.documentElement.clientWidth;
  39. $(function() {
  40. $(".videoMy video").css({
  41. "width": getWidthScale * 1.5 * 0.2 + "px",
  42. "margin-left": -getWidthScale * 1.5 * 0.2 / 2 + "px"
  43. })
  44. // mediumInfo() //获取摄像头信息
  45. })
  46. //视频窗口大小调整
  47. function videoSize() {
  48. if(meetingState) {
  49. $(".videoYour video").css({
  50. "width": getWidthScale * 1 + "px",
  51. "margin-left": -getWidthScale * 1 / 2 + "px"
  52. })
  53. meetingState = false
  54. } else {
  55. $(".videoYour video").css({
  56. "width": getWidthScale * 2.7 + "px",
  57. "margin-left": -getWidthScale * 2.7 / 2 + "px"
  58. })
  59. }
  60. }
  61. //切换摄像头
  62. function shotCut() {
  63. front = !front
  64. try {
  65. // var ls = muteSession.connection.getLocalStreams()[0];
  66. //
  67. // //将传输的媒体流直接关闭掉
  68. // ls.getTracks().forEach(function(track) {
  69. // if(track.kind == 'video') {
  70. // track.stop();
  71. // }
  72. // });
  73. //关闭本地媒体流
  74. if(nativeStream) {
  75. nativeStream.getVideoTracks()[0].stop();
  76. }
  77. navigator.mediaDevices.getUserMedia({
  78. audio: false, //这里是为了摄像头切换时,回显本地媒体,禁止本地声音
  79. video: { facingMode: (front? "user" : "environment") }
  80. }).then((stream) => {
  81. const tracks = stream.getVideoTracks()[0]; //摄像头切换只替换视频通道
  82. var sender = muteSession.connection.getSenders().find(function(s) {
  83. return s.track.kind == tracks.kind;
  84. });
  85. sender.replaceTrack(tracks);
  86. //本地摄像头切换
  87. nativeStream = stream
  88. myVideoView.srcObject = nativeStream;
  89. //微信端浏览器适配,需要先stop,然后执行这个函数
  90. myVideoView.onloadedmetadata = function() { //媒体流直接关闭掉后这里会重新执行
  91. if(nativeStream.active) { //在这里需要做判断,判断媒体流处于活动状态
  92. myVideoView.play();
  93. }
  94. }
  95. }).catch((res) => {
  96. alert('getUserMedia() error1: ' + res.name);
  97. })
  98. } catch(e) {
  99. alert(JSON.stringify(e));
  100. }
  101. }
  102. //监测当前传输速率
  103. function monitoringData() {
  104. //发送的数据
  105. muteSession.connection.getSenders().forEach(getData => {
  106. if(getData.track.kind === "audio") {
  107. getData.getStats().then(reports => {
  108. reports.forEach(report => {
  109. if(report.type === "outbound-rtp") {
  110. console.log("发送的数据audio===" + report.bytesSent)
  111. }
  112. })
  113. })
  114. } else if(getData.track.kind === "video") {
  115. getData.getStats().then(reports => {
  116. reports.forEach(report => {
  117. if(report.type === "outbound-rtp") {
  118. console.log("发送的数据video===" + report.bytesSent)
  119. // $(".senderSpeedVideo").text(report.bytesSent)
  120. }
  121. })
  122. })
  123. }
  124. })
  125. //接收的数据
  126. muteSession.connection.getReceivers().forEach(getData => {
  127. if(getData.track.kind === "audio") {
  128. getData.getStats().then(reports => {
  129. reports.forEach(report => {
  130. if(report.type === "inbound-rtp") {
  131. console.log("接收的数据audio===" + report.bytesReceived)
  132. // $(".receiverSpeedAudio").text(report.bytesReceived)
  133. }
  134. })
  135. })
  136. } else if(getData.track.kind === "video") {
  137. getData.getStats().then(reports => {
  138. reports.forEach(report => {
  139. if(report.type === "inbound-rtp") {
  140. console.log("接收的数据video===" + report.bytesReceived)
  141. // $(".receiverSpeedVideo").text(report.bytesReceived)
  142. $(".receiverSpeedVideo").show()
  143. $(".refreshImg").show()
  144. $(".receiverSpeedVideo").text("黑屏重置按钮")
  145. setTimeout(function() {
  146. $(".receiverSpeedVideo").hide()
  147. }, 5000)
  148. }
  149. })
  150. })
  151. }
  152. })
  153. }
  154. $(".internetSpeed img").click(function() {
  155. yourVideoView.play();
  156. })
  157. //获取摄像头,音频设备信息
  158. function mediumInfo() {
  159. navigator.mediaDevices.enumerateDevices()
  160. .then(gotDevices).catch(handleError);
  161. function gotDevices(deviceInfos) {
  162. deviceInfos.forEach(function(n) {
  163. if(n.kind === 'videoinput') {
  164. videoInputDevices.push(n);
  165. }
  166. })
  167. }
  168. function handleError(error) {
  169. //alert(JSON.stringify(error));
  170. }
  171. }
  172. //注册成功事件
  173. function registered() {
  174. $(".registStatus").show()
  175. if($(".superviseBtn").text() == "我是市民") {
  176. VideoReqBindAgent()
  177. } else if($(".superviseBtn").text() == "我是督办专员") {
  178. $(".registStatus").text("连接成功")
  179. $("#account").val(extenNum)
  180. }
  181. }
  182. //电话呼入事件
  183. function newRTCSession() {
  184. $(".mui-content").hide();
  185. $(".callHtml").show();
  186. $(".dropIcon").hide()
  187. $(".answerIcon").show();
  188. if(!nativeStream) {
  189. setTimeout(function(){
  190. // captureLocalMediaVideo()
  191. },3000)
  192. }
  193. localMediaStream()
  194. }
  195. //确认呼叫后触发
  196. function confirmed() {
  197. $(".dropIcon").show()
  198. $(".answerIcon").hide()
  199. // captureLocalMediaVideo()
  200. //延时函数
  201. timeOut = setTimeout(function() {
  202. monitoringData()
  203. }, 10000)
  204. }
  205. //peerconnection 媒体传输事件
  206. function peerconnection() {
  207. $(".videoYour").show();
  208. $(".customerImg").show();
  209. $(".rightImg").hide();
  210. }
  211. //点击对方视频框
  212. $(".videoYour").click(function() {
  213. if(!$(this).attr("index")) {
  214. $(this).css(bigVideoStyle)
  215. $(".videoYour video").css({
  216. "width": getWidthScale * 2.7 + "px",
  217. "margin-left": -getWidthScale * 2.7 / 2 + "px"
  218. })
  219. $(".videoMy").css(smallVideoStyle)
  220. $(".videoMy video").css({
  221. "width": getWidthScale * 1.5 * 0.2 + "px",
  222. "margin-left": -getWidthScale * 1.5 * 0.2 / 2 + "px"
  223. })
  224. $(this).attr("index", "1")
  225. $(".videoMy").removeAttr("index")
  226. }
  227. })
  228. //点击我方视频框
  229. $(".videoMy").click(function() {
  230. if(!$(this).attr("index")) {
  231. $(this).css(bigVideoStyle)
  232. $(".videoMy video").css({
  233. "width": getWidthScale * 1.5 + "px",
  234. "margin-left": -getWidthScale * 1.5 / 2 + "px"
  235. })
  236. $(".videoYour").css(smallVideoStyle)
  237. $(".videoYour video").css({
  238. "width": getWidthScale * 2.7 * 0.2 + "px",
  239. "margin-left": -getWidthScale * 2.7 * 0.2 / 2 + "px"
  240. })
  241. $(this).attr("index", "1")
  242. $(".videoYour").removeAttr("index")
  243. }
  244. })
  245. //视频呼叫
  246. $(".videoCall").click(function() {
  247. if($(".registStatus").text() == "已注销") {
  248. alert("话机已注销,如有需要请重新进入")
  249. return
  250. }
  251. callVideoState = true;
  252. callVideoFail = true;
  253. calState = $(this).attr("data-attr");
  254. $(".videoMy").show();
  255. $(".answerIcon").hide()
  256. $(".leftText").html("连接中");
  257. videoSize(); //视频窗口大小
  258. // if(!nativeStream) {
  259. // captureLocalMediaVideo()
  260. // }
  261. localMediaStream(); //获取本地媒体流
  262. })
  263. //静音
  264. $(".muteBtn").click(function() {
  265. muteSession.mute({
  266. 'audio': true, // Local audio is muted
  267. })
  268. $(".muteBtn").hide();
  269. $(".unMuteBtn").show();
  270. })
  271. //取消静音
  272. $(".unMuteBtn").click(function() {
  273. muteSession.unmute({
  274. 'audio': true, // Local audio is muted
  275. })
  276. $(".muteBtn").show();
  277. $(".unMuteBtn").hide();
  278. })
  279. //关闭摄像头 摄像头切换时会遇到问题
  280. $(".cameraBtn").click(function() {
  281. muteSession.mute({
  282. 'video': true, // Local audio is muted
  283. })
  284. $(".cameraBtn").hide();
  285. $(".unCameraBtn").show();
  286. })
  287. //取消关闭摄像头
  288. $(".unCameraBtn").click(function() {
  289. muteSession.unmute({
  290. 'video': true, // Local audio is muted
  291. })
  292. $(".cameraBtn").show();
  293. $(".unCameraBtn").hide();
  294. })
  295. //视频呼叫
  296. function videoCall() {
  297. $(".mui-content").hide();
  298. $(".callHtml").show();
  299. var sip_phone_number_ = callNum.toString();
  300. var options = {
  301. 'eventHandlers': eventHandlers,
  302. 'mediaConstraints': {
  303. 'audio': true,
  304. 'video': true
  305. },
  306. 'mediaStream': localStream
  307. };
  308. callVideoState = false;
  309. dbCallVideoState = false;
  310. outgoingSession = userAgent.call(sip_phone_number_, options);
  311. }
  312. //接听
  313. function answerCall() {
  314. clearTimeout(timeInterval)
  315. videoSize(); //视频窗口大小
  316. if(incomingSession) {
  317. incomingSession.answer({
  318. 'mediaConstraints': {
  319. 'audio': true,
  320. 'video': true
  321. },
  322. 'mediaStream': localStream
  323. });
  324. incomingSession = null;
  325. }
  326. }
  327. //挂断
  328. function hangupCall() {
  329. $(".receiverSpeedVideo").text("")
  330. clearTimeout(timeOut)
  331. clearInterval(timer)
  332. closeMediaVideo()
  333. $(".refreshImg").hide()
  334. userAgent.terminateSessions();
  335. console.log('挂断----------->');
  336. if($(".superviseBtn").text() == "我是督办专员") {
  337. $(".mui-content").show();
  338. $(".callHtml").hide();
  339. videoDesExten()
  340. } else if($(".superviseBtn").text() == "我是市民") {
  341. if(acceptExtenNum) {
  342. window.location.href = "acceptingMan.html?extenNum=" + $("#account").val() +
  343. "&telephone=" + $("#telephone").val() +
  344. "&mathRanDom=" + Math.random() +
  345. "&acceptExtenNum=" + acceptExtenNum
  346. } else {
  347. window.location.href = "acceptingMan.html?extenNum=" + $("#account").val() +
  348. "&telephone=" + $("#telephone").val() +
  349. "&mathRanDom=" + Math.random()
  350. }
  351. }
  352. }
  353. //注销
  354. function unReg() {
  355. // window.localStorage.setItem('mathRanDom',Math.random())
  356. if(!superviseMan) {
  357. userAgent.unregister(true);
  358. $(".registStatus").html("已注销")
  359. $(".mui-content").show();
  360. $(".callHtml").hide();
  361. $(".dropIcon").hide();
  362. $(".answerIcon").hide();
  363. console.log('注销----------->');
  364. } else {
  365. if(acceptExtenNum) {
  366. window.location.href = "acceptingMan.html?extenNum=" + $("#account").val() +
  367. "&telephone=" + $("#telephone").val() +
  368. "&mathRanDom=" + Math.random() +
  369. "&acceptExtenNum=" + acceptExtenNum
  370. } else {
  371. window.location.href = "acceptingMan.html?extenNum=" + $("#account").val() +
  372. "&telephone=" + $("#telephone").val() +
  373. "&mathRanDom=" + Math.random()
  374. }
  375. }
  376. }
  377. //开启本地摄像头
  378. function captureLocalMediaVideo() {
  379. navigator.mediaDevices.getUserMedia({
  380. video: {
  381. deviceId: videoInputDevices[currentVideoIndex].deviceId
  382. },
  383. audio: false
  384. }).then((stream) => {
  385. nativeStream = stream;
  386. if(nativeStream) {
  387. myVideoView.srcObject = nativeStream;
  388. myVideoView.play();
  389. myVideoView.onloadedmetadata = function() {
  390. if(nativeStream.active) { //在这里需要做判断
  391. myVideoView.play();
  392. }
  393. }
  394. }
  395. }).catch((res) => {
  396. alert('getUserMedia() error2: ' + res.name);
  397. })
  398. }
  399. //获取本地媒体流
  400. function localMediaStream() {
  401. navigator.mediaDevices.getUserMedia({
  402. // video: { deviceId: videoInputDevices[currentVideoIndex].deviceId },
  403. video: { facingMode: "user" },
  404. // video: { facingMode: { exact: "environment" } }
  405. audio: true
  406. }).then((stream) => {
  407. localStream = stream;
  408. mediumInfo()
  409. setTimeout(function(){
  410. if(localStream) {
  411. myVideoView.srcObject = localStream;
  412. myVideoView.muted = true;
  413. // myVideoView.onloadedmetadata = function() {
  414. // if(localStream.active) { //在这里需要做判断
  415. // myVideoView.play();
  416. // }
  417. // }
  418. }
  419. },3000)
  420. if(callVideoState) {
  421. extenBind()
  422. }
  423. if(dbCallVideoState) {
  424. videoReqIdeAgent()
  425. }
  426. }).catch((res) => {
  427. setTimeout(function() {
  428. localMediaStream()
  429. }, 1000)
  430. })
  431. }
  432. // 关闭摄像头
  433. function closeMediaVideo() {
  434. if(nativeStream) {
  435. nativeStream.getTracks().forEach(function(track) {
  436. track.stop();
  437. });
  438. }
  439. if(localStream) {
  440. localStream.getTracks().forEach(function(track) {
  441. track.stop();
  442. });
  443. }
  444. if(oppoStream) {
  445. oppoStream.getTracks().forEach(function(track) {
  446. track.stop();
  447. });
  448. }
  449. }
  450. function sipCallRTCSession(e, state) {
  451. console.log(e.session)
  452. console.log(state) // 1是呼出,2是呼入
  453. e.session.on("confirmed", function(data){
  454. if(e.session.connection.getReceivers){
  455. peerconnection()
  456. callVideoFail = false
  457. console.log(e.session.connection.getReceivers())
  458. remoteStream = new MediaStream();
  459. e.session.connection.getReceivers().forEach(element => {
  460. // track可能一个音轨或者视频轨迹
  461. remoteStream.addTrack(element.track)
  462. })
  463. $(".videoYour").empty();
  464. $(".videoYour").append('<video id="yourVideo" autoplay ></video>');
  465. yourVideoView = document.getElementById('yourVideo');
  466. videoSize()
  467. yourVideoView.srcObject = remoteStream
  468. yourVideoView.setAttribute('autoplay','true');
  469. yourVideoView.setAttribute('playsinline','true');
  470. yourVideoView.setAttribute('controls','true');
  471. yourVideoView.setAttribute('muted','true');
  472. yourVideoView.play();
  473. // yourVideoView.onloadedmetadata = function() {
  474. // yourVideoView.play();
  475. // yourVideoView.muted = false;
  476. // state===1?console.log("呼叫成功"):console.log("接听成功")
  477. // }
  478. }
  479. })
  480. }
  481. function testStart() {
  482. console.info("get input info: sip_uri = ", sip_uri_, " sip_password = ", sip_password_, " ws_uri = ", ws_uri_);
  483. var socket = new JsSIP.WebSocketInterface(ws_uri_);
  484. var configuration = {
  485. sockets: [socket],
  486. outbound_proxy_set: ws_uri_,
  487. uri: sip_uri_, //与用户代理关联的SIP URI(字符串)。这是您的提供商提供给您的SIP地址
  488. password: sip_password_, //SIP身份验证密码
  489. contact_uri: 'sip:' + $("#account").val() +contact_uri,
  490. register: true, //指示启动时JsSIP用户代理是否应自动注册
  491. session_timers: false, //启用会话计时器(根据RFC 4028)
  492. register_expires: 60,
  493. };
  494. userAgent = new JsSIP.UA(configuration);
  495. // JsSIP.debug.enable('JsSIP:*');
  496. JsSIP.debug.disable('JsSIP:*');
  497. //注册成功
  498. userAgent.on('registered', function(data) {
  499. console.info("registered: ", data.response.status_code, ",", data.response.reason_phrase);
  500. registered();
  501. });
  502. //注册失败
  503. userAgent.on('registrationFailed', function(data) {
  504. console.log('注册失败----------->');
  505. });
  506. //在注册到期前几秒钟触发,可在这里重新注册
  507. userAgent.on('registrationExpiring', function() {
  508. // console.log('注册到期----------->');
  509. // userAgent.register(); //重新注册执行的函数
  510. });
  511. //每次传输连接尝试均触发。
  512. userAgent.on('connecting ', function(data) {
  513. console.log('建立连接----------->');
  514. })
  515. //建立传输连接时触发。
  516. userAgent.on('connected ', function(data) {
  517. console.log('连接成功----------->');
  518. })
  519. //当传输连接尝试(或自动重新尝试)失败时触发。。
  520. userAgent.on('disconnected ', function(data) {
  521. console.log('连接失败----------->')
  522. })
  523. //为传入或传出会话/呼叫激发。
  524. userAgent.on('newRTCSession', function(data) {
  525. console.log('建立会话----------->')
  526. console.info('onNewRTCSession: ', data);
  527. muteSession = data.session
  528. var originator = data.originator;
  529. var session = data.session;
  530. var request = data.request;
  531. if(data.originator == 'remote') {
  532. console.log("电话呼入----------->"); //电话呼入
  533. sipCallRTCSession(data, 2)
  534. incomingSession = data.session;
  535. newRTCSession();
  536. timeInterval = setInterval(function() {
  537. if(incomingSession.isEnded()) {
  538. unReg()
  539. clearTimeout(timeInterval)
  540. }
  541. }, 2000)
  542. } else {
  543. sipCallRTCSession(data, 1)
  544. outgoingSession = data.session;
  545. outgoingSession.on('connecting', function(data) {
  546. currentSession = outgoingSession;
  547. outgoingSession = null;
  548. });
  549. }
  550. //接受呼叫时激发
  551. data.session.on('accepted', function(data) {
  552. console.info("接受呼叫----------->");
  553. if(data.originator == 'remote' && currentSession == null) {
  554. currentSession = incomingSession;
  555. incomingSession = null;
  556. }
  557. });
  558. //确认呼叫后激发
  559. data.session.on('confirmed', function(data) {
  560. console.info("确认呼叫----------->");
  561. $(".leftText").html("通话中");
  562. confirmed()
  563. if(data.originator == 'remote' && currentSession == null) {
  564. currentSession = incomingSession;
  565. incomingSession = null;
  566. }
  567. });
  568. //SDP传递之前激发。可以修改传入和传出SDP的机制。
  569. data.session.on('sdp', function(data) {
  570. console.info("sdp消息----------->");
  571. });
  572. //接收或生成请求的SIP类响应时激发。该事件在SDP处理之前触发,以便在需要时对其进行微调,甚至通过删除数据对象中响应参数的主体来删除它
  573. data.session.on('progress', function(data) {
  574. console.info("接收到sip响应----------->");
  575. if(originator != 'remote') {
  576. $(".dropIcon").show()
  577. }
  578. });
  579. });
  580. //连接到信令服务器,并恢复以前的状态,如果以前停止。重新开始时,如果UA配置中的参数设置为register:true,则向SIP域注册。
  581. userAgent.start();
  582. //网页中有些不稳定,所以开启了定时注册
  583. timer = setInterval(function() {
  584. userAgent.register(); //注册到期时,重新注册,这个函数不会影响,话机状态
  585. console.log("重新注册了----------->")
  586. }, 30 * 1000)
  587. }
  588. var eventHandlers = {
  589. 'progress': function(e) {
  590. console.log('接收到响应----------->');
  591. },
  592. 'failed': function(e) {
  593. if(callVideoFail) {
  594. // setTimeout(function() {
  595. // videoCall()
  596. // }, 2000)
  597. }
  598. console.log('呼叫失败');
  599. },
  600. 'ended': function(e) {
  601. console.log('通话结束----------->');
  602. unReg();
  603. },
  604. 'confirmed': function(e) {
  605. console.log('确认呼叫----------->');
  606. }
  607. };