ovenlivekit 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,7 +26,7 @@ return /******/ (() => { // webpackBootstrap
26
26
  \****************************/
27
27
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
28
28
 
29
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\nconst OvenLiveKit = {};\r\n\r\nconst version = '1.3.0';\r\nconst logHeader = 'OvenLiveKit.js :';\r\nconst logEventHeader = 'OvenLiveKit.js ====';\r\n\r\n// private methods\r\nfunction sendMessage(webSocket, message) {\r\n\r\n if (webSocket) {\r\n webSocket.send(JSON.stringify(message));\r\n }\r\n}\r\n\r\nfunction generateDomainFromUrl(url) {\r\n let result = '';\r\n let match;\r\n if (match = url.match(/^(?:wss?:\\/\\/)?(?:[^@\\n]+@)?(?:www\\.)?([^:\\/\\n\\?\\=]+)/im)) {\r\n result = match[1];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction findIp(string) {\r\n\r\n let result = '';\r\n let match;\r\n\r\n if (match = string.match(new RegExp('\\\\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\b', 'gi'))) {\r\n result = match[0];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction getFormatNumber(sdp, format) {\r\n\r\n const lines = sdp.split('\\r\\n');\r\n let formatNumber = -1;\r\n\r\n for (let i = 0; i < lines.length - 1; i++) {\r\n\r\n lines[i] = lines[i].toLowerCase();\r\n\r\n if (lines[i].indexOf('a=rtpmap') === 0 && lines[i].indexOf(format.toLowerCase()) > -1) {\r\n // parsing \"a=rtpmap:100 H264/90000\" line\r\n // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters >]\r\n formatNumber = lines[i].split(' ')[0].split(':')[1];\r\n break;\r\n }\r\n }\r\n\r\n return formatNumber;\r\n}\r\n\r\nfunction setPreferredVideoFormat(sdp, formatName) {\r\n\r\n const formatNumber = getFormatNumber(sdp, formatName);\r\n\r\n if (formatNumber === -1) {\r\n return sdp;\r\n }\r\n\r\n let newLines = [];\r\n const lines = sdp.split('\\r\\n');\r\n\r\n for (let i = 0; i < lines.length - 1; i++) {\r\n\r\n const line = lines[i];\r\n\r\n if (line.indexOf('m=video') === 0) {\r\n\r\n // m=<media> <port>/<number of ports> <transport> <fmt list>\r\n const others = line.split(' ').slice(0, 3);\r\n const formats = line.split(' ').slice(3);\r\n formats.sort(function (x, y) { return x == formatNumber ? -1 : y == formatNumber ? 1 : 0; });\r\n newLines.push(others.concat(formats).join(' '));\r\n } else {\r\n newLines.push(line);\r\n }\r\n\r\n }\r\n\r\n return newLines.join('\\r\\n') + '\\r\\n';\r\n}\r\n\r\nfunction removeFormat(sdp, formatNumber) {\r\n let newLines = [];\r\n let lines = sdp.split('\\r\\n');\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n\r\n if (lines[i].indexOf('m=video') === 0) {\r\n newLines.push(lines[i].replace(' ' + formatNumber + '', ''));\r\n } else if (lines[i].indexOf(formatNumber + '') > -1) {\r\n\r\n } else {\r\n newLines.push(lines[i]);\r\n }\r\n }\r\n\r\n return newLines.join('\\r\\n')\r\n}\r\n\r\nasync function getStreamForDeviceCheck(type) {\r\n\r\n // High resolution video constraints makes browser to get maximum resolution of video device.\r\n const constraints = {\r\n };\r\n\r\n if (type === 'both') {\r\n constraints.audio = true;\r\n constraints.video = true;\r\n } else if (type === 'audio') {\r\n constraints.audio = true;\r\n } else if (type === 'video') {\r\n constraints.video = true;\r\n }\r\n\r\n return await navigator.mediaDevices.getUserMedia(constraints);\r\n}\r\n\r\nasync function getDevices() {\r\n\r\n return await navigator.mediaDevices.enumerateDevices();\r\n}\r\n\r\nfunction gotDevices(deviceInfos) {\r\n\r\n let devices = {\r\n 'audioinput': [],\r\n 'audiooutput': [],\r\n 'videoinput': [],\r\n 'other': [],\r\n };\r\n\r\n for (let i = 0; i !== deviceInfos.length; ++i) {\r\n\r\n const deviceInfo = deviceInfos[i];\r\n\r\n let info = {};\r\n\r\n info.deviceId = deviceInfo.deviceId;\r\n\r\n if (deviceInfo.kind === 'audioinput') {\r\n\r\n info.label = deviceInfo.label || `microphone ${devices.audioinput.length + 1}`;\r\n devices.audioinput.push(info);\r\n } else if (deviceInfo.kind === 'audiooutput') {\r\n\r\n info.label = deviceInfo.label || `speaker ${devices.audiooutput.length + 1}`;\r\n devices.audiooutput.push(info);\r\n } else if (deviceInfo.kind === 'videoinput') {\r\n\r\n info.label = deviceInfo.label || `camera ${devices.videoinput.length + 1}`;\r\n devices.videoinput.push(info);\r\n } else {\r\n\r\n info.label = deviceInfo.label || `other ${devices.other.length + 1}`;\r\n devices.other.push(info);\r\n }\r\n }\r\n\r\n return devices;\r\n}\r\n\r\nfunction initConfig(instance, options) {\r\n\r\n // webrtc or whip\r\n instance.streamingMode = null;\r\n\r\n instance.inputStream = null;\r\n instance.webSocket = null;\r\n instance.peerConnection = null;\r\n instance.connectionConfig = {};\r\n\r\n instance.videoElement = null;\r\n instance.endpointUrl = null;\r\n instance.resourceUrl = null;\r\n\r\n if (options && options.callbacks) {\r\n\r\n instance.callbacks = options.callbacks;\r\n } else {\r\n instance.callbacks = {};\r\n }\r\n}\r\n\r\nfunction addMethod(instance) {\r\n\r\n function errorHandler(error) {\r\n\r\n if (instance.callbacks.error) {\r\n\r\n instance.callbacks.error(error);\r\n }\r\n }\r\n\r\n async function fetchWithRedirect(url, options) {\r\n let fetched = await fetch(url, options);\r\n\r\n while (fetched.redirected) {\r\n url = fetched.url;\r\n fetched = await fetch(url, options);\r\n }\r\n\r\n return fetched;\r\n }\r\n\r\n function getUserMedia(constraints) {\r\n\r\n if (!constraints) {\r\n\r\n constraints = {\r\n video: {\r\n deviceId: undefined\r\n },\r\n audio: {\r\n deviceId: undefined\r\n }\r\n };\r\n }\r\n\r\n console.info(logHeader, 'Request Stream To Input Devices With Constraints', constraints);\r\n\r\n return navigator.mediaDevices.getUserMedia(constraints)\r\n .then(function (stream) {\r\n\r\n console.info(logHeader, 'Received Media Stream From Input Device', stream);\r\n\r\n instance.inputStream = stream;\r\n\r\n let elem = instance.videoElement;\r\n\r\n // Attach stream to video element when video element is provided.\r\n if (elem) {\r\n\r\n elem.srcObject = stream;\r\n\r\n elem.onloadedmetadata = function (e) {\r\n\r\n elem.play();\r\n };\r\n }\r\n\r\n return new Promise(function (resolve) {\r\n\r\n resolve(stream);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error(logHeader, 'Can\\'t Get Media Stream From Input Device', error);\r\n errorHandler(error);\r\n\r\n return new Promise(function (resolve, reject) {\r\n reject(error);\r\n });\r\n });\r\n }\r\n\r\n function getDisplayMedia(constraints) {\r\n\r\n if (!constraints) {\r\n constraints = {};\r\n }\r\n\r\n console.info(logHeader, 'Request Stream To Display With Constraints', constraints);\r\n\r\n return navigator.mediaDevices.getDisplayMedia(constraints)\r\n .then(function (stream) {\r\n\r\n console.info(logHeader, 'Received Media Stream From Display', stream);\r\n\r\n instance.inputStream = stream;\r\n\r\n let elem = instance.videoElement;\r\n\r\n // Attach stream to video element when video element is provided.\r\n if (elem) {\r\n\r\n elem.srcObject = stream;\r\n\r\n elem.onloadedmetadata = function (e) {\r\n\r\n elem.play();\r\n };\r\n }\r\n\r\n return new Promise(function (resolve) {\r\n\r\n resolve(stream);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error(logHeader, 'Can\\'t Get Media Stream From Display', error);\r\n errorHandler(error);\r\n\r\n return new Promise(function (resolve, reject) {\r\n reject(error);\r\n });\r\n });\r\n }\r\n\r\n function setMediaStream(stream) {\r\n // Check if a valid stream is provided\r\n if (!stream || !(stream instanceof MediaStream)) {\r\n\r\n const error = new Error(\"Invalid MediaStream provided\");\r\n console.error(logHeader, 'Invalid MediaStream', error);\r\n errorHandler(error);\r\n\r\n return new Promise(function (resolve, reject) {\r\n reject(error);\r\n });\r\n }\r\n\r\n console.info(logHeader, 'Received Media Stream', stream);\r\n\r\n instance.inputStream = stream;\r\n\r\n let elem = instance.videoElement;\r\n\r\n // Attach stream to video element when video element is provided.\r\n if (elem) {\r\n elem.srcObject = stream;\r\n\r\n elem.onloadedmetadata = function (e) {\r\n elem.play();\r\n };\r\n }\r\n\r\n return new Promise(function (resolve) {\r\n resolve(stream);\r\n });\r\n }\r\n\r\n // From https://webrtchacks.com/limit-webrtc-bandwidth-sdp/\r\n function setBitrateLimit(sdp, media, bitrate) {\r\n\r\n let lines = sdp.split('\\r\\n');\r\n let line = -1;\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n if (lines[i].indexOf('m=' + media) === 0) {\r\n line = i;\r\n break;\r\n }\r\n }\r\n if (line === -1) {\r\n // Could not find the m line for media\r\n return sdp;\r\n }\r\n\r\n // Pass the m line\r\n line++;\r\n\r\n // Skip i and c lines\r\n while (lines[line].indexOf('i=') === 0 || lines[line].indexOf('c=') === 0) {\r\n\r\n line++;\r\n }\r\n\r\n // If we're on a b line, replace it\r\n if (lines[line].indexOf('b') === 0) {\r\n\r\n lines[line] = 'b=AS:' + bitrate;\r\n\r\n return lines.join('\\r\\n');\r\n }\r\n\r\n // Add a new b line\r\n let newLines = lines.slice(0, line)\r\n\r\n newLines.push('b=AS:' + bitrate)\r\n newLines = newLines.concat(lines.slice(line, lines.length))\r\n\r\n return newLines.join('\\r\\n')\r\n }\r\n\r\n function initWebSocket(endpointUrl) {\r\n\r\n if (!endpointUrl) {\r\n errorHandler('endpointUrl is required');\r\n return;\r\n }\r\n\r\n let webSocket = null;\r\n\r\n try {\r\n\r\n webSocket = new WebSocket(endpointUrl);\r\n } catch (error) {\r\n\r\n errorHandler(error);\r\n }\r\n\r\n\r\n instance.webSocket = webSocket;\r\n\r\n webSocket.onopen = function () {\r\n\r\n // Request offer at the first time.\r\n sendMessage(webSocket, {\r\n command: 'request_offer'\r\n });\r\n };\r\n\r\n webSocket.onmessage = function (e) {\r\n\r\n let message = JSON.parse(e.data);\r\n\r\n if (message.error) {\r\n console.error('webSocket.onmessage', message.error);\r\n errorHandler(message.error);\r\n }\r\n\r\n if (message.command === 'offer') {\r\n\r\n // OME returns offer. Start create peer connection.\r\n createPeerConnection(\r\n message.id,\r\n message.peer_id,\r\n message.sdp,\r\n message.candidates,\r\n message.ice_servers\r\n );\r\n }\r\n };\r\n\r\n webSocket.onerror = function (error) {\r\n\r\n console.error('webSocket.onerror', error);\r\n errorHandler(error);\r\n };\r\n\r\n webSocket.onclose = function (e) {\r\n\r\n if (!instance.webSocketClosedByUser) {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n instance.callbacks.connectionClosed('websocket', e);\r\n }\r\n }\r\n };\r\n\r\n }\r\n\r\n async function startWhip(endpointUrl) {\r\n\r\n if (instance.peerConnection) {\r\n console.error('Connection already established');\r\n errorHandler('Connection already established');\r\n return;\r\n }\r\n\r\n const peerConnectionConfig = {\r\n bundlePolicy: \"max-bundle\"\r\n };\r\n\r\n if (instance.connectionConfig.iceServers) {\r\n\r\n // first priority using ice servers from local config.\r\n peerConnectionConfig.iceServers = instance.connectionConfig.iceServers;\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n } else {\r\n // last priority using default ice servers.\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n }\r\n\r\n console.info(logHeader, 'Create Peer Connection With Config', peerConnectionConfig);\r\n\r\n const peerConnection = new RTCPeerConnection(peerConnectionConfig);\r\n\r\n instance.peerConnection = peerConnection;\r\n\r\n if (!instance.inputStream) {\r\n console.error('No input stream in OvenLiveKit');\r\n errorHandler('No input stream in OvenLiveKit');\r\n return;\r\n }\r\n\r\n for (const track of instance.inputStream.getTracks()) {\r\n //You could add simulcast too here\r\n console.log(logHeader, 'Adding track: ', track);\r\n peerConnection.addTransceiver(track, { 'direction': 'sendonly' });\r\n }\r\n\r\n peerConnection.oniceconnectionstatechange = function (e) {\r\n\r\n let state = peerConnection.iceConnectionState;\r\n\r\n if (instance.callbacks.iceStateChange) {\r\n\r\n console.info(logHeader, 'ICE State', '[' + state + ']');\r\n instance.callbacks.iceStateChange(state);\r\n }\r\n\r\n if (state === 'connected') {\r\n\r\n if (instance.callbacks.connected) {\r\n instance.callbacks.connected(e);\r\n }\r\n }\r\n\r\n if (state === 'failed') {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.error(logHeader, 'Ice connection failed', e);\r\n instance.callbacks.errorHandler(e);\r\n }\r\n }\r\n\r\n if (state === 'disconnected' || state === 'closed') {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.error(logHeader, 'Ice connection disconnected or closed', e);\r\n instance.callbacks.connectionClosed('ice', e);\r\n }\r\n }\r\n }\r\n\r\n const offer = await peerConnection.createOffer();\r\n console.log(logHeader, 'Offer SDP: ', offer.sdp);\r\n\r\n if (instance.connectionConfig.maxVideoBitrate) {\r\n\r\n // if bandwith limit is set. modify sdp from ome to limit acceptable bandwidth of ome\r\n offer.sdp = setBitrateLimit(offer.sdp, 'video', instance.connectionConfig.maxVideoBitrate);\r\n }\r\n\r\n if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {\r\n\r\n offer.sdp = appendFmtp(offer.sdp);\r\n }\r\n\r\n if (instance.connectionConfig.preferredVideoFormat) {\r\n offer.sdp = setPreferredVideoFormat(offer.sdp, instance.connectionConfig.preferredVideoFormat)\r\n }\r\n\r\n const headers = {\r\n \"Content-Type\": \"application/sdp\"\r\n };\r\n\r\n if (instance.connectionConfig.httpHeaders) {\r\n Object.assign(headers, instance.connectionConfig.httpHeaders);\r\n }\r\n\r\n const fetched = await fetchWithRedirect(endpointUrl, {\r\n method: \"POST\",\r\n body: offer.sdp,\r\n headers\r\n });\r\n\r\n if (!fetched.ok) {\r\n console.error('Failed to fetch', fetched.status);\r\n errorHandler(`Failed to fetch ${fetched.status}`);\r\n closePeerConnection();\r\n return;\r\n }\r\n\r\n if (!fetched.headers.get(\"location\")) {\r\n console.error('No location header on answer response');\r\n errorHandler('No location header on answer response');\r\n return;\r\n }\r\n\r\n // update endpointUrl\r\n instance.endpointUrl = fetched.url;\r\n console.log(logHeader, 'Updated endpointUrl: ', instance.endpointUrl);\r\n\r\n const baseUrl = new URL(endpointUrl).origin;\r\n instance.resourceUrl = baseUrl + fetched.headers.get(\"location\");\r\n\r\n const answer = await fetched.text();\r\n console.log(logHeader, 'Answer SDP: ', answer);\r\n\r\n try {\r\n await peerConnection.setLocalDescription(offer);\r\n } catch (error) {\r\n console.error('peerConnection.setLocalDescription', error);\r\n errorHandler(error);\r\n }\r\n\r\n try {\r\n await peerConnection.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: answer\r\n });\r\n } catch (error) {\r\n console.error('peerConnection.setRemoteDescription', error);\r\n errorHandler(error);\r\n }\r\n }\r\n\r\n async function stopWhip() {\r\n\r\n if (!instance.peerConnection) {\r\n console.error('No connection to close');\r\n errorHandler('No connection to close');\r\n return;\r\n }\r\n\r\n closePeerConnection();\r\n\r\n if (instance.resourceUrl) {\r\n\r\n const headers = {\r\n };\r\n\r\n if (instance.connectionConfig.httpHeaders) {\r\n Object.assign(headers, instance.connectionConfig.httpHeaders);\r\n }\r\n\r\n await fetchWithRedirect(instance.resourceUrl, {\r\n method: \"DELETE\",\r\n headers\r\n });\r\n }\r\n }\r\n\r\n function appendFmtp(sdp) {\r\n\r\n const fmtpStr = instance.connectionConfig.sdp.appendFmtp;\r\n\r\n const lines = sdp.split('\\r\\n');\r\n const payloads = [];\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n\r\n if (lines[i].indexOf('m=video') === 0) {\r\n\r\n let tokens = lines[i].split(' ')\r\n\r\n for (let j = 3; j < tokens.length; j++) {\r\n\r\n payloads.push(tokens[j]);\r\n }\r\n\r\n break;\r\n }\r\n }\r\n\r\n for (let i = 0; i < payloads.length; i++) {\r\n\r\n let fmtpLineFound = false;\r\n\r\n for (let j = 0; j < lines.length; j++) {\r\n\r\n if (lines[j].indexOf('a=fmtp:' + payloads[i]) === 0) {\r\n fmtpLineFound = true;\r\n lines[j] += ';' + fmtpStr;\r\n }\r\n }\r\n\r\n if (!fmtpLineFound) {\r\n\r\n for (let j = 0; j < lines.length; j++) {\r\n\r\n if (lines[j].indexOf('a=rtpmap:' + payloads[i]) === 0) {\r\n\r\n lines[j] += '\\r\\na=fmtp:' + payloads[i] + ' ' + fmtpStr;\r\n }\r\n }\r\n }\r\n }\r\n\r\n return lines.join('\\r\\n')\r\n }\r\n\r\n function appendOrientation(sdp) {\r\n\r\n const lines = sdp.split('\\r\\n');\r\n const payloads = [];\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n\r\n if (lines[i].indexOf('m=video') === 0) {\r\n\r\n let tokens = lines[i].split(' ')\r\n\r\n for (let j = 3; j < tokens.length; j++) {\r\n\r\n payloads.push(tokens[j]);\r\n }\r\n\r\n break;\r\n }\r\n }\r\n\r\n for (let i = 0; i < payloads.length; i++) {\r\n\r\n for (let j = 0; j < lines.length; j++) {\r\n\r\n if (lines[j].indexOf('a=rtpmap:' + payloads[i]) === 0) {\r\n\r\n lines[j] += '\\r\\na=extmap:' + payloads[i] + ' urn:3gpp:video-orientation';\r\n }\r\n }\r\n }\r\n\r\n return lines.join('\\r\\n')\r\n }\r\n\r\n function createPeerConnection(id, peerId, offer, candidates, iceServers) {\r\n\r\n let peerConnectionConfig = {};\r\n\r\n if (instance.connectionConfig.iceServers) {\r\n\r\n // first priority using ice servers from local config.\r\n peerConnectionConfig.iceServers = instance.connectionConfig.iceServers;\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n } else if (iceServers) {\r\n\r\n // second priority using ice servers from ome and force using TCP\r\n peerConnectionConfig.iceServers = [];\r\n\r\n for (let i = 0; i < iceServers.length; i++) {\r\n\r\n let iceServer = iceServers[i];\r\n\r\n let regIceServer = {};\r\n\r\n regIceServer.urls = iceServer.urls;\r\n\r\n let hasWebSocketUrl = false;\r\n let webSocketUrl = generateDomainFromUrl(instance.endpointUrl);\r\n\r\n for (let j = 0; j < regIceServer.urls.length; j++) {\r\n\r\n let serverUrl = regIceServer.urls[j];\r\n\r\n if (serverUrl.indexOf(webSocketUrl) > -1) {\r\n hasWebSocketUrl = true;\r\n break;\r\n }\r\n }\r\n\r\n if (!hasWebSocketUrl) {\r\n\r\n if (regIceServer.urls.length > 0) {\r\n\r\n let cloneIceServer = regIceServer.urls[0];\r\n let ip = findIp(cloneIceServer);\r\n\r\n if (webSocketUrl && ip) {\r\n regIceServer.urls.push(cloneIceServer.replace(ip, webSocketUrl));\r\n }\r\n }\r\n }\r\n\r\n regIceServer.username = iceServer.user_name;\r\n regIceServer.credential = iceServer.credential;\r\n\r\n peerConnectionConfig.iceServers.push(regIceServer);\r\n }\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n } else {\r\n peerConnectionConfig.iceTransportPolicy = 'relay';\r\n }\r\n } else {\r\n // last priority using default ice servers.\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n }\r\n\r\n let advancedSetting = {\r\n optional: [\r\n {\r\n googHighStartBitrate: {\r\n exact: !0\r\n }\r\n },\r\n {\r\n googPayloadPadding: {\r\n exact: !0\r\n }\r\n },\r\n {\r\n googScreencastMinBitrate: {\r\n exact: 500\r\n }\r\n },\r\n {\r\n enableDscp: {\r\n exact: true\r\n }\r\n }\r\n ]\r\n };\r\n\r\n console.info(logHeader, 'Create Peer Connection With Config', peerConnectionConfig);\r\n\r\n let peerConnection = new RTCPeerConnection(peerConnectionConfig);\r\n\r\n instance.peerConnection = peerConnection;\r\n\r\n // set local stream\r\n instance.inputStream.getTracks().forEach(function (track) {\r\n\r\n console.info(logHeader, 'Add Track To Peer Connection', track);\r\n peerConnection.addTrack(track, instance.inputStream);\r\n });\r\n\r\n if (instance.connectionConfig.maxVideoBitrate) {\r\n\r\n // if bandwith limit is set. modify sdp from ome to limit acceptable bandwidth of ome\r\n offer.sdp = setBitrateLimit(offer.sdp, 'video', instance.connectionConfig.maxVideoBitrate);\r\n }\r\n\r\n if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {\r\n\r\n offer.sdp = appendFmtp(offer.sdp);\r\n }\r\n\r\n if (instance.connectionConfig.preferredVideoFormat) {\r\n offer.sdp = setPreferredVideoFormat(offer.sdp, instance.connectionConfig.preferredVideoFormat)\r\n }\r\n\r\n\r\n // offer.sdp = appendOrientation(offer.sdp);\r\n console.info(logHeader, 'Modified offer sdp\\n\\n' + offer.sdp);\r\n\r\n peerConnection.setRemoteDescription(new RTCSessionDescription(offer))\r\n .then(function () {\r\n\r\n peerConnection.createAnswer()\r\n .then(function (answer) {\r\n\r\n if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {\r\n\r\n answer.sdp = appendFmtp(answer.sdp);\r\n }\r\n\r\n if (instance.connectionConfig.preferredVideoFormat) {\r\n answer.sdp = setPreferredVideoFormat(answer.sdp, instance.connectionConfig.preferredVideoFormat)\r\n }\r\n\r\n console.info(logHeader, 'Modified answer sdp\\n\\n' + answer.sdp);\r\n\r\n peerConnection.setLocalDescription(answer)\r\n .then(function () {\r\n\r\n sendMessage(instance.webSocket, {\r\n id: id,\r\n peer_id: peerId,\r\n command: 'answer',\r\n sdp: answer\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.setLocalDescription', error);\r\n errorHandler(error);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.createAnswer', error);\r\n errorHandler(error);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.setRemoteDescription', error);\r\n errorHandler(error);\r\n });\r\n\r\n if (candidates) {\r\n\r\n addIceCandidate(peerConnection, candidates);\r\n }\r\n\r\n peerConnection.onicecandidate = function (e) {\r\n\r\n if (e.candidate && e.candidate.candidate) {\r\n\r\n sendMessage(instance.webSocket, {\r\n id: id,\r\n peer_id: peerId,\r\n command: 'candidate',\r\n candidates: [e.candidate]\r\n });\r\n }\r\n };\r\n\r\n peerConnection.oniceconnectionstatechange = function (e) {\r\n\r\n let state = peerConnection.iceConnectionState;\r\n\r\n if (instance.callbacks.iceStateChange) {\r\n\r\n console.info(logHeader, 'ICE State', '[' + state + ']');\r\n instance.callbacks.iceStateChange(state);\r\n }\r\n\r\n if (state === 'connected') {\r\n\r\n if (instance.callbacks.connected) {\r\n instance.callbacks.connected(e);\r\n }\r\n }\r\n\r\n if (state === 'failed' || state === 'disconnected' || state === 'closed') {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.error(logHeader, 'Ice connection closed', e);\r\n instance.callbacks.connectionClosed('ice', e);\r\n }\r\n }\r\n }\r\n }\r\n\r\n function addIceCandidate(peerConnection, candidates) {\r\n\r\n for (let i = 0; i < candidates.length; i++) {\r\n\r\n if (candidates[i] && candidates[i].candidate) {\r\n\r\n let basicCandidate = candidates[i];\r\n\r\n peerConnection.addIceCandidate(new RTCIceCandidate(basicCandidate))\r\n .then(function () {\r\n\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.addIceCandidate', error);\r\n errorHandler(error);\r\n });\r\n }\r\n }\r\n }\r\n\r\n function closePeerConnection() {\r\n if (instance.peerConnection) {\r\n\r\n // remove tracks from peer connection\r\n instance.peerConnection.getSenders().forEach(function (sender) {\r\n instance.peerConnection.removeTrack(sender);\r\n });\r\n\r\n instance.peerConnection.close();\r\n instance.peerConnection = null;\r\n delete instance.peerConnection;\r\n }\r\n }\r\n\r\n function closeWebSocket() {\r\n\r\n if (instance.webSocket) {\r\n\r\n instance.webSocket.close();\r\n instance.webSocket = null;\r\n delete instance.webSocket;\r\n }\r\n }\r\n\r\n function closeInputStream() {\r\n // release video, audio stream\r\n if (instance.inputStream) {\r\n\r\n instance.inputStream.getTracks().forEach(track => {\r\n\r\n track.stop();\r\n instance.inputStream.removeTrack(track);\r\n });\r\n\r\n if (instance.videoElement) {\r\n instance.videoElement.srcObject = null;\r\n }\r\n\r\n instance.inputStream = null;\r\n delete instance.inputStream;\r\n }\r\n }\r\n\r\n // instance methods\r\n instance.attachMedia = function (videoElement) {\r\n\r\n instance.videoElement = videoElement;\r\n };\r\n\r\n instance.getUserMedia = function (constraints) {\r\n\r\n return getUserMedia(constraints);\r\n };\r\n\r\n instance.getDisplayMedia = function (constraints) {\r\n\r\n return getDisplayMedia(constraints);\r\n };\r\n\r\n instance.setMediaStream = function (stream) {\r\n\r\n return setMediaStream(stream);\r\n };\r\n\r\n instance.startStreaming = function (endpointUrl, connectionConfig) {\r\n\r\n console.info(logEventHeader, `Start Streaming to ${endpointUrl} with connectionConfig`, connectionConfig);\r\n\r\n if (!endpointUrl) {\r\n console.error('endpointUrl is required');\r\n errorHandler('endpointUrl is required');\r\n return;\r\n }\r\n\r\n instance.endpointUrl = endpointUrl;\r\n\r\n if (connectionConfig) {\r\n instance.connectionConfig = connectionConfig;\r\n }\r\n\r\n try {\r\n\r\n const protocol = new URL(endpointUrl).protocol;\r\n\r\n if (protocol === 'wss:' || protocol === 'ws:') {\r\n\r\n instance.streamingMode = 'webrtc';\r\n initWebSocket(endpointUrl);\r\n } else if (protocol === 'https:' || protocol === 'http:') {\r\n\r\n instance.streamingMode = 'whip';\r\n startWhip(endpointUrl);\r\n } else {\r\n console.error('Invalid protocol', error);\r\n errorHandler(error);\r\n }\r\n\r\n } catch (error) {\r\n console.error('Cannot parse connection URL', error);\r\n errorHandler(error);\r\n }\r\n };\r\n\r\n instance.stopStreaming = async function () {\r\n\r\n if (instance.streamingMode === 'webrtc') {\r\n\r\n instance.webSocketClosedByUser = true;\r\n\r\n closeWebSocket();\r\n closePeerConnection();\r\n } else if (instance.streamingMode === 'whip') {\r\n\r\n await stopWhip();\r\n }\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.log(logHeader, 'Connection closed by user');\r\n instance.callbacks.connectionClosed('user', 'Connection closed by user');\r\n }\r\n };\r\n\r\n instance.remove = function () {\r\n\r\n if (instance.streamingMode === 'webrtc') {\r\n\r\n instance.webSocketClosedByUser = true;\r\n\r\n closeWebSocket();\r\n closePeerConnection();\r\n } else if (instance.streamingMode === 'whip') {\r\n stopWhip();\r\n }\r\n\r\n closeInputStream();\r\n\r\n console.info(logEventHeader, 'Removed');\r\n\r\n };\r\n}\r\n\r\nOvenLiveKit.getVersion = function () {\r\n return version;\r\n}\r\n\r\n// static methods\r\nOvenLiveKit.create = function (options) {\r\n\r\n console.info(logEventHeader, 'Create WebRTC Input ' + version);\r\n\r\n let instance = {};\r\n\r\n instance.webSocketClosedByUser = false;\r\n\r\n initConfig(instance, options);\r\n addMethod(instance);\r\n\r\n return instance;\r\n};\r\n\r\nOvenLiveKit.getDevices = async function () {\r\n\r\n try {\r\n // First check both audio and video sources are available.\r\n await getStreamForDeviceCheck('both');\r\n } catch (e) {\r\n\r\n console.warn(logHeader, 'Can not find Video and Audio devices', e);\r\n\r\n let videoFound = null;\r\n let audioFound = null;\r\n\r\n try {\r\n videoFound = await getStreamForDeviceCheck('video');\r\n } catch (e) {\r\n console.warn(logHeader, 'Can not find Video devices', e);\r\n }\r\n\r\n try {\r\n audioFound = await getStreamForDeviceCheck('audio');\r\n } catch (e) {\r\n console.warn(logHeader, 'Can not find Audio devices', e);\r\n }\r\n\r\n if (!videoFound && !audioFound) {\r\n throw new Error('No input devices were found.');\r\n }\r\n }\r\n\r\n const deviceInfos = await getDevices();\r\n return gotDevices(deviceInfos)\r\n};\r\n\r\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (OvenLiveKit);\n\n//# sourceURL=webpack://OvenLiveKit/./src/OvenLiveKit.js?");
29
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\nconst OvenLiveKit = {};\r\n\r\nconst version = '1.4.0';\r\nconst logHeader = 'OvenLiveKit.js :';\r\nconst logEventHeader = 'OvenLiveKit.js ====';\r\n\r\n// private methods\r\nfunction sendMessage(webSocket, message) {\r\n\r\n if (webSocket) {\r\n webSocket.send(JSON.stringify(message));\r\n }\r\n}\r\n\r\nfunction generateDomainFromUrl(url) {\r\n let result = '';\r\n let match;\r\n if (match = url.match(/^(?:wss?:\\/\\/)?(?:[^@\\n]+@)?(?:www\\.)?([^:\\/\\n\\?\\=]+)/im)) {\r\n result = match[1];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction findIp(string) {\r\n\r\n let result = '';\r\n let match;\r\n\r\n if (match = string.match(new RegExp('\\\\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\b', 'gi'))) {\r\n result = match[0];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction getFormatNumber(sdp, format) {\r\n\r\n const lines = sdp.split('\\r\\n');\r\n let formatNumber = -1;\r\n\r\n for (let i = 0; i < lines.length - 1; i++) {\r\n\r\n lines[i] = lines[i].toLowerCase();\r\n\r\n if (lines[i].indexOf('a=rtpmap') === 0 && lines[i].indexOf(format.toLowerCase()) > -1) {\r\n // parsing \"a=rtpmap:100 H264/90000\" line\r\n // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters >]\r\n formatNumber = lines[i].split(' ')[0].split(':')[1];\r\n break;\r\n }\r\n }\r\n\r\n return formatNumber;\r\n}\r\n\r\nfunction setPreferredVideoFormat(sdp, formatName) {\r\n\r\n const formatNumber = getFormatNumber(sdp, formatName);\r\n\r\n if (formatNumber === -1) {\r\n return sdp;\r\n }\r\n\r\n let newLines = [];\r\n const lines = sdp.split('\\r\\n');\r\n\r\n for (let i = 0; i < lines.length - 1; i++) {\r\n\r\n const line = lines[i];\r\n\r\n if (line.indexOf('m=video') === 0) {\r\n\r\n // m=<media> <port>/<number of ports> <transport> <fmt list>\r\n const others = line.split(' ').slice(0, 3);\r\n const formats = line.split(' ').slice(3);\r\n formats.sort(function (x, y) { return x == formatNumber ? -1 : y == formatNumber ? 1 : 0; });\r\n newLines.push(others.concat(formats).join(' '));\r\n } else {\r\n newLines.push(line);\r\n }\r\n\r\n }\r\n\r\n return newLines.join('\\r\\n') + '\\r\\n';\r\n}\r\n\r\nfunction removeFormat(sdp, formatNumber) {\r\n let newLines = [];\r\n let lines = sdp.split('\\r\\n');\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n\r\n if (lines[i].indexOf('m=video') === 0) {\r\n newLines.push(lines[i].replace(' ' + formatNumber + '', ''));\r\n } else if (lines[i].indexOf(formatNumber + '') > -1) {\r\n\r\n } else {\r\n newLines.push(lines[i]);\r\n }\r\n }\r\n\r\n return newLines.join('\\r\\n')\r\n}\r\n\r\nasync function getStreamForDeviceCheck(type) {\r\n\r\n // High resolution video constraints makes browser to get maximum resolution of video device.\r\n const constraints = {\r\n };\r\n\r\n if (type === 'both') {\r\n constraints.audio = true;\r\n constraints.video = true;\r\n } else if (type === 'audio') {\r\n constraints.audio = true;\r\n } else if (type === 'video') {\r\n constraints.video = true;\r\n }\r\n\r\n return await navigator.mediaDevices.getUserMedia(constraints);\r\n}\r\n\r\nasync function getDevices() {\r\n\r\n return await navigator.mediaDevices.enumerateDevices();\r\n}\r\n\r\nfunction gotDevices(deviceInfos) {\r\n\r\n let devices = {\r\n 'audioinput': [],\r\n 'audiooutput': [],\r\n 'videoinput': [],\r\n 'other': [],\r\n };\r\n\r\n for (let i = 0; i !== deviceInfos.length; ++i) {\r\n\r\n const deviceInfo = deviceInfos[i];\r\n\r\n let info = {};\r\n\r\n info.deviceId = deviceInfo.deviceId;\r\n\r\n if (deviceInfo.kind === 'audioinput') {\r\n\r\n info.label = deviceInfo.label || `microphone ${devices.audioinput.length + 1}`;\r\n devices.audioinput.push(info);\r\n } else if (deviceInfo.kind === 'audiooutput') {\r\n\r\n info.label = deviceInfo.label || `speaker ${devices.audiooutput.length + 1}`;\r\n devices.audiooutput.push(info);\r\n } else if (deviceInfo.kind === 'videoinput') {\r\n\r\n info.label = deviceInfo.label || `camera ${devices.videoinput.length + 1}`;\r\n devices.videoinput.push(info);\r\n } else {\r\n\r\n info.label = deviceInfo.label || `other ${devices.other.length + 1}`;\r\n devices.other.push(info);\r\n }\r\n }\r\n\r\n return devices;\r\n}\r\n\r\nfunction initConfig(instance, options) {\r\n\r\n // webrtc or whip\r\n instance.streamingMode = null;\r\n\r\n instance.inputStream = null;\r\n instance.webSocket = null;\r\n instance.peerConnection = null;\r\n instance.connectionConfig = {};\r\n\r\n instance.videoElement = null;\r\n instance.endpointUrl = null;\r\n instance.resourceUrl = null;\r\n\r\n if (options && options.callbacks) {\r\n\r\n instance.callbacks = options.callbacks;\r\n } else {\r\n instance.callbacks = {};\r\n }\r\n}\r\n\r\nfunction addMethod(instance) {\r\n\r\n function errorHandler(error) {\r\n\r\n if (instance.callbacks.error) {\r\n\r\n instance.callbacks.error(error);\r\n }\r\n }\r\n\r\n async function fetchWithRedirect(url, options) {\r\n let fetched = await fetch(url, options);\r\n\r\n while (fetched.redirected) {\r\n url = fetched.url;\r\n fetched = await fetch(url, options);\r\n }\r\n\r\n return fetched;\r\n }\r\n\r\n function getUserMedia(constraints) {\r\n\r\n if (!constraints) {\r\n\r\n constraints = {\r\n video: {\r\n deviceId: undefined\r\n },\r\n audio: {\r\n deviceId: undefined\r\n }\r\n };\r\n }\r\n\r\n console.info(logHeader, 'Request Stream To Input Devices With Constraints', constraints);\r\n\r\n return navigator.mediaDevices.getUserMedia(constraints)\r\n .then(function (stream) {\r\n\r\n console.info(logHeader, 'Received Media Stream From Input Device', stream);\r\n\r\n instance.inputStream = stream;\r\n\r\n let elem = instance.videoElement;\r\n\r\n // Attach stream to video element when video element is provided.\r\n if (elem) {\r\n\r\n elem.srcObject = stream;\r\n\r\n elem.onloadedmetadata = function (e) {\r\n\r\n elem.play();\r\n };\r\n }\r\n\r\n return new Promise(function (resolve) {\r\n\r\n resolve(stream);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error(logHeader, 'Can\\'t Get Media Stream From Input Device', error);\r\n errorHandler(error);\r\n\r\n return new Promise(function (resolve, reject) {\r\n reject(error);\r\n });\r\n });\r\n }\r\n\r\n function getDisplayMedia(constraints) {\r\n\r\n if (!constraints) {\r\n constraints = {};\r\n }\r\n\r\n console.info(logHeader, 'Request Stream To Display With Constraints', constraints);\r\n\r\n return navigator.mediaDevices.getDisplayMedia(constraints)\r\n .then(function (stream) {\r\n\r\n console.info(logHeader, 'Received Media Stream From Display', stream);\r\n\r\n instance.inputStream = stream;\r\n\r\n let elem = instance.videoElement;\r\n\r\n // Attach stream to video element when video element is provided.\r\n if (elem) {\r\n\r\n elem.srcObject = stream;\r\n\r\n elem.onloadedmetadata = function (e) {\r\n\r\n elem.play();\r\n };\r\n }\r\n\r\n return new Promise(function (resolve) {\r\n\r\n resolve(stream);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error(logHeader, 'Can\\'t Get Media Stream From Display', error);\r\n errorHandler(error);\r\n\r\n return new Promise(function (resolve, reject) {\r\n reject(error);\r\n });\r\n });\r\n }\r\n\r\n function setMediaStream(stream) {\r\n // Check if a valid stream is provided\r\n if (!stream || !(stream instanceof MediaStream)) {\r\n\r\n const error = new Error(\"Invalid MediaStream provided\");\r\n console.error(logHeader, 'Invalid MediaStream', error);\r\n errorHandler(error);\r\n\r\n return new Promise(function (resolve, reject) {\r\n reject(error);\r\n });\r\n }\r\n\r\n console.info(logHeader, 'Received Media Stream', stream);\r\n\r\n instance.inputStream = stream;\r\n\r\n let elem = instance.videoElement;\r\n\r\n // Attach stream to video element when video element is provided.\r\n if (elem) {\r\n elem.srcObject = stream;\r\n\r\n elem.onloadedmetadata = function (e) {\r\n elem.play();\r\n };\r\n }\r\n\r\n return new Promise(function (resolve) {\r\n resolve(stream);\r\n });\r\n }\r\n\r\n // From https://webrtchacks.com/limit-webrtc-bandwidth-sdp/\r\n function setBitrateLimit(sdp, media, bitrate) {\r\n\r\n let lines = sdp.split('\\r\\n');\r\n let line = -1;\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n if (lines[i].indexOf('m=' + media) === 0) {\r\n line = i;\r\n break;\r\n }\r\n }\r\n if (line === -1) {\r\n // Could not find the m line for media\r\n return sdp;\r\n }\r\n\r\n // Pass the m line\r\n line++;\r\n\r\n // Skip i and c lines\r\n while (lines[line].indexOf('i=') === 0 || lines[line].indexOf('c=') === 0) {\r\n\r\n line++;\r\n }\r\n\r\n // If we're on a b line, replace it\r\n if (lines[line].indexOf('b') === 0) {\r\n\r\n lines[line] = 'b=AS:' + bitrate;\r\n\r\n return lines.join('\\r\\n');\r\n }\r\n\r\n // Add a new b line\r\n let newLines = lines.slice(0, line)\r\n\r\n newLines.push('b=AS:' + bitrate)\r\n newLines = newLines.concat(lines.slice(line, lines.length))\r\n\r\n return newLines.join('\\r\\n')\r\n }\r\n\r\n function initWebSocket(endpointUrl) {\r\n\r\n if (!endpointUrl) {\r\n errorHandler('endpointUrl is required');\r\n return;\r\n }\r\n\r\n let webSocket = null;\r\n\r\n try {\r\n\r\n webSocket = new WebSocket(endpointUrl);\r\n } catch (error) {\r\n\r\n errorHandler(error);\r\n }\r\n\r\n\r\n instance.webSocket = webSocket;\r\n\r\n webSocket.onopen = function () {\r\n\r\n // Request offer at the first time.\r\n sendMessage(webSocket, {\r\n command: 'request_offer'\r\n });\r\n };\r\n\r\n webSocket.onmessage = function (e) {\r\n\r\n let message = JSON.parse(e.data);\r\n\r\n if (message.error) {\r\n console.error('webSocket.onmessage', message.error);\r\n errorHandler(message.error);\r\n }\r\n\r\n if (message.command === 'offer') {\r\n\r\n // OME returns offer. Start create peer connection.\r\n createPeerConnection(\r\n message.id,\r\n message.peer_id,\r\n message.sdp,\r\n message.candidates,\r\n message.ice_servers\r\n );\r\n }\r\n };\r\n\r\n webSocket.onerror = function (error) {\r\n\r\n console.error('webSocket.onerror', error);\r\n errorHandler(error);\r\n };\r\n\r\n webSocket.onclose = function (e) {\r\n\r\n if (!instance.webSocketClosedByUser) {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n instance.callbacks.connectionClosed('websocket', e);\r\n }\r\n }\r\n };\r\n\r\n }\r\n\r\n async function startWhip(endpointUrl) {\r\n\r\n if (instance.peerConnection) {\r\n console.error('Connection already established');\r\n errorHandler('Connection already established');\r\n return;\r\n }\r\n\r\n const peerConnectionConfig = {\r\n bundlePolicy: \"max-bundle\"\r\n };\r\n\r\n if (instance.connectionConfig.iceServers) {\r\n\r\n // first priority using ice servers from local config.\r\n peerConnectionConfig.iceServers = instance.connectionConfig.iceServers;\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n } else {\r\n // last priority using default ice servers.\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n }\r\n\r\n console.info(logHeader, 'Create Peer Connection With Config', peerConnectionConfig);\r\n\r\n const peerConnection = new RTCPeerConnection(peerConnectionConfig);\r\n\r\n instance.peerConnection = peerConnection;\r\n\r\n if (!instance.inputStream) {\r\n console.error('No input stream in OvenLiveKit');\r\n errorHandler('No input stream in OvenLiveKit');\r\n return;\r\n }\r\n\r\n for (const track of instance.inputStream.getTracks()) {\r\n console.log(logHeader, 'Adding track: ', track);\r\n\r\n const transceiverConfig = {\r\n direction: 'sendonly'\r\n };\r\n\r\n // Add simulcast layers if configured\r\n const simulcastConfig = instance.connectionConfig.simulcast;\r\n\r\n if (track.kind === 'video' && simulcastConfig && simulcastConfig.length > 0) {\r\n\r\n transceiverConfig.sendEncodings = [];\r\n\r\n for (let i = 0; i < simulcastConfig.length; i++) {\r\n\r\n const layer = {\r\n rid: i,\r\n active: true,\r\n ...simulcastConfig[i]\r\n };\r\n\r\n console.log(logHeader, `Adding simulcast layer to: ${track.kind}`, layer);\r\n\r\n transceiverConfig.sendEncodings.push(layer);\r\n }\r\n }\r\n\r\n peerConnection.addTransceiver(track, transceiverConfig);\r\n }\r\n\r\n peerConnection.oniceconnectionstatechange = function (e) {\r\n\r\n let state = peerConnection.iceConnectionState;\r\n\r\n if (instance.callbacks.iceStateChange) {\r\n\r\n console.info(logHeader, 'ICE State', '[' + state + ']');\r\n instance.callbacks.iceStateChange(state);\r\n }\r\n\r\n if (state === 'connected') {\r\n\r\n if (instance.callbacks.connected) {\r\n instance.callbacks.connected(e);\r\n }\r\n }\r\n\r\n if (state === 'failed') {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.error(logHeader, 'Ice connection failed', e);\r\n instance.callbacks.errorHandler(e);\r\n }\r\n }\r\n\r\n if (state === 'disconnected' || state === 'closed') {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.error(logHeader, 'Ice connection disconnected or closed', e);\r\n instance.callbacks.connectionClosed('ice', e);\r\n }\r\n }\r\n }\r\n\r\n const offer = await peerConnection.createOffer();\r\n console.log(logHeader, 'Offer SDP: ', offer.sdp);\r\n\r\n if (instance.connectionConfig.maxVideoBitrate) {\r\n\r\n // if bandwidth limit is set. modify sdp from ome to limit acceptable bandwidth of ome\r\n offer.sdp = setBitrateLimit(offer.sdp, 'video', instance.connectionConfig.maxVideoBitrate);\r\n }\r\n\r\n if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {\r\n\r\n offer.sdp = appendFmtp(offer.sdp);\r\n }\r\n\r\n if (instance.connectionConfig.preferredVideoFormat) {\r\n offer.sdp = setPreferredVideoFormat(offer.sdp, instance.connectionConfig.preferredVideoFormat);\r\n } else {\r\n // default to H264\r\n offer.sdp = setPreferredVideoFormat(offer.sdp, 'H264');\r\n }\r\n\r\n const headers = {\r\n \"Content-Type\": \"application/sdp\"\r\n };\r\n\r\n if (instance.connectionConfig.httpHeaders) {\r\n Object.assign(headers, instance.connectionConfig.httpHeaders);\r\n }\r\n\r\n const fetched = await fetchWithRedirect(endpointUrl, {\r\n method: \"POST\",\r\n body: offer.sdp,\r\n headers\r\n });\r\n\r\n if (!fetched.ok) {\r\n console.error('Failed to fetch', fetched.status);\r\n errorHandler(`Failed to fetch ${fetched.status}`);\r\n closePeerConnection();\r\n return;\r\n }\r\n\r\n if (!fetched.headers.get(\"location\")) {\r\n console.error('No location header on answer response');\r\n errorHandler('No location header on answer response');\r\n return;\r\n }\r\n\r\n // update endpointUrl\r\n instance.endpointUrl = fetched.url;\r\n console.log(logHeader, 'Updated endpointUrl: ', instance.endpointUrl);\r\n\r\n const baseUrl = new URL(endpointUrl).origin;\r\n instance.resourceUrl = baseUrl + fetched.headers.get(\"location\");\r\n\r\n const answer = await fetched.text();\r\n console.log(logHeader, 'Answer SDP: ', answer);\r\n\r\n try {\r\n await peerConnection.setLocalDescription(offer);\r\n } catch (error) {\r\n console.error('peerConnection.setLocalDescription', error);\r\n errorHandler(error);\r\n }\r\n\r\n try {\r\n await peerConnection.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: answer\r\n });\r\n } catch (error) {\r\n console.error('peerConnection.setRemoteDescription', error);\r\n errorHandler(error);\r\n }\r\n }\r\n\r\n async function stopWhip() {\r\n\r\n if (!instance.peerConnection) {\r\n console.error('No connection to close');\r\n errorHandler('No connection to close');\r\n return;\r\n }\r\n\r\n closePeerConnection();\r\n\r\n if (instance.resourceUrl) {\r\n\r\n const headers = {\r\n };\r\n\r\n if (instance.connectionConfig.httpHeaders) {\r\n Object.assign(headers, instance.connectionConfig.httpHeaders);\r\n }\r\n\r\n await fetchWithRedirect(instance.resourceUrl, {\r\n method: \"DELETE\",\r\n headers\r\n });\r\n }\r\n }\r\n\r\n function appendFmtp(sdp) {\r\n\r\n const fmtpStr = instance.connectionConfig.sdp.appendFmtp;\r\n\r\n const lines = sdp.split('\\r\\n');\r\n const payloads = [];\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n\r\n if (lines[i].indexOf('m=video') === 0) {\r\n\r\n let tokens = lines[i].split(' ')\r\n\r\n for (let j = 3; j < tokens.length; j++) {\r\n\r\n payloads.push(tokens[j]);\r\n }\r\n\r\n break;\r\n }\r\n }\r\n\r\n for (let i = 0; i < payloads.length; i++) {\r\n\r\n let fmtpLineFound = false;\r\n\r\n for (let j = 0; j < lines.length; j++) {\r\n\r\n if (lines[j].indexOf('a=fmtp:' + payloads[i]) === 0) {\r\n fmtpLineFound = true;\r\n lines[j] += ';' + fmtpStr;\r\n }\r\n }\r\n\r\n if (!fmtpLineFound) {\r\n\r\n for (let j = 0; j < lines.length; j++) {\r\n\r\n if (lines[j].indexOf('a=rtpmap:' + payloads[i]) === 0) {\r\n\r\n lines[j] += '\\r\\na=fmtp:' + payloads[i] + ' ' + fmtpStr;\r\n }\r\n }\r\n }\r\n }\r\n\r\n return lines.join('\\r\\n')\r\n }\r\n\r\n function appendOrientation(sdp) {\r\n\r\n const lines = sdp.split('\\r\\n');\r\n const payloads = [];\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n\r\n if (lines[i].indexOf('m=video') === 0) {\r\n\r\n let tokens = lines[i].split(' ')\r\n\r\n for (let j = 3; j < tokens.length; j++) {\r\n\r\n payloads.push(tokens[j]);\r\n }\r\n\r\n break;\r\n }\r\n }\r\n\r\n for (let i = 0; i < payloads.length; i++) {\r\n\r\n for (let j = 0; j < lines.length; j++) {\r\n\r\n if (lines[j].indexOf('a=rtpmap:' + payloads[i]) === 0) {\r\n\r\n lines[j] += '\\r\\na=extmap:' + payloads[i] + ' urn:3gpp:video-orientation';\r\n }\r\n }\r\n }\r\n\r\n return lines.join('\\r\\n')\r\n }\r\n\r\n function createPeerConnection(id, peerId, offer, candidates, iceServers) {\r\n\r\n let peerConnectionConfig = {};\r\n\r\n if (instance.connectionConfig.iceServers) {\r\n\r\n // first priority using ice servers from local config.\r\n peerConnectionConfig.iceServers = instance.connectionConfig.iceServers;\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n } else if (iceServers) {\r\n\r\n // second priority using ice servers from ome and force using TCP\r\n peerConnectionConfig.iceServers = [];\r\n\r\n for (let i = 0; i < iceServers.length; i++) {\r\n\r\n let iceServer = iceServers[i];\r\n\r\n let regIceServer = {};\r\n\r\n regIceServer.urls = iceServer.urls;\r\n\r\n let hasWebSocketUrl = false;\r\n let webSocketUrl = generateDomainFromUrl(instance.endpointUrl);\r\n\r\n for (let j = 0; j < regIceServer.urls.length; j++) {\r\n\r\n let serverUrl = regIceServer.urls[j];\r\n\r\n if (serverUrl.indexOf(webSocketUrl) > -1) {\r\n hasWebSocketUrl = true;\r\n break;\r\n }\r\n }\r\n\r\n if (!hasWebSocketUrl) {\r\n\r\n if (regIceServer.urls.length > 0) {\r\n\r\n let cloneIceServer = regIceServer.urls[0];\r\n let ip = findIp(cloneIceServer);\r\n\r\n if (webSocketUrl && ip) {\r\n regIceServer.urls.push(cloneIceServer.replace(ip, webSocketUrl));\r\n }\r\n }\r\n }\r\n\r\n regIceServer.username = iceServer.user_name;\r\n regIceServer.credential = iceServer.credential;\r\n\r\n peerConnectionConfig.iceServers.push(regIceServer);\r\n }\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n } else {\r\n peerConnectionConfig.iceTransportPolicy = 'relay';\r\n }\r\n } else {\r\n // last priority using default ice servers.\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n }\r\n\r\n let advancedSetting = {\r\n optional: [\r\n {\r\n googHighStartBitrate: {\r\n exact: !0\r\n }\r\n },\r\n {\r\n googPayloadPadding: {\r\n exact: !0\r\n }\r\n },\r\n {\r\n googScreencastMinBitrate: {\r\n exact: 500\r\n }\r\n },\r\n {\r\n enableDscp: {\r\n exact: true\r\n }\r\n }\r\n ]\r\n };\r\n\r\n console.info(logHeader, 'Create Peer Connection With Config', peerConnectionConfig);\r\n\r\n let peerConnection = new RTCPeerConnection(peerConnectionConfig);\r\n\r\n instance.peerConnection = peerConnection;\r\n\r\n // set local stream\r\n instance.inputStream.getTracks().forEach(function (track) {\r\n\r\n console.info(logHeader, 'Add Track To Peer Connection', track);\r\n peerConnection.addTrack(track, instance.inputStream);\r\n });\r\n\r\n if (instance.connectionConfig.maxVideoBitrate) {\r\n\r\n // if bandwith limit is set. modify sdp from ome to limit acceptable bandwidth of ome\r\n offer.sdp = setBitrateLimit(offer.sdp, 'video', instance.connectionConfig.maxVideoBitrate);\r\n }\r\n\r\n if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {\r\n\r\n offer.sdp = appendFmtp(offer.sdp);\r\n }\r\n\r\n if (instance.connectionConfig.preferredVideoFormat) {\r\n offer.sdp = setPreferredVideoFormat(offer.sdp, instance.connectionConfig.preferredVideoFormat)\r\n }\r\n\r\n\r\n // offer.sdp = appendOrientation(offer.sdp);\r\n console.info(logHeader, 'Modified offer sdp\\n\\n' + offer.sdp);\r\n\r\n peerConnection.setRemoteDescription(new RTCSessionDescription(offer))\r\n .then(function () {\r\n\r\n peerConnection.createAnswer()\r\n .then(function (answer) {\r\n\r\n if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {\r\n\r\n answer.sdp = appendFmtp(answer.sdp);\r\n }\r\n\r\n if (instance.connectionConfig.preferredVideoFormat) {\r\n answer.sdp = setPreferredVideoFormat(answer.sdp, instance.connectionConfig.preferredVideoFormat)\r\n }\r\n\r\n console.info(logHeader, 'Modified answer sdp\\n\\n' + answer.sdp);\r\n\r\n peerConnection.setLocalDescription(answer)\r\n .then(function () {\r\n\r\n sendMessage(instance.webSocket, {\r\n id: id,\r\n peer_id: peerId,\r\n command: 'answer',\r\n sdp: answer\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.setLocalDescription', error);\r\n errorHandler(error);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.createAnswer', error);\r\n errorHandler(error);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.setRemoteDescription', error);\r\n errorHandler(error);\r\n });\r\n\r\n if (candidates) {\r\n\r\n addIceCandidate(peerConnection, candidates);\r\n }\r\n\r\n peerConnection.onicecandidate = function (e) {\r\n\r\n if (e.candidate && e.candidate.candidate) {\r\n\r\n sendMessage(instance.webSocket, {\r\n id: id,\r\n peer_id: peerId,\r\n command: 'candidate',\r\n candidates: [e.candidate]\r\n });\r\n }\r\n };\r\n\r\n peerConnection.oniceconnectionstatechange = function (e) {\r\n\r\n let state = peerConnection.iceConnectionState;\r\n\r\n if (instance.callbacks.iceStateChange) {\r\n\r\n console.info(logHeader, 'ICE State', '[' + state + ']');\r\n instance.callbacks.iceStateChange(state);\r\n }\r\n\r\n if (state === 'connected') {\r\n\r\n if (instance.callbacks.connected) {\r\n instance.callbacks.connected(e);\r\n }\r\n }\r\n\r\n if (state === 'failed' || state === 'disconnected' || state === 'closed') {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.error(logHeader, 'Ice connection closed', e);\r\n instance.callbacks.connectionClosed('ice', e);\r\n }\r\n }\r\n }\r\n }\r\n\r\n function addIceCandidate(peerConnection, candidates) {\r\n\r\n for (let i = 0; i < candidates.length; i++) {\r\n\r\n if (candidates[i] && candidates[i].candidate) {\r\n\r\n let basicCandidate = candidates[i];\r\n\r\n peerConnection.addIceCandidate(new RTCIceCandidate(basicCandidate))\r\n .then(function () {\r\n\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.addIceCandidate', error);\r\n errorHandler(error);\r\n });\r\n }\r\n }\r\n }\r\n\r\n function closePeerConnection() {\r\n if (instance.peerConnection) {\r\n\r\n // remove tracks from peer connection\r\n instance.peerConnection.getSenders().forEach(function (sender) {\r\n instance.peerConnection.removeTrack(sender);\r\n });\r\n\r\n instance.peerConnection.close();\r\n instance.peerConnection = null;\r\n delete instance.peerConnection;\r\n }\r\n }\r\n\r\n function closeWebSocket() {\r\n\r\n if (instance.webSocket) {\r\n\r\n instance.webSocket.close();\r\n instance.webSocket = null;\r\n delete instance.webSocket;\r\n }\r\n }\r\n\r\n function closeInputStream() {\r\n // release video, audio stream\r\n if (instance.inputStream) {\r\n\r\n instance.inputStream.getTracks().forEach(track => {\r\n\r\n track.stop();\r\n instance.inputStream.removeTrack(track);\r\n });\r\n\r\n if (instance.videoElement) {\r\n instance.videoElement.srcObject = null;\r\n }\r\n\r\n instance.inputStream = null;\r\n delete instance.inputStream;\r\n }\r\n }\r\n\r\n // instance methods\r\n instance.attachMedia = function (videoElement) {\r\n\r\n instance.videoElement = videoElement;\r\n };\r\n\r\n instance.getUserMedia = function (constraints) {\r\n\r\n return getUserMedia(constraints);\r\n };\r\n\r\n instance.getDisplayMedia = function (constraints) {\r\n\r\n return getDisplayMedia(constraints);\r\n };\r\n\r\n instance.setMediaStream = function (stream) {\r\n\r\n return setMediaStream(stream);\r\n };\r\n\r\n instance.startStreaming = function (endpointUrl, connectionConfig) {\r\n\r\n console.info(logEventHeader, `Start Streaming to ${endpointUrl} with connectionConfig`, connectionConfig);\r\n\r\n if (!endpointUrl) {\r\n console.error('endpointUrl is required');\r\n errorHandler('endpointUrl is required');\r\n return;\r\n }\r\n\r\n instance.endpointUrl = endpointUrl;\r\n\r\n if (connectionConfig) {\r\n instance.connectionConfig = connectionConfig;\r\n }\r\n\r\n try {\r\n\r\n const protocol = new URL(endpointUrl).protocol;\r\n\r\n if (protocol === 'wss:' || protocol === 'ws:') {\r\n\r\n instance.streamingMode = 'webrtc';\r\n initWebSocket(endpointUrl);\r\n } else if (protocol === 'https:' || protocol === 'http:') {\r\n\r\n instance.streamingMode = 'whip';\r\n startWhip(endpointUrl);\r\n } else {\r\n console.error('Invalid protocol', error);\r\n errorHandler(error);\r\n }\r\n\r\n } catch (error) {\r\n console.error('Cannot parse connection URL', error);\r\n errorHandler(error);\r\n }\r\n };\r\n\r\n instance.stopStreaming = async function () {\r\n\r\n if (instance.streamingMode === 'webrtc') {\r\n\r\n instance.webSocketClosedByUser = true;\r\n\r\n closeWebSocket();\r\n closePeerConnection();\r\n } else if (instance.streamingMode === 'whip') {\r\n\r\n await stopWhip();\r\n }\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.log(logHeader, 'Connection closed by user');\r\n instance.callbacks.connectionClosed('user', 'Connection closed by user');\r\n }\r\n };\r\n\r\n instance.remove = function () {\r\n\r\n if (instance.streamingMode === 'webrtc') {\r\n\r\n instance.webSocketClosedByUser = true;\r\n\r\n closeWebSocket();\r\n closePeerConnection();\r\n } else if (instance.streamingMode === 'whip') {\r\n stopWhip();\r\n }\r\n\r\n closeInputStream();\r\n\r\n console.info(logEventHeader, 'Removed');\r\n\r\n };\r\n}\r\n\r\nOvenLiveKit.getVersion = function () {\r\n return version;\r\n}\r\n\r\n// static methods\r\nOvenLiveKit.create = function (options) {\r\n\r\n console.info(logEventHeader, 'Create WebRTC Input ' + version);\r\n\r\n let instance = {};\r\n\r\n instance.webSocketClosedByUser = false;\r\n\r\n initConfig(instance, options);\r\n addMethod(instance);\r\n\r\n return instance;\r\n};\r\n\r\nOvenLiveKit.getDevices = async function () {\r\n\r\n try {\r\n // First check both audio and video sources are available.\r\n await getStreamForDeviceCheck('both');\r\n } catch (e) {\r\n\r\n console.warn(logHeader, 'Can not find Video and Audio devices', e);\r\n\r\n let videoFound = null;\r\n let audioFound = null;\r\n\r\n try {\r\n videoFound = await getStreamForDeviceCheck('video');\r\n } catch (e) {\r\n console.warn(logHeader, 'Can not find Video devices', e);\r\n }\r\n\r\n try {\r\n audioFound = await getStreamForDeviceCheck('audio');\r\n } catch (e) {\r\n console.warn(logHeader, 'Can not find Audio devices', e);\r\n }\r\n\r\n if (!videoFound && !audioFound) {\r\n throw new Error('No input devices were found.');\r\n }\r\n }\r\n\r\n const deviceInfos = await getDevices();\r\n return gotDevices(deviceInfos)\r\n};\r\n\r\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (OvenLiveKit);\n\n//# sourceURL=webpack://OvenLiveKit/./src/OvenLiveKit.js?");
30
30
 
31
31
  /***/ })
32
32
 
@@ -1,2 +1,2 @@
1
- !function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.OvenLiveKit=n():e.OvenLiveKit=n()}(self,(()=>(()=>{"use strict";var e={d:(n,o)=>{for(var t in o)e.o(o,t)&&!e.o(n,t)&&Object.defineProperty(n,t,{enumerable:!0,get:o[t]})},o:(e,n)=>Object.prototype.hasOwnProperty.call(e,n)},n={};e.d(n,{default:()=>u});const o={},t="1.3.0",i="OvenLiveKit.js :",c="OvenLiveKit.js ====";function r(e,n){e&&e.send(JSON.stringify(n))}function a(e){let n,o="";return(n=e.match(/^(?:wss?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n\?\=]+)/im))&&(o=n[1]),o}function s(e){let n,o="";return(n=e.match(new RegExp("\\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b","gi")))&&(o=n[0]),o}function l(e,n){const o=function(e,n){const o=e.split("\r\n");let t=-1;for(let e=0;e<o.length-1;e++)if(o[e]=o[e].toLowerCase(),0===o[e].indexOf("a=rtpmap")&&o[e].indexOf(n.toLowerCase())>-1){t=o[e].split(" ")[0].split(":")[1];break}return t}(e,n);if(-1===o)return e;let t=[];const i=e.split("\r\n");for(let e=0;e<i.length-1;e++){const n=i[e];if(0===n.indexOf("m=video")){const e=n.split(" ").slice(0,3),i=n.split(" ").slice(3);i.sort((function(e,n){return e==o?-1:n==o?1:0})),t.push(e.concat(i).join(" "))}else t.push(n)}return t.join("\r\n")+"\r\n"}async function d(e){const n={};return"both"===e?(n.audio=!0,n.video=!0):"audio"===e?n.audio=!0:"video"===e&&(n.video=!0),await navigator.mediaDevices.getUserMedia(n)}function f(e){function n(n){e.callbacks.error&&e.callbacks.error(n)}async function o(e,n){let o=await fetch(e,n);for(;o.redirected;)e=o.url,o=await fetch(e,n);return o}function t(e,n,o){let t=e.split("\r\n"),i=-1;for(let e=0;e<t.length;e++)if(0===t[e].indexOf("m="+n)){i=e;break}if(-1===i)return e;for(i++;0===t[i].indexOf("i=")||0===t[i].indexOf("c=");)i++;if(0===t[i].indexOf("b"))return t[i]="b=AS:"+o,t.join("\r\n");let c=t.slice(0,i);return c.push("b=AS:"+o),c=c.concat(t.slice(i,t.length)),c.join("\r\n")}async function d(){if(!e.peerConnection)return console.error("No connection to close"),void n("No connection to close");if(u(),e.resourceUrl){const n={};e.connectionConfig.httpHeaders&&Object.assign(n,e.connectionConfig.httpHeaders),await o(e.resourceUrl,{method:"DELETE",headers:n})}}function f(n){const o=e.connectionConfig.sdp.appendFmtp,t=n.split("\r\n"),i=[];for(let e=0;e<t.length;e++)if(0===t[e].indexOf("m=video")){let n=t[e].split(" ");for(let e=3;e<n.length;e++)i.push(n[e]);break}for(let e=0;e<i.length;e++){let n=!1;for(let c=0;c<t.length;c++)0===t[c].indexOf("a=fmtp:"+i[e])&&(n=!0,t[c]+=";"+o);if(!n)for(let n=0;n<t.length;n++)0===t[n].indexOf("a=rtpmap:"+i[e])&&(t[n]+="\r\na=fmtp:"+i[e]+" "+o)}return t.join("\r\n")}function u(){e.peerConnection&&(e.peerConnection.getSenders().forEach((function(n){e.peerConnection.removeTrack(n)})),e.peerConnection.close(),e.peerConnection=null,delete e.peerConnection)}function p(){e.webSocket&&(e.webSocket.close(),e.webSocket=null,delete e.webSocket)}e.attachMedia=function(n){e.videoElement=n},e.getUserMedia=function(o){return function(o){return o||(o={video:{deviceId:void 0},audio:{deviceId:void 0}}),console.info(i,"Request Stream To Input Devices With Constraints",o),navigator.mediaDevices.getUserMedia(o).then((function(n){console.info(i,"Received Media Stream From Input Device",n),e.inputStream=n;let o=e.videoElement;return o&&(o.srcObject=n,o.onloadedmetadata=function(e){o.play()}),new Promise((function(e){e(n)}))})).catch((function(e){return console.error(i,"Can't Get Media Stream From Input Device",e),n(e),new Promise((function(n,o){o(e)}))}))}(o)},e.getDisplayMedia=function(o){return function(o){return o||(o={}),console.info(i,"Request Stream To Display With Constraints",o),navigator.mediaDevices.getDisplayMedia(o).then((function(n){console.info(i,"Received Media Stream From Display",n),e.inputStream=n;let o=e.videoElement;return o&&(o.srcObject=n,o.onloadedmetadata=function(e){o.play()}),new Promise((function(e){e(n)}))})).catch((function(e){return console.error(i,"Can't Get Media Stream From Display",e),n(e),new Promise((function(n,o){o(e)}))}))}(o)},e.setMediaStream=function(o){return function(o){if(!(o&&o instanceof MediaStream)){const e=new Error("Invalid MediaStream provided");return console.error(i,"Invalid MediaStream",e),n(e),new Promise((function(n,o){o(e)}))}console.info(i,"Received Media Stream",o),e.inputStream=o;let t=e.videoElement;return t&&(t.srcObject=o,t.onloadedmetadata=function(e){t.play()}),new Promise((function(e){e(o)}))}(o)},e.startStreaming=function(d,p){if(console.info(c,`Start Streaming to ${d} with connectionConfig`,p),!d)return console.error("endpointUrl is required"),void n("endpointUrl is required");e.endpointUrl=d,p&&(e.connectionConfig=p);try{const c=new URL(d).protocol;"wss:"===c||"ws:"===c?(e.streamingMode="webrtc",function(o){if(!o)return void n("endpointUrl is required");let c=null;try{c=new WebSocket(o)}catch(e){n(e)}e.webSocket=c,c.onopen=function(){r(c,{command:"request_offer"})},c.onmessage=function(o){let c=JSON.parse(o.data);c.error&&(console.error("webSocket.onmessage",c.error),n(c.error)),"offer"===c.command&&function(o,c,d,u,p){let m={};if(e.connectionConfig.iceServers)m.iceServers=e.connectionConfig.iceServers,e.connectionConfig.iceTransportPolicy&&(m.iceTransportPolicy=e.connectionConfig.iceTransportPolicy);else if(p){m.iceServers=[];for(let n=0;n<p.length;n++){let o=p[n],t={};t.urls=o.urls;let i=!1,c=a(e.endpointUrl);for(let e=0;e<t.urls.length;e++)if(t.urls[e].indexOf(c)>-1){i=!0;break}if(!i&&t.urls.length>0){let e=t.urls[0],n=s(e);c&&n&&t.urls.push(e.replace(n,c))}t.username=o.user_name,t.credential=o.credential,m.iceServers.push(t)}e.connectionConfig.iceTransportPolicy?m.iceTransportPolicy=e.connectionConfig.iceTransportPolicy:m.iceTransportPolicy="relay"}else e.connectionConfig.iceTransportPolicy&&(m.iceTransportPolicy=e.connectionConfig.iceTransportPolicy);console.info(i,"Create Peer Connection With Config",m);let C=new RTCPeerConnection(m);e.peerConnection=C,e.inputStream.getTracks().forEach((function(n){console.info(i,"Add Track To Peer Connection",n),C.addTrack(n,e.inputStream)})),e.connectionConfig.maxVideoBitrate&&(d.sdp=t(d.sdp,"video",e.connectionConfig.maxVideoBitrate)),e.connectionConfig.sdp&&e.connectionConfig.sdp.appendFmtp&&(d.sdp=f(d.sdp)),e.connectionConfig.preferredVideoFormat&&(d.sdp=l(d.sdp,e.connectionConfig.preferredVideoFormat)),console.info(i,"Modified offer sdp\n\n"+d.sdp),C.setRemoteDescription(new RTCSessionDescription(d)).then((function(){C.createAnswer().then((function(t){e.connectionConfig.sdp&&e.connectionConfig.sdp.appendFmtp&&(t.sdp=f(t.sdp)),e.connectionConfig.preferredVideoFormat&&(t.sdp=l(t.sdp,e.connectionConfig.preferredVideoFormat)),console.info(i,"Modified answer sdp\n\n"+t.sdp),C.setLocalDescription(t).then((function(){r(e.webSocket,{id:o,peer_id:c,command:"answer",sdp:t})})).catch((function(e){console.error("peerConnection.setLocalDescription",e),n(e)}))})).catch((function(e){console.error("peerConnection.createAnswer",e),n(e)}))})).catch((function(e){console.error("peerConnection.setRemoteDescription",e),n(e)})),u&&function(e,o){for(let t=0;t<o.length;t++)if(o[t]&&o[t].candidate){let i=o[t];e.addIceCandidate(new RTCIceCandidate(i)).then((function(){})).catch((function(e){console.error("peerConnection.addIceCandidate",e),n(e)}))}}(C,u),C.onicecandidate=function(n){n.candidate&&n.candidate.candidate&&r(e.webSocket,{id:o,peer_id:c,command:"candidate",candidates:[n.candidate]})},C.oniceconnectionstatechange=function(n){let o=C.iceConnectionState;e.callbacks.iceStateChange&&(console.info(i,"ICE State","["+o+"]"),e.callbacks.iceStateChange(o)),"connected"===o&&e.callbacks.connected&&e.callbacks.connected(n),"failed"!==o&&"disconnected"!==o&&"closed"!==o||e.callbacks.connectionClosed&&(console.error(i,"Ice connection closed",n),e.callbacks.connectionClosed("ice",n))}}(c.id,c.peer_id,c.sdp,c.candidates,c.ice_servers)},c.onerror=function(e){console.error("webSocket.onerror",e),n(e)},c.onclose=function(n){e.webSocketClosedByUser||e.callbacks.connectionClosed&&e.callbacks.connectionClosed("websocket",n)}}(d)):"https:"===c||"http:"===c?(e.streamingMode="whip",async function(c){if(e.peerConnection)return console.error("Connection already established"),void n("Connection already established");const r={bundlePolicy:"max-bundle"};e.connectionConfig.iceServers?(r.iceServers=e.connectionConfig.iceServers,e.connectionConfig.iceTransportPolicy&&(r.iceTransportPolicy=e.connectionConfig.iceTransportPolicy)):e.connectionConfig.iceTransportPolicy&&(r.iceTransportPolicy=e.connectionConfig.iceTransportPolicy),console.info(i,"Create Peer Connection With Config",r);const a=new RTCPeerConnection(r);if(e.peerConnection=a,!e.inputStream)return console.error("No input stream in OvenLiveKit"),void n("No input stream in OvenLiveKit");for(const n of e.inputStream.getTracks())console.log(i,"Adding track: ",n),a.addTransceiver(n,{direction:"sendonly"});a.oniceconnectionstatechange=function(n){let o=a.iceConnectionState;e.callbacks.iceStateChange&&(console.info(i,"ICE State","["+o+"]"),e.callbacks.iceStateChange(o)),"connected"===o&&e.callbacks.connected&&e.callbacks.connected(n),"failed"===o&&e.callbacks.connectionClosed&&(console.error(i,"Ice connection failed",n),e.callbacks.errorHandler(n)),"disconnected"!==o&&"closed"!==o||e.callbacks.connectionClosed&&(console.error(i,"Ice connection disconnected or closed",n),e.callbacks.connectionClosed("ice",n))};const s=await a.createOffer();console.log(i,"Offer SDP: ",s.sdp),e.connectionConfig.maxVideoBitrate&&(s.sdp=t(s.sdp,"video",e.connectionConfig.maxVideoBitrate)),e.connectionConfig.sdp&&e.connectionConfig.sdp.appendFmtp&&(s.sdp=f(s.sdp)),e.connectionConfig.preferredVideoFormat&&(s.sdp=l(s.sdp,e.connectionConfig.preferredVideoFormat));const d={"Content-Type":"application/sdp"};e.connectionConfig.httpHeaders&&Object.assign(d,e.connectionConfig.httpHeaders);const p=await o(c,{method:"POST",body:s.sdp,headers:d});if(!p.ok)return console.error("Failed to fetch",p.status),n(`Failed to fetch ${p.status}`),void u();if(!p.headers.get("location"))return console.error("No location header on answer response"),void n("No location header on answer response");e.endpointUrl=p.url,console.log(i,"Updated endpointUrl: ",e.endpointUrl);const m=new URL(c).origin;e.resourceUrl=m+p.headers.get("location");const C=await p.text();console.log(i,"Answer SDP: ",C);try{await a.setLocalDescription(s)}catch(e){console.error("peerConnection.setLocalDescription",e),n(e)}try{await a.setRemoteDescription({type:"answer",sdp:C})}catch(e){console.error("peerConnection.setRemoteDescription",e),n(e)}}(d)):(console.error("Invalid protocol",error),n(error))}catch(e){console.error("Cannot parse connection URL",e),n(e)}},e.stopStreaming=async function(){"webrtc"===e.streamingMode?(e.webSocketClosedByUser=!0,p(),u()):"whip"===e.streamingMode&&await d(),e.callbacks.connectionClosed&&(console.log(i,"Connection closed by user"),e.callbacks.connectionClosed("user","Connection closed by user"))},e.remove=function(){"webrtc"===e.streamingMode?(e.webSocketClosedByUser=!0,p(),u()):"whip"===e.streamingMode&&d(),e.inputStream&&(e.inputStream.getTracks().forEach((n=>{n.stop(),e.inputStream.removeTrack(n)})),e.videoElement&&(e.videoElement.srcObject=null),e.inputStream=null,delete e.inputStream),console.info(c,"Removed")}}o.getVersion=function(){return t},o.create=function(e){console.info(c,"Create WebRTC Input "+t);let n={webSocketClosedByUser:!1};return function(e,n){e.streamingMode=null,e.inputStream=null,e.webSocket=null,e.peerConnection=null,e.connectionConfig={},e.videoElement=null,e.endpointUrl=null,e.resourceUrl=null,n&&n.callbacks?e.callbacks=n.callbacks:e.callbacks={}}(n,e),f(n),n},o.getDevices=async function(){try{await d("both")}catch(e){console.warn(i,"Can not find Video and Audio devices",e);let n=null,o=null;try{n=await d("video")}catch(e){console.warn(i,"Can not find Video devices",e)}try{o=await d("audio")}catch(e){console.warn(i,"Can not find Audio devices",e)}if(!n&&!o)throw new Error("No input devices were found.")}return function(e){let n={audioinput:[],audiooutput:[],videoinput:[],other:[]};for(let o=0;o!==e.length;++o){const t=e[o];let i={};i.deviceId=t.deviceId,"audioinput"===t.kind?(i.label=t.label||`microphone ${n.audioinput.length+1}`,n.audioinput.push(i)):"audiooutput"===t.kind?(i.label=t.label||`speaker ${n.audiooutput.length+1}`,n.audiooutput.push(i)):"videoinput"===t.kind?(i.label=t.label||`camera ${n.videoinput.length+1}`,n.videoinput.push(i)):(i.label=t.label||`other ${n.other.length+1}`,n.other.push(i))}return n}(await async function(){return await navigator.mediaDevices.enumerateDevices()}())};const u=o;return n.default})()));
1
+ !function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.OvenLiveKit=n():e.OvenLiveKit=n()}(self,(()=>(()=>{"use strict";var e={d:(n,o)=>{for(var t in o)e.o(o,t)&&!e.o(n,t)&&Object.defineProperty(n,t,{enumerable:!0,get:o[t]})},o:(e,n)=>Object.prototype.hasOwnProperty.call(e,n)},n={};e.d(n,{default:()=>u});const o={},t="1.4.0",i="OvenLiveKit.js :",c="OvenLiveKit.js ====";function r(e,n){e&&e.send(JSON.stringify(n))}function a(e){let n,o="";return(n=e.match(/^(?:wss?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n\?\=]+)/im))&&(o=n[1]),o}function s(e){let n,o="";return(n=e.match(new RegExp("\\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b","gi")))&&(o=n[0]),o}function l(e,n){const o=function(e,n){const o=e.split("\r\n");let t=-1;for(let e=0;e<o.length-1;e++)if(o[e]=o[e].toLowerCase(),0===o[e].indexOf("a=rtpmap")&&o[e].indexOf(n.toLowerCase())>-1){t=o[e].split(" ")[0].split(":")[1];break}return t}(e,n);if(-1===o)return e;let t=[];const i=e.split("\r\n");for(let e=0;e<i.length-1;e++){const n=i[e];if(0===n.indexOf("m=video")){const e=n.split(" ").slice(0,3),i=n.split(" ").slice(3);i.sort((function(e,n){return e==o?-1:n==o?1:0})),t.push(e.concat(i).join(" "))}else t.push(n)}return t.join("\r\n")+"\r\n"}async function d(e){const n={};return"both"===e?(n.audio=!0,n.video=!0):"audio"===e?n.audio=!0:"video"===e&&(n.video=!0),await navigator.mediaDevices.getUserMedia(n)}function f(e){function n(n){e.callbacks.error&&e.callbacks.error(n)}async function o(e,n){let o=await fetch(e,n);for(;o.redirected;)e=o.url,o=await fetch(e,n);return o}function t(e,n,o){let t=e.split("\r\n"),i=-1;for(let e=0;e<t.length;e++)if(0===t[e].indexOf("m="+n)){i=e;break}if(-1===i)return e;for(i++;0===t[i].indexOf("i=")||0===t[i].indexOf("c=");)i++;if(0===t[i].indexOf("b"))return t[i]="b=AS:"+o,t.join("\r\n");let c=t.slice(0,i);return c.push("b=AS:"+o),c=c.concat(t.slice(i,t.length)),c.join("\r\n")}async function d(){if(!e.peerConnection)return console.error("No connection to close"),void n("No connection to close");if(u(),e.resourceUrl){const n={};e.connectionConfig.httpHeaders&&Object.assign(n,e.connectionConfig.httpHeaders),await o(e.resourceUrl,{method:"DELETE",headers:n})}}function f(n){const o=e.connectionConfig.sdp.appendFmtp,t=n.split("\r\n"),i=[];for(let e=0;e<t.length;e++)if(0===t[e].indexOf("m=video")){let n=t[e].split(" ");for(let e=3;e<n.length;e++)i.push(n[e]);break}for(let e=0;e<i.length;e++){let n=!1;for(let c=0;c<t.length;c++)0===t[c].indexOf("a=fmtp:"+i[e])&&(n=!0,t[c]+=";"+o);if(!n)for(let n=0;n<t.length;n++)0===t[n].indexOf("a=rtpmap:"+i[e])&&(t[n]+="\r\na=fmtp:"+i[e]+" "+o)}return t.join("\r\n")}function u(){e.peerConnection&&(e.peerConnection.getSenders().forEach((function(n){e.peerConnection.removeTrack(n)})),e.peerConnection.close(),e.peerConnection=null,delete e.peerConnection)}function p(){e.webSocket&&(e.webSocket.close(),e.webSocket=null,delete e.webSocket)}e.attachMedia=function(n){e.videoElement=n},e.getUserMedia=function(o){return function(o){return o||(o={video:{deviceId:void 0},audio:{deviceId:void 0}}),console.info(i,"Request Stream To Input Devices With Constraints",o),navigator.mediaDevices.getUserMedia(o).then((function(n){console.info(i,"Received Media Stream From Input Device",n),e.inputStream=n;let o=e.videoElement;return o&&(o.srcObject=n,o.onloadedmetadata=function(e){o.play()}),new Promise((function(e){e(n)}))})).catch((function(e){return console.error(i,"Can't Get Media Stream From Input Device",e),n(e),new Promise((function(n,o){o(e)}))}))}(o)},e.getDisplayMedia=function(o){return function(o){return o||(o={}),console.info(i,"Request Stream To Display With Constraints",o),navigator.mediaDevices.getDisplayMedia(o).then((function(n){console.info(i,"Received Media Stream From Display",n),e.inputStream=n;let o=e.videoElement;return o&&(o.srcObject=n,o.onloadedmetadata=function(e){o.play()}),new Promise((function(e){e(n)}))})).catch((function(e){return console.error(i,"Can't Get Media Stream From Display",e),n(e),new Promise((function(n,o){o(e)}))}))}(o)},e.setMediaStream=function(o){return function(o){if(!(o&&o instanceof MediaStream)){const e=new Error("Invalid MediaStream provided");return console.error(i,"Invalid MediaStream",e),n(e),new Promise((function(n,o){o(e)}))}console.info(i,"Received Media Stream",o),e.inputStream=o;let t=e.videoElement;return t&&(t.srcObject=o,t.onloadedmetadata=function(e){t.play()}),new Promise((function(e){e(o)}))}(o)},e.startStreaming=function(d,p){if(console.info(c,`Start Streaming to ${d} with connectionConfig`,p),!d)return console.error("endpointUrl is required"),void n("endpointUrl is required");e.endpointUrl=d,p&&(e.connectionConfig=p);try{const c=new URL(d).protocol;"wss:"===c||"ws:"===c?(e.streamingMode="webrtc",function(o){if(!o)return void n("endpointUrl is required");let c=null;try{c=new WebSocket(o)}catch(e){n(e)}e.webSocket=c,c.onopen=function(){r(c,{command:"request_offer"})},c.onmessage=function(o){let c=JSON.parse(o.data);c.error&&(console.error("webSocket.onmessage",c.error),n(c.error)),"offer"===c.command&&function(o,c,d,u,p){let m={};if(e.connectionConfig.iceServers)m.iceServers=e.connectionConfig.iceServers,e.connectionConfig.iceTransportPolicy&&(m.iceTransportPolicy=e.connectionConfig.iceTransportPolicy);else if(p){m.iceServers=[];for(let n=0;n<p.length;n++){let o=p[n],t={};t.urls=o.urls;let i=!1,c=a(e.endpointUrl);for(let e=0;e<t.urls.length;e++)if(t.urls[e].indexOf(c)>-1){i=!0;break}if(!i&&t.urls.length>0){let e=t.urls[0],n=s(e);c&&n&&t.urls.push(e.replace(n,c))}t.username=o.user_name,t.credential=o.credential,m.iceServers.push(t)}e.connectionConfig.iceTransportPolicy?m.iceTransportPolicy=e.connectionConfig.iceTransportPolicy:m.iceTransportPolicy="relay"}else e.connectionConfig.iceTransportPolicy&&(m.iceTransportPolicy=e.connectionConfig.iceTransportPolicy);console.info(i,"Create Peer Connection With Config",m);let g=new RTCPeerConnection(m);e.peerConnection=g,e.inputStream.getTracks().forEach((function(n){console.info(i,"Add Track To Peer Connection",n),g.addTrack(n,e.inputStream)})),e.connectionConfig.maxVideoBitrate&&(d.sdp=t(d.sdp,"video",e.connectionConfig.maxVideoBitrate)),e.connectionConfig.sdp&&e.connectionConfig.sdp.appendFmtp&&(d.sdp=f(d.sdp)),e.connectionConfig.preferredVideoFormat&&(d.sdp=l(d.sdp,e.connectionConfig.preferredVideoFormat)),console.info(i,"Modified offer sdp\n\n"+d.sdp),g.setRemoteDescription(new RTCSessionDescription(d)).then((function(){g.createAnswer().then((function(t){e.connectionConfig.sdp&&e.connectionConfig.sdp.appendFmtp&&(t.sdp=f(t.sdp)),e.connectionConfig.preferredVideoFormat&&(t.sdp=l(t.sdp,e.connectionConfig.preferredVideoFormat)),console.info(i,"Modified answer sdp\n\n"+t.sdp),g.setLocalDescription(t).then((function(){r(e.webSocket,{id:o,peer_id:c,command:"answer",sdp:t})})).catch((function(e){console.error("peerConnection.setLocalDescription",e),n(e)}))})).catch((function(e){console.error("peerConnection.createAnswer",e),n(e)}))})).catch((function(e){console.error("peerConnection.setRemoteDescription",e),n(e)})),u&&function(e,o){for(let t=0;t<o.length;t++)if(o[t]&&o[t].candidate){let i=o[t];e.addIceCandidate(new RTCIceCandidate(i)).then((function(){})).catch((function(e){console.error("peerConnection.addIceCandidate",e),n(e)}))}}(g,u),g.onicecandidate=function(n){n.candidate&&n.candidate.candidate&&r(e.webSocket,{id:o,peer_id:c,command:"candidate",candidates:[n.candidate]})},g.oniceconnectionstatechange=function(n){let o=g.iceConnectionState;e.callbacks.iceStateChange&&(console.info(i,"ICE State","["+o+"]"),e.callbacks.iceStateChange(o)),"connected"===o&&e.callbacks.connected&&e.callbacks.connected(n),"failed"!==o&&"disconnected"!==o&&"closed"!==o||e.callbacks.connectionClosed&&(console.error(i,"Ice connection closed",n),e.callbacks.connectionClosed("ice",n))}}(c.id,c.peer_id,c.sdp,c.candidates,c.ice_servers)},c.onerror=function(e){console.error("webSocket.onerror",e),n(e)},c.onclose=function(n){e.webSocketClosedByUser||e.callbacks.connectionClosed&&e.callbacks.connectionClosed("websocket",n)}}(d)):"https:"===c||"http:"===c?(e.streamingMode="whip",async function(c){if(e.peerConnection)return console.error("Connection already established"),void n("Connection already established");const r={bundlePolicy:"max-bundle"};e.connectionConfig.iceServers?(r.iceServers=e.connectionConfig.iceServers,e.connectionConfig.iceTransportPolicy&&(r.iceTransportPolicy=e.connectionConfig.iceTransportPolicy)):e.connectionConfig.iceTransportPolicy&&(r.iceTransportPolicy=e.connectionConfig.iceTransportPolicy),console.info(i,"Create Peer Connection With Config",r);const a=new RTCPeerConnection(r);if(e.peerConnection=a,!e.inputStream)return console.error("No input stream in OvenLiveKit"),void n("No input stream in OvenLiveKit");for(const n of e.inputStream.getTracks()){console.log(i,"Adding track: ",n);const o={direction:"sendonly"},t=e.connectionConfig.simulcast;if("video"===n.kind&&t&&t.length>0){o.sendEncodings=[];for(let e=0;e<t.length;e++){const c={rid:e,active:!0,...t[e]};console.log(i,`Adding simulcast layer to: ${n.kind}`,c),o.sendEncodings.push(c)}}a.addTransceiver(n,o)}a.oniceconnectionstatechange=function(n){let o=a.iceConnectionState;e.callbacks.iceStateChange&&(console.info(i,"ICE State","["+o+"]"),e.callbacks.iceStateChange(o)),"connected"===o&&e.callbacks.connected&&e.callbacks.connected(n),"failed"===o&&e.callbacks.connectionClosed&&(console.error(i,"Ice connection failed",n),e.callbacks.errorHandler(n)),"disconnected"!==o&&"closed"!==o||e.callbacks.connectionClosed&&(console.error(i,"Ice connection disconnected or closed",n),e.callbacks.connectionClosed("ice",n))};const s=await a.createOffer();console.log(i,"Offer SDP: ",s.sdp),e.connectionConfig.maxVideoBitrate&&(s.sdp=t(s.sdp,"video",e.connectionConfig.maxVideoBitrate)),e.connectionConfig.sdp&&e.connectionConfig.sdp.appendFmtp&&(s.sdp=f(s.sdp)),e.connectionConfig.preferredVideoFormat?s.sdp=l(s.sdp,e.connectionConfig.preferredVideoFormat):s.sdp=l(s.sdp,"H264");const d={"Content-Type":"application/sdp"};e.connectionConfig.httpHeaders&&Object.assign(d,e.connectionConfig.httpHeaders);const p=await o(c,{method:"POST",body:s.sdp,headers:d});if(!p.ok)return console.error("Failed to fetch",p.status),n(`Failed to fetch ${p.status}`),void u();if(!p.headers.get("location"))return console.error("No location header on answer response"),void n("No location header on answer response");e.endpointUrl=p.url,console.log(i,"Updated endpointUrl: ",e.endpointUrl);const m=new URL(c).origin;e.resourceUrl=m+p.headers.get("location");const g=await p.text();console.log(i,"Answer SDP: ",g);try{await a.setLocalDescription(s)}catch(e){console.error("peerConnection.setLocalDescription",e),n(e)}try{await a.setRemoteDescription({type:"answer",sdp:g})}catch(e){console.error("peerConnection.setRemoteDescription",e),n(e)}}(d)):(console.error("Invalid protocol",error),n(error))}catch(e){console.error("Cannot parse connection URL",e),n(e)}},e.stopStreaming=async function(){"webrtc"===e.streamingMode?(e.webSocketClosedByUser=!0,p(),u()):"whip"===e.streamingMode&&await d(),e.callbacks.connectionClosed&&(console.log(i,"Connection closed by user"),e.callbacks.connectionClosed("user","Connection closed by user"))},e.remove=function(){"webrtc"===e.streamingMode?(e.webSocketClosedByUser=!0,p(),u()):"whip"===e.streamingMode&&d(),e.inputStream&&(e.inputStream.getTracks().forEach((n=>{n.stop(),e.inputStream.removeTrack(n)})),e.videoElement&&(e.videoElement.srcObject=null),e.inputStream=null,delete e.inputStream),console.info(c,"Removed")}}o.getVersion=function(){return t},o.create=function(e){console.info(c,"Create WebRTC Input "+t);let n={webSocketClosedByUser:!1};return function(e,n){e.streamingMode=null,e.inputStream=null,e.webSocket=null,e.peerConnection=null,e.connectionConfig={},e.videoElement=null,e.endpointUrl=null,e.resourceUrl=null,n&&n.callbacks?e.callbacks=n.callbacks:e.callbacks={}}(n,e),f(n),n},o.getDevices=async function(){try{await d("both")}catch(e){console.warn(i,"Can not find Video and Audio devices",e);let n=null,o=null;try{n=await d("video")}catch(e){console.warn(i,"Can not find Video devices",e)}try{o=await d("audio")}catch(e){console.warn(i,"Can not find Audio devices",e)}if(!n&&!o)throw new Error("No input devices were found.")}return function(e){let n={audioinput:[],audiooutput:[],videoinput:[],other:[]};for(let o=0;o!==e.length;++o){const t=e[o];let i={};i.deviceId=t.deviceId,"audioinput"===t.kind?(i.label=t.label||`microphone ${n.audioinput.length+1}`,n.audioinput.push(i)):"audiooutput"===t.kind?(i.label=t.label||`speaker ${n.audiooutput.length+1}`,n.audiooutput.push(i)):"videoinput"===t.kind?(i.label=t.label||`camera ${n.videoinput.length+1}`,n.videoinput.push(i)):(i.label=t.label||`other ${n.other.length+1}`,n.other.push(i))}return n}(await async function(){return await navigator.mediaDevices.enumerateDevices()}())};const u=o;return n.default})()));
2
2
  //# sourceMappingURL=OvenLiveKit.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"OvenLiveKit.min.js","mappings":"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","sources":["webpack://OvenLiveKit/webpack/universalModuleDefinition","webpack://OvenLiveKit/webpack/bootstrap","webpack://OvenLiveKit/webpack/runtime/define property getters","webpack://OvenLiveKit/webpack/runtime/hasOwnProperty shorthand","webpack://OvenLiveKit/./src/OvenLiveKit.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"OvenLiveKit\"] = factory();\n\telse\n\t\troot[\"OvenLiveKit\"] = factory();\n})(self, () => {\nreturn ","// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","const OvenLiveKit = {};\r\n\r\nconst version = '1.3.0';\r\nconst logHeader = 'OvenLiveKit.js :';\r\nconst logEventHeader = 'OvenLiveKit.js ====';\r\n\r\n// private methods\r\nfunction sendMessage(webSocket, message) {\r\n\r\n if (webSocket) {\r\n webSocket.send(JSON.stringify(message));\r\n }\r\n}\r\n\r\nfunction generateDomainFromUrl(url) {\r\n let result = '';\r\n let match;\r\n if (match = url.match(/^(?:wss?:\\/\\/)?(?:[^@\\n]+@)?(?:www\\.)?([^:\\/\\n\\?\\=]+)/im)) {\r\n result = match[1];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction findIp(string) {\r\n\r\n let result = '';\r\n let match;\r\n\r\n if (match = string.match(new RegExp('\\\\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\b', 'gi'))) {\r\n result = match[0];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction getFormatNumber(sdp, format) {\r\n\r\n const lines = sdp.split('\\r\\n');\r\n let formatNumber = -1;\r\n\r\n for (let i = 0; i < lines.length - 1; i++) {\r\n\r\n lines[i] = lines[i].toLowerCase();\r\n\r\n if (lines[i].indexOf('a=rtpmap') === 0 && lines[i].indexOf(format.toLowerCase()) > -1) {\r\n // parsing \"a=rtpmap:100 H264/90000\" line\r\n // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters >]\r\n formatNumber = lines[i].split(' ')[0].split(':')[1];\r\n break;\r\n }\r\n }\r\n\r\n return formatNumber;\r\n}\r\n\r\nfunction setPreferredVideoFormat(sdp, formatName) {\r\n\r\n const formatNumber = getFormatNumber(sdp, formatName);\r\n\r\n if (formatNumber === -1) {\r\n return sdp;\r\n }\r\n\r\n let newLines = [];\r\n const lines = sdp.split('\\r\\n');\r\n\r\n for (let i = 0; i < lines.length - 1; i++) {\r\n\r\n const line = lines[i];\r\n\r\n if (line.indexOf('m=video') === 0) {\r\n\r\n // m=<media> <port>/<number of ports> <transport> <fmt list>\r\n const others = line.split(' ').slice(0, 3);\r\n const formats = line.split(' ').slice(3);\r\n formats.sort(function (x, y) { return x == formatNumber ? -1 : y == formatNumber ? 1 : 0; });\r\n newLines.push(others.concat(formats).join(' '));\r\n } else {\r\n newLines.push(line);\r\n }\r\n\r\n }\r\n\r\n return newLines.join('\\r\\n') + '\\r\\n';\r\n}\r\n\r\nfunction removeFormat(sdp, formatNumber) {\r\n let newLines = [];\r\n let lines = sdp.split('\\r\\n');\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n\r\n if (lines[i].indexOf('m=video') === 0) {\r\n newLines.push(lines[i].replace(' ' + formatNumber + '', ''));\r\n } else if (lines[i].indexOf(formatNumber + '') > -1) {\r\n\r\n } else {\r\n newLines.push(lines[i]);\r\n }\r\n }\r\n\r\n return newLines.join('\\r\\n')\r\n}\r\n\r\nasync function getStreamForDeviceCheck(type) {\r\n\r\n // High resolution video constraints makes browser to get maximum resolution of video device.\r\n const constraints = {\r\n };\r\n\r\n if (type === 'both') {\r\n constraints.audio = true;\r\n constraints.video = true;\r\n } else if (type === 'audio') {\r\n constraints.audio = true;\r\n } else if (type === 'video') {\r\n constraints.video = true;\r\n }\r\n\r\n return await navigator.mediaDevices.getUserMedia(constraints);\r\n}\r\n\r\nasync function getDevices() {\r\n\r\n return await navigator.mediaDevices.enumerateDevices();\r\n}\r\n\r\nfunction gotDevices(deviceInfos) {\r\n\r\n let devices = {\r\n 'audioinput': [],\r\n 'audiooutput': [],\r\n 'videoinput': [],\r\n 'other': [],\r\n };\r\n\r\n for (let i = 0; i !== deviceInfos.length; ++i) {\r\n\r\n const deviceInfo = deviceInfos[i];\r\n\r\n let info = {};\r\n\r\n info.deviceId = deviceInfo.deviceId;\r\n\r\n if (deviceInfo.kind === 'audioinput') {\r\n\r\n info.label = deviceInfo.label || `microphone ${devices.audioinput.length + 1}`;\r\n devices.audioinput.push(info);\r\n } else if (deviceInfo.kind === 'audiooutput') {\r\n\r\n info.label = deviceInfo.label || `speaker ${devices.audiooutput.length + 1}`;\r\n devices.audiooutput.push(info);\r\n } else if (deviceInfo.kind === 'videoinput') {\r\n\r\n info.label = deviceInfo.label || `camera ${devices.videoinput.length + 1}`;\r\n devices.videoinput.push(info);\r\n } else {\r\n\r\n info.label = deviceInfo.label || `other ${devices.other.length + 1}`;\r\n devices.other.push(info);\r\n }\r\n }\r\n\r\n return devices;\r\n}\r\n\r\nfunction initConfig(instance, options) {\r\n\r\n // webrtc or whip\r\n instance.streamingMode = null;\r\n\r\n instance.inputStream = null;\r\n instance.webSocket = null;\r\n instance.peerConnection = null;\r\n instance.connectionConfig = {};\r\n\r\n instance.videoElement = null;\r\n instance.endpointUrl = null;\r\n instance.resourceUrl = null;\r\n\r\n if (options && options.callbacks) {\r\n\r\n instance.callbacks = options.callbacks;\r\n } else {\r\n instance.callbacks = {};\r\n }\r\n}\r\n\r\nfunction addMethod(instance) {\r\n\r\n function errorHandler(error) {\r\n\r\n if (instance.callbacks.error) {\r\n\r\n instance.callbacks.error(error);\r\n }\r\n }\r\n\r\n async function fetchWithRedirect(url, options) {\r\n let fetched = await fetch(url, options);\r\n\r\n while (fetched.redirected) {\r\n url = fetched.url;\r\n fetched = await fetch(url, options);\r\n }\r\n\r\n return fetched;\r\n }\r\n\r\n function getUserMedia(constraints) {\r\n\r\n if (!constraints) {\r\n\r\n constraints = {\r\n video: {\r\n deviceId: undefined\r\n },\r\n audio: {\r\n deviceId: undefined\r\n }\r\n };\r\n }\r\n\r\n console.info(logHeader, 'Request Stream To Input Devices With Constraints', constraints);\r\n\r\n return navigator.mediaDevices.getUserMedia(constraints)\r\n .then(function (stream) {\r\n\r\n console.info(logHeader, 'Received Media Stream From Input Device', stream);\r\n\r\n instance.inputStream = stream;\r\n\r\n let elem = instance.videoElement;\r\n\r\n // Attach stream to video element when video element is provided.\r\n if (elem) {\r\n\r\n elem.srcObject = stream;\r\n\r\n elem.onloadedmetadata = function (e) {\r\n\r\n elem.play();\r\n };\r\n }\r\n\r\n return new Promise(function (resolve) {\r\n\r\n resolve(stream);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error(logHeader, 'Can\\'t Get Media Stream From Input Device', error);\r\n errorHandler(error);\r\n\r\n return new Promise(function (resolve, reject) {\r\n reject(error);\r\n });\r\n });\r\n }\r\n\r\n function getDisplayMedia(constraints) {\r\n\r\n if (!constraints) {\r\n constraints = {};\r\n }\r\n\r\n console.info(logHeader, 'Request Stream To Display With Constraints', constraints);\r\n\r\n return navigator.mediaDevices.getDisplayMedia(constraints)\r\n .then(function (stream) {\r\n\r\n console.info(logHeader, 'Received Media Stream From Display', stream);\r\n\r\n instance.inputStream = stream;\r\n\r\n let elem = instance.videoElement;\r\n\r\n // Attach stream to video element when video element is provided.\r\n if (elem) {\r\n\r\n elem.srcObject = stream;\r\n\r\n elem.onloadedmetadata = function (e) {\r\n\r\n elem.play();\r\n };\r\n }\r\n\r\n return new Promise(function (resolve) {\r\n\r\n resolve(stream);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error(logHeader, 'Can\\'t Get Media Stream From Display', error);\r\n errorHandler(error);\r\n\r\n return new Promise(function (resolve, reject) {\r\n reject(error);\r\n });\r\n });\r\n }\r\n\r\n function setMediaStream(stream) {\r\n // Check if a valid stream is provided\r\n if (!stream || !(stream instanceof MediaStream)) {\r\n\r\n const error = new Error(\"Invalid MediaStream provided\");\r\n console.error(logHeader, 'Invalid MediaStream', error);\r\n errorHandler(error);\r\n\r\n return new Promise(function (resolve, reject) {\r\n reject(error);\r\n });\r\n }\r\n\r\n console.info(logHeader, 'Received Media Stream', stream);\r\n\r\n instance.inputStream = stream;\r\n\r\n let elem = instance.videoElement;\r\n\r\n // Attach stream to video element when video element is provided.\r\n if (elem) {\r\n elem.srcObject = stream;\r\n\r\n elem.onloadedmetadata = function (e) {\r\n elem.play();\r\n };\r\n }\r\n\r\n return new Promise(function (resolve) {\r\n resolve(stream);\r\n });\r\n }\r\n\r\n // From https://webrtchacks.com/limit-webrtc-bandwidth-sdp/\r\n function setBitrateLimit(sdp, media, bitrate) {\r\n\r\n let lines = sdp.split('\\r\\n');\r\n let line = -1;\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n if (lines[i].indexOf('m=' + media) === 0) {\r\n line = i;\r\n break;\r\n }\r\n }\r\n if (line === -1) {\r\n // Could not find the m line for media\r\n return sdp;\r\n }\r\n\r\n // Pass the m line\r\n line++;\r\n\r\n // Skip i and c lines\r\n while (lines[line].indexOf('i=') === 0 || lines[line].indexOf('c=') === 0) {\r\n\r\n line++;\r\n }\r\n\r\n // If we're on a b line, replace it\r\n if (lines[line].indexOf('b') === 0) {\r\n\r\n lines[line] = 'b=AS:' + bitrate;\r\n\r\n return lines.join('\\r\\n');\r\n }\r\n\r\n // Add a new b line\r\n let newLines = lines.slice(0, line)\r\n\r\n newLines.push('b=AS:' + bitrate)\r\n newLines = newLines.concat(lines.slice(line, lines.length))\r\n\r\n return newLines.join('\\r\\n')\r\n }\r\n\r\n function initWebSocket(endpointUrl) {\r\n\r\n if (!endpointUrl) {\r\n errorHandler('endpointUrl is required');\r\n return;\r\n }\r\n\r\n let webSocket = null;\r\n\r\n try {\r\n\r\n webSocket = new WebSocket(endpointUrl);\r\n } catch (error) {\r\n\r\n errorHandler(error);\r\n }\r\n\r\n\r\n instance.webSocket = webSocket;\r\n\r\n webSocket.onopen = function () {\r\n\r\n // Request offer at the first time.\r\n sendMessage(webSocket, {\r\n command: 'request_offer'\r\n });\r\n };\r\n\r\n webSocket.onmessage = function (e) {\r\n\r\n let message = JSON.parse(e.data);\r\n\r\n if (message.error) {\r\n console.error('webSocket.onmessage', message.error);\r\n errorHandler(message.error);\r\n }\r\n\r\n if (message.command === 'offer') {\r\n\r\n // OME returns offer. Start create peer connection.\r\n createPeerConnection(\r\n message.id,\r\n message.peer_id,\r\n message.sdp,\r\n message.candidates,\r\n message.ice_servers\r\n );\r\n }\r\n };\r\n\r\n webSocket.onerror = function (error) {\r\n\r\n console.error('webSocket.onerror', error);\r\n errorHandler(error);\r\n };\r\n\r\n webSocket.onclose = function (e) {\r\n\r\n if (!instance.webSocketClosedByUser) {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n instance.callbacks.connectionClosed('websocket', e);\r\n }\r\n }\r\n };\r\n\r\n }\r\n\r\n async function startWhip(endpointUrl) {\r\n\r\n if (instance.peerConnection) {\r\n console.error('Connection already established');\r\n errorHandler('Connection already established');\r\n return;\r\n }\r\n\r\n const peerConnectionConfig = {\r\n bundlePolicy: \"max-bundle\"\r\n };\r\n\r\n if (instance.connectionConfig.iceServers) {\r\n\r\n // first priority using ice servers from local config.\r\n peerConnectionConfig.iceServers = instance.connectionConfig.iceServers;\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n } else {\r\n // last priority using default ice servers.\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n }\r\n\r\n console.info(logHeader, 'Create Peer Connection With Config', peerConnectionConfig);\r\n\r\n const peerConnection = new RTCPeerConnection(peerConnectionConfig);\r\n\r\n instance.peerConnection = peerConnection;\r\n\r\n if (!instance.inputStream) {\r\n console.error('No input stream in OvenLiveKit');\r\n errorHandler('No input stream in OvenLiveKit');\r\n return;\r\n }\r\n\r\n for (const track of instance.inputStream.getTracks()) {\r\n //You could add simulcast too here\r\n console.log(logHeader, 'Adding track: ', track);\r\n peerConnection.addTransceiver(track, { 'direction': 'sendonly' });\r\n }\r\n\r\n peerConnection.oniceconnectionstatechange = function (e) {\r\n\r\n let state = peerConnection.iceConnectionState;\r\n\r\n if (instance.callbacks.iceStateChange) {\r\n\r\n console.info(logHeader, 'ICE State', '[' + state + ']');\r\n instance.callbacks.iceStateChange(state);\r\n }\r\n\r\n if (state === 'connected') {\r\n\r\n if (instance.callbacks.connected) {\r\n instance.callbacks.connected(e);\r\n }\r\n }\r\n\r\n if (state === 'failed') {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.error(logHeader, 'Ice connection failed', e);\r\n instance.callbacks.errorHandler(e);\r\n }\r\n }\r\n\r\n if (state === 'disconnected' || state === 'closed') {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.error(logHeader, 'Ice connection disconnected or closed', e);\r\n instance.callbacks.connectionClosed('ice', e);\r\n }\r\n }\r\n }\r\n\r\n const offer = await peerConnection.createOffer();\r\n console.log(logHeader, 'Offer SDP: ', offer.sdp);\r\n\r\n if (instance.connectionConfig.maxVideoBitrate) {\r\n\r\n // if bandwith limit is set. modify sdp from ome to limit acceptable bandwidth of ome\r\n offer.sdp = setBitrateLimit(offer.sdp, 'video', instance.connectionConfig.maxVideoBitrate);\r\n }\r\n\r\n if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {\r\n\r\n offer.sdp = appendFmtp(offer.sdp);\r\n }\r\n\r\n if (instance.connectionConfig.preferredVideoFormat) {\r\n offer.sdp = setPreferredVideoFormat(offer.sdp, instance.connectionConfig.preferredVideoFormat)\r\n }\r\n\r\n const headers = {\r\n \"Content-Type\": \"application/sdp\"\r\n };\r\n\r\n if (instance.connectionConfig.httpHeaders) {\r\n Object.assign(headers, instance.connectionConfig.httpHeaders);\r\n }\r\n\r\n const fetched = await fetchWithRedirect(endpointUrl, {\r\n method: \"POST\",\r\n body: offer.sdp,\r\n headers\r\n });\r\n\r\n if (!fetched.ok) {\r\n console.error('Failed to fetch', fetched.status);\r\n errorHandler(`Failed to fetch ${fetched.status}`);\r\n closePeerConnection();\r\n return;\r\n }\r\n\r\n if (!fetched.headers.get(\"location\")) {\r\n console.error('No location header on answer response');\r\n errorHandler('No location header on answer response');\r\n return;\r\n }\r\n\r\n // update endpointUrl\r\n instance.endpointUrl = fetched.url;\r\n console.log(logHeader, 'Updated endpointUrl: ', instance.endpointUrl);\r\n\r\n const baseUrl = new URL(endpointUrl).origin;\r\n instance.resourceUrl = baseUrl + fetched.headers.get(\"location\");\r\n\r\n const answer = await fetched.text();\r\n console.log(logHeader, 'Answer SDP: ', answer);\r\n\r\n try {\r\n await peerConnection.setLocalDescription(offer);\r\n } catch (error) {\r\n console.error('peerConnection.setLocalDescription', error);\r\n errorHandler(error);\r\n }\r\n\r\n try {\r\n await peerConnection.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: answer\r\n });\r\n } catch (error) {\r\n console.error('peerConnection.setRemoteDescription', error);\r\n errorHandler(error);\r\n }\r\n }\r\n\r\n async function stopWhip() {\r\n\r\n if (!instance.peerConnection) {\r\n console.error('No connection to close');\r\n errorHandler('No connection to close');\r\n return;\r\n }\r\n\r\n closePeerConnection();\r\n\r\n if (instance.resourceUrl) {\r\n\r\n const headers = {\r\n };\r\n\r\n if (instance.connectionConfig.httpHeaders) {\r\n Object.assign(headers, instance.connectionConfig.httpHeaders);\r\n }\r\n\r\n await fetchWithRedirect(instance.resourceUrl, {\r\n method: \"DELETE\",\r\n headers\r\n });\r\n }\r\n }\r\n\r\n function appendFmtp(sdp) {\r\n\r\n const fmtpStr = instance.connectionConfig.sdp.appendFmtp;\r\n\r\n const lines = sdp.split('\\r\\n');\r\n const payloads = [];\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n\r\n if (lines[i].indexOf('m=video') === 0) {\r\n\r\n let tokens = lines[i].split(' ')\r\n\r\n for (let j = 3; j < tokens.length; j++) {\r\n\r\n payloads.push(tokens[j]);\r\n }\r\n\r\n break;\r\n }\r\n }\r\n\r\n for (let i = 0; i < payloads.length; i++) {\r\n\r\n let fmtpLineFound = false;\r\n\r\n for (let j = 0; j < lines.length; j++) {\r\n\r\n if (lines[j].indexOf('a=fmtp:' + payloads[i]) === 0) {\r\n fmtpLineFound = true;\r\n lines[j] += ';' + fmtpStr;\r\n }\r\n }\r\n\r\n if (!fmtpLineFound) {\r\n\r\n for (let j = 0; j < lines.length; j++) {\r\n\r\n if (lines[j].indexOf('a=rtpmap:' + payloads[i]) === 0) {\r\n\r\n lines[j] += '\\r\\na=fmtp:' + payloads[i] + ' ' + fmtpStr;\r\n }\r\n }\r\n }\r\n }\r\n\r\n return lines.join('\\r\\n')\r\n }\r\n\r\n function appendOrientation(sdp) {\r\n\r\n const lines = sdp.split('\\r\\n');\r\n const payloads = [];\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n\r\n if (lines[i].indexOf('m=video') === 0) {\r\n\r\n let tokens = lines[i].split(' ')\r\n\r\n for (let j = 3; j < tokens.length; j++) {\r\n\r\n payloads.push(tokens[j]);\r\n }\r\n\r\n break;\r\n }\r\n }\r\n\r\n for (let i = 0; i < payloads.length; i++) {\r\n\r\n for (let j = 0; j < lines.length; j++) {\r\n\r\n if (lines[j].indexOf('a=rtpmap:' + payloads[i]) === 0) {\r\n\r\n lines[j] += '\\r\\na=extmap:' + payloads[i] + ' urn:3gpp:video-orientation';\r\n }\r\n }\r\n }\r\n\r\n return lines.join('\\r\\n')\r\n }\r\n\r\n function createPeerConnection(id, peerId, offer, candidates, iceServers) {\r\n\r\n let peerConnectionConfig = {};\r\n\r\n if (instance.connectionConfig.iceServers) {\r\n\r\n // first priority using ice servers from local config.\r\n peerConnectionConfig.iceServers = instance.connectionConfig.iceServers;\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n } else if (iceServers) {\r\n\r\n // second priority using ice servers from ome and force using TCP\r\n peerConnectionConfig.iceServers = [];\r\n\r\n for (let i = 0; i < iceServers.length; i++) {\r\n\r\n let iceServer = iceServers[i];\r\n\r\n let regIceServer = {};\r\n\r\n regIceServer.urls = iceServer.urls;\r\n\r\n let hasWebSocketUrl = false;\r\n let webSocketUrl = generateDomainFromUrl(instance.endpointUrl);\r\n\r\n for (let j = 0; j < regIceServer.urls.length; j++) {\r\n\r\n let serverUrl = regIceServer.urls[j];\r\n\r\n if (serverUrl.indexOf(webSocketUrl) > -1) {\r\n hasWebSocketUrl = true;\r\n break;\r\n }\r\n }\r\n\r\n if (!hasWebSocketUrl) {\r\n\r\n if (regIceServer.urls.length > 0) {\r\n\r\n let cloneIceServer = regIceServer.urls[0];\r\n let ip = findIp(cloneIceServer);\r\n\r\n if (webSocketUrl && ip) {\r\n regIceServer.urls.push(cloneIceServer.replace(ip, webSocketUrl));\r\n }\r\n }\r\n }\r\n\r\n regIceServer.username = iceServer.user_name;\r\n regIceServer.credential = iceServer.credential;\r\n\r\n peerConnectionConfig.iceServers.push(regIceServer);\r\n }\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n } else {\r\n peerConnectionConfig.iceTransportPolicy = 'relay';\r\n }\r\n } else {\r\n // last priority using default ice servers.\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n }\r\n\r\n let advancedSetting = {\r\n optional: [\r\n {\r\n googHighStartBitrate: {\r\n exact: !0\r\n }\r\n },\r\n {\r\n googPayloadPadding: {\r\n exact: !0\r\n }\r\n },\r\n {\r\n googScreencastMinBitrate: {\r\n exact: 500\r\n }\r\n },\r\n {\r\n enableDscp: {\r\n exact: true\r\n }\r\n }\r\n ]\r\n };\r\n\r\n console.info(logHeader, 'Create Peer Connection With Config', peerConnectionConfig);\r\n\r\n let peerConnection = new RTCPeerConnection(peerConnectionConfig);\r\n\r\n instance.peerConnection = peerConnection;\r\n\r\n // set local stream\r\n instance.inputStream.getTracks().forEach(function (track) {\r\n\r\n console.info(logHeader, 'Add Track To Peer Connection', track);\r\n peerConnection.addTrack(track, instance.inputStream);\r\n });\r\n\r\n if (instance.connectionConfig.maxVideoBitrate) {\r\n\r\n // if bandwith limit is set. modify sdp from ome to limit acceptable bandwidth of ome\r\n offer.sdp = setBitrateLimit(offer.sdp, 'video', instance.connectionConfig.maxVideoBitrate);\r\n }\r\n\r\n if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {\r\n\r\n offer.sdp = appendFmtp(offer.sdp);\r\n }\r\n\r\n if (instance.connectionConfig.preferredVideoFormat) {\r\n offer.sdp = setPreferredVideoFormat(offer.sdp, instance.connectionConfig.preferredVideoFormat)\r\n }\r\n\r\n\r\n // offer.sdp = appendOrientation(offer.sdp);\r\n console.info(logHeader, 'Modified offer sdp\\n\\n' + offer.sdp);\r\n\r\n peerConnection.setRemoteDescription(new RTCSessionDescription(offer))\r\n .then(function () {\r\n\r\n peerConnection.createAnswer()\r\n .then(function (answer) {\r\n\r\n if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {\r\n\r\n answer.sdp = appendFmtp(answer.sdp);\r\n }\r\n\r\n if (instance.connectionConfig.preferredVideoFormat) {\r\n answer.sdp = setPreferredVideoFormat(answer.sdp, instance.connectionConfig.preferredVideoFormat)\r\n }\r\n\r\n console.info(logHeader, 'Modified answer sdp\\n\\n' + answer.sdp);\r\n\r\n peerConnection.setLocalDescription(answer)\r\n .then(function () {\r\n\r\n sendMessage(instance.webSocket, {\r\n id: id,\r\n peer_id: peerId,\r\n command: 'answer',\r\n sdp: answer\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.setLocalDescription', error);\r\n errorHandler(error);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.createAnswer', error);\r\n errorHandler(error);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.setRemoteDescription', error);\r\n errorHandler(error);\r\n });\r\n\r\n if (candidates) {\r\n\r\n addIceCandidate(peerConnection, candidates);\r\n }\r\n\r\n peerConnection.onicecandidate = function (e) {\r\n\r\n if (e.candidate && e.candidate.candidate) {\r\n\r\n sendMessage(instance.webSocket, {\r\n id: id,\r\n peer_id: peerId,\r\n command: 'candidate',\r\n candidates: [e.candidate]\r\n });\r\n }\r\n };\r\n\r\n peerConnection.oniceconnectionstatechange = function (e) {\r\n\r\n let state = peerConnection.iceConnectionState;\r\n\r\n if (instance.callbacks.iceStateChange) {\r\n\r\n console.info(logHeader, 'ICE State', '[' + state + ']');\r\n instance.callbacks.iceStateChange(state);\r\n }\r\n\r\n if (state === 'connected') {\r\n\r\n if (instance.callbacks.connected) {\r\n instance.callbacks.connected(e);\r\n }\r\n }\r\n\r\n if (state === 'failed' || state === 'disconnected' || state === 'closed') {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.error(logHeader, 'Ice connection closed', e);\r\n instance.callbacks.connectionClosed('ice', e);\r\n }\r\n }\r\n }\r\n }\r\n\r\n function addIceCandidate(peerConnection, candidates) {\r\n\r\n for (let i = 0; i < candidates.length; i++) {\r\n\r\n if (candidates[i] && candidates[i].candidate) {\r\n\r\n let basicCandidate = candidates[i];\r\n\r\n peerConnection.addIceCandidate(new RTCIceCandidate(basicCandidate))\r\n .then(function () {\r\n\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.addIceCandidate', error);\r\n errorHandler(error);\r\n });\r\n }\r\n }\r\n }\r\n\r\n function closePeerConnection() {\r\n if (instance.peerConnection) {\r\n\r\n // remove tracks from peer connection\r\n instance.peerConnection.getSenders().forEach(function (sender) {\r\n instance.peerConnection.removeTrack(sender);\r\n });\r\n\r\n instance.peerConnection.close();\r\n instance.peerConnection = null;\r\n delete instance.peerConnection;\r\n }\r\n }\r\n\r\n function closeWebSocket() {\r\n\r\n if (instance.webSocket) {\r\n\r\n instance.webSocket.close();\r\n instance.webSocket = null;\r\n delete instance.webSocket;\r\n }\r\n }\r\n\r\n function closeInputStream() {\r\n // release video, audio stream\r\n if (instance.inputStream) {\r\n\r\n instance.inputStream.getTracks().forEach(track => {\r\n\r\n track.stop();\r\n instance.inputStream.removeTrack(track);\r\n });\r\n\r\n if (instance.videoElement) {\r\n instance.videoElement.srcObject = null;\r\n }\r\n\r\n instance.inputStream = null;\r\n delete instance.inputStream;\r\n }\r\n }\r\n\r\n // instance methods\r\n instance.attachMedia = function (videoElement) {\r\n\r\n instance.videoElement = videoElement;\r\n };\r\n\r\n instance.getUserMedia = function (constraints) {\r\n\r\n return getUserMedia(constraints);\r\n };\r\n\r\n instance.getDisplayMedia = function (constraints) {\r\n\r\n return getDisplayMedia(constraints);\r\n };\r\n\r\n instance.setMediaStream = function (stream) {\r\n\r\n return setMediaStream(stream);\r\n };\r\n\r\n instance.startStreaming = function (endpointUrl, connectionConfig) {\r\n\r\n console.info(logEventHeader, `Start Streaming to ${endpointUrl} with connectionConfig`, connectionConfig);\r\n\r\n if (!endpointUrl) {\r\n console.error('endpointUrl is required');\r\n errorHandler('endpointUrl is required');\r\n return;\r\n }\r\n\r\n instance.endpointUrl = endpointUrl;\r\n\r\n if (connectionConfig) {\r\n instance.connectionConfig = connectionConfig;\r\n }\r\n\r\n try {\r\n\r\n const protocol = new URL(endpointUrl).protocol;\r\n\r\n if (protocol === 'wss:' || protocol === 'ws:') {\r\n\r\n instance.streamingMode = 'webrtc';\r\n initWebSocket(endpointUrl);\r\n } else if (protocol === 'https:' || protocol === 'http:') {\r\n\r\n instance.streamingMode = 'whip';\r\n startWhip(endpointUrl);\r\n } else {\r\n console.error('Invalid protocol', error);\r\n errorHandler(error);\r\n }\r\n\r\n } catch (error) {\r\n console.error('Cannot parse connection URL', error);\r\n errorHandler(error);\r\n }\r\n };\r\n\r\n instance.stopStreaming = async function () {\r\n\r\n if (instance.streamingMode === 'webrtc') {\r\n\r\n instance.webSocketClosedByUser = true;\r\n\r\n closeWebSocket();\r\n closePeerConnection();\r\n } else if (instance.streamingMode === 'whip') {\r\n\r\n await stopWhip();\r\n }\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.log(logHeader, 'Connection closed by user');\r\n instance.callbacks.connectionClosed('user', 'Connection closed by user');\r\n }\r\n };\r\n\r\n instance.remove = function () {\r\n\r\n if (instance.streamingMode === 'webrtc') {\r\n\r\n instance.webSocketClosedByUser = true;\r\n\r\n closeWebSocket();\r\n closePeerConnection();\r\n } else if (instance.streamingMode === 'whip') {\r\n stopWhip();\r\n }\r\n\r\n closeInputStream();\r\n\r\n console.info(logEventHeader, 'Removed');\r\n\r\n };\r\n}\r\n\r\nOvenLiveKit.getVersion = function () {\r\n return version;\r\n}\r\n\r\n// static methods\r\nOvenLiveKit.create = function (options) {\r\n\r\n console.info(logEventHeader, 'Create WebRTC Input ' + version);\r\n\r\n let instance = {};\r\n\r\n instance.webSocketClosedByUser = false;\r\n\r\n initConfig(instance, options);\r\n addMethod(instance);\r\n\r\n return instance;\r\n};\r\n\r\nOvenLiveKit.getDevices = async function () {\r\n\r\n try {\r\n // First check both audio and video sources are available.\r\n await getStreamForDeviceCheck('both');\r\n } catch (e) {\r\n\r\n console.warn(logHeader, 'Can not find Video and Audio devices', e);\r\n\r\n let videoFound = null;\r\n let audioFound = null;\r\n\r\n try {\r\n videoFound = await getStreamForDeviceCheck('video');\r\n } catch (e) {\r\n console.warn(logHeader, 'Can not find Video devices', e);\r\n }\r\n\r\n try {\r\n audioFound = await getStreamForDeviceCheck('audio');\r\n } catch (e) {\r\n console.warn(logHeader, 'Can not find Audio devices', e);\r\n }\r\n\r\n if (!videoFound && !audioFound) {\r\n throw new Error('No input devices were found.');\r\n }\r\n }\r\n\r\n const deviceInfos = await getDevices();\r\n return gotDevices(deviceInfos)\r\n};\r\n\r\nexport default OvenLiveKit;"],"names":["root","factory","exports","module","define","amd","self","__webpack_require__","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","OvenLiveKit","version","logHeader","logEventHeader","sendMessage","webSocket","message","send","JSON","stringify","generateDomainFromUrl","url","match","result","findIp","string","RegExp","setPreferredVideoFormat","sdp","formatName","formatNumber","format","lines","split","i","length","toLowerCase","indexOf","getFormatNumber","newLines","line","others","slice","formats","sort","x","y","push","concat","join","async","getStreamForDeviceCheck","type","constraints","audio","video","navigator","mediaDevices","getUserMedia","addMethod","instance","errorHandler","error","callbacks","fetchWithRedirect","options","fetched","fetch","redirected","setBitrateLimit","media","bitrate","stopWhip","peerConnection","console","closePeerConnection","resourceUrl","headers","connectionConfig","httpHeaders","assign","method","appendFmtp","fmtpStr","payloads","tokens","j","fmtpLineFound","getSenders","forEach","sender","removeTrack","close","closeWebSocket","attachMedia","videoElement","deviceId","undefined","info","then","stream","inputStream","elem","srcObject","onloadedmetadata","e","play","Promise","resolve","catch","reject","getDisplayMedia","setMediaStream","MediaStream","Error","startStreaming","endpointUrl","protocol","URL","streamingMode","WebSocket","onopen","command","onmessage","parse","data","id","peerId","offer","candidates","iceServers","peerConnectionConfig","iceTransportPolicy","iceServer","regIceServer","urls","hasWebSocketUrl","webSocketUrl","cloneIceServer","ip","replace","username","user_name","credential","RTCPeerConnection","getTracks","track","addTrack","maxVideoBitrate","preferredVideoFormat","setRemoteDescription","RTCSessionDescription","createAnswer","answer","setLocalDescription","peer_id","candidate","basicCandidate","addIceCandidate","RTCIceCandidate","onicecandidate","oniceconnectionstatechange","state","iceConnectionState","iceStateChange","connected","connectionClosed","createPeerConnection","ice_servers","onerror","onclose","webSocketClosedByUser","initWebSocket","bundlePolicy","log","addTransceiver","createOffer","body","ok","status","baseUrl","origin","text","startWhip","stopStreaming","remove","stop","getVersion","create","initConfig","getDevices","warn","videoFound","audioFound","deviceInfos","devices","deviceInfo","kind","label","audioinput","audiooutput","videoinput","other","gotDevices","enumerateDevices"],"sourceRoot":""}
1
+ {"version":3,"file":"OvenLiveKit.min.js","mappings":"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","sources":["webpack://OvenLiveKit/webpack/universalModuleDefinition","webpack://OvenLiveKit/webpack/bootstrap","webpack://OvenLiveKit/webpack/runtime/define property getters","webpack://OvenLiveKit/webpack/runtime/hasOwnProperty shorthand","webpack://OvenLiveKit/./src/OvenLiveKit.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"OvenLiveKit\"] = factory();\n\telse\n\t\troot[\"OvenLiveKit\"] = factory();\n})(self, () => {\nreturn ","// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","const OvenLiveKit = {};\r\n\r\nconst version = '1.4.0';\r\nconst logHeader = 'OvenLiveKit.js :';\r\nconst logEventHeader = 'OvenLiveKit.js ====';\r\n\r\n// private methods\r\nfunction sendMessage(webSocket, message) {\r\n\r\n if (webSocket) {\r\n webSocket.send(JSON.stringify(message));\r\n }\r\n}\r\n\r\nfunction generateDomainFromUrl(url) {\r\n let result = '';\r\n let match;\r\n if (match = url.match(/^(?:wss?:\\/\\/)?(?:[^@\\n]+@)?(?:www\\.)?([^:\\/\\n\\?\\=]+)/im)) {\r\n result = match[1];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction findIp(string) {\r\n\r\n let result = '';\r\n let match;\r\n\r\n if (match = string.match(new RegExp('\\\\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\b', 'gi'))) {\r\n result = match[0];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction getFormatNumber(sdp, format) {\r\n\r\n const lines = sdp.split('\\r\\n');\r\n let formatNumber = -1;\r\n\r\n for (let i = 0; i < lines.length - 1; i++) {\r\n\r\n lines[i] = lines[i].toLowerCase();\r\n\r\n if (lines[i].indexOf('a=rtpmap') === 0 && lines[i].indexOf(format.toLowerCase()) > -1) {\r\n // parsing \"a=rtpmap:100 H264/90000\" line\r\n // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters >]\r\n formatNumber = lines[i].split(' ')[0].split(':')[1];\r\n break;\r\n }\r\n }\r\n\r\n return formatNumber;\r\n}\r\n\r\nfunction setPreferredVideoFormat(sdp, formatName) {\r\n\r\n const formatNumber = getFormatNumber(sdp, formatName);\r\n\r\n if (formatNumber === -1) {\r\n return sdp;\r\n }\r\n\r\n let newLines = [];\r\n const lines = sdp.split('\\r\\n');\r\n\r\n for (let i = 0; i < lines.length - 1; i++) {\r\n\r\n const line = lines[i];\r\n\r\n if (line.indexOf('m=video') === 0) {\r\n\r\n // m=<media> <port>/<number of ports> <transport> <fmt list>\r\n const others = line.split(' ').slice(0, 3);\r\n const formats = line.split(' ').slice(3);\r\n formats.sort(function (x, y) { return x == formatNumber ? -1 : y == formatNumber ? 1 : 0; });\r\n newLines.push(others.concat(formats).join(' '));\r\n } else {\r\n newLines.push(line);\r\n }\r\n\r\n }\r\n\r\n return newLines.join('\\r\\n') + '\\r\\n';\r\n}\r\n\r\nfunction removeFormat(sdp, formatNumber) {\r\n let newLines = [];\r\n let lines = sdp.split('\\r\\n');\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n\r\n if (lines[i].indexOf('m=video') === 0) {\r\n newLines.push(lines[i].replace(' ' + formatNumber + '', ''));\r\n } else if (lines[i].indexOf(formatNumber + '') > -1) {\r\n\r\n } else {\r\n newLines.push(lines[i]);\r\n }\r\n }\r\n\r\n return newLines.join('\\r\\n')\r\n}\r\n\r\nasync function getStreamForDeviceCheck(type) {\r\n\r\n // High resolution video constraints makes browser to get maximum resolution of video device.\r\n const constraints = {\r\n };\r\n\r\n if (type === 'both') {\r\n constraints.audio = true;\r\n constraints.video = true;\r\n } else if (type === 'audio') {\r\n constraints.audio = true;\r\n } else if (type === 'video') {\r\n constraints.video = true;\r\n }\r\n\r\n return await navigator.mediaDevices.getUserMedia(constraints);\r\n}\r\n\r\nasync function getDevices() {\r\n\r\n return await navigator.mediaDevices.enumerateDevices();\r\n}\r\n\r\nfunction gotDevices(deviceInfos) {\r\n\r\n let devices = {\r\n 'audioinput': [],\r\n 'audiooutput': [],\r\n 'videoinput': [],\r\n 'other': [],\r\n };\r\n\r\n for (let i = 0; i !== deviceInfos.length; ++i) {\r\n\r\n const deviceInfo = deviceInfos[i];\r\n\r\n let info = {};\r\n\r\n info.deviceId = deviceInfo.deviceId;\r\n\r\n if (deviceInfo.kind === 'audioinput') {\r\n\r\n info.label = deviceInfo.label || `microphone ${devices.audioinput.length + 1}`;\r\n devices.audioinput.push(info);\r\n } else if (deviceInfo.kind === 'audiooutput') {\r\n\r\n info.label = deviceInfo.label || `speaker ${devices.audiooutput.length + 1}`;\r\n devices.audiooutput.push(info);\r\n } else if (deviceInfo.kind === 'videoinput') {\r\n\r\n info.label = deviceInfo.label || `camera ${devices.videoinput.length + 1}`;\r\n devices.videoinput.push(info);\r\n } else {\r\n\r\n info.label = deviceInfo.label || `other ${devices.other.length + 1}`;\r\n devices.other.push(info);\r\n }\r\n }\r\n\r\n return devices;\r\n}\r\n\r\nfunction initConfig(instance, options) {\r\n\r\n // webrtc or whip\r\n instance.streamingMode = null;\r\n\r\n instance.inputStream = null;\r\n instance.webSocket = null;\r\n instance.peerConnection = null;\r\n instance.connectionConfig = {};\r\n\r\n instance.videoElement = null;\r\n instance.endpointUrl = null;\r\n instance.resourceUrl = null;\r\n\r\n if (options && options.callbacks) {\r\n\r\n instance.callbacks = options.callbacks;\r\n } else {\r\n instance.callbacks = {};\r\n }\r\n}\r\n\r\nfunction addMethod(instance) {\r\n\r\n function errorHandler(error) {\r\n\r\n if (instance.callbacks.error) {\r\n\r\n instance.callbacks.error(error);\r\n }\r\n }\r\n\r\n async function fetchWithRedirect(url, options) {\r\n let fetched = await fetch(url, options);\r\n\r\n while (fetched.redirected) {\r\n url = fetched.url;\r\n fetched = await fetch(url, options);\r\n }\r\n\r\n return fetched;\r\n }\r\n\r\n function getUserMedia(constraints) {\r\n\r\n if (!constraints) {\r\n\r\n constraints = {\r\n video: {\r\n deviceId: undefined\r\n },\r\n audio: {\r\n deviceId: undefined\r\n }\r\n };\r\n }\r\n\r\n console.info(logHeader, 'Request Stream To Input Devices With Constraints', constraints);\r\n\r\n return navigator.mediaDevices.getUserMedia(constraints)\r\n .then(function (stream) {\r\n\r\n console.info(logHeader, 'Received Media Stream From Input Device', stream);\r\n\r\n instance.inputStream = stream;\r\n\r\n let elem = instance.videoElement;\r\n\r\n // Attach stream to video element when video element is provided.\r\n if (elem) {\r\n\r\n elem.srcObject = stream;\r\n\r\n elem.onloadedmetadata = function (e) {\r\n\r\n elem.play();\r\n };\r\n }\r\n\r\n return new Promise(function (resolve) {\r\n\r\n resolve(stream);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error(logHeader, 'Can\\'t Get Media Stream From Input Device', error);\r\n errorHandler(error);\r\n\r\n return new Promise(function (resolve, reject) {\r\n reject(error);\r\n });\r\n });\r\n }\r\n\r\n function getDisplayMedia(constraints) {\r\n\r\n if (!constraints) {\r\n constraints = {};\r\n }\r\n\r\n console.info(logHeader, 'Request Stream To Display With Constraints', constraints);\r\n\r\n return navigator.mediaDevices.getDisplayMedia(constraints)\r\n .then(function (stream) {\r\n\r\n console.info(logHeader, 'Received Media Stream From Display', stream);\r\n\r\n instance.inputStream = stream;\r\n\r\n let elem = instance.videoElement;\r\n\r\n // Attach stream to video element when video element is provided.\r\n if (elem) {\r\n\r\n elem.srcObject = stream;\r\n\r\n elem.onloadedmetadata = function (e) {\r\n\r\n elem.play();\r\n };\r\n }\r\n\r\n return new Promise(function (resolve) {\r\n\r\n resolve(stream);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error(logHeader, 'Can\\'t Get Media Stream From Display', error);\r\n errorHandler(error);\r\n\r\n return new Promise(function (resolve, reject) {\r\n reject(error);\r\n });\r\n });\r\n }\r\n\r\n function setMediaStream(stream) {\r\n // Check if a valid stream is provided\r\n if (!stream || !(stream instanceof MediaStream)) {\r\n\r\n const error = new Error(\"Invalid MediaStream provided\");\r\n console.error(logHeader, 'Invalid MediaStream', error);\r\n errorHandler(error);\r\n\r\n return new Promise(function (resolve, reject) {\r\n reject(error);\r\n });\r\n }\r\n\r\n console.info(logHeader, 'Received Media Stream', stream);\r\n\r\n instance.inputStream = stream;\r\n\r\n let elem = instance.videoElement;\r\n\r\n // Attach stream to video element when video element is provided.\r\n if (elem) {\r\n elem.srcObject = stream;\r\n\r\n elem.onloadedmetadata = function (e) {\r\n elem.play();\r\n };\r\n }\r\n\r\n return new Promise(function (resolve) {\r\n resolve(stream);\r\n });\r\n }\r\n\r\n // From https://webrtchacks.com/limit-webrtc-bandwidth-sdp/\r\n function setBitrateLimit(sdp, media, bitrate) {\r\n\r\n let lines = sdp.split('\\r\\n');\r\n let line = -1;\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n if (lines[i].indexOf('m=' + media) === 0) {\r\n line = i;\r\n break;\r\n }\r\n }\r\n if (line === -1) {\r\n // Could not find the m line for media\r\n return sdp;\r\n }\r\n\r\n // Pass the m line\r\n line++;\r\n\r\n // Skip i and c lines\r\n while (lines[line].indexOf('i=') === 0 || lines[line].indexOf('c=') === 0) {\r\n\r\n line++;\r\n }\r\n\r\n // If we're on a b line, replace it\r\n if (lines[line].indexOf('b') === 0) {\r\n\r\n lines[line] = 'b=AS:' + bitrate;\r\n\r\n return lines.join('\\r\\n');\r\n }\r\n\r\n // Add a new b line\r\n let newLines = lines.slice(0, line)\r\n\r\n newLines.push('b=AS:' + bitrate)\r\n newLines = newLines.concat(lines.slice(line, lines.length))\r\n\r\n return newLines.join('\\r\\n')\r\n }\r\n\r\n function initWebSocket(endpointUrl) {\r\n\r\n if (!endpointUrl) {\r\n errorHandler('endpointUrl is required');\r\n return;\r\n }\r\n\r\n let webSocket = null;\r\n\r\n try {\r\n\r\n webSocket = new WebSocket(endpointUrl);\r\n } catch (error) {\r\n\r\n errorHandler(error);\r\n }\r\n\r\n\r\n instance.webSocket = webSocket;\r\n\r\n webSocket.onopen = function () {\r\n\r\n // Request offer at the first time.\r\n sendMessage(webSocket, {\r\n command: 'request_offer'\r\n });\r\n };\r\n\r\n webSocket.onmessage = function (e) {\r\n\r\n let message = JSON.parse(e.data);\r\n\r\n if (message.error) {\r\n console.error('webSocket.onmessage', message.error);\r\n errorHandler(message.error);\r\n }\r\n\r\n if (message.command === 'offer') {\r\n\r\n // OME returns offer. Start create peer connection.\r\n createPeerConnection(\r\n message.id,\r\n message.peer_id,\r\n message.sdp,\r\n message.candidates,\r\n message.ice_servers\r\n );\r\n }\r\n };\r\n\r\n webSocket.onerror = function (error) {\r\n\r\n console.error('webSocket.onerror', error);\r\n errorHandler(error);\r\n };\r\n\r\n webSocket.onclose = function (e) {\r\n\r\n if (!instance.webSocketClosedByUser) {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n instance.callbacks.connectionClosed('websocket', e);\r\n }\r\n }\r\n };\r\n\r\n }\r\n\r\n async function startWhip(endpointUrl) {\r\n\r\n if (instance.peerConnection) {\r\n console.error('Connection already established');\r\n errorHandler('Connection already established');\r\n return;\r\n }\r\n\r\n const peerConnectionConfig = {\r\n bundlePolicy: \"max-bundle\"\r\n };\r\n\r\n if (instance.connectionConfig.iceServers) {\r\n\r\n // first priority using ice servers from local config.\r\n peerConnectionConfig.iceServers = instance.connectionConfig.iceServers;\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n } else {\r\n // last priority using default ice servers.\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n }\r\n\r\n console.info(logHeader, 'Create Peer Connection With Config', peerConnectionConfig);\r\n\r\n const peerConnection = new RTCPeerConnection(peerConnectionConfig);\r\n\r\n instance.peerConnection = peerConnection;\r\n\r\n if (!instance.inputStream) {\r\n console.error('No input stream in OvenLiveKit');\r\n errorHandler('No input stream in OvenLiveKit');\r\n return;\r\n }\r\n\r\n for (const track of instance.inputStream.getTracks()) {\r\n console.log(logHeader, 'Adding track: ', track);\r\n\r\n const transceiverConfig = {\r\n direction: 'sendonly'\r\n };\r\n\r\n // Add simulcast layers if configured\r\n const simulcastConfig = instance.connectionConfig.simulcast;\r\n\r\n if (track.kind === 'video' && simulcastConfig && simulcastConfig.length > 0) {\r\n\r\n transceiverConfig.sendEncodings = [];\r\n\r\n for (let i = 0; i < simulcastConfig.length; i++) {\r\n\r\n const layer = {\r\n rid: i,\r\n active: true,\r\n ...simulcastConfig[i]\r\n };\r\n\r\n console.log(logHeader, `Adding simulcast layer to: ${track.kind}`, layer);\r\n\r\n transceiverConfig.sendEncodings.push(layer);\r\n }\r\n }\r\n\r\n peerConnection.addTransceiver(track, transceiverConfig);\r\n }\r\n\r\n peerConnection.oniceconnectionstatechange = function (e) {\r\n\r\n let state = peerConnection.iceConnectionState;\r\n\r\n if (instance.callbacks.iceStateChange) {\r\n\r\n console.info(logHeader, 'ICE State', '[' + state + ']');\r\n instance.callbacks.iceStateChange(state);\r\n }\r\n\r\n if (state === 'connected') {\r\n\r\n if (instance.callbacks.connected) {\r\n instance.callbacks.connected(e);\r\n }\r\n }\r\n\r\n if (state === 'failed') {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.error(logHeader, 'Ice connection failed', e);\r\n instance.callbacks.errorHandler(e);\r\n }\r\n }\r\n\r\n if (state === 'disconnected' || state === 'closed') {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.error(logHeader, 'Ice connection disconnected or closed', e);\r\n instance.callbacks.connectionClosed('ice', e);\r\n }\r\n }\r\n }\r\n\r\n const offer = await peerConnection.createOffer();\r\n console.log(logHeader, 'Offer SDP: ', offer.sdp);\r\n\r\n if (instance.connectionConfig.maxVideoBitrate) {\r\n\r\n // if bandwidth limit is set. modify sdp from ome to limit acceptable bandwidth of ome\r\n offer.sdp = setBitrateLimit(offer.sdp, 'video', instance.connectionConfig.maxVideoBitrate);\r\n }\r\n\r\n if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {\r\n\r\n offer.sdp = appendFmtp(offer.sdp);\r\n }\r\n\r\n if (instance.connectionConfig.preferredVideoFormat) {\r\n offer.sdp = setPreferredVideoFormat(offer.sdp, instance.connectionConfig.preferredVideoFormat);\r\n } else {\r\n // default to H264\r\n offer.sdp = setPreferredVideoFormat(offer.sdp, 'H264');\r\n }\r\n\r\n const headers = {\r\n \"Content-Type\": \"application/sdp\"\r\n };\r\n\r\n if (instance.connectionConfig.httpHeaders) {\r\n Object.assign(headers, instance.connectionConfig.httpHeaders);\r\n }\r\n\r\n const fetched = await fetchWithRedirect(endpointUrl, {\r\n method: \"POST\",\r\n body: offer.sdp,\r\n headers\r\n });\r\n\r\n if (!fetched.ok) {\r\n console.error('Failed to fetch', fetched.status);\r\n errorHandler(`Failed to fetch ${fetched.status}`);\r\n closePeerConnection();\r\n return;\r\n }\r\n\r\n if (!fetched.headers.get(\"location\")) {\r\n console.error('No location header on answer response');\r\n errorHandler('No location header on answer response');\r\n return;\r\n }\r\n\r\n // update endpointUrl\r\n instance.endpointUrl = fetched.url;\r\n console.log(logHeader, 'Updated endpointUrl: ', instance.endpointUrl);\r\n\r\n const baseUrl = new URL(endpointUrl).origin;\r\n instance.resourceUrl = baseUrl + fetched.headers.get(\"location\");\r\n\r\n const answer = await fetched.text();\r\n console.log(logHeader, 'Answer SDP: ', answer);\r\n\r\n try {\r\n await peerConnection.setLocalDescription(offer);\r\n } catch (error) {\r\n console.error('peerConnection.setLocalDescription', error);\r\n errorHandler(error);\r\n }\r\n\r\n try {\r\n await peerConnection.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: answer\r\n });\r\n } catch (error) {\r\n console.error('peerConnection.setRemoteDescription', error);\r\n errorHandler(error);\r\n }\r\n }\r\n\r\n async function stopWhip() {\r\n\r\n if (!instance.peerConnection) {\r\n console.error('No connection to close');\r\n errorHandler('No connection to close');\r\n return;\r\n }\r\n\r\n closePeerConnection();\r\n\r\n if (instance.resourceUrl) {\r\n\r\n const headers = {\r\n };\r\n\r\n if (instance.connectionConfig.httpHeaders) {\r\n Object.assign(headers, instance.connectionConfig.httpHeaders);\r\n }\r\n\r\n await fetchWithRedirect(instance.resourceUrl, {\r\n method: \"DELETE\",\r\n headers\r\n });\r\n }\r\n }\r\n\r\n function appendFmtp(sdp) {\r\n\r\n const fmtpStr = instance.connectionConfig.sdp.appendFmtp;\r\n\r\n const lines = sdp.split('\\r\\n');\r\n const payloads = [];\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n\r\n if (lines[i].indexOf('m=video') === 0) {\r\n\r\n let tokens = lines[i].split(' ')\r\n\r\n for (let j = 3; j < tokens.length; j++) {\r\n\r\n payloads.push(tokens[j]);\r\n }\r\n\r\n break;\r\n }\r\n }\r\n\r\n for (let i = 0; i < payloads.length; i++) {\r\n\r\n let fmtpLineFound = false;\r\n\r\n for (let j = 0; j < lines.length; j++) {\r\n\r\n if (lines[j].indexOf('a=fmtp:' + payloads[i]) === 0) {\r\n fmtpLineFound = true;\r\n lines[j] += ';' + fmtpStr;\r\n }\r\n }\r\n\r\n if (!fmtpLineFound) {\r\n\r\n for (let j = 0; j < lines.length; j++) {\r\n\r\n if (lines[j].indexOf('a=rtpmap:' + payloads[i]) === 0) {\r\n\r\n lines[j] += '\\r\\na=fmtp:' + payloads[i] + ' ' + fmtpStr;\r\n }\r\n }\r\n }\r\n }\r\n\r\n return lines.join('\\r\\n')\r\n }\r\n\r\n function appendOrientation(sdp) {\r\n\r\n const lines = sdp.split('\\r\\n');\r\n const payloads = [];\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n\r\n if (lines[i].indexOf('m=video') === 0) {\r\n\r\n let tokens = lines[i].split(' ')\r\n\r\n for (let j = 3; j < tokens.length; j++) {\r\n\r\n payloads.push(tokens[j]);\r\n }\r\n\r\n break;\r\n }\r\n }\r\n\r\n for (let i = 0; i < payloads.length; i++) {\r\n\r\n for (let j = 0; j < lines.length; j++) {\r\n\r\n if (lines[j].indexOf('a=rtpmap:' + payloads[i]) === 0) {\r\n\r\n lines[j] += '\\r\\na=extmap:' + payloads[i] + ' urn:3gpp:video-orientation';\r\n }\r\n }\r\n }\r\n\r\n return lines.join('\\r\\n')\r\n }\r\n\r\n function createPeerConnection(id, peerId, offer, candidates, iceServers) {\r\n\r\n let peerConnectionConfig = {};\r\n\r\n if (instance.connectionConfig.iceServers) {\r\n\r\n // first priority using ice servers from local config.\r\n peerConnectionConfig.iceServers = instance.connectionConfig.iceServers;\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n } else if (iceServers) {\r\n\r\n // second priority using ice servers from ome and force using TCP\r\n peerConnectionConfig.iceServers = [];\r\n\r\n for (let i = 0; i < iceServers.length; i++) {\r\n\r\n let iceServer = iceServers[i];\r\n\r\n let regIceServer = {};\r\n\r\n regIceServer.urls = iceServer.urls;\r\n\r\n let hasWebSocketUrl = false;\r\n let webSocketUrl = generateDomainFromUrl(instance.endpointUrl);\r\n\r\n for (let j = 0; j < regIceServer.urls.length; j++) {\r\n\r\n let serverUrl = regIceServer.urls[j];\r\n\r\n if (serverUrl.indexOf(webSocketUrl) > -1) {\r\n hasWebSocketUrl = true;\r\n break;\r\n }\r\n }\r\n\r\n if (!hasWebSocketUrl) {\r\n\r\n if (regIceServer.urls.length > 0) {\r\n\r\n let cloneIceServer = regIceServer.urls[0];\r\n let ip = findIp(cloneIceServer);\r\n\r\n if (webSocketUrl && ip) {\r\n regIceServer.urls.push(cloneIceServer.replace(ip, webSocketUrl));\r\n }\r\n }\r\n }\r\n\r\n regIceServer.username = iceServer.user_name;\r\n regIceServer.credential = iceServer.credential;\r\n\r\n peerConnectionConfig.iceServers.push(regIceServer);\r\n }\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n } else {\r\n peerConnectionConfig.iceTransportPolicy = 'relay';\r\n }\r\n } else {\r\n // last priority using default ice servers.\r\n\r\n if (instance.connectionConfig.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.connectionConfig.iceTransportPolicy;\r\n }\r\n }\r\n\r\n let advancedSetting = {\r\n optional: [\r\n {\r\n googHighStartBitrate: {\r\n exact: !0\r\n }\r\n },\r\n {\r\n googPayloadPadding: {\r\n exact: !0\r\n }\r\n },\r\n {\r\n googScreencastMinBitrate: {\r\n exact: 500\r\n }\r\n },\r\n {\r\n enableDscp: {\r\n exact: true\r\n }\r\n }\r\n ]\r\n };\r\n\r\n console.info(logHeader, 'Create Peer Connection With Config', peerConnectionConfig);\r\n\r\n let peerConnection = new RTCPeerConnection(peerConnectionConfig);\r\n\r\n instance.peerConnection = peerConnection;\r\n\r\n // set local stream\r\n instance.inputStream.getTracks().forEach(function (track) {\r\n\r\n console.info(logHeader, 'Add Track To Peer Connection', track);\r\n peerConnection.addTrack(track, instance.inputStream);\r\n });\r\n\r\n if (instance.connectionConfig.maxVideoBitrate) {\r\n\r\n // if bandwith limit is set. modify sdp from ome to limit acceptable bandwidth of ome\r\n offer.sdp = setBitrateLimit(offer.sdp, 'video', instance.connectionConfig.maxVideoBitrate);\r\n }\r\n\r\n if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {\r\n\r\n offer.sdp = appendFmtp(offer.sdp);\r\n }\r\n\r\n if (instance.connectionConfig.preferredVideoFormat) {\r\n offer.sdp = setPreferredVideoFormat(offer.sdp, instance.connectionConfig.preferredVideoFormat)\r\n }\r\n\r\n\r\n // offer.sdp = appendOrientation(offer.sdp);\r\n console.info(logHeader, 'Modified offer sdp\\n\\n' + offer.sdp);\r\n\r\n peerConnection.setRemoteDescription(new RTCSessionDescription(offer))\r\n .then(function () {\r\n\r\n peerConnection.createAnswer()\r\n .then(function (answer) {\r\n\r\n if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {\r\n\r\n answer.sdp = appendFmtp(answer.sdp);\r\n }\r\n\r\n if (instance.connectionConfig.preferredVideoFormat) {\r\n answer.sdp = setPreferredVideoFormat(answer.sdp, instance.connectionConfig.preferredVideoFormat)\r\n }\r\n\r\n console.info(logHeader, 'Modified answer sdp\\n\\n' + answer.sdp);\r\n\r\n peerConnection.setLocalDescription(answer)\r\n .then(function () {\r\n\r\n sendMessage(instance.webSocket, {\r\n id: id,\r\n peer_id: peerId,\r\n command: 'answer',\r\n sdp: answer\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.setLocalDescription', error);\r\n errorHandler(error);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.createAnswer', error);\r\n errorHandler(error);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.setRemoteDescription', error);\r\n errorHandler(error);\r\n });\r\n\r\n if (candidates) {\r\n\r\n addIceCandidate(peerConnection, candidates);\r\n }\r\n\r\n peerConnection.onicecandidate = function (e) {\r\n\r\n if (e.candidate && e.candidate.candidate) {\r\n\r\n sendMessage(instance.webSocket, {\r\n id: id,\r\n peer_id: peerId,\r\n command: 'candidate',\r\n candidates: [e.candidate]\r\n });\r\n }\r\n };\r\n\r\n peerConnection.oniceconnectionstatechange = function (e) {\r\n\r\n let state = peerConnection.iceConnectionState;\r\n\r\n if (instance.callbacks.iceStateChange) {\r\n\r\n console.info(logHeader, 'ICE State', '[' + state + ']');\r\n instance.callbacks.iceStateChange(state);\r\n }\r\n\r\n if (state === 'connected') {\r\n\r\n if (instance.callbacks.connected) {\r\n instance.callbacks.connected(e);\r\n }\r\n }\r\n\r\n if (state === 'failed' || state === 'disconnected' || state === 'closed') {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.error(logHeader, 'Ice connection closed', e);\r\n instance.callbacks.connectionClosed('ice', e);\r\n }\r\n }\r\n }\r\n }\r\n\r\n function addIceCandidate(peerConnection, candidates) {\r\n\r\n for (let i = 0; i < candidates.length; i++) {\r\n\r\n if (candidates[i] && candidates[i].candidate) {\r\n\r\n let basicCandidate = candidates[i];\r\n\r\n peerConnection.addIceCandidate(new RTCIceCandidate(basicCandidate))\r\n .then(function () {\r\n\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.addIceCandidate', error);\r\n errorHandler(error);\r\n });\r\n }\r\n }\r\n }\r\n\r\n function closePeerConnection() {\r\n if (instance.peerConnection) {\r\n\r\n // remove tracks from peer connection\r\n instance.peerConnection.getSenders().forEach(function (sender) {\r\n instance.peerConnection.removeTrack(sender);\r\n });\r\n\r\n instance.peerConnection.close();\r\n instance.peerConnection = null;\r\n delete instance.peerConnection;\r\n }\r\n }\r\n\r\n function closeWebSocket() {\r\n\r\n if (instance.webSocket) {\r\n\r\n instance.webSocket.close();\r\n instance.webSocket = null;\r\n delete instance.webSocket;\r\n }\r\n }\r\n\r\n function closeInputStream() {\r\n // release video, audio stream\r\n if (instance.inputStream) {\r\n\r\n instance.inputStream.getTracks().forEach(track => {\r\n\r\n track.stop();\r\n instance.inputStream.removeTrack(track);\r\n });\r\n\r\n if (instance.videoElement) {\r\n instance.videoElement.srcObject = null;\r\n }\r\n\r\n instance.inputStream = null;\r\n delete instance.inputStream;\r\n }\r\n }\r\n\r\n // instance methods\r\n instance.attachMedia = function (videoElement) {\r\n\r\n instance.videoElement = videoElement;\r\n };\r\n\r\n instance.getUserMedia = function (constraints) {\r\n\r\n return getUserMedia(constraints);\r\n };\r\n\r\n instance.getDisplayMedia = function (constraints) {\r\n\r\n return getDisplayMedia(constraints);\r\n };\r\n\r\n instance.setMediaStream = function (stream) {\r\n\r\n return setMediaStream(stream);\r\n };\r\n\r\n instance.startStreaming = function (endpointUrl, connectionConfig) {\r\n\r\n console.info(logEventHeader, `Start Streaming to ${endpointUrl} with connectionConfig`, connectionConfig);\r\n\r\n if (!endpointUrl) {\r\n console.error('endpointUrl is required');\r\n errorHandler('endpointUrl is required');\r\n return;\r\n }\r\n\r\n instance.endpointUrl = endpointUrl;\r\n\r\n if (connectionConfig) {\r\n instance.connectionConfig = connectionConfig;\r\n }\r\n\r\n try {\r\n\r\n const protocol = new URL(endpointUrl).protocol;\r\n\r\n if (protocol === 'wss:' || protocol === 'ws:') {\r\n\r\n instance.streamingMode = 'webrtc';\r\n initWebSocket(endpointUrl);\r\n } else if (protocol === 'https:' || protocol === 'http:') {\r\n\r\n instance.streamingMode = 'whip';\r\n startWhip(endpointUrl);\r\n } else {\r\n console.error('Invalid protocol', error);\r\n errorHandler(error);\r\n }\r\n\r\n } catch (error) {\r\n console.error('Cannot parse connection URL', error);\r\n errorHandler(error);\r\n }\r\n };\r\n\r\n instance.stopStreaming = async function () {\r\n\r\n if (instance.streamingMode === 'webrtc') {\r\n\r\n instance.webSocketClosedByUser = true;\r\n\r\n closeWebSocket();\r\n closePeerConnection();\r\n } else if (instance.streamingMode === 'whip') {\r\n\r\n await stopWhip();\r\n }\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.log(logHeader, 'Connection closed by user');\r\n instance.callbacks.connectionClosed('user', 'Connection closed by user');\r\n }\r\n };\r\n\r\n instance.remove = function () {\r\n\r\n if (instance.streamingMode === 'webrtc') {\r\n\r\n instance.webSocketClosedByUser = true;\r\n\r\n closeWebSocket();\r\n closePeerConnection();\r\n } else if (instance.streamingMode === 'whip') {\r\n stopWhip();\r\n }\r\n\r\n closeInputStream();\r\n\r\n console.info(logEventHeader, 'Removed');\r\n\r\n };\r\n}\r\n\r\nOvenLiveKit.getVersion = function () {\r\n return version;\r\n}\r\n\r\n// static methods\r\nOvenLiveKit.create = function (options) {\r\n\r\n console.info(logEventHeader, 'Create WebRTC Input ' + version);\r\n\r\n let instance = {};\r\n\r\n instance.webSocketClosedByUser = false;\r\n\r\n initConfig(instance, options);\r\n addMethod(instance);\r\n\r\n return instance;\r\n};\r\n\r\nOvenLiveKit.getDevices = async function () {\r\n\r\n try {\r\n // First check both audio and video sources are available.\r\n await getStreamForDeviceCheck('both');\r\n } catch (e) {\r\n\r\n console.warn(logHeader, 'Can not find Video and Audio devices', e);\r\n\r\n let videoFound = null;\r\n let audioFound = null;\r\n\r\n try {\r\n videoFound = await getStreamForDeviceCheck('video');\r\n } catch (e) {\r\n console.warn(logHeader, 'Can not find Video devices', e);\r\n }\r\n\r\n try {\r\n audioFound = await getStreamForDeviceCheck('audio');\r\n } catch (e) {\r\n console.warn(logHeader, 'Can not find Audio devices', e);\r\n }\r\n\r\n if (!videoFound && !audioFound) {\r\n throw new Error('No input devices were found.');\r\n }\r\n }\r\n\r\n const deviceInfos = await getDevices();\r\n return gotDevices(deviceInfos)\r\n};\r\n\r\nexport default OvenLiveKit;"],"names":["root","factory","exports","module","define","amd","self","__webpack_require__","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","OvenLiveKit","version","logHeader","logEventHeader","sendMessage","webSocket","message","send","JSON","stringify","generateDomainFromUrl","url","match","result","findIp","string","RegExp","setPreferredVideoFormat","sdp","formatName","formatNumber","format","lines","split","i","length","toLowerCase","indexOf","getFormatNumber","newLines","line","others","slice","formats","sort","x","y","push","concat","join","async","getStreamForDeviceCheck","type","constraints","audio","video","navigator","mediaDevices","getUserMedia","addMethod","instance","errorHandler","error","callbacks","fetchWithRedirect","options","fetched","fetch","redirected","setBitrateLimit","media","bitrate","stopWhip","peerConnection","console","closePeerConnection","resourceUrl","headers","connectionConfig","httpHeaders","assign","method","appendFmtp","fmtpStr","payloads","tokens","j","fmtpLineFound","getSenders","forEach","sender","removeTrack","close","closeWebSocket","attachMedia","videoElement","deviceId","undefined","info","then","stream","inputStream","elem","srcObject","onloadedmetadata","e","play","Promise","resolve","catch","reject","getDisplayMedia","setMediaStream","MediaStream","Error","startStreaming","endpointUrl","protocol","URL","streamingMode","WebSocket","onopen","command","onmessage","parse","data","id","peerId","offer","candidates","iceServers","peerConnectionConfig","iceTransportPolicy","iceServer","regIceServer","urls","hasWebSocketUrl","webSocketUrl","cloneIceServer","ip","replace","username","user_name","credential","RTCPeerConnection","getTracks","track","addTrack","maxVideoBitrate","preferredVideoFormat","setRemoteDescription","RTCSessionDescription","createAnswer","answer","setLocalDescription","peer_id","candidate","basicCandidate","addIceCandidate","RTCIceCandidate","onicecandidate","oniceconnectionstatechange","state","iceConnectionState","iceStateChange","connected","connectionClosed","createPeerConnection","ice_servers","onerror","onclose","webSocketClosedByUser","initWebSocket","bundlePolicy","log","transceiverConfig","direction","simulcastConfig","simulcast","kind","sendEncodings","layer","rid","active","addTransceiver","createOffer","body","ok","status","baseUrl","origin","text","startWhip","stopStreaming","remove","stop","getVersion","create","initConfig","getDevices","warn","videoFound","audioFound","deviceInfos","devices","deviceInfo","label","audioinput","audiooutput","videoinput","other","gotDevices","enumerateDevices"],"sourceRoot":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ovenlivekit",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "OvenLiveKit for Web is an open source JavaScript SDK suite for live streaming from web browsers to OvenMediaEngine.",
5
5
  "main": "dist/OvenLiveKit.min.js",
6
6
  "scripts": {
@@ -1,6 +1,6 @@
1
1
  const OvenLiveKit = {};
2
2
 
3
- const version = '1.3.0';
3
+ const version = '1.4.0';
4
4
  const logHeader = 'OvenLiveKit.js :';
5
5
  const logEventHeader = 'OvenLiveKit.js ====';
6
6
 
@@ -491,9 +491,34 @@ function addMethod(instance) {
491
491
  }
492
492
 
493
493
  for (const track of instance.inputStream.getTracks()) {
494
- //You could add simulcast too here
495
494
  console.log(logHeader, 'Adding track: ', track);
496
- peerConnection.addTransceiver(track, { 'direction': 'sendonly' });
495
+
496
+ const transceiverConfig = {
497
+ direction: 'sendonly'
498
+ };
499
+
500
+ // Add simulcast layers if configured
501
+ const simulcastConfig = instance.connectionConfig.simulcast;
502
+
503
+ if (track.kind === 'video' && simulcastConfig && simulcastConfig.length > 0) {
504
+
505
+ transceiverConfig.sendEncodings = [];
506
+
507
+ for (let i = 0; i < simulcastConfig.length; i++) {
508
+
509
+ const layer = {
510
+ rid: i,
511
+ active: true,
512
+ ...simulcastConfig[i]
513
+ };
514
+
515
+ console.log(logHeader, `Adding simulcast layer to: ${track.kind}`, layer);
516
+
517
+ transceiverConfig.sendEncodings.push(layer);
518
+ }
519
+ }
520
+
521
+ peerConnection.addTransceiver(track, transceiverConfig);
497
522
  }
498
523
 
499
524
  peerConnection.oniceconnectionstatechange = function (e) {
@@ -535,7 +560,7 @@ function addMethod(instance) {
535
560
 
536
561
  if (instance.connectionConfig.maxVideoBitrate) {
537
562
 
538
- // if bandwith limit is set. modify sdp from ome to limit acceptable bandwidth of ome
563
+ // if bandwidth limit is set. modify sdp from ome to limit acceptable bandwidth of ome
539
564
  offer.sdp = setBitrateLimit(offer.sdp, 'video', instance.connectionConfig.maxVideoBitrate);
540
565
  }
541
566
 
@@ -545,7 +570,10 @@ function addMethod(instance) {
545
570
  }
546
571
 
547
572
  if (instance.connectionConfig.preferredVideoFormat) {
548
- offer.sdp = setPreferredVideoFormat(offer.sdp, instance.connectionConfig.preferredVideoFormat)
573
+ offer.sdp = setPreferredVideoFormat(offer.sdp, instance.connectionConfig.preferredVideoFormat);
574
+ } else {
575
+ // default to H264
576
+ offer.sdp = setPreferredVideoFormat(offer.sdp, 'H264');
549
577
  }
550
578
 
551
579
  const headers = {