diff --git a/dist/RTSPtoWEBPlayer.js b/dist/RTSPtoWEBPlayer.js index c84ff20..08a9d0d 100644 --- a/dist/RTSPtoWEBPlayer.js +++ b/dist/RTSPtoWEBPlayer.js @@ -181,7 +181,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ RTSPtoWEBPlayer)\n/* harmony export */ });\n/* harmony import */ var webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! webrtc-adapter */ \"./node_modules/webrtc-adapter/src/js/adapter_core.js\");\n/* harmony import */ var hls_js_dist_hls_light_min_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! hls.js/dist/hls.light.min.js */ \"./node_modules/hls.js/dist/hls.light.min.js\");\n/* harmony import */ var hls_js_dist_hls_light_min_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(hls_js_dist_hls_light_min_js__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _rtsp_to_web_player_css__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./rtsp-to-web-player.css */ \"./src/rtsp-to-web-player.css\");\nfunction ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }\nfunction _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }\nfunction _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\nfunction _toPropertyKey(t) { var i = _toPrimitive(t, \"string\"); return \"symbol\" == typeof i ? i : String(i); }\nfunction _toPrimitive(t, r) { if (\"object\" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || \"default\"); if (\"object\" != typeof i) return i; throw new TypeError(\"@@toPrimitive must return a primitive value.\"); } return (\"string\" === r ? String : Number)(t); }\n\n\n\nclass RTSPtoWEBPlayer {\n constructor(options) {\n var _this = this;\n _defineProperty(this, \"MSE\", null);\n _defineProperty(this, \"MSEStreamingStarted\", false);\n _defineProperty(this, \"MSESourceBuffer\", null);\n _defineProperty(this, \"turn\", []);\n _defineProperty(this, \"codec\", null);\n _defineProperty(this, \"webSocket\", null);\n _defineProperty(this, \"webrtc\", null);\n _defineProperty(this, \"webRtcSocket\", null);\n _defineProperty(this, \"currentPlayerType\", null);\n _defineProperty(this, \"hidden\", 'hidden');\n _defineProperty(this, \"paused\", false);\n _defineProperty(this, \"presets\", null);\n _defineProperty(this, \"audio_tracks\", null);\n _defineProperty(this, \"switchFlag\", false);\n _defineProperty(this, \"user_state\", {\n paused: false\n });\n _defineProperty(this, \"options\", {\n parentElement: null,\n source: null,\n controls: true,\n muted: true,\n autoplay: true,\n loop: false,\n hlsjsconfig: {},\n webrtcconfig: {\n iceServers: [{\n urls: ['stun:stun.l.google.com:19302']\n }],\n sdpSemantics: 'unified-plan',\n bundlePolicy: 'max-compat'\n //iceTransportPolicy: \"relay\",\n // for option \"relay\" need use turn server\n },\n debug: false,\n getPresets: null,\n onResolutionChange: null,\n latency: null\n });\n _defineProperty(this, \"createElements\", () => {\n //video\n this.video = document.createElement('video');\n this.video.setAttribute('playsinline', '');\n this.video.muted = this.options.muted ? true : false;\n this.video.controls = this.options.controls ? true : false;\n this.video.autoplay = this.options.autoplay ? true : false;\n this.video.loop = this.options.loop ? true : false;\n this.addVideoListeners();\n //wrapper\n this.player = document.createElement('div');\n this.player.classList.add('RTSPtoWEBPlayer');\n this.player.append(this.video);\n });\n _defineProperty(this, \"attachTo\", element => {\n this.options.parentElement = element;\n this.options.parentElement.innerHTML = '';\n this.options.parentElement.append(this.player);\n if (this.options.source) {\n this.load(this.options.source);\n }\n });\n _defineProperty(this, \"load\", source => {\n this.options.source = source;\n this.destroy();\n const sourceType = new URL(this.options.source);\n if (sourceType.protocol === 'http:' || sourceType.protocol === 'https:') {\n if (this.options.source.indexOf('m3u8') !== -1) {\n this.currentPlayerType = 'hls';\n this.hlsPlayer();\n } else if (this.options.source.indexOf('.mp4') !== -1) {\n this.currentPlayerType = 'mp4';\n this.mp4Player();\n } else {\n this.currentPlayerType = 'rtc';\n this.webRtcPlayer();\n }\n } else if (sourceType.protocol === 'ws:' || sourceType.protocol === 'wss:') {\n if (this.options.source.indexOf('webrtc') !== -1) {\n this.currentPlayerType = 'ws-rtc';\n this.webRtcOverSocket();\n } else if (this.options.source.indexOf('on-air') !== -1 || this.options.source.indexOf('preview') !== -1) {\n this.currentPlayerType = 'ws-new';\n this.newMsePlayer();\n } else {\n this.currentPlayerType = 'ws';\n this.msePlayer();\n }\n } else {\n this.currentPlayerType = null;\n }\n });\n _defineProperty(this, \"newMsePlayer\", () => {\n this.webSocket = new WebSocket(this.options.source);\n this.webSocket.onclose = e => {\n this.debugLogger(e);\n };\n this.webSocket.onmessage = _ref => {\n let {\n data\n } = _ref;\n this.messageHandlerMSE(data);\n };\n });\n _defineProperty(this, \"webRtcOverSocket\", () => {\n this.webRtcSocket = new WebSocket(this.options.source);\n this.webRtcSocket.onopen = () => {\n this.webRtcPlayer();\n };\n this.webRtcSocket.onclose = e => {\n this.debugLogger(e);\n this.webRtcSocket.onmessage = null;\n };\n this.webRtcSocket.onerror = e => {\n this.debugLogger(e);\n };\n this.webRtcSocket.onmessage = _ref2 => {\n let {\n data\n } = _ref2;\n this.webRtcSocketMessageHandler(data);\n };\n });\n _defineProperty(this, \"webRtcSocketMessageHandler\", data => {\n data = JSON.parse(data);\n switch (data.method) {\n case 'meta_response':\n this.presets = data.payload.streams;\n if (typeof this.options.getPresets === 'function') {\n this.options.getPresets(this.presets, data.payload.audio_tracks);\n }\n const arr_video = data.payload.streams.filter(item => {\n return item.default;\n });\n const arr_audio = data.payload.audio_tracks.filter(item => {\n return item.default;\n });\n const default_video = arr_video.length > 0 ? arr_video[0].idx : data.payload.streams[0].idx;\n const default_audio = arr_audio.length > 0 ? arr_audio[0].idx : data.payload.audio_tracks[0].idx;\n this.user_state.video_track_id = default_video;\n this.user_state.audio_track_id = default_audio;\n //создаем локальный оффер\n this.webRtcSocketOffer();\n break;\n case 'offer':\n this.webrtc.setRemoteDescription(new RTCSessionDescription(data.payload));\n break;\n case 'ice_candidate':\n this.webrtc.addIceCandidate(data.payload);\n break;\n case 'answer':\n this.webrtc.setRemoteDescription(new RTCSessionDescription(data.payload));\n break;\n default:\n console.warn('unsupported method', data.method);\n return;\n }\n });\n _defineProperty(this, \"webRtcSocketOffer\", async () => {\n const offer = await this.webrtc.createOffer({\n offerToReceiveAudio: true,\n offerToReceiveVideo: true\n });\n await this.webrtc.setLocalDescription(offer);\n });\n _defineProperty(this, \"messageHandlerMSE\", data => {\n if (typeof data === 'string') {\n try {\n data = JSON.parse(data);\n switch (data.method) {\n case 'play_response':\n break;\n case 'meta_response':\n this.presets = data.payload.streams;\n if (typeof this.options.getPresets === 'function') {\n this.options.getPresets(this.presets, data.payload.audio_tracks);\n }\n const arr_video = data.payload.streams.filter(item => {\n return item.default;\n });\n const arr_audio = data.payload.audio_tracks.filter(item => {\n return item.default;\n });\n const default_video = arr_video.length > 0 ? arr_video[0].idx : data.payload.streams[0].idx;\n const default_audio = arr_audio.length > 0 ? arr_audio[0].idx : data.payload.audio_tracks[0].idx;\n this.user_state.video_track_id = default_video;\n this.user_state.audio_track_id = default_audio;\n this.playPresetMSE(default_video, default_audio);\n break;\n default:\n console.log(data.method);\n return;\n }\n if (data.method === 'play_response') {} else {}\n } catch (e) {\n this.debugLogger(e);\n }\n } else if (typeof data === 'object') {\n data.arrayBuffer().then(packet => {\n this.readPacket(packet);\n });\n //\n }\n });\n _defineProperty(this, \"getPresets\", () => {\n return this.presets;\n });\n _defineProperty(this, \"playPresetMSE\", (videoIdx, audioIdx) => {\n this.codec = this.presets.filter(item => item.idx === videoIdx)[0].codecs;\n const answer = JSON.stringify({\n method: 'user_state',\n payload: this.user_state\n });\n this.MSE = new MediaSource();\n this.video.src = window.URL.createObjectURL(this.MSE);\n this.MSE.addEventListener('sourceopen', () => {\n this.MSESourceBuffer = this.MSE.addSourceBuffer(\"video/mp4; codecs=\\\"\".concat(this.codec, \"\\\"\"));\n this.MSESourceBuffer.mode = 'segments';\n this.MSESourceBuffer.addEventListener('updateend', this.pushPacket);\n this.webSocket.send(answer);\n });\n });\n _defineProperty(this, \"switchStream\", index => {\n this.codec = this.presets[index].codecs;\n this.user_state.video_track_id = this.presets[index].idx;\n if (this.webRtcSocket) {\n this.webRtcSocket.send(JSON.stringify({\n method: 'user_state',\n payload: this.user_state\n }));\n } else {\n this.switchFlag = true;\n this.webSocket.send(JSON.stringify({\n method: 'user_state',\n payload: this.user_state\n }));\n this.MSESourceBuffer.timestampOffset = this.MSESourceBuffer.appendWindowStart = this.MSESourceBuffer.buffered.end(this.MSESourceBuffer.buffered.length - 1);\n }\n });\n _defineProperty(this, \"switchAudio\", idx => {\n this.user_state.audio_track_id = idx;\n if (this.webRtcSocket) {\n this.webRtcSocket.send(JSON.stringify({\n method: 'user_state',\n payload: this.user_state\n }));\n } else {\n this.webSocket.send(JSON.stringify({\n method: 'set_audio_track',\n payload: {\n audio_track_id: idx\n }\n }));\n this.video.currentTime += 0.01;\n }\n });\n _defineProperty(this, \"addMseListeners\", () => {\n this.MSE.addEventListener('sourceopen', this.sourceOpenHandler);\n });\n _defineProperty(this, \"sourceOpenHandler\", () => {\n this.websocketEvents();\n });\n _defineProperty(this, \"websocketEvents\", () => {\n this.webSocket = new WebSocket(this.options.source);\n this.webSocket.binaryType = 'arraybuffer';\n this.webSocket.onclose = () => {\n this.webSocket.onmessage = null;\n };\n this.webSocket.onmessage = _ref3 => {\n let {\n data\n } = _ref3;\n if (this.codec === null) {\n if (typeof data === 'object') {\n this.codec = new TextDecoder('utf-8').decode(new Uint8Array(data).slice(1));\n } else {\n this.codec = data;\n }\n this.MSESourceBuffer = this.MSE.addSourceBuffer(\"video/mp4; codecs=\\\"\".concat(this.codec, \"\\\"\"));\n this.MSESourceBuffer.mode = 'segments';\n this.MSE.duration = Infinity;\n this.MSESourceBuffer.addEventListener('updateend', this.pushPacket);\n } else {\n if (!this.paused) {\n this.readPacket(data);\n }\n }\n if (document[this.hidden] && this.video.buffered.length) {\n this.video.currentTime = this.video.buffered.end(this.video.buffered.length - 1) - 1;\n }\n };\n });\n _defineProperty(this, \"readPacket\", packet => {\n if (this.video.buffered && this.video.currentTime > 0) {\n if (typeof this.options.latency === 'function') {\n this.options.latency(this.video.buffered.length, this.video.buffered.end(this.video.buffered.length - 1), this.video.currentTime);\n }\n if (this.video.currentTime < this.video.buffered.start(this.video.buffered.length - 1)) {\n this.video.currentTime = this.video.buffered.end(this.video.buffered.length - 1);\n }\n }\n if (!this.MSEStreamingStarted) {\n try {\n this.MSESourceBuffer.appendBuffer(packet);\n this.MSEStreamingStarted = true;\n } catch (e) {\n this.debugLogger(e);\n }\n return;\n }\n this.turn.push(packet);\n this.pushPacket();\n });\n _defineProperty(this, \"pushPacket\", () => {\n if (!this.MSESourceBuffer.updating) {\n if (this.turn.length > 0) {\n const packet = this.turn.shift();\n try {\n this.MSESourceBuffer.appendBuffer(packet);\n } catch (err) {\n this.debugLogger(err);\n }\n } else {\n this.MSEStreamingStarted = false;\n }\n }\n });\n _defineProperty(this, \"mp4Player\", () => {\n this.video.src = this.options.source;\n });\n _defineProperty(this, \"msePlayer\", () => {\n this.MSE = new MediaSource();\n this.video.src = window.URL.createObjectURL(this.MSE);\n this.addMseListeners();\n });\n _defineProperty(this, \"hlsPlayer\", () => {\n if (this.video.canPlayType('application/vnd.apple.mpegurl')) {\n this.video.src = this.options.source;\n } else if (hls_js_dist_hls_light_min_js__WEBPACK_IMPORTED_MODULE_1___default().isSupported()) {\n this.hls = new (hls_js_dist_hls_light_min_js__WEBPACK_IMPORTED_MODULE_1___default())(this.options.hlsjsconfig);\n this.hls.loadSource(this.options.source);\n this.hls.attachMedia(this.video);\n } else {\n console.warn('UNSUPPOERED MEDIA SOURCE');\n }\n });\n _defineProperty(this, \"webRtcPlayer\", async () => {\n this.mediaStream = new MediaStream();\n this.video.srcObject = this.mediaStream;\n this.webrtc = new RTCPeerConnection(this.options.webrtcconfig);\n this.webrtc.onnegotiationneeded = this.handleNegotiationNeeded;\n this.webrtc.onsignalingstatechange = this.signalingstatechange;\n this.webrtc.onicegatheringstatechange = this.icegatheringstatechange;\n this.webrtc.onicecandidate = this.icecandidate;\n this.webrtc.onicecandidateerror = this.icecandidateerror;\n this.webrtc.onconnectionstatechange = this.connectionstatechange;\n this.webrtc.oniceconnectionstatechange = this.iceconnectionstatechange;\n this.webrtc.ontrack = this.onTrack;\n if (!this.webRtcSocket) {\n /*\n * for older schema initiate connection create local description\n */\n const offer = await this.webrtc.createOffer({\n offerToReceiveAudio: false,\n offerToReceiveVideo: true\n });\n await this.webrtc.setLocalDescription(offer);\n }\n });\n _defineProperty(this, \"handleNegotiationNeeded\", async e => {\n /*\n * in this project this handler is not needed, but in another it can be useful\n */\n this.debugLogger('handleNegotiationNeeded');\n if (this.webRtcSocket) {\n const offer = await this.webrtc.createOffer({\n offerToReceiveAudio: false,\n offerToReceiveVideo: true\n });\n await this.webrtc.setLocalDescription(offer);\n }\n });\n _defineProperty(this, \"signalingstatechange\", async () => {\n switch (this.webrtc.signalingState) {\n case 'have-remote-offer':\n this.debugLogger('this.webrtc.signalingState====>[have-remote-offer]');\n if (this.webRtcSocket) {\n const answer = await this.webrtc.createAnswer();\n await this.webrtc.setLocalDescription(answer);\n this.webRtcSocket.send(JSON.stringify({\n method: 'answer',\n payload: {\n sdp: answer,\n user_state: this.user_state\n }\n }));\n }\n break;\n case 'have-local-offer':\n this.debugLogger('this.webrtc.signalingState====>[have-local-offer]');\n if (!this.webRtcSocket) {\n const suuid = new URL(this.options.source).pathname.split('/').slice(-1);\n const formData = new FormData();\n formData.append('data', btoa(this.webrtc.localDescription.sdp));\n formData.append('suuid', suuid);\n const response = await fetch(this.options.source, {\n method: 'POST',\n body: formData\n });\n if (response.ok) {\n const remoteDescription = await response.text();\n this.webrtc.setRemoteDescription(new RTCSessionDescription({\n type: 'answer',\n sdp: atob(remoteDescription)\n }));\n }\n } else {\n this.webRtcSocket.send(JSON.stringify({\n method: 'offer',\n payload: {\n user_state: this.user_state,\n sdp: this.webrtc.localDescription\n }\n }));\n }\n break;\n case 'stable':\n /*\n * There is no ongoing exchange of offer and answer underway.\n * This may mean that the RTCPeerConnection object is new, in which case both the localDescription and remoteDescription are null;\n * it may also mean that negotiation is complete and a connection has been established.\n */\n this.debugLogger('this.webrtc.signalingState====>[stable]');\n break;\n case 'closed':\n /*\n * The RTCPeerConnection has been closed.\n */\n this.debugLogger('this.webrtc.signalingState====>[closed]');\n this.destroy();\n break;\n default:\n console.log(\"unhandled signalingState is \".concat(this.webrtc.signalingState));\n break;\n }\n });\n _defineProperty(this, \"icegatheringstatechange\", () => {\n switch (this.webrtc.iceGatheringState) {\n case 'gathering':\n /* collection of candidates has begun */\n this.debugLogger('collection of candidates has begun');\n break;\n case 'complete':\n /* collection of candidates is finished */\n this.debugLogger('collection of candidates is finished');\n break;\n }\n });\n _defineProperty(this, \"icecandidate\", event => {\n this.debugLogger('icecandidate\\n', event);\n if (this.webRtcSocket) {\n if (event.candidate && event.candidate.candidate !== '') {\n this.webRtcSocket.send(JSON.stringify({\n method: 'ice_candidate',\n payload: event.candidate\n }));\n }\n }\n });\n _defineProperty(this, \"icecandidateerror\", event => {\n this.debugLogger('icecandidateerror\\n', \"hostCandidate: \".concat(event.hostCandidate, \" CODE: \").concat(event.errorCode, \" TEXT: \").concat(event.errorText));\n });\n _defineProperty(this, \"connectionstatechange\", e => {\n //console.log(e)\n switch (this.webrtc.connectionState) {\n case 'new':\n case 'connected':\n this.debugLogger('connected');\n break;\n case 'disconnected':\n this.debugLogger('disconnected...');\n break;\n case 'closed':\n this.debugLogger('Offline');\n break;\n case 'failed':\n this.webrtc.restartIce();\n this.debugLogger('Error');\n break;\n default:\n this.debugLogger(\"Unhadled state: \".concat(this.webrtc.connectionState));\n break;\n }\n });\n _defineProperty(this, \"iceconnectionstatechange\", () => {\n this.debugLogger('iceconnectionstatechange\\n', this.webrtc.iceConnectionState);\n });\n _defineProperty(this, \"onTrack\", event => {\n this.debugLogger('onTrack\\n');\n //make sure there is only one video track in mediaStream\n if (event.track.kind === 'video' && this.mediaStream.getVideoTracks().length > 0) {\n this.mediaStream.removeTrack(this.mediaStream.getVideoTracks()[0]);\n }\n if (event.track.kind === 'audio' && this.mediaStream.getAudioTracks().length > 0) {\n this.mediaStream.removeTrack(this.mediaStream.getAudioTracks()[0]);\n }\n this.mediaStream.addTrack(event.track);\n });\n _defineProperty(this, \"destroy\", () => {\n this.codec = null;\n this.presets = null;\n this.audio_tracks = null;\n if (this.currentPlayerType != null) {\n switch (this.currentPlayerType) {\n case 'hls':\n if (this.hls != null) {\n this.hls.destroy();\n }\n break;\n case 'rtc':\n if (this.webrtc != null) {\n this.webrtc.close();\n this.webrtc = null;\n this.video.srcObject = null;\n this.mediaStream = null;\n }\n break;\n case 'ws':\n case 'ws-new':\n this.webSocket.onerror = null;\n this.webSocket.onopen = null;\n this.webSocket.onmessage = null;\n this.webSocket.onclose = null;\n this.webSocket.close(1000);\n this.turn = [];\n break;\n case 'ws-rtc':\n this.webRtcSocket.onerror = null;\n this.webRtcSocket.onopen = null;\n this.webRtcSocket.onmessage = null;\n this.webRtcSocket.onclose = null;\n this.webRtcSocket.close(1000);\n this.turn = [];\n if (this.webrtc != null) {\n this.webrtc.close();\n this.webrtc = null;\n this.video.srcObject = null;\n this.mediaStream = null;\n }\n break;\n default:\n }\n this.video.pause();\n this.video.removeAttribute('src'); // empty source\n this.video.load();\n }\n });\n _defineProperty(this, \"addVideoListeners\", () => {\n this.video.addEventListener('error', e => {\n this.debugLogger('[ video listener ]', e);\n this.destroy();\n });\n this.video.addEventListener('play', () => {\n this.paused = false;\n });\n this.video.addEventListener('pause', () => {\n this.paused = true;\n });\n this.video.addEventListener('resize', () => {\n if (typeof this.options.onResolutionChange === 'function') {\n this.options.onResolutionChange(this.video.videoWidth, this.video.videoHeight);\n }\n });\n this.video.addEventListener('progress', () => {\n if (this.currentPlayerType === 'ws' && this.video.buffered.length > 0) {\n if (this.video.currentTime < this.video.buffered.start(this.video.buffered.length - 1)) {\n this.video.currentTime = this.video.buffered.end(this.video.buffered.length - 1) - 1;\n }\n }\n });\n this.video.addEventListener('canplay', () => {\n if (this.currentPlayerType === 'ws') {\n if (this.video.paused && this.video.autoplay) {\n this.video.play();\n }\n }\n });\n });\n _defineProperty(this, \"getImageBase64\", () => {\n const canvas = document.createElement('canvas');\n canvas.width = this.video.videoWidth;\n canvas.height = this.video.videoHeight;\n canvas.getContext('2d').drawImage(this.video, 0, 0, canvas.width, canvas.height);\n const dataURL = canvas.toDataURL();\n canvas.remove();\n return dataURL;\n });\n _defineProperty(this, \"debugLogger\", function () {\n if (_this.options.debug) {\n for (var _len = arguments.length, arg = new Array(_len), _key = 0; _key < _len; _key++) {\n arg[_key] = arguments[_key];\n }\n if (_this.options.debug === 'trace') {\n console.trace(...arg);\n } else {\n const d = new Date();\n console.log(d.toLocaleTimeString() + \".\".concat(d.getMilliseconds()), ...arg);\n }\n }\n });\n this.options = _objectSpread(_objectSpread({}, this.options), options);\n this.createElements();\n if (this.options.parentElement) {\n this.attachTo(this.options.parentElement);\n }\n this.defDocumentHidden();\n }\n defDocumentHidden() {\n if (typeof document.hidden !== 'undefined') {\n this.hidden = 'hidden';\n } else if (typeof document.msHidden !== 'undefined') {\n this.hidden = 'msHidden';\n } else if (typeof document.webkitHidden !== 'undefined') {\n this.hidden = 'webkitHidden';\n }\n }\n}\n\n//# sourceURL=webpack://rtsptowebplayer/./src/rtsp-to-web-player.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ RTSPtoWEBPlayer)\n/* harmony export */ });\n/* harmony import */ var webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! webrtc-adapter */ \"./node_modules/webrtc-adapter/src/js/adapter_core.js\");\n/* harmony import */ var hls_js_dist_hls_light_min_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! hls.js/dist/hls.light.min.js */ \"./node_modules/hls.js/dist/hls.light.min.js\");\n/* harmony import */ var hls_js_dist_hls_light_min_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(hls_js_dist_hls_light_min_js__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _rtsp_to_web_player_css__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./rtsp-to-web-player.css */ \"./src/rtsp-to-web-player.css\");\nfunction ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }\nfunction _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }\nfunction _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\nfunction _toPropertyKey(t) { var i = _toPrimitive(t, \"string\"); return \"symbol\" == typeof i ? i : String(i); }\nfunction _toPrimitive(t, r) { if (\"object\" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || \"default\"); if (\"object\" != typeof i) return i; throw new TypeError(\"@@toPrimitive must return a primitive value.\"); } return (\"string\" === r ? String : Number)(t); }\n\n\n\nclass RTSPtoWEBPlayer {\n constructor(options) {\n var _this = this;\n _defineProperty(this, \"MSE\", null);\n _defineProperty(this, \"MSEStreamingStarted\", false);\n _defineProperty(this, \"MSESourceBuffer\", null);\n _defineProperty(this, \"turn\", []);\n _defineProperty(this, \"codec\", null);\n _defineProperty(this, \"webSocket\", null);\n _defineProperty(this, \"webrtc\", null);\n _defineProperty(this, \"webRtcSocket\", null);\n _defineProperty(this, \"currentPlayerType\", null);\n _defineProperty(this, \"hidden\", 'hidden');\n _defineProperty(this, \"paused\", false);\n _defineProperty(this, \"presets\", null);\n _defineProperty(this, \"audio_tracks\", null);\n _defineProperty(this, \"switchFlag\", false);\n _defineProperty(this, \"user_state\", {\n paused: false\n });\n _defineProperty(this, \"options\", {\n parentElement: null,\n source: null,\n controls: true,\n muted: true,\n autoplay: true,\n loop: false,\n hlsjsconfig: {},\n webrtcconfig: {\n iceServers: [{\n urls: ['stun:stun.l.google.com:19302']\n }],\n sdpSemantics: 'unified-plan',\n bundlePolicy: 'max-compat'\n //iceTransportPolicy: \"relay\",\n // for option \"relay\" need use turn server\n },\n debug: false,\n getPresets: null,\n onResolutionChange: null,\n latency: null,\n onWsClose: null\n });\n _defineProperty(this, \"createElements\", () => {\n //video\n this.video = document.createElement('video');\n this.video.setAttribute('playsinline', '');\n this.video.muted = this.options.muted ? true : false;\n this.video.controls = this.options.controls ? true : false;\n this.video.autoplay = this.options.autoplay ? true : false;\n this.video.loop = this.options.loop ? true : false;\n this.addVideoListeners();\n //wrapper\n this.player = document.createElement('div');\n this.player.classList.add('RTSPtoWEBPlayer');\n this.player.append(this.video);\n });\n _defineProperty(this, \"attachTo\", element => {\n this.options.parentElement = element;\n this.options.parentElement.innerHTML = '';\n this.options.parentElement.append(this.player);\n if (this.options.source) {\n this.load(this.options.source);\n }\n });\n _defineProperty(this, \"load\", source => {\n this.options.source = source;\n this.destroy();\n const sourceType = new URL(this.options.source);\n if (sourceType.protocol === 'http:' || sourceType.protocol === 'https:') {\n if (this.options.source.indexOf('m3u8') !== -1) {\n this.currentPlayerType = 'hls';\n this.hlsPlayer();\n } else if (this.options.source.indexOf('.mp4') !== -1) {\n this.currentPlayerType = 'mp4';\n this.mp4Player();\n } else {\n this.currentPlayerType = 'rtc';\n this.webRtcPlayer();\n }\n } else if (sourceType.protocol === 'ws:' || sourceType.protocol === 'wss:') {\n if (this.options.source.indexOf('webrtc') !== -1) {\n this.currentPlayerType = 'ws-rtc';\n this.webRtcOverSocket();\n } else if (this.options.source.indexOf('on-air') !== -1 || this.options.source.indexOf('preview') !== -1) {\n this.currentPlayerType = 'ws-new';\n this.newMsePlayer();\n } else {\n this.currentPlayerType = 'ws';\n this.msePlayer();\n }\n } else {\n this.currentPlayerType = null;\n }\n });\n _defineProperty(this, \"newMsePlayer\", () => {\n this.webSocket = new WebSocket(this.options.source);\n this.webSocket.onclose = e => {\n if (typeof this.options.onWsClose === 'function') {\n this.options.onWsClose(e.code, e.reason);\n }\n this.debugLogger(e);\n };\n this.webSocket.onmessage = _ref => {\n let {\n data\n } = _ref;\n this.messageHandlerMSE(data);\n };\n });\n _defineProperty(this, \"webRtcOverSocket\", () => {\n this.webRtcSocket = new WebSocket(this.options.source);\n this.webRtcSocket.onopen = () => {\n this.webRtcPlayer();\n };\n this.webRtcSocket.onclose = e => {\n this.debugLogger(e);\n this.webRtcSocket.onmessage = null;\n if (typeof this.options.onWsClose === 'function') {\n this.options.onWsClose(e.code, e.reason);\n }\n };\n this.webRtcSocket.onerror = e => {\n this.debugLogger(e);\n };\n this.webRtcSocket.onmessage = _ref2 => {\n let {\n data\n } = _ref2;\n this.webRtcSocketMessageHandler(data);\n };\n });\n _defineProperty(this, \"webRtcSocketMessageHandler\", data => {\n data = JSON.parse(data);\n switch (data.method) {\n case 'meta_response':\n this.presets = data.payload.streams;\n if (typeof this.options.getPresets === 'function') {\n this.options.getPresets(this.presets, data.payload.audio_tracks);\n }\n const arr_video = data.payload.streams.filter(item => {\n return item.default;\n });\n const arr_audio = data.payload.audio_tracks.filter(item => {\n return item.default;\n });\n const default_video = arr_video.length > 0 ? arr_video[0].idx : data.payload.streams[0].idx;\n const default_audio = arr_audio.length > 0 ? arr_audio[0].idx : data.payload.audio_tracks[0].idx;\n this.user_state.video_track_id = default_video;\n this.user_state.audio_track_id = default_audio;\n //создаем локальный оффер\n this.webRtcSocketOffer();\n break;\n case 'offer':\n this.webrtc.setRemoteDescription(new RTCSessionDescription(data.payload));\n break;\n case 'ice_candidate':\n this.webrtc.addIceCandidate(data.payload);\n break;\n case 'answer':\n this.webrtc.setRemoteDescription(new RTCSessionDescription(data.payload));\n break;\n default:\n console.warn('unsupported method', data.method);\n return;\n }\n });\n _defineProperty(this, \"webRtcSocketOffer\", async () => {\n const offer = await this.webrtc.createOffer({\n offerToReceiveAudio: true,\n offerToReceiveVideo: true\n });\n await this.webrtc.setLocalDescription(offer);\n });\n _defineProperty(this, \"messageHandlerMSE\", data => {\n if (typeof data === 'string') {\n try {\n data = JSON.parse(data);\n switch (data.method) {\n case 'play_response':\n break;\n case 'meta_response':\n this.presets = data.payload.streams;\n if (typeof this.options.getPresets === 'function') {\n this.options.getPresets(this.presets, data.payload.audio_tracks);\n }\n const arr_video = data.payload.streams.filter(item => {\n return item.default;\n });\n const arr_audio = data.payload.audio_tracks.filter(item => {\n return item.default;\n });\n const default_video = arr_video.length > 0 ? arr_video[0].idx : data.payload.streams[0].idx;\n const default_audio = arr_audio.length > 0 ? arr_audio[0].idx : data.payload.audio_tracks[0].idx;\n this.user_state.video_track_id = default_video;\n this.user_state.audio_track_id = default_audio;\n this.playPresetMSE(default_video, default_audio);\n break;\n default:\n console.log(data.method);\n return;\n }\n if (data.method === 'play_response') {} else {}\n } catch (e) {\n this.debugLogger(e);\n }\n } else if (typeof data === 'object') {\n data.arrayBuffer().then(packet => {\n this.readPacket(packet);\n });\n //\n }\n });\n _defineProperty(this, \"getPresets\", () => {\n return this.presets;\n });\n _defineProperty(this, \"playPresetMSE\", (videoIdx, audioIdx) => {\n this.codec = this.presets.filter(item => item.idx === videoIdx)[0].codecs;\n const answer = JSON.stringify({\n method: 'user_state',\n payload: this.user_state\n });\n this.MSE = new MediaSource();\n this.video.src = window.URL.createObjectURL(this.MSE);\n this.MSE.addEventListener('sourceopen', () => {\n this.MSESourceBuffer = this.MSE.addSourceBuffer(\"video/mp4; codecs=\\\"\".concat(this.codec, \"\\\"\"));\n this.MSESourceBuffer.mode = 'segments';\n this.MSESourceBuffer.addEventListener('updateend', this.pushPacket);\n this.webSocket.send(answer);\n });\n });\n _defineProperty(this, \"switchStream\", index => {\n this.codec = this.presets[index].codecs;\n this.user_state.video_track_id = this.presets[index].idx;\n if (this.webRtcSocket) {\n this.webRtcSocket.send(JSON.stringify({\n method: 'user_state',\n payload: this.user_state\n }));\n } else {\n this.switchFlag = true;\n this.webSocket.send(JSON.stringify({\n method: 'user_state',\n payload: this.user_state\n }));\n this.MSESourceBuffer.timestampOffset = this.MSESourceBuffer.appendWindowStart = this.MSESourceBuffer.buffered.end(this.MSESourceBuffer.buffered.length - 1);\n }\n });\n _defineProperty(this, \"switchAudio\", idx => {\n this.user_state.audio_track_id = idx;\n if (this.webRtcSocket) {\n this.webRtcSocket.send(JSON.stringify({\n method: 'user_state',\n payload: this.user_state\n }));\n } else {\n this.webSocket.send(JSON.stringify({\n method: 'set_audio_track',\n payload: {\n audio_track_id: idx\n }\n }));\n this.video.currentTime += 0.01;\n }\n });\n _defineProperty(this, \"addMseListeners\", () => {\n this.MSE.addEventListener('sourceopen', this.sourceOpenHandler);\n });\n _defineProperty(this, \"sourceOpenHandler\", () => {\n this.websocketEvents();\n });\n _defineProperty(this, \"websocketEvents\", () => {\n this.webSocket = new WebSocket(this.options.source);\n this.webSocket.binaryType = 'arraybuffer';\n this.webSocket.onclose = e => {\n this.webSocket.onmessage = null;\n if (typeof this.options.onWsClose === 'function') {\n this.options.onWsClose(e.code, e.reason);\n }\n };\n this.webSocket.onmessage = _ref3 => {\n let {\n data\n } = _ref3;\n if (this.codec === null) {\n if (typeof data === 'object') {\n this.codec = new TextDecoder('utf-8').decode(new Uint8Array(data).slice(1));\n } else {\n this.codec = data;\n }\n this.MSESourceBuffer = this.MSE.addSourceBuffer(\"video/mp4; codecs=\\\"\".concat(this.codec, \"\\\"\"));\n this.MSESourceBuffer.mode = 'segments';\n this.MSE.duration = Infinity;\n this.MSESourceBuffer.addEventListener('updateend', this.pushPacket);\n } else {\n if (!this.paused) {\n this.readPacket(data);\n }\n }\n if (document[this.hidden] && this.video.buffered.length) {\n this.video.currentTime = this.video.buffered.end(this.video.buffered.length - 1) - 1;\n }\n };\n });\n _defineProperty(this, \"readPacket\", packet => {\n if (this.video.buffered && this.video.currentTime > 0) {\n if (typeof this.options.latency === 'function') {\n this.options.latency(this.video.buffered.length, this.video.buffered.end(this.video.buffered.length - 1), this.video.currentTime);\n }\n if (this.video.currentTime < this.video.buffered.start(this.video.buffered.length - 1)) {\n this.video.currentTime = this.video.buffered.end(this.video.buffered.length - 1);\n }\n }\n if (!this.MSEStreamingStarted) {\n try {\n this.MSESourceBuffer.appendBuffer(packet);\n this.MSEStreamingStarted = true;\n } catch (e) {\n this.debugLogger(e);\n }\n return;\n }\n this.turn.push(packet);\n this.pushPacket();\n });\n _defineProperty(this, \"pushPacket\", () => {\n if (!this.MSESourceBuffer.updating) {\n if (this.turn.length > 0) {\n const packet = this.turn.shift();\n try {\n this.MSESourceBuffer.appendBuffer(packet);\n } catch (err) {\n this.debugLogger(err);\n }\n } else {\n this.MSEStreamingStarted = false;\n }\n }\n });\n _defineProperty(this, \"mp4Player\", () => {\n this.video.src = this.options.source;\n });\n _defineProperty(this, \"msePlayer\", () => {\n this.MSE = new MediaSource();\n this.video.src = window.URL.createObjectURL(this.MSE);\n this.addMseListeners();\n });\n _defineProperty(this, \"hlsPlayer\", () => {\n if (this.video.canPlayType('application/vnd.apple.mpegurl')) {\n this.video.src = this.options.source;\n } else if (hls_js_dist_hls_light_min_js__WEBPACK_IMPORTED_MODULE_1___default().isSupported()) {\n this.hls = new (hls_js_dist_hls_light_min_js__WEBPACK_IMPORTED_MODULE_1___default())(this.options.hlsjsconfig);\n this.hls.loadSource(this.options.source);\n this.hls.attachMedia(this.video);\n } else {\n console.warn('UNSUPPOERED MEDIA SOURCE');\n }\n });\n _defineProperty(this, \"webRtcPlayer\", async () => {\n this.mediaStream = new MediaStream();\n this.video.srcObject = this.mediaStream;\n this.webrtc = new RTCPeerConnection(this.options.webrtcconfig);\n this.webrtc.onnegotiationneeded = this.handleNegotiationNeeded;\n this.webrtc.onsignalingstatechange = this.signalingstatechange;\n this.webrtc.onicegatheringstatechange = this.icegatheringstatechange;\n this.webrtc.onicecandidate = this.icecandidate;\n this.webrtc.onicecandidateerror = this.icecandidateerror;\n this.webrtc.onconnectionstatechange = this.connectionstatechange;\n this.webrtc.oniceconnectionstatechange = this.iceconnectionstatechange;\n this.webrtc.ontrack = this.onTrack;\n if (!this.webRtcSocket) {\n /*\n * for older schema initiate connection create local description\n */\n const offer = await this.webrtc.createOffer({\n offerToReceiveAudio: false,\n offerToReceiveVideo: true\n });\n await this.webrtc.setLocalDescription(offer);\n }\n });\n _defineProperty(this, \"handleNegotiationNeeded\", async e => {\n /*\n * in this project this handler is not needed, but in another it can be useful\n */\n this.debugLogger('handleNegotiationNeeded');\n if (this.webRtcSocket) {\n const offer = await this.webrtc.createOffer({\n offerToReceiveAudio: false,\n offerToReceiveVideo: true\n });\n await this.webrtc.setLocalDescription(offer);\n }\n });\n _defineProperty(this, \"signalingstatechange\", async () => {\n switch (this.webrtc.signalingState) {\n case 'have-remote-offer':\n this.debugLogger('this.webrtc.signalingState====>[have-remote-offer]');\n if (this.webRtcSocket) {\n const answer = await this.webrtc.createAnswer();\n await this.webrtc.setLocalDescription(answer);\n this.webRtcSocket.send(JSON.stringify({\n method: 'answer',\n payload: {\n sdp: answer,\n user_state: this.user_state\n }\n }));\n }\n break;\n case 'have-local-offer':\n this.debugLogger('this.webrtc.signalingState====>[have-local-offer]');\n if (!this.webRtcSocket) {\n const suuid = new URL(this.options.source).pathname.split('/').slice(-1);\n const formData = new FormData();\n formData.append('data', btoa(this.webrtc.localDescription.sdp));\n formData.append('suuid', suuid);\n const response = await fetch(this.options.source, {\n method: 'POST',\n body: formData\n });\n if (response.ok) {\n const remoteDescription = await response.text();\n this.webrtc.setRemoteDescription(new RTCSessionDescription({\n type: 'answer',\n sdp: atob(remoteDescription)\n }));\n }\n } else {\n this.webRtcSocket.send(JSON.stringify({\n method: 'offer',\n payload: {\n user_state: this.user_state,\n sdp: this.webrtc.localDescription\n }\n }));\n }\n break;\n case 'stable':\n /*\n * There is no ongoing exchange of offer and answer underway.\n * This may mean that the RTCPeerConnection object is new, in which case both the localDescription and remoteDescription are null;\n * it may also mean that negotiation is complete and a connection has been established.\n */\n this.debugLogger('this.webrtc.signalingState====>[stable]');\n break;\n case 'closed':\n /*\n * The RTCPeerConnection has been closed.\n */\n this.debugLogger('this.webrtc.signalingState====>[closed]');\n this.destroy();\n break;\n default:\n console.log(\"unhandled signalingState is \".concat(this.webrtc.signalingState));\n break;\n }\n });\n _defineProperty(this, \"icegatheringstatechange\", () => {\n switch (this.webrtc.iceGatheringState) {\n case 'gathering':\n /* collection of candidates has begun */\n this.debugLogger('collection of candidates has begun');\n break;\n case 'complete':\n /* collection of candidates is finished */\n this.debugLogger('collection of candidates is finished');\n break;\n }\n });\n _defineProperty(this, \"icecandidate\", event => {\n this.debugLogger('icecandidate\\n', event);\n if (this.webRtcSocket) {\n if (event.candidate && event.candidate.candidate !== '') {\n this.webRtcSocket.send(JSON.stringify({\n method: 'ice_candidate',\n payload: event.candidate\n }));\n }\n }\n });\n _defineProperty(this, \"icecandidateerror\", event => {\n this.debugLogger('icecandidateerror\\n', \"hostCandidate: \".concat(event.hostCandidate, \" CODE: \").concat(event.errorCode, \" TEXT: \").concat(event.errorText));\n });\n _defineProperty(this, \"connectionstatechange\", e => {\n //console.log(e)\n switch (this.webrtc.connectionState) {\n case 'new':\n case 'connected':\n this.debugLogger('connected');\n break;\n case 'disconnected':\n this.debugLogger('disconnected...');\n break;\n case 'closed':\n this.debugLogger('Offline');\n break;\n case 'failed':\n this.webrtc.restartIce();\n this.debugLogger('Error');\n break;\n default:\n this.debugLogger(\"Unhadled state: \".concat(this.webrtc.connectionState));\n break;\n }\n });\n _defineProperty(this, \"iceconnectionstatechange\", () => {\n this.debugLogger('iceconnectionstatechange\\n', this.webrtc.iceConnectionState);\n });\n _defineProperty(this, \"onTrack\", event => {\n this.debugLogger('onTrack\\n');\n //make sure there is only one video track in mediaStream\n if (event.track.kind === 'video' && this.mediaStream.getVideoTracks().length > 0) {\n this.mediaStream.removeTrack(this.mediaStream.getVideoTracks()[0]);\n }\n if (event.track.kind === 'audio' && this.mediaStream.getAudioTracks().length > 0) {\n this.mediaStream.removeTrack(this.mediaStream.getAudioTracks()[0]);\n }\n this.mediaStream.addTrack(event.track);\n });\n _defineProperty(this, \"destroy\", () => {\n this.codec = null;\n this.presets = null;\n this.audio_tracks = null;\n if (this.currentPlayerType != null) {\n switch (this.currentPlayerType) {\n case 'hls':\n if (this.hls != null) {\n this.hls.destroy();\n }\n break;\n case 'rtc':\n if (this.webrtc != null) {\n this.webrtc.close();\n this.webrtc = null;\n this.video.srcObject = null;\n this.mediaStream = null;\n }\n break;\n case 'ws':\n case 'ws-new':\n this.webSocket.onerror = null;\n this.webSocket.onopen = null;\n this.webSocket.onmessage = null;\n this.webSocket.onclose = null;\n this.webSocket.close(1000);\n this.turn = [];\n break;\n case 'ws-rtc':\n this.webRtcSocket.onerror = null;\n this.webRtcSocket.onopen = null;\n this.webRtcSocket.onmessage = null;\n this.webRtcSocket.onclose = null;\n this.webRtcSocket.close(1000);\n this.turn = [];\n if (this.webrtc != null) {\n this.webrtc.close();\n this.webrtc = null;\n this.video.srcObject = null;\n this.mediaStream = null;\n }\n break;\n default:\n }\n this.video.pause();\n this.video.removeAttribute('src'); // empty source\n this.video.load();\n }\n });\n _defineProperty(this, \"addVideoListeners\", () => {\n this.video.addEventListener('error', e => {\n this.debugLogger('[ video listener ]', e);\n this.destroy();\n });\n this.video.addEventListener('play', () => {\n this.paused = false;\n });\n this.video.addEventListener('pause', () => {\n this.paused = true;\n });\n this.video.addEventListener('resize', () => {\n if (typeof this.options.onResolutionChange === 'function') {\n this.options.onResolutionChange(this.video.videoWidth, this.video.videoHeight);\n }\n });\n this.video.addEventListener('progress', () => {\n if (this.currentPlayerType === 'ws' && this.video.buffered.length > 0) {\n if (this.video.currentTime < this.video.buffered.start(this.video.buffered.length - 1)) {\n this.video.currentTime = this.video.buffered.end(this.video.buffered.length - 1) - 1;\n }\n }\n });\n this.video.addEventListener('canplay', () => {\n if (this.currentPlayerType === 'ws') {\n if (this.video.paused && this.video.autoplay) {\n this.video.play();\n }\n }\n });\n });\n _defineProperty(this, \"getImageBase64\", () => {\n const canvas = document.createElement('canvas');\n canvas.width = this.video.videoWidth;\n canvas.height = this.video.videoHeight;\n canvas.getContext('2d').drawImage(this.video, 0, 0, canvas.width, canvas.height);\n const dataURL = canvas.toDataURL();\n canvas.remove();\n return dataURL;\n });\n _defineProperty(this, \"debugLogger\", function () {\n if (_this.options.debug) {\n for (var _len = arguments.length, arg = new Array(_len), _key = 0; _key < _len; _key++) {\n arg[_key] = arguments[_key];\n }\n if (_this.options.debug === 'trace') {\n console.trace(...arg);\n } else {\n const d = new Date();\n console.log(d.toLocaleTimeString() + \".\".concat(d.getMilliseconds()), ...arg);\n }\n }\n });\n this.options = _objectSpread(_objectSpread({}, this.options), options);\n this.createElements();\n if (this.options.parentElement) {\n this.attachTo(this.options.parentElement);\n }\n this.defDocumentHidden();\n }\n defDocumentHidden() {\n if (typeof document.hidden !== 'undefined') {\n this.hidden = 'hidden';\n } else if (typeof document.msHidden !== 'undefined') {\n this.hidden = 'msHidden';\n } else if (typeof document.webkitHidden !== 'undefined') {\n this.hidden = 'webkitHidden';\n }\n }\n}\n\n//# sourceURL=webpack://rtsptowebplayer/./src/rtsp-to-web-player.js?"); /***/ }), diff --git a/dist/index.html b/dist/index.html index a40b432..222cef8 100644 --- a/dist/index.html +++ b/dist/index.html @@ -35,6 +35,9 @@ parentElement: document.getElementById('player'), autoplay: true, debug: true, + onWsClose: (code,reason)=>{ + console.log(code,reason); + }, getPresets: (video, audio) => { document.getElementById("control").innerHTML = ''; for (let i = 0; i < video.length; i++) { diff --git a/index.html b/index.html index 50f3ba5..1b19be3 100644 --- a/index.html +++ b/index.html @@ -31,7 +31,10 @@ const options = { parentElement: document.getElementById('player'), - debug: true + debug: true, + onWsClose: (code,reason)=>{ + console.log(code,reason); + }, }; const player = new RTSPtoWEBPlayer(options); const play = () => { diff --git a/src/rtsp-to-web-player.js b/src/rtsp-to-web-player.js index ac2baff..7a541d5 100644 --- a/src/rtsp-to-web-player.js +++ b/src/rtsp-to-web-player.js @@ -43,6 +43,7 @@ export default class RTSPtoWEBPlayer { getPresets: null, onResolutionChange: null, latency: null, + onWsClose: null, }; constructor(options) { @@ -117,6 +118,9 @@ export default class RTSPtoWEBPlayer { this.webSocket = new WebSocket(this.options.source); this.webSocket.onclose = e => { + if (typeof this.options.onWsClose === 'function') { + this.options.onWsClose(e.code, e.reason); + } this.debugLogger(e); }; @@ -133,6 +137,9 @@ export default class RTSPtoWEBPlayer { this.webRtcSocket.onclose = e => { this.debugLogger(e); this.webRtcSocket.onmessage = null; + if (typeof this.options.onWsClose === 'function') { + this.options.onWsClose(e.code, e.reason); + } }; this.webRtcSocket.onerror = e => { this.debugLogger(e); @@ -312,8 +319,11 @@ export default class RTSPtoWEBPlayer { websocketEvents = () => { this.webSocket = new WebSocket(this.options.source); this.webSocket.binaryType = 'arraybuffer'; - this.webSocket.onclose = () => { + this.webSocket.onclose = e => { this.webSocket.onmessage = null; + if (typeof this.options.onWsClose === 'function') { + this.options.onWsClose(e.code, e.reason); + } }; this.webSocket.onmessage = ({data}) => { if (this.codec === null) {