ngbootcamp.factory("liveService", function($http, $q) { let INSTANCE = null; let audioInputDevices = [] let videoInputDevices = [] let audioOutputDevices = [] let localStream = null; let remoteStream = null; let connecting = false; let reconnect = false; let reconnecting = false; let recognition = null; let recognitionLock = false; let remoteVideoLoaded = false; let callProcess = null; let socketId = null; let callFromAccountId = null; let authData = null; let connectedCallback = null; let connectedActionCallback = null; let connectedServiceCallback = null; let statusCheckCallback = null; let linkReadMessageCallback = null; let callCallback = null; let callMeCallback = null; let peerMap = {}; let senders = [] let enabledVideo = true let enabledAudio = true let createPeer = (data) => { let peer = new RTCPeerConnection({"iceServers":[ // {"urls":"stun:stun-alice.momona.io:7008",username:"test",credential:"pass"}, {"urls":"stun:stun.l.google.com:19302"}, ]}); // let peer = new RTCPeerConnection({"iceServers":[]}); peer.ontrack = (e) => { console.log("linkService", "B", e); let stream = event.streams[0]; remoteStream = stream; if (remoteVideo) { // remoteVideo.removeEventListener("loadeddata", videoEventListener); // remoteVideo.addEventListener("loadeddata", videoEventListener, false); remoteVideo.volume = 1; remoteVideo.muted = false; remoteVideo.srcObject = stream; remoteVideo.load(); } }; peer.onaddstream = function(event) { let stream = event.stream; // remoteStream = stream; console.log('-- peer.onaddstream() stream.id=',stream); /* let video = document.getElementById("video"); video.volume = 1; video.muted = false; video.addEventListener("loadeddata", function(e) { console.log("linkService", "動画ロード", e); video.play(); }, false); video.srcObject = stream; video.load(); */ }; peer.onicecandidate = (e) => { if (e.candidate) { // console.log(e.candidate); socket.emit("call_connection", { to_account_id:data.data.from_account_id, type:"candidate", ice:e.candidate, }); } else { console.log(e); } }; peer.onnegotiationneeded = function(evt) { console.log('-- onnegotiationneeded() ---'); }; peer.onicecandidateerror = function (evt) { console.error('ICE candidate ERROR:', evt); }; peer.onsignalingstatechange = function() { console.log('== signaling status=' + peer.signalingState); }; peer.oniceconnectionstatechange = function() { console.log('== ice connection status=' + peer.iceConnectionState); if (peer.iceConnectionState === 'disconnected') { console.log('-- disconnected --'); } }; peer.onicegatheringstatechange = function() { console.log('==***== ice gathering state=' + peer.iceGatheringState); }; peer.onconnectionstatechange = function() { console.log('==***== connection state=' + peer.connectionState); if (peer.connectionState == "failed") { console.log("liveService.createPeer onconnectionstatechange 再接続") reconnect = true; reconnecting = false; reconnectProcess(); } }; peer.onremovestream = function(event) { console.log('-- peer.onremovestream()'); }; if (localStream != null) { localStream.getTracks().forEach(t => { console.log("liveService.createPeer", t); if (t.kind == "video" || t.kind == "audio") senders.push(peer.addTrack(t, localStream)) t.enabled = (t.kind == "video" && enabledVideo) || (t.kind == "audio" && enabledAudio) }); } return peer; } let setSelfVideo = (callback) => { if (localStream) { callback(true) return; } try { navigator.getUserMedia({video:true, audio:true}, (stream) => { localStream = stream; try { console.log(localStream.getTracks()); console.log(localStream.getTracks()[0].getSettings()); } catch (e2) { console.log(e2); } let videoSelf = document.getElementById("self-video"); videoSelf.style.display = "none"; console.log("VIDEO", videoSelf, stream); if (videoSelf) videoSelf.srcObject = stream; callback(true) }) } catch (e) { navigator.mediaDevices.getUserMedia({ audio: true, video: true }) .then(function(stream) { localStream = stream; let videoSelf = document.getElementById("self-video"); console.log(videoSelf, stream); if (videoSelf) videoSelf.srcObject = stream; callback(true) }).catch(function(err) { console.log(err.name + ": " + err.message); callback(false) }); } } return { setCallCallback: (callback) => { callCallback = callback; }, setCallMeCallback: (callback) => { callMeCallback = callback; }, setStatusCheckCallback: (callback) => { statusCheckCallback = callback }, isConnecting: () => { return reconnecting }, isConnected: () => { let p = peerMap[socketId] console.log(p) return p && p.state == "connected" }, setInputDevice: (element, device) => { if (!element) return try { element.setSinkId(device.deviceId) .then(function() { console.log("liveService.setInputDevice", device); }) .catch(function(err) { console.log("liveService.setInputDevice err", device, err); }); } catch (e) { console.log(e) } }, getAudioInputDevices: () => { return audioInputDevices }, getVideoInputDevices: () => { return videoInputDevices }, getAudioOutputDevices: () => { return audioOutputDevices }, getDevices: (callback) => { navigator.mediaDevices.enumerateDevices() .then(function(devices) { audioInputDevices = [] videoInputDevices = [] audioOutputDevices = [] devices.forEach(function(device) { if (!device) { return; } console.log("liveService.getDevice", device.kind + ": " + device.label +" id = " + device.deviceId); switch (device.kind) { case "audioinput": audioInputDevices.push(device) break; case "audioinput": videoInputDevices.push(device) break; case "audiooutput": audioOutputDevices.push(device) break; default:break; } }); if (callback) callback() }) .catch(function(err) { console.log("liveService.getDevice", err); if (callback) callback() }); }, linkReadMessage: (callback) => { linkReadMessageCallback = callback; console.log("linkService", "read_message", $scope.params); setTimeout(() => { socket.emit("read_message", { user_id:$scope.loginData.id, token:$scope.loginData.token, }); }, 500); }, statusUpdate: () => { console.log("linkService", "status_update", $scope.params); let cameraWidth = -1; let cameraHeight = -1; if (localStream != null) { let set = localStream.getTracks()[0].getSettings(); console.log(localStream.getTracks()); cameraWidth = set.width; cameraHeight = set.height; } setTimeout(() => { socket.emit("status_update", { sid:"", code:"", user_id:$scope.loginData.id, call:remoteVideoLoaded, camera_width:cameraWidth, camera_height:cameraHeight, }); }, 500); }, init: ($scope) => { INSTANCE = this; $scope.initialized = false; $scope.params = {}; reconnect = false; reconnecting = false; let reconnectProcess = function() { if (reconnect) { setTimeout(reconnectProcess, 1000); if (reconnecting) return; reconnecting = true; reconnect = false; connectedCallback = function(data) { console.log("linkService", "再接続完了", authData, data); reconnecting = false; if (data.result) { reconnect = false; if (connectedActionCallback) connectedActionCallback() connectedActionCallback = null } } // 認証済みのときは接続後に自動認証 if (authData) { setTimeout(() => { socket.emit("connect_service", { // 通常ユーザー account_id:authData.id, token:authData.token, // サイネージ id:authData.id, pass:authData.pass, // 共通 type:authData.type, terminal_type:authData.terminal_type, }); }, 500); } } } socket.on('connected', function(data) { console.log("linkService.connected", data); socketId = data.socket_id; reconnect = false; reconnecting = false; $scope.$apply(() => { if (connectedCallback != null) connectedCallback(data); connectedCallback = null; }); /* $scope.linkInitialize(linkInitializeCallback); */ }); socket.on('connected_service', function(data) { console.log("linkService.connected_service", data); $scope.$apply(() => { if (connectedServiceCallback != null) connectedServiceCallback(data); connectedServiceCallback = null; }); /* $scope.linkInitialize(linkInitializeCallback); */ }); socket.on('disconnect', (data) => { console.log("linkService.disconnect", data); socketId = null; reconnect = true; reconnecting = false; reconnectProcess(); }); socket.on("service_liver", (data) => { console.log("linkService.service_liver", data) }) socket.on('status_check', (data) => { console.log("linkService.status_check", data); $scope.$apply(() => { if (data.result) { } else { } if (statusCheckCallback != null) statusCheckCallback(data); }); }); socket.on('status_share', (data) => { console.log("linkService.status_share"); console.log(data); }); socket.on('force_call', (data) => { console.log("linkService.force_call"); console.log(data); }); socket.on('reboot', (data) => { location.reload(true); }); socket.on('read_message', (data) => { console.log("linkService.read_message", data); $scope.$apply(() => { if (data.result) { socketId = data.socket_id; } else { } if (linkReadMessageCallback != null) linkReadMessageCallback(data); linkReadMessageCallback = null; }); }); socket.on("call_disconnection", function(data) { console.log("liveService.call_disconnection", data); if (callCallback) callCallback(false, data) }) socket.on("call_connection", function(data) { console.log("liveService.call_connection", data); if (!data.result) { if (!data.connection) { reconnect = true; reconnecting = false; reconnectProcess() } if (callCallback) callCallback(true, data) return; } if (callCallback) callCallback(true, data) switch (data.data.type) { case "call me": { if (data.data.from_account_id == $scope.loginData.id) { console.log("linkService", "SELF CALL BACK"); return; } if (callMeCallback) callMeCallback(data) // let peer = createPeer(data); // peerMap[data.socket_id] = peer; // peer.createOffer() // .then((sessionDescription) => { // console.log("linkService", "call me 1"); // return peer.setLocalDescription(sessionDescription); // }).then(() => { // console.log("linkService", "call me 2"); // socket.emit("call_connection", { // from_account_id:$scope.loginData.id, // to_account_id:data.data.from_account_id, // type:peer.localDescription.type, // sdp:peer.localDescription.sdp, // }); // }).catch((e) => { // console.log(e); // }); break; } case "offer": { let offer = new RTCSessionDescription(data.data); let peer = createPeer(data); peerMap[data.socket_id] = peer; peer.setRemoteDescription(offer) .then((sessionDescription) => { console.log("linkService", "offer1", data); callFromAccountId = data.data.from_account_id let peer = peerMap[data.socket_id]; console.log(peer); if (peer == null) return; peer.createAnswer() .then((sessionDescription) => { // console.log("linkService", "offer2", data); return peer.setLocalDescription(sessionDescription); }).then(() => { // console.log("linkService", "offer3", data); socket.emit("call_connection", { // from_account_id:$scope.loginData.id, from_account_id:data.data.to_account_id, to_account_id:data.data.from_account_id, type:peer.localDescription.type, sdp:peer.localDescription.sdp, }); }).catch((e) => { console.log(e); }); }).catch((e) => { console.log(e); }); break; } case "answer": { let answer = new RTCSessionDescription(data.data); let peer = peerMap[data.socket_id]; if (peer == null) return; if (data.socket_id != socketId) { callFromAccountId = data.data.from_account_id // console.log("linkService", "other answer"); // return; } peer.setRemoteDescription(answer) .then(() => { // console.log("linkService", "answer1", data); }).catch((e) => { console.log(e); // 再接続 }); break; } case "candidate": { let peer = peerMap[data.socket_id]; if (data.socket_id == socketId) { // console.log("linkService", "self candidate", data); return; } if (peer == null) { console.log("linkService", "candidate null"); return; } peer.addIceCandidate(new RTCIceCandidate(data.data.ice)); break; } } }); }, setRemoteVideo: (video) => { remoteVideo = video; remoteVideo.addEventListener('loadstart', function(e) { console.log("remoteVideo 動画ロード", e); remoteVideoLoaded = true; remoteVideo.play(); }) }, connectService: (auth, params, callback) => { if (!auth || !auth.id && !auth.token && !auth.pass) { console.log("liveService.connectService", auth) return } console.log("liveService.connectService start", auth) connectedServiceCallback = callback; authData = auth; setTimeout(() => { setSelfVideo((r) => { socket.emit("connect_service", { // 通常ユーザー account_id:authData.id, token:authData.token, // サイネージ id:authData.id, pass:authData.pass, // 共通 type:authData.type, terminal_type:authData.terminal_type, }); }) }, 500); }, rmJoin: (id, name, is_operator) => { setTimeout(() => { socket.emit("rm_join", { id:id, name:name, is_operator:is_operator, }); }, 500); }, callEnd: (recognition) => { try { if (callProcess) clearInterval(callProcess); let videoSelf = document.getElementById("self-video"); if (videoSelf) videoSelf.srcObject = null; if (localStream) { for (let v of localStream.getTracks()) { console.log(v); v.stop(); } } } catch (e) { console.log(e); } try { let video = document.getElementById("remote-video"); if (video) video.srcObject = null; remoteStream = null; } catch (e) { console.log(e); } localStream = null; connecting = false; calling = false; recognitionLock = false; try { if (recognition != null) recognition.start(); } catch (e) { console.log(e) } try { if (callFromAccountId) { let aId = callFromAccountId callFromAccountId = null setTimeout(() => { socket.emit("call_disconnection", { to_account_id:aId, }); }, 500); } } catch (e) { console.log(e) } }, call: (item, recognition, callback) => { if (connecting) { if (callback) callback({result:false,wait_connecting:true}) return; } console.log("liveService.call", socket, item) connecting = true; recognitionLock = true; if (recognition) recognition.stop(); setSelfVideo((r) => { socket.emit("call_connection", { id:item.id, type:"call me", request_type:item.request_type, // リクエスト種別 (緊急等) to_account_type:item.to_account_type, // 着信先アカウント種別 (to_account_id=null) to_account_id:item.to_account_id, // 着信先アカウント (to_account_typeは無視) // from_account_id:item.id, // from_account_name:item.name, }); connectedActionCallback = () => { console.log("liveService.call 再接続後call") socket.emit("call_connection", { id:item.id, type:"call me", request_type:item.request_type, // リクエスト種別 (緊急等) to_account_type:item.to_account_type, // 着信先アカウント種別 (to_account_id=null) to_account_id:item.to_account_id, // 着信先アカウント (to_account_typeは無視) // from_account_id:item.id, // from_account_name:item.name, }); } callProcess = setInterval(() => { if (remoteVideoLoaded) { clearInterval(callProcess); if (callback) callback({result:true}) return; } console.log("linkService", "RECALL"); socket.emit("call_connection", { id:item.id, type:"call me", request_type:item.request_type, // リクエスト種別 (緊急等) to_account_type:item.to_account_type, // 着信先アカウント種別 (to_account_id=null) to_account_id:item.to_account_id, // 着信先アカウント (to_account_typeは無視) // from_account_id:item.id, // from_account_name:item.name, }); // let peer = new RTCPeerConnection({"iceServers":[{"urls":"stun://ws-alice.momona.io:7008"}]}); // peer.addStream(localStream); }, 5000); }) }, receiveCall: (data, recognition, callback) => { if (connecting && remoteVideoLoaded) { console.log("LiveService.receiveCall already connecting") if (callback) callback({result:false,wait_connecting:true}) return; } connecting = true; recognitionLock = true; if (recognition) recognition.stop(); callFromAccountId = data.data.from_account_id setSelfVideo((r) => { let peer = createPeer(data); peerMap[data.socket_id] = peer; peer.createOffer() .then((sessionDescription) => { console.log("linkService", "call me 1"); return peer.setLocalDescription(sessionDescription); }).then(() => { console.log("linkService", "call me 2", data); socket.emit("call_connection", { to_account_id:data.data.from_account_id, type:peer.localDescription.type, sdp:peer.localDescription.sdp, }); }).catch((e) => { console.log(e); }); }) }, setEnabledVideo: (t) => { enabledVideo = t if (localStream != null) { if (t) { for (let v of localStream.getTracks()) { console.log(v); if (v.kind == "video") v.enabled = true } } else { for (let v of localStream.getTracks()) { console.log(v); if (v.kind == "video") v.enabled = false } } } }, setEnabledAudio: (t) => { enabledAudio = t if (localStream != null) { if (t) { for (let v of localStream.getTracks()) { console.log(v); if (v.kind == "audio") v.enabled = true } } else { for (let v of localStream.getTracks()) { console.log(v); if (v.kind == "audio") v.enabled = false } } } }, } });