2021-01-13 14:04:04 +00:00
|
|
|
import React, { Component } from 'react'
|
|
|
|
|
2021-01-11 09:48:50 +00:00
|
|
|
import './App.css';
|
|
|
|
|
2021-01-13 14:04:04 +00:00
|
|
|
const io = require("socket.io-client");
|
|
|
|
|
|
|
|
let localStream = null;
|
|
|
|
let remoteStream = null;
|
|
|
|
|
2021-01-20 08:25:06 +00:00
|
|
|
// const iceServer = {
|
|
|
|
// 'iceServer': [
|
|
|
|
// // { 'urls': 'stun:stun.services.mozilla.com' }, // public
|
|
|
|
// // { 'urls': 'stun:stun.l.google.com:19302' } // public
|
|
|
|
// // { 'urls': 'dev.linx.safemobile.com:19302' },
|
|
|
|
// // {
|
|
|
|
// // urls: 'turn:dev.linx.safemobile.com:19302',
|
|
|
|
// // username: "safemobile",
|
|
|
|
// // credential: "Safemobile123"
|
|
|
|
// // }
|
|
|
|
// // ,
|
|
|
|
// {
|
|
|
|
// urls: 'turn:numb.viagenie.ca',
|
|
|
|
// username: "webrtc@live.com",
|
|
|
|
// credential: "muazkh"
|
|
|
|
// }
|
|
|
|
// ]
|
|
|
|
// }
|
2021-01-11 09:48:50 +00:00
|
|
|
|
2021-01-13 14:04:04 +00:00
|
|
|
let localVideo = document.getElementById("localVideo");
|
|
|
|
let remoteVideo = document.getElementById("remoteVideo");
|
|
|
|
|
|
|
|
const streamConstraints = {
|
|
|
|
audio: false,
|
|
|
|
video: true
|
|
|
|
};
|
|
|
|
|
|
|
|
class App extends Component {
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
|
|
|
|
this.state = {
|
|
|
|
login: '',
|
|
|
|
password: '',
|
|
|
|
user: null,
|
|
|
|
hubStatus: 0, // 0 uninitialized | 1 connecting | 2 connected | 3 connection error
|
|
|
|
socket: null,
|
|
|
|
arsSent: false,
|
|
|
|
dest_asset_id: '',
|
|
|
|
isCaller: false,
|
|
|
|
callId: null,
|
2021-01-20 08:25:06 +00:00
|
|
|
rtcPeerConnection: null,
|
|
|
|
stunUrl: 'stun:dev.linx.safemobile.com:19302',
|
|
|
|
turnUrl: 'turn:dev.linx.safemobile.com:19302',
|
|
|
|
turnUsername: 'safemobile',
|
|
|
|
turnCredential: 'Safemobile123'
|
2021-01-13 14:04:04 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
handleLoginClick = async () => {
|
2021-01-14 12:47:50 +00:00
|
|
|
const loginRequest = await fetch('https://dev.linx.safemobile.com/api/login', {
|
2021-01-20 08:25:06 +00:00
|
|
|
// const loginRequest = await fetch('http://localhost:41418/login', {
|
2021-01-13 14:04:04 +00:00
|
|
|
method: 'POST',
|
|
|
|
headers: {
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
"login": this.state.login,
|
|
|
|
"password": this.state.password
|
|
|
|
})
|
|
|
|
});
|
|
|
|
const loginResponseJson = await loginRequest.json();
|
|
|
|
this.setState({ user: loginResponseJson.data }, () => {
|
|
|
|
// Connect to HUB
|
2021-01-20 08:25:06 +00:00
|
|
|
// this.setState({ socket: io("localhost:41414", {reconnect:true, transports: ['websocket']}) }, () => {
|
2021-01-14 12:47:50 +00:00
|
|
|
this.setState({ socket: io("https://hub.dev.linx.safemobile.com/", {reconnect:true, transports: ['websocket']}) }, () => {
|
2021-01-13 14:04:04 +00:00
|
|
|
const socket = this.state.socket;
|
|
|
|
socket.on('connect', () => {
|
|
|
|
this.setState({ hubStatus: 2 })
|
|
|
|
// Send ARS after connected to HUB
|
|
|
|
socket.emit('ars', JSON.stringify({
|
|
|
|
ars: true,
|
|
|
|
asset_id: this.state.user.asset.id,
|
|
|
|
account_id: this.state.user.account.id
|
|
|
|
}));
|
|
|
|
this.setState({ arsSent: true });
|
|
|
|
});
|
|
|
|
|
|
|
|
// HUB 'video' event handler
|
|
|
|
socket.on('video', (data) => {
|
|
|
|
const parseDate = JSON.parse(data);
|
|
|
|
console.log('video', parseDate);
|
|
|
|
|
|
|
|
// The response to the 'notify-request' (hardcoded 'YES')
|
|
|
|
if(parseDate.type === 'notify-request') {
|
|
|
|
this.setState({ dest_asset_id: parseInt(parseDate.origin_asset_id), callId: parseInt(parseDate.video_call_id) });
|
|
|
|
socket.emit('video', JSON.stringify({
|
|
|
|
origin_asset_id: this.state.user.asset.id,
|
|
|
|
dest_asset_id: parseInt(parseDate.origin_asset_id),
|
|
|
|
type: 'notify-answer',
|
|
|
|
origin_asset_priority: this.state.user.asset.priority,
|
|
|
|
origin_asset_type_name: this.state.user.user_role.name,
|
|
|
|
origin_asset_name: this.state.user.name,
|
|
|
|
video_call_id: parseDate.video_call_id,
|
2021-01-14 12:42:56 +00:00
|
|
|
answer: 'accepted' // answer: 'rejected'
|
2021-01-13 14:04:04 +00:00
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handler for 'notify-end'(when the video closed)
|
|
|
|
if(parseDate.type === 'notify-end') {
|
|
|
|
console.log('notify-end');
|
|
|
|
this.cleanVideoStreams();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handler for 'notify-answer'
|
|
|
|
if(parseDate.type === 'notify-answer') {
|
2021-01-14 12:42:56 +00:00
|
|
|
|
|
|
|
if(parseDate.answer === 'rejected') {
|
|
|
|
this.closeVideo()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-01-13 14:04:04 +00:00
|
|
|
// Create media stream
|
|
|
|
navigator.mediaDevices.getUserMedia(streamConstraints).then(stream => {
|
|
|
|
console.log('Created getUserMedia', stream);
|
|
|
|
localStream = stream;
|
|
|
|
localVideo.srcObject = stream;
|
2021-01-20 08:25:06 +00:00
|
|
|
console.log('iceServers', this.calculateIceServers())
|
2021-01-13 14:04:04 +00:00
|
|
|
// Create SDP
|
2021-01-20 08:25:06 +00:00
|
|
|
this.setState({ rtcPeerConnection: new RTCPeerConnection(this.calculateIceServers()) }, () => {
|
2021-01-13 14:04:04 +00:00
|
|
|
this.state.rtcPeerConnection.onicecandidate = this.onIceCandidate;
|
|
|
|
this.state.rtcPeerConnection.ontrack = this.onAddStream;
|
|
|
|
this.state.rtcPeerConnection.addTrack(localStream.getTracks()[0], localStream); // video
|
|
|
|
// rtcPeerConnection.addTrack(localStream.getTracks()[1], localStream) // audio
|
|
|
|
// Create a offer
|
|
|
|
this.state.rtcPeerConnection.createOffer().then(sessionDescription => {
|
|
|
|
// Save SDP in state and send it to destination asset_id as offer
|
|
|
|
this.setState({ isCaller: true, callId: parseInt(parseDate.video_call_id) }, () => {
|
|
|
|
this.state.rtcPeerConnection.setLocalDescription(sessionDescription);
|
|
|
|
console.log('Created SDP', sessionDescription);
|
|
|
|
socket.emit('video', JSON.stringify({
|
|
|
|
origin_asset_id: this.state.user.asset.id,
|
|
|
|
type: 'offer',
|
|
|
|
sdp: sessionDescription,
|
|
|
|
dest_asset_id: parseInt(this.state.dest_asset_id),
|
|
|
|
video_call_id: parseDate.video_call_id
|
|
|
|
}));
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch(e => { console.log(e); })
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch(err => {
|
|
|
|
console.log('An error occured', err);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handler for 'offer'
|
|
|
|
if(parseDate.type === 'offer') {
|
|
|
|
// When we are NOT the caller
|
|
|
|
if(!this.state.isCaller) {
|
|
|
|
// Create media stream
|
|
|
|
navigator.mediaDevices.getUserMedia(streamConstraints).then(stream => {
|
|
|
|
console.log('Created getUserMedia', stream);
|
|
|
|
localStream = stream;
|
|
|
|
localVideo.srcObject = stream;
|
2021-01-20 08:25:06 +00:00
|
|
|
console.log('iceServers', this.calculateIceServers())
|
2021-01-13 14:04:04 +00:00
|
|
|
// Create SDP
|
2021-01-20 08:25:06 +00:00
|
|
|
this.setState({ rtcPeerConnection: new RTCPeerConnection(this.calculateIceServers()) }, () => {
|
2021-01-13 14:04:04 +00:00
|
|
|
this.state.rtcPeerConnection.onicecandidate = this.onIceCandidate;
|
|
|
|
this.state.rtcPeerConnection.ontrack = this.onAddStream;
|
|
|
|
this.state.rtcPeerConnection.addTrack(localStream.getTracks()[0], localStream); // video
|
|
|
|
// rtcPeerConnection.addTrack(localStream.getTracks()[1], localStream) // audio
|
|
|
|
// Set setRemoteDescription to be sender SDP
|
|
|
|
this.state.rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(parseDate.sdp));
|
|
|
|
// Create an answer
|
|
|
|
this.state.rtcPeerConnection.createAnswer().then(sessionDescription => {
|
|
|
|
// Save SDP in state and send it to destination asset_id as offer
|
|
|
|
this.setState({ sdp: sessionDescription }, () => {
|
|
|
|
this.state.rtcPeerConnection.setLocalDescription(sessionDescription);
|
|
|
|
console.log('Created SDP', sessionDescription);
|
|
|
|
socket.emit('video', JSON.stringify({
|
|
|
|
origin_asset_id: this.state.user.asset.id,
|
|
|
|
type: 'offer',
|
|
|
|
sdp: sessionDescription,
|
|
|
|
dest_asset_id: parseInt(this.state.dest_asset_id),
|
|
|
|
video_call_id: parseDate.video_call_id
|
|
|
|
}));
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch(e => { console.log(e); })
|
|
|
|
})
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
// When we ARE the caller
|
|
|
|
console.log('parseDate.sdp', parseDate.sdp);
|
|
|
|
this.state.rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(parseDate.sdp));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(parseDate.type === 'candidate') {
|
2021-01-14 12:47:50 +00:00
|
|
|
if(this.state.rtcPeerConnection) {
|
|
|
|
const candidate = new RTCIceCandidate({
|
|
|
|
sdpMLineIndex: parseDate.label,
|
|
|
|
candidate: parseDate.candidate
|
|
|
|
});
|
|
|
|
this.state.rtcPeerConnection.addIceCandidate(candidate);
|
|
|
|
}
|
2021-01-13 14:04:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-01-20 08:25:06 +00:00
|
|
|
calculateIceServers = () => {
|
|
|
|
return {
|
|
|
|
'iceServer': [
|
|
|
|
{
|
|
|
|
'urls': this.state.stunUrl
|
|
|
|
}, {
|
|
|
|
urls: this.state.turnUrl,
|
|
|
|
username: this.state.turnUsername,
|
|
|
|
credential: this.state.turnCredential
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-13 14:04:04 +00:00
|
|
|
onAddStream = (event) => {
|
|
|
|
console.log('onAddStream', event);
|
|
|
|
remoteVideo.srcObject = event.streams[0];
|
|
|
|
remoteStream = event.streams[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
onIceCandidate = (event) => {
|
|
|
|
if(event.candidate) {
|
|
|
|
console.log('sending ice candidate', event.candidate);
|
|
|
|
this.state.socket.emit('video', JSON.stringify({
|
|
|
|
type: 'candidate',
|
|
|
|
label: event.candidate.sdpMLineIndex,
|
|
|
|
id: event.candidate.sdpMid,
|
|
|
|
candidate: event.candidate.candidate,
|
|
|
|
dest_asset_id: this.state.dest_asset_id
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
handleChangeLogin = (e) => {
|
|
|
|
this.setState({ login: e.target.value});
|
|
|
|
}
|
|
|
|
|
|
|
|
handleChangePassword = (e) => {
|
|
|
|
this.setState({ password: e.target.value});
|
|
|
|
}
|
|
|
|
|
|
|
|
handleChangeDestAssetId = (e) => {
|
|
|
|
this.setState({ dest_asset_id: e.target.value});
|
|
|
|
}
|
|
|
|
|
2021-01-20 08:25:06 +00:00
|
|
|
handleChangeStunUrl = (e) => {
|
|
|
|
this.setState({ stunUrl: e.target.value});
|
|
|
|
}
|
|
|
|
|
|
|
|
handleChangeTurnUrl = (e) => {
|
|
|
|
this.setState({ turnUrl: e.target.value});
|
|
|
|
}
|
|
|
|
|
|
|
|
handleChangeTurnUsername = (e) => {
|
|
|
|
this.setState({ turnUsername: e.target.value});
|
|
|
|
}
|
|
|
|
|
|
|
|
handleChangeTurnCredential = (e) => {
|
|
|
|
this.setState({ turnCredential: e.target.value});
|
|
|
|
}
|
|
|
|
|
2021-01-13 14:04:04 +00:00
|
|
|
handleClickEvent = () => {
|
|
|
|
this.state.socket.emit('video', JSON.stringify({
|
|
|
|
origin_asset_id: this.state.user.asset.id,
|
|
|
|
dest_asset_id: parseInt(this.state.dest_asset_id),
|
|
|
|
type: 'notify-request',
|
|
|
|
origin_asset_priority: this.state.user.asset.priority,
|
|
|
|
origin_asset_type_name: this.state.user.user_role.name,
|
|
|
|
origin_asset_name: this.state.user.name,
|
|
|
|
video_call_id: null
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
closeVideo = () => {
|
|
|
|
this.state.socket.emit('video', JSON.stringify({
|
|
|
|
origin_asset_id: this.state.user.asset.id,
|
|
|
|
dest_asset_id: parseInt(this.state.dest_asset_id),
|
|
|
|
type: 'notify-end',
|
|
|
|
video_call_id: this.state.callId
|
|
|
|
}));
|
|
|
|
this.cleanVideoStreams();
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanVideoStreams = () => {
|
2021-01-14 12:42:56 +00:00
|
|
|
this.state.rtcPeerConnection != null && this.state.rtcPeerConnection.close();
|
2021-01-13 14:04:04 +00:00
|
|
|
this.setState({ callId: null, rtcPeerConnection: null });
|
|
|
|
localVideo.srcObject = null;
|
|
|
|
remoteVideo.srcObject = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2021-01-20 08:25:06 +00:00
|
|
|
const { login, password, user, hubStatus, arsSent, dest_asset_id, isCaller, callId, stunUrl, turnUrl, turnUsername, turnCredential } = { ...this.state };
|
2021-01-13 14:04:04 +00:00
|
|
|
return (
|
|
|
|
<div className="App">
|
2021-01-20 08:25:06 +00:00
|
|
|
|
2021-01-13 14:04:04 +00:00
|
|
|
<h1>HUB TESTER</h1>
|
2021-01-20 08:25:06 +00:00
|
|
|
|
|
|
|
<br></br>
|
|
|
|
|
|
|
|
<h2><u>STUN and TURN servers</u></h2>
|
|
|
|
|
|
|
|
STUN Server URL :
|
|
|
|
<input
|
|
|
|
placeholder='ex: stun:stun.services.mozilla.com'
|
|
|
|
onChange={this.handleChangeStunUrl}
|
|
|
|
value={stunUrl}
|
|
|
|
style={{ margin: 10, width: 250 }}
|
|
|
|
/><br></br>
|
|
|
|
|
|
|
|
TURN Server URL :
|
|
|
|
<input
|
|
|
|
placeholder='ex: turn:numb.viagenie.ca'
|
|
|
|
onChange={this.handleChangeTurnUrl}
|
|
|
|
value={turnUrl}
|
|
|
|
style={{ margin: 10, width: 250 }}
|
|
|
|
/><br></br>
|
|
|
|
|
|
|
|
TURN Server Username :
|
|
|
|
<input
|
|
|
|
placeholder='ex: gigel'
|
|
|
|
onChange={this.handleChangeTurnUsername}
|
|
|
|
value={turnUsername}
|
|
|
|
style={{ margin: 10, width: 250 }}
|
|
|
|
/><br></br>
|
|
|
|
|
|
|
|
TURN Server Credential :
|
|
|
|
<input
|
|
|
|
placeholder='ex: superGiGi'
|
|
|
|
onChange={this.handleChangeTurnCredential}
|
|
|
|
value={turnCredential}
|
|
|
|
style={{ margin: 10, width: 250 }}
|
|
|
|
/>
|
|
|
|
|
|
|
|
<br></br><br></br>
|
|
|
|
|
|
|
|
<h2><u>Login</u></h2>
|
|
|
|
|
|
|
|
Username :
|
2021-01-13 14:04:04 +00:00
|
|
|
<input
|
|
|
|
placeholder='user'
|
|
|
|
onChange={this.handleChangeLogin}
|
|
|
|
value={login}
|
2021-01-20 08:25:06 +00:00
|
|
|
style={{ margin: 10, width: 250 }}
|
|
|
|
/><br></br>
|
|
|
|
|
|
|
|
Password :
|
2021-01-13 14:04:04 +00:00
|
|
|
<input
|
|
|
|
placeholder='password'
|
|
|
|
onChange={this.handleChangePassword}
|
|
|
|
value={password}
|
2021-01-20 08:25:06 +00:00
|
|
|
style={{ margin: 10, width: 250 }}
|
|
|
|
/><br></br>
|
|
|
|
|
|
|
|
<button onClick={this.handleLoginClick} style={{ margin: 10, width: 100 }}>LOGIN</button>
|
2021-01-13 14:04:04 +00:00
|
|
|
|
|
|
|
<br></br>
|
2021-01-20 08:25:06 +00:00
|
|
|
|
2021-01-13 14:04:04 +00:00
|
|
|
<h2><u>isCaller:</u> {isCaller ? 'TRUE' : 'FALSE'}</h2>
|
|
|
|
<h2><u>User details:</u></h2>
|
|
|
|
{
|
|
|
|
user && (
|
|
|
|
<React.Fragment>
|
|
|
|
<h4>account_id: {user.account.id}</h4>
|
|
|
|
<h4>user_id: {user.id}</h4>
|
|
|
|
<h4>asset_id: {user.asset.id}</h4>
|
|
|
|
<h4>user_role: {user.user_role.name}</h4>
|
|
|
|
</React.Fragment>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-01-20 08:25:06 +00:00
|
|
|
<h2><u>HUB</u></h2>
|
2021-01-13 14:04:04 +00:00
|
|
|
|
|
|
|
<h4>status: {(hubStatus === 0 ? 'uninitialized' : hubStatus === 1 ? 'connecting' : hubStatus === 2 ? 'connected' : 'connection error' )}</h4>
|
|
|
|
<h4>ARS Sent: {arsSent === true ? 'TRUE' : 'FALSE'}</h4>
|
|
|
|
<h4>Call id: {callId}</h4>
|
|
|
|
|
|
|
|
<br></br>
|
|
|
|
|
|
|
|
<input
|
|
|
|
placeholder='destination_asset_id'
|
|
|
|
onChange={this.handleChangeDestAssetId}
|
|
|
|
value={dest_asset_id}
|
|
|
|
/>
|
|
|
|
|
|
|
|
<button onClick={this.handleClickEvent} disabled={dest_asset_id.length === 0 ? true : false}>Send `video` notify-request</button>
|
|
|
|
|
|
|
|
<br></br>
|
|
|
|
|
|
|
|
<button onClick={this.closeVideo} style={{ margin: 40}} disabled={callId !== null ? false : true}>Close Video</button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2021-01-11 09:48:50 +00:00
|
|
|
export default App;
|