339 lines
14 KiB
JavaScript
339 lines
14 KiB
JavaScript
import axios from 'axios';
|
|
import { OpenVidu } from 'openvidu-browser';
|
|
import React, { Component } from 'react';
|
|
import './App.css';
|
|
import UserVideoComponent from './UserVideoComponent';
|
|
|
|
const OPENVIDU_SERVER_URL = 'https://' + window.location.hostname + ':4443';
|
|
const OPENVIDU_SERVER_SECRET = 'MY_SECRET';
|
|
|
|
|
|
class App extends Component {
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
mySessionId: 'SessionA',
|
|
myUserName: 'Participant' + Math.floor(Math.random() * 100),
|
|
session: undefined,
|
|
mainStreamManager: undefined,
|
|
publisher: undefined,
|
|
subscribers: [],
|
|
};
|
|
|
|
this.joinSession = this.joinSession.bind(this);
|
|
this.leaveSession = this.leaveSession.bind(this);
|
|
this.handleChangeSessionId = this.handleChangeSessionId.bind(this);
|
|
this.handleChangeUserName = this.handleChangeUserName.bind(this);
|
|
this.handleMainVideoStream = this.handleMainVideoStream.bind(this);
|
|
this.onbeforeunload = this.onbeforeunload.bind(this);
|
|
}
|
|
|
|
componentDidMount() {
|
|
window.addEventListener('beforeunload', this.onbeforeunload);
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
window.removeEventListener('beforeunload', this.onbeforeunload);
|
|
}
|
|
|
|
onbeforeunload(event) {
|
|
this.leaveSession();
|
|
}
|
|
|
|
handleChangeSessionId(e) {
|
|
this.setState({
|
|
mySessionId: e.target.value,
|
|
});
|
|
}
|
|
|
|
handleChangeUserName(e) {
|
|
this.setState({
|
|
myUserName: e.target.value,
|
|
});
|
|
}
|
|
|
|
handleMainVideoStream(stream) {
|
|
if (this.state.mainStreamManager !== stream) {
|
|
this.setState({
|
|
mainStreamManager: stream
|
|
});
|
|
}
|
|
}
|
|
|
|
deleteSubscriber(streamManager) {
|
|
let subscribers = this.state.subscribers;
|
|
let index = subscribers.indexOf(streamManager, 0);
|
|
if (index > -1) {
|
|
subscribers.splice(index, 1);
|
|
this.setState({
|
|
subscriber: subscribers,
|
|
});
|
|
}
|
|
}
|
|
|
|
joinSession() {
|
|
// --- 1) Get an OpenVidu object ---
|
|
|
|
this.OV = new OpenVidu();
|
|
|
|
// --- 2) Init a session ---
|
|
|
|
this.setState(
|
|
{
|
|
session: this.OV.initSession(),
|
|
},
|
|
() => {
|
|
var mySession = this.state.session;
|
|
|
|
// --- 3) Specify the actions when events take place in the session ---
|
|
|
|
// On every new Stream received...
|
|
mySession.on('streamCreated', (event) => {
|
|
// Subscribe to the Stream to receive it. Second parameter is undefined
|
|
// so OpenVidu doesn't create an HTML video by its own
|
|
var subscriber = mySession.subscribe(event.stream, undefined);
|
|
var subscribers = this.state.subscribers;
|
|
subscribers.push(subscriber);
|
|
|
|
// Update the state with the new subscribers
|
|
this.setState({
|
|
subscribers: subscribers,
|
|
});
|
|
});
|
|
|
|
// On every Stream destroyed...
|
|
mySession.on('streamDestroyed', (event) => {
|
|
|
|
// Remove the stream from 'subscribers' array
|
|
this.deleteSubscriber(event.stream.streamManager);
|
|
});
|
|
|
|
// --- 4) Connect to the session with a valid user token ---
|
|
|
|
// 'getToken' method is simulating what your server-side should do.
|
|
// 'token' parameter should be retrieved and returned by your own backend
|
|
this.getToken().then((token) => {
|
|
// First param is the token got from OpenVidu Server. Second param can be retrieved by every user on event
|
|
// 'streamCreated' (property Stream.connection.data), and will be appended to DOM as the user's nickname
|
|
mySession
|
|
.connect(
|
|
token,
|
|
{ clientData: this.state.myUserName },
|
|
)
|
|
.then(() => {
|
|
|
|
// --- 5) Get your own camera stream ---
|
|
|
|
// Init a publisher passing undefined as targetElement (we don't want OpenVidu to insert a video
|
|
// element: we will manage it on our own) and with the desired properties
|
|
let publisher = this.OV.initPublisher(undefined, {
|
|
audioSource: undefined, // The source of audio. If undefined default microphone
|
|
videoSource: undefined, // The source of video. If undefined default webcam
|
|
publishAudio: true, // Whether you want to start publishing with your audio unmuted or not
|
|
publishVideo: true, // Whether you want to start publishing with your video enabled or not
|
|
resolution: '640x480', // The resolution of your video
|
|
frameRate: 30, // The frame rate of your video
|
|
insertMode: 'APPEND', // How the video is inserted in the target element 'video-container'
|
|
mirror: false, // Whether to mirror your local video or not
|
|
});
|
|
|
|
// --- 6) Publish your stream ---
|
|
|
|
mySession.publish(publisher);
|
|
|
|
// Set the main video in the page to display our webcam and store our Publisher
|
|
this.setState({
|
|
mainStreamManager: publisher,
|
|
publisher: publisher,
|
|
});
|
|
})
|
|
.catch((error) => {
|
|
console.log('There was an error connecting to the session:', error.code, error.message);
|
|
});
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
leaveSession() {
|
|
|
|
// --- 7) Leave the session by calling 'disconnect' method over the Session object ---
|
|
|
|
const mySession = this.state.session;
|
|
|
|
if (mySession) {
|
|
mySession.disconnect();
|
|
}
|
|
|
|
// Empty all properties...
|
|
this.OV = null;
|
|
this.setState({
|
|
session: undefined,
|
|
subscribers: [],
|
|
mySessionId: 'SessionA',
|
|
myUserName: 'Participant' + Math.floor(Math.random() * 100),
|
|
mainStreamManager: undefined,
|
|
publisher: undefined
|
|
});
|
|
}
|
|
|
|
render() {
|
|
const mySessionId = this.state.mySessionId;
|
|
const myUserName = this.state.myUserName;
|
|
|
|
return (
|
|
<div className="container">
|
|
{this.state.session === undefined ? (
|
|
<div id="join">
|
|
<div id="img-div">
|
|
<img src="resources/images/openvidu_grey_bg_transp_cropped.png" alt="OpenVidu logo" />
|
|
</div>
|
|
<div id="join-dialog" className="jumbotron vertical-center">
|
|
<h1> Join a video session </h1>
|
|
<form className="form-group" onSubmit={this.joinSession}>
|
|
<p>
|
|
<label>Participant: </label>
|
|
<input
|
|
className="form-control"
|
|
type="text"
|
|
id="userName"
|
|
value={myUserName}
|
|
onChange={this.handleChangeUserName}
|
|
required
|
|
/>
|
|
</p>
|
|
<p>
|
|
<label> Session: </label>
|
|
<input
|
|
className="form-control"
|
|
type="text"
|
|
id="sessionId"
|
|
value={mySessionId}
|
|
onChange={this.handleChangeSessionId}
|
|
required
|
|
/>
|
|
</p>
|
|
<p className="text-center">
|
|
<input className="btn btn-lg btn-success" name="commit" type="submit" value="JOIN" />
|
|
</p>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
|
|
{this.state.session !== undefined ? (
|
|
<div id="session">
|
|
<div id="session-header">
|
|
<h1 id="session-title">{mySessionId}</h1>
|
|
<input
|
|
className="btn btn-large btn-danger"
|
|
type="button"
|
|
id="buttonLeaveSession"
|
|
onClick={this.leaveSession}
|
|
value="Leave session"
|
|
/>
|
|
</div>
|
|
|
|
{this.state.mainStreamManager !== undefined ? (
|
|
<div id="main-video" className="col-md-6">
|
|
<UserVideoComponent streamManager={this.state.mainStreamManager} />
|
|
</div>
|
|
) : null}
|
|
<div id="video-container" className="col-md-6">
|
|
{this.state.publisher !== undefined ? (
|
|
<div className="stream-container col-md-6 col-xs-6" onClick={() => this.handleMainVideoStream(this.state.publisher)}>
|
|
<UserVideoComponent
|
|
streamManager={this.state.publisher} />
|
|
</div>
|
|
) : null}
|
|
{this.state.subscribers.map((sub, i) => (
|
|
<div key={i} className="stream-container col-md-6 col-xs-6" onClick={() => this.handleMainVideoStream(sub)}>
|
|
<UserVideoComponent streamManager={sub} />
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* --------------------------
|
|
* SERVER-SIDE RESPONSIBILITY
|
|
* --------------------------
|
|
* These methods retrieve the mandatory user token from OpenVidu Server.
|
|
* This behavior MUST BE IN YOUR SERVER-SIDE IN PRODUCTION (by using
|
|
* the API REST, openvidu-java-client or openvidu-node-client):
|
|
* 1) Initialize a session in OpenVidu Server (POST /api/sessions)
|
|
* 2) Generate a token in OpenVidu Server (POST /api/tokens)
|
|
* 3) The token must be consumed in Session.connect() method
|
|
*/
|
|
|
|
getToken() {
|
|
return this.createSession(this.state.mySessionId).then((sessionId) => this.createToken(sessionId));
|
|
}
|
|
|
|
createSession(sessionId) {
|
|
return new Promise((resolve, reject) => {
|
|
var data = JSON.stringify({ customSessionId: sessionId });
|
|
axios
|
|
.post(OPENVIDU_SERVER_URL + '/api/sessions', data, {
|
|
headers: {
|
|
Authorization: 'Basic ' + btoa('OPENVIDUAPP:' + OPENVIDU_SERVER_SECRET),
|
|
'Content-Type': 'application/json',
|
|
},
|
|
})
|
|
.then((response) => {
|
|
console.log('CREATE SESION', response);
|
|
resolve(response.data.id);
|
|
})
|
|
.catch((response) => {
|
|
var error = Object.assign({}, response);
|
|
if (error.response.status === 409) {
|
|
resolve(sessionId);
|
|
} else {
|
|
console.log(error);
|
|
console.warn(
|
|
'No connection to OpenVidu Server. This may be a certificate error at ' +
|
|
OPENVIDU_SERVER_URL,
|
|
);
|
|
if (
|
|
window.confirm(
|
|
'No connection to OpenVidu Server. This may be a certificate error at "' +
|
|
OPENVIDU_SERVER_URL +
|
|
'"\n\nClick OK to navigate and accept it. ' +
|
|
'If no certificate warning is shown, then check that your OpenVidu Server is up and running at "' +
|
|
OPENVIDU_SERVER_URL +
|
|
'"',
|
|
)
|
|
) {
|
|
window.location.assign(OPENVIDU_SERVER_URL + '/accept-certificate');
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
createToken(sessionId) {
|
|
return new Promise((resolve, reject) => {
|
|
var data = JSON.stringify({ session: sessionId });
|
|
axios
|
|
.post(OPENVIDU_SERVER_URL + '/api/tokens', data, {
|
|
headers: {
|
|
Authorization: 'Basic ' + btoa('OPENVIDUAPP:' + OPENVIDU_SERVER_SECRET),
|
|
'Content-Type': 'application/json',
|
|
},
|
|
})
|
|
.then((response) => {
|
|
console.log('TOKEN', response);
|
|
resolve(response.data.token);
|
|
})
|
|
.catch((error) => reject(error));
|
|
});
|
|
}
|
|
}
|
|
|
|
export default App;
|