Advertisement
Guest User

Untitled

a guest
Jul 21st, 2017
158
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 24.84 KB | None | 0 0
  1. import * as angular from "angular";
  2. import * as SimpleWebRTC from "simplewebrtc";
  3. import { CONSTANTS, ServerConfig } from "../constants";
  4. import { IActiveCall, ICall } from "../models/call";
  5. import { ICallInformation } from "../models/callInformation";
  6. import { CallStatus } from "../models/callStatus";
  7. import { ICallSummaryModel } from "../models/callSummary";
  8. import { IEmptyResponse } from "../models/emptyResponse";
  9. import { ProviderStatus } from "../models/provider";
  10. import { IProviderDetails } from "../models/providerDetails";
  11. import { IUserProfile } from "../models/user";
  12. import { FCMService } from "../services/fcm";
  13. import { CurrentUserService } from "./currentUser";
  14. import { ITrackService } from "./gaService";
  15. import { IHistoryService } from "./historyService";
  16. import { ILocalStorageService } from "./localStorageService";
  17. import { IMenuService } from "./menuService";
  18. import { ProvidersService } from "./providers";
  19. import { SnackbarService } from "./snackbar";
  20.  
  21. declare var NODE_ENV: string;
  22. declare var AudioToggle: AudioToggle;
  23.  
  24. export enum EventType {
  25. VideoPaused,
  26. videoResumed
  27. }
  28.  
  29. export enum InternetConnectionState {
  30. OK,
  31. noConnection,
  32. }
  33.  
  34. export class CallingService {
  35.  
  36. public get webrtc(): SimpleWebRTC {
  37. return this.rtc;
  38. }
  39. private rtc: SimpleWebRTC;
  40. private videoPausedEvents: Array<() => void> = [];
  41. private videoResumedEvents: Array<() => void> = [];
  42. private videoAdded: boolean = false;
  43. private internetConnectionstate: InternetConnectionState;
  44. private previousICEState: any;
  45. private retryInProgress: boolean = false;
  46. private connectionTimeOutStarted: boolean = false;
  47. private timeoutPromise: any;
  48. private retryPromise: any;
  49. private connectionChecker: any;
  50. private previousStats: any;
  51. private previousSent: any;
  52. private peerConnection: any;
  53.  
  54. private senderMsg: any;
  55. private receiverMsg: any;
  56. private retryInterval: any;
  57. private retryCounter: number = 0;
  58.  
  59. constructor(
  60. private $q: ng.IQService,
  61. private $http: ng.IHttpService,
  62. private $translate: ng.translate.ITranslateService,
  63. private $window: ng.IWindowService,
  64. private $timeout: ng.ITimeoutService,
  65. private $interval: ng.IIntervalService,
  66. private fcmService: FCMService,
  67. private localStorageService: ILocalStorageService,
  68. private snackbarService: SnackbarService,
  69. private currentUser: CurrentUserService,
  70. private historyService: IHistoryService,
  71. private providerService: ProvidersService,
  72. private menuService: IMenuService,
  73. private $log: ng.ILogService,
  74. private trackService: ITrackService,
  75. private $rootScope: angular.IRootScopeService,
  76. ) {
  77. fcmService.setCallingService(this);
  78. fcmService.onAcceptAdd(() => {
  79. const callInfo = this.localStorageService.ReadObjectSafely<ICall>(CONSTANTS.LocalStorage.callInfo);
  80. this.trackService.trackRoomEvent("Call", "accept call", "The call was accepted", 1, callInfo.roomId);
  81. });
  82.  
  83. }
  84.  
  85. public initializeCallToProvider(provider: IProviderDetails) {
  86. if (this.localStorageService.ReadObjectSafely<boolean>(CONSTANTS.LocalStorage.isActiveCall)) {
  87. return;
  88. }
  89.  
  90. this.providerService.getStatus(provider.id)
  91. .then((result: ng.IHttpPromiseCallbackArg<number>) => {
  92. let online = result.data;
  93. if (online === ProviderStatus.Online) {
  94. let currentUser = this.localStorageService.ReadObjectSafely<IUserProfile>(CONSTANTS.LocalStorage.userProfile);
  95. let roomId = this.createRoomId();
  96. let callInfo: ICall = {
  97. roomId: roomId,
  98. callerInfo: {
  99. id: currentUser.id,
  100. userId: currentUser.userId,
  101. hasImage: currentUser.hasImage,
  102. name: `${currentUser.firstName} ${currentUser.lastName}`
  103. },
  104. calleeInfo: {
  105. id: provider.id,
  106. userId: provider.userId,
  107. hasImage: provider.hasImage,
  108. name: provider.name
  109. },
  110. isCaller: true,
  111. isCreated: false,
  112. duration: 0
  113. };
  114.  
  115. this.localStorageService.WriteObject<ICall>(CONSTANTS.LocalStorage.callInfo, callInfo);
  116. this.localStorageService.WriteObject<boolean>(CONSTANTS.LocalStorage.isActiveCall, true);
  117. this.trackService.trackRoomEvent("Call", "calling", "Calling provider", 1, callInfo.roomId);
  118. this.historyService.go("waiting");
  119. } else {
  120. this.snackbarService.show({ message: this.$translate.instant("COACH_IS_BUSY") });
  121. }
  122. });
  123. }
  124.  
  125. callAccepted(): void {
  126. let callInfo = this.localStorageService.ReadObjectSafely<ICall>(CONSTANTS.LocalStorage.callInfo);
  127. this.fcmService.sendCallAcceptedNotification(callInfo.callerInfo.userId, callInfo.roomId)
  128. .then(() => {
  129. this.$log.debug("Call accepted");
  130. this.historyService.goReplace("conversation");
  131. })
  132. .then(() => this.readyToCall(callInfo.roomId))
  133. .catch((result: any) => {
  134. this.$log.error("call accepted error: " + JSON.stringify(result));
  135. this.snackbarService.show({ message: this.$translate.instant("CONNECTION_FAILED") });
  136. this.closeConnection();
  137. this.goBack();
  138. });
  139. }
  140.  
  141. callDeclined(): ng.IPromise<{}> {
  142. let deferred = this.$q.defer();
  143. let isActiveCall = this.localStorageService.ReadObjectSafely<Boolean>(CONSTANTS.LocalStorage.isActiveCall);
  144. if (!isActiveCall) {
  145. deferred.reject();
  146. return deferred.promise;
  147. }
  148.  
  149. this.localStorageService.WriteObject(CONSTANTS.LocalStorage.isActiveCall, false);
  150. let callInfo: ICall = this.localStorageService.ReadObjectSafely<ICall>(CONSTANTS.LocalStorage.callInfo);
  151. if (!callInfo.callerInfo) {
  152. this.closeConnection();
  153. deferred.reject();
  154. return deferred.promise;
  155. }
  156.  
  157. this.fcmService.sendCallDeclinedNotification(callInfo.callerInfo.userId, callInfo.roomId, null)
  158. .then(() => {
  159. this.closeConnection();
  160. deferred.resolve();
  161. })
  162. .catch((result: any) => {
  163. this.$log.error("call declined error: " + JSON.stringify(result));
  164. this.snackbarService.show({ message: this.$translate.instant("CONNECTION_FAILED") });
  165. deferred.reject();
  166. });
  167.  
  168. return deferred.promise;
  169. }
  170.  
  171. endCall(): void {
  172. let isActiveCall = this.localStorageService.ReadObjectSafely<boolean>(CONSTANTS.LocalStorage.isActiveCall);
  173. if (!isActiveCall) {
  174. this.$log.debug("endCall: !isActiveCall");
  175. return;
  176. }
  177.  
  178. let callInfo = this.localStorageService.ReadObjectSafely<ICall>(CONSTANTS.LocalStorage.callInfo);
  179. if (callInfo === null) {
  180. this.$log.debug("endCall: callInfo === null");
  181. return;
  182. }
  183.  
  184. let userId: string = callInfo.isCaller ? callInfo.calleeInfo.userId : callInfo.callerInfo.userId;
  185. this.fcmService.sendCallEndNotification(userId, callInfo.roomId)
  186. .then(() => this.endedCall())
  187. .catch((result: any) => {
  188. this.historyService.goBack();
  189. this.$log.warn("Error end call: " + JSON.stringify(result));
  190. })
  191. .finally(() => this.closeConnection());
  192. }
  193.  
  194. endedCall(): void {
  195. let isActiveCall = this.localStorageService.ReadObjectSafely<boolean>(CONSTANTS.LocalStorage.isActiveCall);
  196. if (!isActiveCall) { return; }
  197. if (!this.videoAdded) {
  198. const callInfo = this.localStorageService.ReadObjectSafely<ICall>(CONSTANTS.LocalStorage.callInfo);
  199. this.trackService.trackRoomEvent("Call", "add video", `The ${this.menuService.isStudentMode ? "student's" : "coach's"} video wasn't added`, 0, callInfo.roomId);
  200. }
  201.  
  202. this.changeUserStatus("online");
  203.  
  204. this.closeConnection();
  205. this.historyService.goReplace("summary");
  206. }
  207.  
  208. public createRoom(roomId: string): ng.IPromise<string> {
  209. this.rtc = new SimpleWebRTC({
  210. socketio: { forceNew: true },
  211. url: ServerConfig.simpleWebRtcUrl,
  212. localVideoEl: "localVideo",
  213. remoteVideosEl: "remoteVideos",
  214. autoRequestMedia: false,
  215. peerVolumeWhenSpeaking: 1,
  216. debug: NODE_ENV === "dev"
  217. });
  218. this.initWebRtcEvents();
  219. let deferred = this.$q.defer();
  220. this.rtc.createRoom(roomId, (error: any, roomId: string) => {
  221. if (error) {
  222. this.$log.error("Error: ", + JSON.stringify(error));
  223. }
  224. deferred.resolve(roomId);
  225. });
  226. return deferred.promise as ng.IPromise<string>;
  227. }
  228.  
  229. joinRoom(roomId: string, newConnection: boolean): ng.IPromise<{}> {
  230. this.rtc = new SimpleWebRTC({
  231. socketio: { forceNew: newConnection },
  232. url: ServerConfig.simpleWebRtcUrl,
  233. localVideoEl: "localVideo",
  234. remoteVideosEl: "remoteVideos",
  235. autoRequestMedia: false,
  236. autoRemoveVideos: true,
  237. peerVolumeWhenSpeaking: 1,
  238. debug: NODE_ENV === "dev"
  239. });
  240. this.initWebRtcEvents();
  241. let deferred = this.$q.defer();
  242. this.rtc.joinRoom(roomId, () => {
  243. deferred.resolve();
  244. });
  245. return deferred.promise;
  246. }
  247.  
  248. private readyToCall(roomId: string): ng.IPromise<void> {
  249. this.$log.info("on readyToCall");
  250. return this.joinRoom(roomId, true)
  251. .then(() => this.changeUserStatus("busy"));
  252. }
  253.  
  254. closeConnection(): void {
  255. this.localStorageService.WriteObject(CONSTANTS.LocalStorage.isActiveCall, false);
  256. this.videoPausedEvents = [];
  257. this.videoResumedEvents = [];
  258. if (!this.rtc) { return; }
  259. this.rtc.stopLocalVideo();
  260. this.rtc.leaveRoom();
  261. this.rtc.disconnect();
  262. this.rtc = null;
  263. }
  264.  
  265. private createRoomId(): string {
  266. let roomId = "";
  267. let abc = CONSTANTS.AvailableCharacters;
  268. for (let i = 0; i < CONSTANTS.RoomIdLength; i++) {
  269. let randPoz = Math.floor(Math.random() * abc.length);
  270. roomId += abc.substring(randPoz, randPoz + 1);
  271. }
  272. return roomId;
  273. }
  274.  
  275. createCall(calleeId: number, roomId: string): angular.IHttpPromise<ICall> {
  276. let data = {
  277. CaleeId: calleeId,
  278. RoomId: roomId
  279. };
  280. return this.$http.post(`${ServerConfig.serverBaseUrl}api/call/create`, data)
  281. .then((result: angular.IHttpPromiseCallbackArg<ICall>) => {
  282. return result.data;
  283. });
  284. }
  285.  
  286. setCallDuration(roomId: string, duration: number): angular.IHttpPromise<IEmptyResponse> {
  287. let data = {
  288. RoomId: roomId,
  289. Duration: duration
  290. };
  291. return this.$http.post(`${ServerConfig.serverBaseUrl}api/call/setduration`, data);
  292. }
  293.  
  294. getCurrentCallInformation(): angular.IHttpPromise<ICallInformation> {
  295. let callInfo = this.localStorageService.ReadObjectSafely<ICall>(CONSTANTS.LocalStorage.callInfo);
  296. return this.getCallInformation((callInfo === null) ? "" : callInfo.roomId);
  297. }
  298.  
  299. // Get information about finished call
  300. getCallInformation(roomId: string): angular.IHttpPromise<ICallInformation> {
  301. let deferred = this.$q.defer();
  302. this.$http.get(ServerConfig.serverBaseUrl + "api/call/callinformation", { params: { roomId: roomId } })
  303. .then((result: any) => {
  304. let callInformation: ICallInformation = this.renderCallInformation(result);
  305. deferred.resolve(callInformation);
  306. })
  307. .catch((result: any) => {
  308. this.$log.error("Error has occured: " + result);
  309. deferred.resolve([]);
  310. // todo: handle this exception
  311. });
  312. return deferred.promise;
  313. }
  314.  
  315. // Get information about active call
  316. public getActiveCallInformation(roomId: string): angular.IPromise<IActiveCall> {
  317. let deferred = this.$q.defer();
  318. this.$http.get(ServerConfig.serverBaseUrl + "api/call/activecallinformation", { params: { roomId: roomId } })
  319. .then((result: angular.IHttpPromiseCallbackArg<IActiveCall>) => {
  320. deferred.resolve(result.data);
  321. })
  322. .catch((reason) => {
  323. deferred.reject(reason);
  324. });
  325. return deferred.promise as angular.IPromise<IActiveCall>;
  326. }
  327.  
  328. public getCallStatus(roomId: string): angular.IPromise<CallStatus> {
  329. let deferred = this.$q.defer();
  330. this.$http.get(ServerConfig.serverBaseUrl + "api/call/getcallstatus", { params: { roomId: roomId } })
  331. .then((result) => {
  332. deferred.resolve(result.data as CallStatus);
  333. })
  334. .catch((reason) => {
  335. deferred.reject(reason);
  336. });
  337. return deferred.promise as angular.IPromise<CallStatus>;
  338. }
  339.  
  340. public sendCallSummary(data: ICallSummaryModel): angular.IHttpPromise<IEmptyResponse> {
  341. let callInfo = this.localStorageService.ReadObjectSafely<ICall>(CONSTANTS.LocalStorage.callInfo);
  342. data.callerId = callInfo.callerInfo.id;
  343. data.calleeId = callInfo.calleeInfo.id;
  344. data.isCaller = callInfo.isCaller;
  345. return this.$http.post(ServerConfig.serverBaseUrl + "api/call/setcallsummary", data);
  346. }
  347.  
  348. public muteAudio(): void {
  349. this.rtc.mute();
  350. }
  351.  
  352. public unmuteAudio(): void {
  353. this.rtc.unmute();
  354. }
  355.  
  356. public pauseVideo(): void {
  357. this.rtc.pauseVideo();
  358. }
  359.  
  360. public resumeVideo(): void {
  361. this.rtc.resumeVideo();
  362. }
  363.  
  364. public registerVideoEventCallback(eventType: EventType, cb: () => void) {
  365. switch (eventType) {
  366. case EventType.VideoPaused: this.videoPausedEvents.push(cb); break;
  367. case EventType.videoResumed: this.videoResumedEvents.push(cb); break;
  368. default: this.$log.debug("Error EventType"); break;
  369. }
  370. }
  371.  
  372. public unregisterVideoEventCallback(eventType: EventType, cb: () => void) {
  373. switch (eventType) {
  374. case EventType.VideoPaused: {
  375. let index = this.videoPausedEvents.indexOf(cb);
  376. if (index > -1) {
  377. this.videoPausedEvents.splice(index, 1);
  378. }
  379. break;
  380. }
  381. case EventType.videoResumed: {
  382. let index = this.videoResumedEvents.indexOf(cb);
  383. if (index > -1) {
  384. this.videoResumedEvents.splice(index, 1);
  385. }
  386. break;
  387. }
  388. default: this.$log.debug("Error EventType"); break;
  389. }
  390. }
  391.  
  392. private renderCallInformation(result: any): ICallInformation {
  393. let data: any = result.data;
  394. let callInformation: ICallInformation = {
  395. rateExperience: data.rateExperience,
  396. commentExperience: data.commentExperience,
  397. rateCallQuality: data.rateCallQuality,
  398. commentCallQuality: data.commentCallQuality,
  399. callerName: data.callerName,
  400. callerId: data.callerId,
  401. calleeName: data.calleeName,
  402. calleeId: data.calleeId,
  403. callDurationMin: data.callDurationMin,
  404. minutesCharges: data.minutesCharges,
  405. freeMinutes: data.freeMinutes,
  406. totalCharge: data.totalCharge,
  407. roomId: data.roomId,
  408. calleeHasImage: data.calleeHasImage,
  409. callerHasImage: data.callerHasImage
  410. };
  411.  
  412. return callInformation;
  413. }
  414.  
  415. private changeUserStatus(toStateName: string): ng.IPromise<void> {
  416. let state = CONSTANTS.UserStatusList.find((s) => s.state === toStateName);
  417. return this.currentUser.setUserStatus(state.value)
  418. .then(() => {
  419. let userProfile = this.localStorageService.ReadObjectSafely<IUserProfile>(CONSTANTS.LocalStorage.userProfile);
  420. this.currentUser.setUserStatus(state.value);
  421. userProfile.status = {
  422. id: state.value,
  423. name: state.name
  424. };
  425. this.menuService.userProfile = userProfile;
  426. })
  427. .catch(() => {
  428. this.$log.error("Change coach status failed");
  429. });
  430. }
  431.  
  432. public startLocalVideo(): void {
  433. this.rtc.startLocalVideo();
  434. }
  435.  
  436. public refreshVideos() {
  437. if (device.platform === "iOS") {
  438. this.$window.requestAnimationFrame(this.$window.cordova.plugins.iosrtc.refreshVideos);
  439. }
  440. }
  441.  
  442. private goBack() {
  443. if (!this.historyService.isHistoryEmpty()) {
  444. this.historyService.goBack();
  445. }
  446. else {
  447. this.historyService.goReplace(this.menuService.isStudentMode ? "providerlist" : "coach");
  448. }
  449. }
  450.  
  451. private initWebRtcEvents(): void {
  452. let self = this;
  453. this.rtc.on("connectivityError", (peer) => {
  454. const callInfo = this.localStorageService.ReadObjectSafely<ICall>(CONSTANTS.LocalStorage.callInfo);
  455. this.trackService.trackRoomEvent("Call", "connectivity", `Connectivity error to peer`, 1, callInfo.roomId);
  456. });
  457.  
  458. this.rtc.on("iceFailed", (peer) => {
  459. const callInfo = this.localStorageService.ReadObjectSafely<ICall>(CONSTANTS.LocalStorage.callInfo);
  460. this.trackService.trackRoomEvent("Call", "connectivity",
  461. `Local ICE/p2p server failed:
  462. 1. ICE server down
  463. 2. P2P connections blocked by Firewall
  464. 3. Symmetric NAT which does not reuse session address binding. This results some NAT Traversal techniques failing in traverse packets through NAT devices
  465. `, 1, callInfo.roomId);
  466. });
  467. this.rtc.on("localMediaError", () => {
  468. const callInfo = this.localStorageService.ReadObjectSafely<ICall>(CONSTANTS.LocalStorage.callInfo);
  469. this.trackService.trackRoomEvent("Call", "media",
  470. `The access to the camera wasn't obtained for ${this.currentUser.userProfile.firstName} ${this.currentUser.userProfile.lastName}`, 1, callInfo.roomId);
  471. });
  472. this.rtc.on("videoAdded", (video: HTMLVideoElement, peer: SimpleWebRTCPeer) => {
  473. if (device.platform !== "iOS") {
  474. self.changeAudioOutput(AudioToggle.SPEAKER);
  475. }
  476. if (!self.videoAdded) {
  477. self.videoAdded = true;
  478. const callInfo = self.localStorageService.ReadObjectSafely<ICall>(CONSTANTS.LocalStorage.callInfo);
  479. self.trackService.trackRoomEvent("Call", "add video", `The ${self.menuService.isStudentMode ? "student's" : "coach's"} video was added`, 2, callInfo.roomId);
  480. }
  481.  
  482. if (peer && peer.pc) {
  483. peer.pc.on("iceConnectionStateChange", (event: any) => {
  484. this.handleIceStateChange(peer.pc, event);
  485. });
  486. let pc = peer.pc as any;
  487. let rtcpc = pc.pc.peerconnection;
  488.  
  489. this.$log.info(rtcpc);
  490. }
  491. });
  492. this.rtc.on("mute", (data: any) => {
  493. this.rtc.getPeers(data.id).forEach((peer: any) => {
  494. if (data.name === "video") {
  495. this.$log.debug("CallingService: Peer video paused");
  496. for (let cb of this.videoPausedEvents) {
  497. cb();
  498. }
  499. }
  500. });
  501. });
  502. this.rtc.on("unmute", (data: any) => {
  503. this.rtc.getPeers(data.id).forEach((peer: any) => {
  504. if (data.name === "video") {
  505. this.$log.debug("CallingService: peer video resumed");
  506. for (let cb of this.videoResumedEvents) {
  507. cb();
  508. }
  509. }
  510. });
  511. });
  512. }
  513.  
  514. private handleIceStateChange(pc: any, event: any) {
  515. let rtcpc: RTCPeerConnection = pc.pc.peerconnection;
  516. this.$log.info("ICE Connection state:" + pc.iceConnectionState);
  517. switch (pc.iceConnectionState) {
  518. case "checking":
  519. break;
  520. case "connected":
  521. case "completed":
  522. if (this.connectionTimeOutStarted) {
  523. this.$timeout.cancel(this.timeoutPromise);
  524. this.$rootScope.$broadcast("timer-resume");
  525. this.connectionTimeOutStarted = false;
  526. }
  527. if (!this.connectionChecker) {
  528. this.connectionChecker = this.$interval(() => { this.checkStats(pc); }, CONSTANTS.statInterval);
  529. }
  530. break;
  531. case "disconnected":
  532. case "failed":
  533. if (!this.connectionTimeOutStarted) {
  534. this.$rootScope.$broadcast("timer-stop");
  535. this.timeoutPromise = this.$timeout(() => {
  536. if (this.internetConnectionstate === InternetConnectionState.noConnection) {
  537. this.endCallBasedOnConnection();
  538. }
  539. this.connectionTimeOutStarted = false;
  540. }, CONSTANTS.waitingConnectionTimeout);
  541. this.connectionTimeOutStarted = true;
  542. }
  543. if (this.connectionChecker) {
  544. this.$interval.cancel(this.connectionChecker);
  545. this.connectionChecker = null;
  546. }
  547. this.$interval.cancel(this.retryInterval);
  548. break;
  549. case "closed":
  550. if (!this.retryInProgress || this.connectionTimeOutStarted) {
  551. this.endCallBasedOnConnection();
  552. }
  553. this.$interval.cancel(this.retryInterval);
  554. break;
  555. default: break;
  556. }
  557. }
  558.  
  559. private checkStats(pc: any): void {
  560. this.$log.info("Checking data flow...");
  561. this.peerConnection = pc;
  562. let rtcpc: RTCPeerConnection = pc.pc.peerconnection;
  563. this.$log.info("IceSingaling state: " + rtcpc.signalingState);
  564. if (rtcpc) {
  565. if (pc.getLocalStreams()[0]) {
  566. let remoteSelector: MediaStreamTrack = pc.getRemoteStreams()[0].getVideoTracks()[0];
  567. let localSelector: MediaStreamTrack = pc.getLocalStreams()[0].getVideoTracks()[0];
  568.  
  569. rtcpc.getStats(localSelector, (data: any) => {
  570. for (let tmp in data) {
  571. if (tmp.startsWith("ssrc")) {
  572. let actualSent = data.get(tmp);
  573. if (this.previousSent) {
  574. if (this.previousSent.bytesSent === actualSent.bytesSent && navigator.onLine) {
  575. this.$log.info("Cannot send data.");
  576. this.senderMsg = this.snackbarService.show({ message: "Waiting for the other peer..." });
  577. } else {
  578. if (this.senderMsg) {
  579. this.senderMsg.hide();
  580. }
  581. this.snackbarService.hide();
  582. }
  583. }
  584. this.previousSent = actualSent;
  585. }
  586. }
  587. });
  588.  
  589. rtcpc.getStats(remoteSelector, (data: any) => {
  590. for (let tmp in data) {
  591. if (tmp.startsWith("ssrc")) {
  592. let actualStats = data.get(tmp);
  593. if (this.previousStats) {
  594. if (this.previousStats.bytesReceived === actualStats.bytesReceived) {
  595. this.$interval.cancel(this.connectionChecker);
  596. this.connectionChecker = null;
  597. if (navigator.onLine && !this.retryInProgress) {
  598. this.$log.info("NO INCOMING DATA!");
  599. this.retryCounter = 0;
  600. this.retryInterval = this.$interval(() => { this.retryChecker(pc, rtcpc, remoteSelector, this.previousStats); }, 1000);
  601. }
  602. } else {
  603. if (this.receiverMsg) {
  604. this.receiverMsg.hide();
  605. }
  606. this.retryInProgress = false;
  607. }
  608. }
  609. this.previousStats = actualStats;
  610. }
  611. }
  612. });
  613. } else {
  614. /*
  615. this.$interval.cancel(this.connectionChecker);
  616. this.connectionChecker = null;
  617.  
  618. if (this.localStorageService.ReadObjectSafely<boolean>(CONSTANTS.LocalStorage.isActiveCall)) {
  619. this.$timeout(() => {
  620. this.connectionChecker = this.$interval(() => { this.checkStats(pc); }, CONSTANTS.statInterval);
  621. }, CONSTANTS.retryTimeout);
  622.  
  623. this.retryAttempt();
  624. } */
  625. this.$log.info("No local streams.. :(");
  626. this.$log.info(pc.getLocalStreams());
  627. }
  628. }
  629. }
  630.  
  631. private retryChecker(pc: any, rtcpc: any, remoteSelector: any, previousStats: any): void {
  632. if (this.retryCounter > CONSTANTS.retryTotal) {
  633. this.snackbarService.show({ message: "Could not reconnect." });
  634. this.$interval.cancel(this.retryInterval);
  635. this.endCallBasedOnConnection();
  636. } else {
  637. this.snackbarService.show({ message: "Retry..." + ((this.retryCounter++ % CONSTANTS.retryCount) + 1) + "/" + CONSTANTS.retryCount });
  638. rtcpc.getStats(remoteSelector, (data: any) => {
  639. for (let tmp in data) {
  640. if (tmp.startsWith("ssrc")) {
  641. let actualStats = data.get(tmp);
  642. if (previousStats.bytesReceived === actualStats.bytesReceived && navigator.onLine) {
  643. if (!this.retryInProgress) {
  644. this.retryAttempt();
  645. }
  646. } else if (previousStats.bytesReceived !== actualStats.bytesReceived && navigator.onLine) {
  647. this.retryInProgress = false;
  648. this.$interval.cancel(this.retryInterval);
  649. this.connectionChecker = this.$interval(() => { this.checkStats(pc); }, CONSTANTS.statInterval);
  650. }
  651. }
  652. }
  653. });
  654. }
  655. }
  656.  
  657. private retryAttempt(): void {
  658. this.$log.info("Retry triggered, retrying...");
  659. this.retryInProgress = true;
  660. let rtcpc: RTCPeerConnection = this.peerConnection.pc.peerconnection;
  661. this.$timeout(() => { this.retryInProgress = false; }, CONSTANTS.retryTimeout);
  662.  
  663. if (navigator.onLine) {
  664. let callInfo = this.localStorageService.ReadObjectSafely<ICall>(CONSTANTS.LocalStorage.callInfo);
  665. if (callInfo) {
  666. if (this.peerConnection) {
  667. let offerOptions = {
  668. offerToReceiveAudio: 1,
  669. offerToReceiveVideo: 1,
  670. iceRestart:true
  671. };
  672. this.peerConnection.offer(offerOptions, (desc: string, answer: any) => {
  673. this.$log.info("Getting answer...");
  674. this.$log.info(answer);
  675. });
  676. }
  677. }
  678. }
  679. }
  680.  
  681. private endCallBasedOnConnection() {
  682. this.$interval.cancel(this.connectionChecker);
  683. this.$interval.cancel(this.retryInterval);
  684. this.$timeout.cancel(this.timeoutPromise);
  685. this.connectionChecker = null;
  686. this.retryInProgress = false;
  687. if (this.localStorageService.ReadObjectSafely<boolean>(CONSTANTS.LocalStorage.isActiveCall) && navigator.onLine) {
  688. this.endCall();
  689. }
  690. else {
  691. this.closeConnection();
  692. this.historyService.goReplace(this.menuService.isStudentMode ? "providerlist" : "coach");
  693. }
  694. }
  695.  
  696. public changeAudioOutput(type: string) {
  697. if (AudioToggle) {
  698. AudioToggle.setAudioMode(type);
  699. this.$log.debug("CallingService: audio output device changed to " + type);
  700. }
  701. // add when it needs the browser support
  702. /* navigator.mediaDevices
  703. .enumerateDevices()
  704. .then((deviceInfos: any[]) => {
  705. let speaker: any;
  706. deviceInfos.forEach((deviceInfo) => {
  707. if ((deviceInfo.label.search(/speaker/i)) > -1) {
  708. speaker = deviceInfo;
  709. }
  710. });
  711. if (speaker) {
  712. let videoElement = peer.videoEl;
  713. if (typeof videoElement.sinkId !== "undefined") {
  714. videoElement.setSinkId(speaker.deviceId)
  715. .then(() => {
  716. this.$log.debug("Success, audio output device attached: " + speaker.deviceId);
  717. });
  718. }
  719. }
  720. });*/
  721. }
  722. }
  723.  
  724. CallingService.$inject = [
  725. "$q",
  726. "$http",
  727. "$translate",
  728. "$window",
  729. "$timeout",
  730. "$interval",
  731. "FCMService",
  732. "LocalStorageService",
  733. "SnackbarService",
  734. "CurrentUserService",
  735. "HistoryService",
  736. "ProvidersService",
  737. "MenuService",
  738. "$log",
  739. "GAService",
  740. "$rootScope"
  741. ];
  742.  
  743. export default angular.module("app.services.calling", [])
  744. .service("CallingService", CallingService)
  745. .name;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement