livstream-player
Version:
A lightweight livestream player SDK for React apps
959 lines • 50.2 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { ReloadOutlined } from '@ant-design/icons';
import { Col, Row, Tag, Tooltip } from 'antd';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import IconCameraOnvif from '@assets/icons/camera_onvif.svg';
import IconCameraRtsp from '@assets/icons/camera_rtsp.svg';
import Info from '@assets/icons/info.svg';
import Info11 from '@assets/icons/info_11.svg';
import Info12 from '@assets/icons/info_12.svg';
import Info3 from '@assets/icons/info_3.svg';
import Info5 from '@assets/icons/info_5.svg';
import Info6 from '@assets/icons/info_6.svg';
import Info7 from '@assets/icons/info_7.svg';
import LiveIconOn from '@assets/icons/live-streaming-on.svg';
import OffStream from '@assets/icons/off_stream.svg';
import PlayIconNoActive from '@assets/icons/play-button-no-active.svg';
import PlayIcon from '@assets/icons/play-button.svg';
import Thumbnail from '@assets/images/thumbnail.jpg';
import * as SERVICE from '@constants/service.types.js';
import { ZOOM_STATE } from '@constants/system.keys.js';
import useWindowDimensions from '@hooks/useWindowDimensions.js';
import { changeDialPadData, getEndStreamReturn, getEndStreamUtraFastReturn, socketEmitEvent } from '@redux/actions.js';
import { calcPercentForVideo } from '@utils/css.js';
import PlayerComponent from '@components/Player/index.jsx';
import './index.scss';
import { editOffer, generateSdpFragment, parseOffer, supportsNonAdvertisedCodec } from './utils.js';
import { useLivestream } from '../../contexts/contexts.js';
const RETRY_PAUSE = 2000;
const pc_config = {
iceServers: [{ urls: 'stun:stun01.beex.vn:3478' }],
bundlePolicy: 'balanced',
rtcpMuxPolicy: 'negotiate',
sdpSemantics: 'unified-plan'
};
const pc_constraints = { optional: [{ DtlsSrtpKeyAgreement: true }] };
const ViewStreamComponent = ({ serial, gatewaySerial, name, typeCamera, state, resolution, iconSize, thumbnail, zoomState, serviceId, isViewAll, permission, isAllowStreaming, allScreen }) => {
var _a, _b, _c;
const [countRetryStream] = useState(1);
const [isFullscreen, setIsFullscreen] = useState(false);
const dispatch = useDispatch();
const { width: windowWidth, height: windowHeight } = useWindowDimensions();
const { streamInfo } = useLivestream();
const { namespace, listDeviceContact, streamToken = (_b = (_a = Object.fromEntries(new URLSearchParams(location.search))) === null || _a === void 0 ? void 0 : _a.streamToken) !== null && _b !== void 0 ? _b : streamInfo === null || streamInfo === void 0 ? void 0 : streamInfo.streamToken } = useSelector((state) => state);
const peerConnection = useRef({});
const [streaming, setStreaming] = useState();
const [, setWaitForWatch] = useState(0);
const [, setWaitForStart] = useState(0);
const [streamSerial, setStreamSerial] = useState(serial);
const [isConnected, setIsConnected] = useState(false);
const [loading, setLoading] = useState(false);
const refTryLoad = useRef();
const [tryLoadTimer, setTryLoadTimer] = useState(10);
const [isLoadReload, setIsLoadReload] = useState(false);
const [onPlay, setOnPlay] = useState(false);
const [sourceObject, setSourceObject] = useState();
const [checkTransferZoomState] = useState(false);
const [zoomStatePlayer, setZoomStatePlayer] = useState();
const [zoomStateDefault, setZoomStateDefault] = useState();
const [nonAdvertisedCodecs, setNonAdvertisedCodecs] = useState([]);
const [queuedCandidates, setQueuedCandidates] = useState([]);
const [sessionUrl, setSessionUrl] = useState('');
const [offerData, setOfferData] = useState('');
const [internal, setInternal] = useState(false);
const refVideoStreamWS = useRef(null);
const refVideoStreamPC = useRef(null);
const restartTimeout = useRef();
const onError = useCallback((err) => {
console.log('Error: ========>', err);
if (!restartTimeout.current) {
if (refVideoStreamPC.current !== null) {
refVideoStreamPC.current.close();
refVideoStreamPC.current = null;
}
restartTimeout.current = window.setTimeout(() => {
restartTimeout.current = null;
loadStream();
}, RETRY_PAUSE);
if (sessionUrl) {
fetch(sessionUrl, {
method: 'DELETE'
});
}
setSessionUrl('');
setQueuedCandidates([]);
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[sessionUrl]);
useEffect(() => {
(() => __awaiter(void 0, void 0, void 0, function* () {
if (gatewaySerial) {
try {
const url = `https://${gatewaySerial}.ezcam.vn:9090/info`;
const promise = yield fetch(url, {
signal: AbortSignal.timeout(1000)
});
const response = yield promise.json();
setInternal((response === null || response === void 0 ? void 0 : response.localMedia) === 'on');
}
catch (error) {
console.log('error', error);
setInternal(false);
}
}
}))();
}, [gatewaySerial]);
useEffect(() => {
if (zoomState) {
setZoomStatePlayer(zoomState);
}
}, [zoomState]);
const compare = (a, b) => {
if (a.resolution > b.resolution) {
return -1;
}
if (a.resolution < b.resolution) {
return 1;
}
return 0;
};
useEffect(() => {
var _a, _b, _c, _d, _e, _f, _g, _h;
const subSourcesTemp = (streaming === null || streaming === void 0 ? void 0 : streaming.subSources)
? JSON.parse(streaming === null || streaming === void 0 ? void 0 : streaming.subSources)
: null;
if ((subSourcesTemp === null || subSourcesTemp === void 0 ? void 0 : subSourcesTemp.length) > 0 && zoomStatePlayer && zoomState) {
const dataArr = (_a = subSourcesTemp === null || subSourcesTemp === void 0 ? void 0 : subSourcesTemp.filter((item) => item.resolution >= 0 && item.zoomState >= 0)) === null || _a === void 0 ? void 0 : _a.sort(compare);
let dataArrTemp = [...dataArr];
(_b = zoomState === null || zoomState === void 0 ? void 0 : zoomState.dataZoomStates) === null || _b === void 0 ? void 0 : _b.map((item) => {
if (!(dataArrTemp === null || dataArrTemp === void 0 ? void 0 : dataArrTemp.find((item1) => (item1 === null || item1 === void 0 ? void 0 : item1.resolution) === (item === null || item === void 0 ? void 0 : item.resolution)))) {
dataArrTemp.push(item);
}
});
const dataArrTemp1 = dataArrTemp === null || dataArrTemp === void 0 ? void 0 : dataArrTemp.sort(compare);
if ((dataArrTemp1 === null || dataArrTemp1 === void 0 ? void 0 : dataArrTemp1.length) > 0) {
if (((_c = streaming === null || streaming === void 0 ? void 0 : streaming.mainSource) === null || _c === void 0 ? void 0 : _c.resolution) >= 0 &&
((_d = streaming === null || streaming === void 0 ? void 0 : streaming.mainSource) === null || _d === void 0 ? void 0 : _d.zoomState) >= 0) {
setZoomStatePlayer((state) => {
var _a, _b, _c, _d;
return ({
resolution: (state === null || state === void 0 ? void 0 : state.resolution) || ((_a = streaming === null || streaming === void 0 ? void 0 : streaming.mainSource) === null || _a === void 0 ? void 0 : _a.resolution) || 720,
zoomState: (_d = (_b = state === null || state === void 0 ? void 0 : state.zoomState) !== null && _b !== void 0 ? _b : (_c = streaming === null || streaming === void 0 ? void 0 : streaming.mainSource) === null || _c === void 0 ? void 0 : _c.zoomState) !== null && _d !== void 0 ? _d : ZOOM_STATE.FOUR,
dataZoomStates: dataArrTemp1 === null || dataArrTemp1 === void 0 ? void 0 : dataArrTemp1.map((item) => ({
resolution: item === null || item === void 0 ? void 0 : item.resolution,
zoomState: item === null || item === void 0 ? void 0 : item.zoomState
}))
});
});
setZoomStateDefault({
resolution: (state === null || state === void 0 ? void 0 : state.resolution) || ((_e = streaming === null || streaming === void 0 ? void 0 : streaming.mainSource) === null || _e === void 0 ? void 0 : _e.resolution) || 720,
zoomState: (_h = (_f = state === null || state === void 0 ? void 0 : state.zoomState) !== null && _f !== void 0 ? _f : (_g = streaming === null || streaming === void 0 ? void 0 : streaming.mainSource) === null || _g === void 0 ? void 0 : _g.zoomState) !== null && _h !== void 0 ? _h : ZOOM_STATE.FOUR,
dataZoomStates: dataArrTemp1 === null || dataArrTemp1 === void 0 ? void 0 : dataArrTemp1.map((item) => {
var _a, _b;
return ({
resolution: (_a = item === null || item === void 0 ? void 0 : item.resolution) !== null && _a !== void 0 ? _a : 720,
zoomState: (_b = item === null || item === void 0 ? void 0 : item.zoomState) !== null && _b !== void 0 ? _b : ZOOM_STATE.FOUR
});
})
});
}
else {
setZoomStatePlayer((state) => (Object.assign(Object.assign({}, state), { dataZoomStates: dataArrTemp1 === null || dataArrTemp1 === void 0 ? void 0 : dataArrTemp1.map((item) => ({
resolution: item === null || item === void 0 ? void 0 : item.resolution,
zoomState: item === null || item === void 0 ? void 0 : item.zoomState
})) })));
setZoomStateDefault(Object.assign(Object.assign({}, zoomState), { dataZoomStates: dataArrTemp1 === null || dataArrTemp1 === void 0 ? void 0 : dataArrTemp1.map((item) => {
var _a, _b;
return ({
resolution: (_a = item === null || item === void 0 ? void 0 : item.resolution) !== null && _a !== void 0 ? _a : 720,
zoomState: (_b = item === null || item === void 0 ? void 0 : item.zoomState) !== null && _b !== void 0 ? _b : ZOOM_STATE.FOUR
});
}) }));
}
}
}
}, [streaming]);
const createStream = useCallback((dataBody) => {
var _a, _b;
const data = {
serial: dataBody.serial,
namespace: dataBody.namespace,
type: dataBody.type,
qualityLevel: (_b = (_a = dataBody.qualityLevel) !== null && _a !== void 0 ? _a : zoomState === null || zoomState === void 0 ? void 0 : zoomState.zoomState) !== null && _b !== void 0 ? _b : ZOOM_STATE.FOUR,
retry: dataBody.retry,
serviceId: dataBody.serviceId
};
const requestId = new Date().getTime();
let dataObj = {
appServiceType: SERVICE.START_STREAM_MONITOR,
requestId,
body: JSON.stringify(data)
};
setWaitForWatch(requestId);
dispatch(socketEmitEvent(dataObj));
}, [dispatch]);
const createRtspStream = useCallback((dataBody) => {
const requestId = new Date().getTime();
let dataObj = {
appServiceType: SERVICE.CREATE_RTSP_STREAM,
requestId,
body: JSON.stringify(dataBody)
};
dispatch(socketEmitEvent(dataObj));
}, [dispatch]);
const watchStream = useCallback((dataBody) => {
const requestId = new Date().getTime();
let dataObj = {
appServiceType: SERVICE.WATCH_STREAM,
requestId,
body: JSON.stringify(dataBody)
};
setWaitForWatch(0);
setWaitForStart(requestId);
dispatch(socketEmitEvent(dataObj));
}, [dispatch]);
const handleTryLoad = () => {
setIsLoadReload(true);
refTryLoad.current = setInterval(() => {
setTryLoadTimer((timer) => timer - 1);
}, 1000);
//Create stream on reload
setStreamSerial(serial);
let dataBody = {
namespace,
serial,
type: internal ? 'internal' : 'external',
serviceId,
retry: 0
};
createStream(dataBody);
};
useEffect(() => {
if (isViewAll) {
setOnPlay(true);
}
}, [isViewAll]);
useEffect(() => {
if (tryLoadTimer === 0) {
setIsLoadReload(false);
clearInterval(refTryLoad.current);
setWaitForWatch(0);
setTryLoadTimer(10);
}
}, [tryLoadTimer]);
useEffect(() => {
if (streaming && streaming.started && !isConnected) {
clearInterval(refTryLoad.current);
setIsConnected(true);
}
}, [isConnected, streaming]);
useEffect(() => {
const { serial, streamMonitorId, path, wsHost, wssPort, subSources, zoomState, resolution, protocol, hlsPort, version } = streamInfo;
setTimeout(() => {
setStreaming({
serial,
streamMonitorId: Number(streamMonitorId),
streamId: null,
sdpOffer: null,
started: false,
status: null,
path,
wsHost,
wssPort: Number(wssPort),
subSources: JSON.parse(subSources),
protocol,
hlsPort: hlsPort === 'null' ? null : Number(hlsPort),
version,
mainSource: {
zoomState: Number(zoomState),
resolution: Number(resolution)
}
});
});
}, [streamInfo]);
const updateStream = useCallback((requestId, dispatch, streamId, candidate, isDone) => {
let dataBody = {
namespace,
streamId,
candidate,
isDone: isDone
};
let dataObj = {
appServiceType: SERVICE.UPDATE_STREAM,
requestId,
body: JSON.stringify(dataBody)
};
dispatch(socketEmitEvent(dataObj));
}, [namespace]);
// Create Stream
useEffect(() => {
if (onPlay && state && serial && serviceId) {
setLoading(true);
setStreamSerial(serial);
let dataBody = {
namespace,
serial,
type: internal ? 'internal' : 'external',
serviceId,
retry: 0
};
createStream(dataBody);
}
}, [createStream, internal, namespace, onPlay, serial, serviceId, state]);
const onChangeHover = useCallback(() => {
if (serial && namespace) {
let dataBody = {
namespace,
serial
};
let dataObj = {
appServiceType: SERVICE.GET_DEVICE_CONTACT_INFO,
requestId: new Date().getTime(),
body: JSON.stringify(dataBody)
};
dispatch(socketEmitEvent(dataObj));
}
}, [serial, namespace, dispatch]);
const restartCallBack = useCallback((dataObject) => {
if (streaming && serial) {
let refTimeOut;
if (refTimeOut) {
clearInterval(refTimeOut);
}
const { streamId, serial } = streaming;
if (streamId) {
dispatch(getEndStreamReturn(streamId));
}
else {
dispatch(getEndStreamUtraFastReturn(serial));
}
refTimeOut = setTimeout(() => {
let dataBody = {
namespace,
serial,
type: internal ? 'internal' : 'external',
serviceId,
qualityLevel: dataObject === null || dataObject === void 0 ? void 0 : dataObject.qualityLevel,
retry: 1
};
createStream(dataBody);
}, 1000);
}
}, [createStream, dispatch, internal, namespace, serial, serviceId, streaming]);
const restartLoadingCallBack = useCallback((dataObject) => {
if (serial) {
let refTimeOut;
if (refTimeOut) {
clearInterval(refTimeOut);
}
dispatch(getEndStreamUtraFastReturn(serial));
refTimeOut = setTimeout(() => {
let dataBody = {
namespace,
serial,
type: internal ? 'internal' : 'external',
serviceId,
qualityLevel: dataObject === null || dataObject === void 0 ? void 0 : dataObject.qualityLevel,
retry: 1
};
createStream(dataBody);
}, 1000);
}
}, [createStream, dispatch, internal, namespace, serial, serviceId]);
const restartQualityCallBack = useCallback((dataObject) => {
if (streaming && serial) {
let refTimeOut;
if (refTimeOut) {
clearInterval(refTimeOut);
}
const { streamId, serial } = streaming;
if (streamId) {
dispatch(getEndStreamReturn(streamId));
}
else {
dispatch(getEndStreamUtraFastReturn(serial));
}
refTimeOut = setTimeout(() => {
let dataBody = {
namespace,
serial,
type: internal ? 'internal' : 'external',
serviceId,
qualityLevel: dataObject === null || dataObject === void 0 ? void 0 : dataObject.qualityLevel,
retry: 0
};
createStream(dataBody);
}, 1000);
}
}, [createStream, dispatch, internal, namespace, serial, serviceId, streaming]);
useEffect(() => {
if (streaming &&
streaming.serial &&
!streaming.streamId &&
streaming.streamMonitorId &&
!streaming.sdpOffer &&
!streaming.started &&
!streaming.path &&
!streaming.wsHost &&
!streaming.wssPort) {
let dataBody = {
namespace,
serial: streaming.serial,
streamMonitorId: streaming.streamMonitorId,
serviceId
};
createRtspStream(dataBody);
}
}, [namespace, streaming, createRtspStream, serviceId]);
const sendLocalCandidates = useCallback((candidates, url) => {
if (url && offerData && streamToken) {
fetch(url, {
method: 'PATCH',
headers: {
'Content-Type': 'application/trickle-ice-sdpfrag',
'If-Match': '*',
Authorization: `BeeX a:${streamToken}`
},
body: generateSdpFragment(offerData, candidates)
})
.then((res) => {
switch (res.status) {
case 204:
break;
case 404:
throw new Error('stream not found');
default:
throw new Error(`bad status code ${res.status}`);
}
})
.catch((err) => {
console.log('Error ===========> ', err);
onError(err.toString());
});
}
}, [sessionUrl, offerData, streamToken, onError]);
const onLocalCandidate = useCallback((evt) => {
if (restartTimeout.current) {
return;
}
if (evt.candidate !== null) {
if (sessionUrl === '') {
setQueuedCandidates((prev) => [...prev, evt.candidate]);
}
else {
sendLocalCandidates([evt.candidate]);
}
}
}, [sendLocalCandidates, sessionUrl]);
const onConnectionState = useCallback(() => {
if (restartTimeout.current !== null) {
return;
}
if (refVideoStreamPC.current.iceConnectionState === 'disconnected') {
onError('peer connection closed');
}
}, [onError]);
const onTrack = (evt) => {
setSourceObject(evt.streams[0]);
};
const onRemoteAnswer = useCallback((sdp, sessionUrl) => {
if (restartTimeout.current) {
return;
}
refVideoStreamPC.current
.setRemoteDescription(new RTCSessionDescription({
type: 'answer',
sdp
}))
.then(() => {
if (queuedCandidates.length !== 0) {
sendLocalCandidates(queuedCandidates, sessionUrl);
setQueuedCandidates([]);
}
})
.catch((err) => {
console.log('Error ========>', err);
onError(err.toString());
});
}, [onError, queuedCandidates, sendLocalCandidates]);
const sendOffer = (offer) => {
if (streaming &&
(streaming === null || streaming === void 0 ? void 0 : streaming.path) &&
(streaming === null || streaming === void 0 ? void 0 : streaming.wsHost) &&
(streaming === null || streaming === void 0 ? void 0 : streaming.wssPort)) {
const baseEndpoint = `https://${streaming.wsHost}:${streaming.wssPort}`;
fetch(`${baseEndpoint}${streaming.path}/whep`, {
method: 'POST',
headers: {
'Content-Type': 'application/sdp',
Authorization: 'BeeX a:' + streamToken
},
body: offer.sdp
})
.then((res) => {
switch (res.status) {
case 201:
break;
case 404:
throw new Error('stream not found');
case 400:
return res.json().then((e) => {
throw new Error(e.error);
});
default:
throw new Error(`bad status code ${res.status}`);
}
const sessionUrl = `${baseEndpoint}${res.headers.get('location')}`;
setSessionUrl(`${baseEndpoint}${res.headers.get('location')}`);
return res.text().then((sdp) => onRemoteAnswer(sdp, sessionUrl));
})
.catch((err) => {
onError(err.toString());
});
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
const createOffer = () => {
refVideoStreamPC.current
.createOffer()
.then((offer) => {
offer.sdp = editOffer(offer.sdp, nonAdvertisedCodecs);
setOfferData(parseOffer(offer.sdp));
refVideoStreamPC.current
.setLocalDescription(offer)
.then(() => {
sendOffer(offer);
})
.catch((err) => {
onError(err.toString());
});
})
.catch((err) => {
onError(err.toString());
});
};
const loadStream = useCallback(() => {
// create peer connection webrtc
refVideoStreamPC.current = new RTCPeerConnection(pc_config);
const direction = 'sendrecv';
refVideoStreamPC.current.addTransceiver('video', { direction });
refVideoStreamPC.current.addTransceiver('audio', { direction });
refVideoStreamPC.current.onicecandidate = (evt) => onLocalCandidate(evt);
refVideoStreamPC.current.oniceconnectionstatechange = () => onConnectionState();
refVideoStreamPC.current.ontrack = (evt) => onTrack(evt);
createOffer();
}, [createOffer, onConnectionState, onLocalCandidate]);
const getNonAdvertisedCodecs = useCallback(() => {
Promise.all([
['pcma/8000/2'],
[
'multiopus/48000/6',
'channel_mapping=0,4,1,2,3,5;num_streams=4;coupled_streams=2'
],
['L16/48000/2']
].map((c) => supportsNonAdvertisedCodec(c[0], c[1]).then((r) => (r ? c[0] : false))))
.then((c) => c.filter((e) => e !== false))
.then((codecs) => {
setNonAdvertisedCodecs(codecs);
loadStream();
});
}, [loadStream]);
useEffect(() => {
if (streaming &&
streaming.serial &&
!streaming.streamId &&
streaming.streamMonitorId &&
!streaming.sdpOffer &&
!streaming.started &&
streaming.path &&
streaming.wsHost &&
streaming.wssPort &&
!streaming.hlsPort &&
countRetryStream) {
const start = () => {
const onRemoteCandidate = (msg) => {
if (refVideoStreamPC.current === null) {
return;
}
refVideoStreamPC.current.addIceCandidate(JSON.parse(msg.data));
};
const onRemoteDescription = (msg) => {
if (refVideoStreamPC.current === null ||
refVideoStreamWS.current === null) {
return;
}
if (refVideoStreamPC.current) {
const d = JSON.parse(msg.data);
if (d && d instanceof Array) {
refVideoStreamPC.current = new RTCPeerConnection({
iceServers: d
});
}
else {
refVideoStreamPC.current.setRemoteDescription(new RTCSessionDescription(JSON.parse(msg.data)));
}
}
refVideoStreamWS.current.onmessage = (msg) => onRemoteCandidate(msg);
};
const onIceCandidate = (evt) => {
if (refVideoStreamWS.current === null) {
return;
}
if (evt.candidate !== null) {
if (evt.candidate.candidate !== '') {
refVideoStreamWS.current.send(JSON.stringify(evt.candidate));
}
}
};
const onIceServers = (msg) => {
if (refVideoStreamWS.current === null) {
return;
}
const iceServers = JSON.parse(msg.data);
refVideoStreamPC.current = new RTCPeerConnection({
iceServers
});
refVideoStreamWS.current.onmessage = (msg) => onRemoteDescription(msg);
refVideoStreamPC.current.onicecandidate = (evt) => onIceCandidate(evt);
refVideoStreamPC.current.oniceconnectionstatechange = () => {
if (refVideoStreamPC.current === null) {
return;
}
// should restart
if (refVideoStreamPC.current.iceConnectionState === 'disconnected') {
console.log('Retry connect when iceConnectionState is disconnected');
start();
}
};
refVideoStreamPC.current.ontrack = (evt) => {
setSourceObject(evt.streams[0]);
};
const direction = 'sendrecv';
refVideoStreamPC.current.addTransceiver('video', { direction });
refVideoStreamPC.current.addTransceiver('audio', { direction });
refVideoStreamPC.current.createOffer().then((desc) => {
if (refVideoStreamPC.current === null ||
refVideoStreamWS.current === null) {
return;
}
refVideoStreamPC.current.setLocalDescription(desc);
setWaitForStart(0);
refVideoStreamWS.current.send(JSON.stringify(desc));
});
};
refVideoStreamWS.current = new WebSocket(`wss://${streaming.wsHost}:${streaming.wssPort}${streaming.path}/ws`);
refVideoStreamWS.current.onerror = () => {
if (refVideoStreamWS.current === null) {
return;
}
if (refVideoStreamWS.current &&
refVideoStreamWS.current.readyState === 1) {
refVideoStreamWS.current.close();
}
refVideoStreamWS.current = null;
console.log('Retry connect on error');
start();
};
refVideoStreamWS.current.onclose = () => {
refVideoStreamWS.current = null;
};
refVideoStreamWS.current.onmessage = (msg) => onIceServers(msg);
refVideoStreamWS.current.onopen = () => {
const data = { token: streamToken };
if (refVideoStreamWS === null || refVideoStreamWS === void 0 ? void 0 : refVideoStreamWS.current) {
if (refVideoStreamWS.current.readyState === 1) {
refVideoStreamWS.current.send(JSON.stringify(data));
}
else {
let timeoutRef = setTimeout(() => {
refVideoStreamWS.current.send(JSON.stringify(data));
if (timeoutRef) {
clearTimeout(timeoutRef);
}
}, 2000);
}
}
};
};
const requestId = new Date().getTime();
setWaitForWatch(0);
setWaitForStart(requestId);
if ((streaming === null || streaming === void 0 ? void 0 : streaming.version) === 'v2') {
// Handle with new method
getNonAdvertisedCodecs();
}
else {
start();
}
}
}, [streaming, streamToken, countRetryStream]);
useEffect(() => {
if (streaming &&
streaming.serial &&
!streaming.streamId &&
streaming.streamMonitorId &&
!streaming.sdpOffer &&
!streaming.started &&
streaming.path &&
streaming.wsHost &&
streaming.wssPort &&
streaming.hlsPort &&
countRetryStream) {
console.log('Streaming: ', streaming);
setSourceObject({
mode: 'hls',
link: (streaming === null || streaming === void 0 ? void 0 : streaming.version) === 'v2'
? `https://${streaming === null || streaming === void 0 ? void 0 : streaming.wsHost}:${streaming === null || streaming === void 0 ? void 0 : streaming.hlsPort}${streaming === null || streaming === void 0 ? void 0 : streaming.path}/index.m3u8?token=${streamToken}`
: `https://${streaming === null || streaming === void 0 ? void 0 : streaming.wsHost}:${streaming === null || streaming === void 0 ? void 0 : streaming.hlsPort}/${streamToken}${streaming === null || streaming === void 0 ? void 0 : streaming.path}/index.m3u8`
});
}
}, [streaming, streamToken, countRetryStream]);
// Watch Stream
useEffect(() => {
if (streaming &&
streaming.serial &&
streaming.streamId &&
!streaming.sdpOffer &&
!streaming.started) {
let dataBody = {
namespace,
serial: streaming.serial,
streamId: streaming.streamId
};
watchStream(dataBody);
}
}, [namespace, streaming, watchStream]);
useEffect(() => {
if (streaming && streaming.sdpOffer && !streaming.started) {
const offerOptions = {
offerToReceiveAudio: true,
offerToReceiveVideo: true
};
if (peerConnection.current && peerConnection.current.connectionState) {
peerConnection.current.close();
}
peerConnection.current = new RTCPeerConnection(pc_config, pc_constraints);
let sdpData = {
type: 'offer',
sdp: streaming.sdpOffer
};
let remoteSessionDescription = new RTCSessionDescription(sdpData);
if (peerConnection.current) {
peerConnection.current
.setRemoteDescription(remoteSessionDescription)
.then(() => {
peerConnection.current
.createAnswer(offerOptions)
.then((localDescription) => {
peerConnection.current
.setLocalDescription(localDescription)
.then(() => {
// start stream here
let dataBody = {
streamId: streaming.streamId,
sdpAnswer: localDescription.sdp,
namespace
};
let dataObj = {
appServiceType: SERVICE.START_STREAM,
requestId: new Date().getTime(),
body: JSON.stringify(dataBody)
};
setWaitForStart(0);
return dispatch(socketEmitEvent(dataObj));
})
.catch(() => { });
})
.catch(() => { });
})
.catch(() => { });
peerConnection.current.onicecandidate = (event) => {
const requestId = new Date().getTime();
if (event.candidate) {
peerConnection.current.addIceCandidate(event.candidate);
// call update stream
updateStream(requestId, dispatch, streaming.streamId, event.candidate.candidate, 0);
}
else {
//call update stream
updateStream(requestId, dispatch, streaming.streamId, '', 1);
}
};
peerConnection.current.ontrack = (event) => {
if (event) {
setSourceObject(event.streams[0]);
}
};
}
}
}, [dispatch, namespace, streaming, updateStream]);
useEffect(() => {
var _a;
if (streaming && (zoomState === null || zoomState === void 0 ? void 0 : zoomState.zoomState) && streaming.started) {
setLoading(false);
let dataBody = {
streamId: streaming.streamId,
zoomState: (_a = zoomStatePlayer === null || zoomStatePlayer === void 0 ? void 0 : zoomStatePlayer.zoomState) !== null && _a !== void 0 ? _a : ZOOM_STATE.FOUR,
namespace
};
let dataObj = {
appServiceType: SERVICE.ZOOM_STATE_STREAM,
requestId: new Date().getTime(),
body: JSON.stringify(dataBody)
};
dispatch(socketEmitEvent(dataObj));
}
}, [dispatch, streaming, namespace]);
// Stop stream on exit
useEffect(() => {
return () => {
if (streaming &&
streaming.started &&
streaming.status &&
streaming.status.length > 0) {
const { streamId } = streaming;
let dataBody = {
streamId,
namespace
};
let dataObj = {
appServiceType: SERVICE.STOP_STREAM,
requestId: new Date().getTime(),
body: JSON.stringify(dataBody)
};
setStreaming(null);
dispatch(socketEmitEvent(dataObj));
dispatch(getEndStreamReturn(streamId));
}
};
}, [dispatch, namespace, streaming]);
// Stop stream ultra fast on exit
useEffect(() => {
return () => {
if (streaming &&
streaming.wsHost &&
streaming.wssPort &&
streaming.path &&
!streaming.hlsPort) {
if (refVideoStreamWS.current &&
refVideoStreamWS.current.readyState === 1) {
refVideoStreamWS.current.close();
}
refVideoStreamWS.current = null;
if (refVideoStreamPC.current) {
refVideoStreamPC.current.close();
}
refVideoStreamPC.current = null;
setStreaming(null);
dispatch(getEndStreamUtraFastReturn(streaming.serial));
}
};
}, [dispatch, streaming]);
// Stop stream hls fast on exit
useEffect(() => {
return () => {
if (streaming &&
streaming.wsHost &&
streaming.wssPort &&
streaming.path &&
streaming.hlsPort) {
setStreaming(null);
setSourceObject(null);
dispatch(getEndStreamUtraFastReturn(streaming.serial));
}
};
}, [dispatch, streaming]);
const onRotateCamera = useCallback((navigator) => {
if (streaming === null || streaming === void 0 ? void 0 : streaming.serial) {
let dataBody = {
navigator,
namespace,
serial: streaming === null || streaming === void 0 ? void 0 : streaming.serial
};
let dataObj = {
appServiceType: SERVICE.ROTATE_VIEW_CAMERA,
requestId: new Date().getTime(),
body: JSON.stringify(dataBody)
};
return dispatch(socketEmitEvent(dataObj));
}
}, [dispatch, namespace, streaming]);
const onZoomCamera = useCallback((zoom) => {
if (streaming === null || streaming === void 0 ? void 0 : streaming.serial) {
let dataBody = {
zoom,
namespace,
serial: streaming === null || streaming === void 0 ? void 0 : streaming.serial
};
let dataObj = {
appServiceType: SERVICE.ZOOM_VIEW_CAMERA,
requestId: new Date().getTime(),
body: JSON.stringify(dataBody)
};
return dispatch(socketEmitEvent(dataObj));
}
}, [dispatch, namespace, streaming]);
return (_jsxs("div", { className: 'view-stream-component', style: { height: '100%' }, children: [state === 1 || isConnected ? (!sourceObject && (_jsx("div", { className: 'icon-live-stream', children: _jsx("img", { width: iconSize ? iconSize : 26, height: iconSize ? iconSize : 26, className: 'icon', alt: 'live-stream-icon', src: LiveIconOn }) }))) : (_jsxs(_Fragment, { children: [_jsx("div", { className: 'name-camera', children: name }), _jsx("div", { className: 'icon-live-stream', children: _jsx("img", { width: iconSize ? iconSize : 26, height: iconSize ? iconSize : 26, className: 'icon', alt: 'live-stream-icon', src: OffStream }) })] })), !sourceObject && (_jsx("div", { className: 'icon-type-stream', children: _jsx("img", { width: iconSize ? iconSize : 26, height: iconSize ? iconSize : 26, className: 'icon', alt: 'live-stream-icon', src: typeCamera === 'onvif' ? IconCameraOnvif : IconCameraRtsp }) })), !sourceObject && (_jsx(Tooltip, { placement: 'left', mouseEnterDelay: 0.5, title: _jsx(_Fragment, { children: _jsxs(Row, { children: [_jsx(Col, { span: 24, children: _jsxs(Row, { style: {
borderBottom: '1px solid #303030',
paddingBottom: '10px'
}, children: [_jsx(Col, { span: 18, style: {
fontWeight: 'bold',
fontSize: '13px',
color: 'hsla(0,0%,100%,.85)'
}, children: (listDeviceContact === null || listDeviceContact === void 0 ? void 0 : listDeviceContact.name) || '' }), _jsx(Col, { span: 6, children: (listDeviceContact === null || listDeviceContact === void 0 ? void 0 : listDeviceContact.state) === 1 ? (_jsx(Tag, { color: 'rgb(14, 147, 71)', children: "Online" })) : (_jsx(Tag, { color: 'rgb(79, 77, 77)', children: "Offline" })) })] }) }), (listDeviceContact === null || listDeviceContact === void 0 ? void 0 : listDeviceContact.note) && (_jsxs(Col, { span: 24, style: {
color: 'hsla(0, 0%, 100%, 0.65)',
marginTop: '10px',
fontSize: '13px'
}, children: [_jsx("img", { className: 'info-icon-stream', alt: 'live-stream-icon', style: { marginTop: '-2px' }, src: Info3 }), (listDeviceContact === null || listDeviceContact === void 0 ? void 0 : listDeviceContact.note) || ''] })), state === 0 && (_jsx(Col, { span: 24, children: _jsxs(Row, { children: [(listDeviceContact === null || listDeviceContact === void 0 ? void 0 : listDeviceContact.errorTime) && (_jsxs(Col, { span: 24, style: {
color: 'hsla(0, 0%, 100%, 0.65)',
marginTop: '5px',
fontSize: '13px'
}, children: [_jsx("img", { className: 'info-icon-stream', alt: 'live-stream-icon', style: { marginTop: '-2px' }, src: Info11 }), "Time:", ' ', _jsx("span", { style: { color: 'rgb(245, 47, 57)' }, children: (listDeviceContact === null || listDeviceContact === void 0 ? void 0 : listDeviceContact.errorTime) || '' })] })), (listDeviceContact === null || listDeviceContact === void 0 ? void 0 : listDeviceContact.errorMessage) && (_jsxs(Col, { span: 24, style: {
color: 'hsla(0, 0%, 100%, 0.65)',
marginTop: '5px',
fontSize: '13px'
}, children: [_jsx("img", { className: 'info-icon-stream', alt: 'live-stream-icon', style: { marginTop: '-2px' }, src: Info12 }), "Reason:", ' ', _jsx("span", { style: { color: 'rgb(245, 47, 57)' }, children: (listDeviceContact === null || listDeviceContact === void 0 ? void 0 : listDeviceContact.errorMessage) || '' })] }))] }) })), _jsxs(Col, { span: 24, style: { marginTop: '10px', fontSize: '13px' }, children: [_jsx("img", { className: 'info-icon-stream', alt: 'live-stream-icon', style: { marginTop: '-2px' }, src: Info5 }), _jsx("span", { style: { color: '#0E9347' }, children: "PERSON IN CHARGE" })] }), ((_c = listDeviceContact === null || listDeviceContact === void 0 ? void 0 : listDeviceContact.assigners) === null || _c === void 0 ? void 0 : _c.length) > 0 &&
listDeviceContact.assigners.map((item, index) => (_jsx(Col, { span: 24, children: _jsxs(Row, { children: [item.fullName && (_jsxs(Col, { span: 24, style: {
color: 'hsla(0, 0%, 100%, 0.65)',
marginTop: '5px',
fontSize: '13px'
}, children: [_jsx("img", { className: 'info-icon-stream', alt: 'live-stream-icon', style: { marginTop: '-2px' }, src: Info6 }), "H\u1ECD t\u00EAn: ", item.fullName || ''] })), item.phoneNumber && (_jsxs(Col, { span: 24, style: {
color: 'hsla(0, 0%, 100%, 0.65)',
marginTop: '5px',
fontSize: '13px'
}, children: [_jsx("img", { className: 'info-icon-stream', alt: 'live-stream-icon', style: { marginTop: '-2px' }, src: Info7 }), "Phone number:", ' ', _jsx("span", { style: {
color: '#0E9347',
textDecoration: 'underline',
cursor: 'pointer'
}, onClick: () => {
dispatch(changeDialPadData({
show: true,
phone: item.phoneNumber
}));
}, children: item.phoneNumber || '' })] }))] }) }, index)))] }) }), children: _jsx("div", { className: 'icon-info-stream', onMouseOver: () => onChangeHover(), children: _jsx("img", { width: iconSize ? iconSize : 26, height: iconSize ? iconSize : 26, className: 'icon', alt: 'live-stream-icon', src: Info }) }) })), state === 0 &&
!isConnected &&
permission &&
permission.split('_') &&
permission.split('_').find((item) => item === 'live') && (_jsxs(_Fragment, { children: [_jsx("div", { onClick: () => !isLoadReload && isAllowStreaming !== 0 && handleTryLoad(), className: 'preload-spinner reconnect', children: _jsx(ReloadOutlined, { className: isAllowStreaming === 0 ? 'no-active' : '', style: { fontSize: 48 }, spin: isLoadReload }) }), isAllowStreaming === 0 && (_jsx("div", { className: 'text-no-active', children: _jsx(Tag, { color: '#f50', children: "Not active" }) }))] })), onPlay ? (_jsx(PlayerComponent, { name: name, onvif: typeCamera === 'onvif', onRotate: onRotateCamera, onZoom: onZoomCamera, resolution: resolution, zoomStateProps: zoomStateDefault !== null && zoomStateDefault !== void 0 ? zoomStateDefault : zoomState, isFullscreenProps: isFullscreen, restartCallBack: restartCallBack, restartLoadingCallBack: restartLoadingCallBack, fullScreenCallBack: (isFull) => setIsFullscreen(isFull), status: streaming && streaming.status, type: 'streaming', loading: loading, sourceObject: sourceObject, poster: thumbnail && thumbnail.match(/http/g) ? thumbnail : Thumbnail, setOnPlay: setOnPlay, serial: serial, checkTransferZoomState: checkTransferZoomState, zoomStatePlayer: zoomStatePlayer, setZoomStatePlayer: setZoomStatePlayer, restartQualityCallBack: restartQualityCallBack, isHlsStreaming: !!(streaming === null || streaming === void 0 ? void 0 : streaming.hlsPort), allScreen: allScreen, protocol: streaming === null || streaming === void 0 ? void 0 : streaming.protocol })) : (_jsxs("div", { className: 'video-wrapper thumbnail', style: {
paddingBottom: allScreen
? '56.25%'
: `${calcPercentForVideo(windowHeight - 108, windowWidth - 324)}%`
}, children: [state === 1 &&
permission &&
permission.split('_') &&
permission.split('_').find((item) => item === 'live') && (_jsxs(_Fragment, { children: [_jsx("img", { alt: 'play-icon', onClick: () => {
if (isAllowStreaming !== 0) {
setOnPlay(true);
}
}, src: isAllowStreaming === 0 ? PlayIconNoActive : PlayIcon }), isAllowStreaming === 0 && (_jsx("div", { className: 'text-no-active', children: _jsx