import React from "react";
import { API, WEBSOCKET } from '../../../../LocalConfiguration';
import { Checkbox } from 'react-ui-icheck';
import { withNamespaces } from 'react-i18next';
/**
* Livewtream with Websocket
* @extends Component
* @hideconstructor
*/
class LiveStream extends React.Component {
/**
* @constructs
* @param props
*/
constructor(props) {
super(props);
this.constraints = { audio: false, video: true };
this.mediaElement = React.createRef();
this.state = {
videoPlaying : false,
configuration: {
background: 0,
vis_zones: false,
vis_paths: false,
vis_boxes: false,
vis_header: false
}
}
}
/**
*
*/
componentDidMount() {
fetch(API + "/livestream/" + this.props.match.params.id)
.then(response => response.json())
.then(json => {
this.signaling = new WebSocket(WEBSOCKET + '/livestream_signaling/' + json.sessionId); // handles JSON.stringify/parse
this.pc = new RTCPeerConnection({ iceServers: json.iceServers });
// send any ice candidates to the other peer
this.pc.onicecandidate = ({ candidate }) => {
if (candidate == null) return;
candidate = JSON.parse(JSON.stringify(candidate));
candidate['type'] = 'candidate';
this.signaling.send(JSON.stringify(candidate));
};
// let the "negotiationneeded" event trigger offer generation
this.pc.onnegotiationneeded = async () => {
try {
await this.pc.setLocalDescription(await this.pc.createOffer());
// send the offer to the other peer
this.signaling.send(JSON.stringify(this.pc.localDescription));
}
catch (err) {
console.error(err);
}
};
// once media for a remote track arrives, show it in the remote video element
this.pc.ontrack = (event) => {
// don't set srcObject again if it is already set.
if (this.mediaElement.current.srcObject) return;
this.mediaElement.current.srcObject = event.streams[0];
console.log("video playing");
this.setState({
videoPlaying : true,
})
};
this.signaling.onmessage = async ev => {
let data = JSON.parse(ev.data);
try {
if (data) {
// if we get an offer, we need to reply with an answer
// mozno nam netreba reagovat na offer, zatial to ponechavam
if (data.type == 'offer') {
await this.pc.setRemoteDescription(data);
const stream = await navigator.mediaDevices.getUserMedia(this.constraints);
stream.getTracks().forEach((track) => this.pc.addTrack(track, stream));
await this.pc.setLocalDescription(await this.pc.createAnswer());
this.signaling.send(JSON.stringify(this.pc.localDescription));
} else if (data.type == 'answer') {
await this.pc.setRemoteDescription(data);
} else if (data.type == 'candidate') {
await this.pc.addIceCandidate(data);
} else if (data.type == 'config') {
this.setState({
configuration: data.configuration
});
} else {
console.log('Unsupported SDP type. Your code may differ here.')
}
}
}
catch (err) {
console.error(err);
}
};
this.signaling.onopen = () => this.start();
});
}
/**
*
*/
componentWillUnmount() {
this.pc.close();
this.signaling.close();
}
start = () => {
const offerOptions = {
offerToReceiveVideo: 1,
offerToReceiveAudio: 0
};
let offer = this.pc.createOffer(offerOptions);
offer.then(async value => {
await this.pc.setLocalDescription(value);
// console.log(JSON.stringify(value));
this.signaling.send(JSON.stringify(value))
});
};
/**
*
* @param ev
*/
handleShowZonesChange = (ev) => {
ev.preventDefault();
let config = this.state.configuration;
config.vis_zones = ev.target.checked;
this.signaling.send(JSON.stringify({
configuration: config,
type: 'config'
}));
};
/**
*
* @param ev
*/
handleShowPathsChange = (ev) => {
ev.preventDefault();
let config = this.state.configuration;
config.vis_paths = ev.target.checked;
this.signaling.send(JSON.stringify({
configuration: config,
type: 'config'
}));
};
/**
*
* @param ev
*/
handleShowBoxesChange = (ev) => {
ev.preventDefault();
let config = this.state.configuration;
config.vis_boxes = ev.target.checked;
this.signaling.send(JSON.stringify({
configuration: config,
type: 'config'
}));
};
/**
*
* @param ev
*/
handleShowHeaderChange = (ev) => {
ev.preventDefault();
let config = this.state.configuration;
config.vis_header = ev.target.checked;
this.signaling.send(JSON.stringify({
configuration: config,
type: 'config'
}));
};
/**
*
* @param ev
*/
handleBackgroundChange = (ev) => {
ev.preventDefault();
let config = this.state.configuration;
config.background = ev.target.value;
this.signaling.send(JSON.stringify({
configuration: config,
type: 'config'
}));
};
/**
*
* @returns {*}
*/
render() {
const { t } = this.props;
return (
<div className="content-wrapper">
<section className="content-header">
<h1>
{t('monitoredArea.livestream')}
<small>{t('livestream.liveFootageFromMonitoredArea')}</small>
</h1>
<ol className="breadcrumb">
<li><a href="/live"><i className="fa fa-play"></i>{t('sidebar.liveStreams')}</a></li>
<li><a href={"/live/" + this.props.match.params.id}>{t('monitoredArea.livestream')}</a></li>
</ol>
<br/>
<div className="row">
<div className="col-md-12">
<div className="box">
<div className="box-header">
<div className="row-md-flex-center">
<div className="col-md-3">
<select className="form-control"
value={this.state.configuration.background}
onChange={this.handleBackgroundChange}>
<option value="0">{t('livestream.noBackground')}</option>
<option value="1">{t('livestream.capturedVideo')}</option>
<option value="2">{t('livestream.foregroundMask')}</option>
<option value="3">{t('livestream.objectSpeed')}</option>
readOnly={true}
</select>
</div>
<div className="col-md-2">
<Checkbox
checkboxClass="icheckbox_square-blue"
checked={this.state.configuration.vis_header}
label={" " + t('livestream.header')}
labelClassName="font-normal"
onChange={this.handleShowHeaderChange}
/>
</div>
<div className="col-md-2">
<Checkbox
checkboxClass="icheckbox_square-blue"
checked={this.state.configuration.vis_zones}
label={" " + t('livestream.zones')}
labelClassName="font-normal"
onChange={this.handleShowZonesChange}
/>
</div>
<div className="col-md-2">
<Checkbox
checkboxClass="icheckbox_square-blue"
checked={this.state.configuration.vis_boxes}
label={" " + t('livestream.boxes')}
labelClassName="font-normal"
onChange={this.handleShowBoxesChange}
/>
</div>
<div className="col-md-2">
<Checkbox
checkboxClass="icheckbox_square-blue"
checked={this.state.configuration.vis_paths}
label={" " + t('livestream.paths')}
labelClassName="font-normal"
onChange={this.handleShowPathsChange}
/>
</div>
</div>
</div>
<div className="box-body">
<video width="70%" ref={this.mediaElement} autoPlay={true} className="center-video"/>
{this.state.videoPlaying ? null : <div className="container-center loading-overlay">
<i className="fa fa-refresh fa-spin loading"/>
</div>}
</div>
</div>
</div>
</div>
</section>
</div>
)
}
}
export default withNamespaces()(LiveStream);