Keine Beschreibung

mobileSip.js 18KB

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