ovenlivekit 1.0.2 → 1.0.4

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.
@@ -0,0 +1,2 @@
1
+ github: airensoft
2
+ open_collective: ovenmediaengine
package/README.md CHANGED
@@ -40,7 +40,7 @@ import OvenLiveKit from 'ovenlivekit'
40
40
  This is the simplest example of sending a device media stream such as a webcam to OvenMediaEngine's WebRTC Provider.
41
41
  ```JavaScript
42
42
  // Initialize OvenLiveKit
43
- let ovenLivekit = OvenLiveKit.create();
43
+ const ovenLivekit = OvenLiveKit.create();
44
44
 
45
45
  // Get media stream from user device
46
46
  ovenLivekit.getUserMedia().then(function () {
@@ -104,7 +104,7 @@ var config = {
104
104
  }
105
105
 
106
106
  // Initialize ovenLivekit instance
107
- let ovenLivekit = OvenLiveKit.create(config);
107
+ const ovenLivekit = OvenLiveKit.create(config);
108
108
 
109
109
  // Release all resources and destroy the instance
110
110
  ovenLivekit.remove();
@@ -219,7 +219,7 @@ OvenLiveKit also provides APIs to control a media stream from a user device.
219
219
  ```
220
220
  ```JavaScript
221
221
  // Create instance
222
- let ovenLivekit = OvenLiveKit.create();
222
+ const ovenLivekit = OvenLiveKit.create();
223
223
 
224
224
  // Attaching video element for playing device stream
225
225
  ovenLivekit.attachMedia(document.getElementById('myVideo'));
@@ -254,7 +254,7 @@ ovenLivekit.getUserMedia(constraints).then(function (stream) {
254
254
  Congrats on getting the media stream from the user device and then ready to stream into OvenMediaEngine.
255
255
  ```JavaScript
256
256
  // Create instance
257
- let ovenLivekit = OvenLiveKit.create();
257
+ const ovenLivekit = OvenLiveKit.create();
258
258
 
259
259
  ovenLivekit.getUserMedia().then(function () {
260
260
 
@@ -293,6 +293,9 @@ ovenLivekit.getUserMedia().then(function () {
293
293
  - String: String you want to append to a=fmtp of SDP.
294
294
  - If set video format is appended to the a=fmtp sections of SDP.
295
295
 
296
+ #### `instance.stopStreaming()`
297
+ - Close peer connection and websocket associated with OvenMediaEngine.
298
+
296
299
  ## For more information
297
300
  * [WebRTC Input in OvenPlayer Demo](https://demo.ovenplayer.com/demo_input.html)
298
301
  * Test Player based on OvenPlayer.
@@ -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 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 checkIOSVersion() {\r\n var agent = window.navigator.userAgent,\r\n start = agent.indexOf('OS ');\r\n if ((agent.indexOf('iPhone') > -1 || agent.indexOf('iPad') > -1) && start > -1) {\r\n return window.Number(agent.substr(start + 3, 3).replace('_', '.'));\r\n }\r\n return 0;\r\n}\r\n\r\nfunction getFormatNumber(sdp, format) {\r\n\r\n const lines = sdp.split('\\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') > -1 && lines[i].indexOf(format.toLowerCase()) > -1) {\r\n // parsing \"a=rtpmap:100 H264/90000\" line\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 removeFormat(sdp, formatNumber) {\r\n let newLines = [];\r\n let lines = sdp.split('\\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('\\n')\r\n}\r\n\r\nasync function getStreamForDeviceCheck() {\r\n\r\n // High resolution video constraints makes browser to get maximum resolution of video device.\r\n const constraints = {\r\n audio: { deviceId: undefined },\r\n video: { deviceId: undefined, width: 1920, height: 1080 }\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\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 instance.stream = null;\r\n instance.webSocket = null;\r\n instance.peerConnection = null;\r\n instance.connectionConfig = {};\r\n\r\n instance.status = 'creating';\r\n\r\n instance.videoElement = null;\r\n instance.connectionUrl = 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\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 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, 'Requested Constraint To Input Devices', 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.stream = 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, 'Requested Constraint To Display', 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.stream = 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 // From https://webrtchacks.com/limit-webrtc-bandwidth-sdp/\r\n function setBitrateLimit(sdp, media, bitrate) {\r\n\r\n let lines = sdp.split('\\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('\\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('\\n')\r\n }\r\n\r\n function initWebSocket(connectionUrl) {\r\n\r\n if (!connectionUrl) {\r\n errorHandler('connectionUrl is required');\r\n return;\r\n }\r\n\r\n instance.connectionUrl = connectionUrl;\r\n\r\n let webSocket = null;\r\n\r\n try {\r\n\r\n webSocket = new WebSocket(connectionUrl);\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.removing) {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n\r\n instance.callbacks.connectionClosed('websocket', e);\r\n }\r\n }\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('\\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].replace('\\r', ''));\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] += '\\na=fmtp:' + payloads[i] + ' ' + fmtpStr;\r\n }\r\n }\r\n }\r\n }\r\n\r\n return lines.join('\\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.connectionUrl);\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 peerConnectionConfig.iceTransportPolicy = 'relay';\r\n } else {\r\n // last priority using default ice servers.\r\n\r\n if (instance.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.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.stream.getTracks().forEach(function (track) {\r\n\r\n console.info(logHeader, 'Add Track To Peer Connection', track);\r\n peerConnection.addTrack(track, instance.stream);\r\n });\r\n\r\n\r\n if (checkIOSVersion() >= 15) {\r\n const formatNumber = getFormatNumber(offer.sdp, 'H264');\r\n\r\n if (formatNumber > 0) {\r\n offer.sdp = removeFormat(offer.sdp, formatNumber);\r\n }\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 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 (checkIOSVersion() >= 15) {\r\n\r\n const formatNumber = getFormatNumber(answer.sdp, 'H264');\r\n\r\n if (formatNumber > 0) {\r\n\r\n answer.sdp = removeFormat(answer.sdp, formatNumber);\r\n }\r\n }\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 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 console.info(logHeader, 'Candidate Sent', '\\n', e.candidate.candidate, '\\n', e);\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\r\n console.info(logHeader, 'Iceconnection Connected', e);\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\r\n console.error(logHeader, 'Iceconnection 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 // 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.startStreaming = function (connectionUrl, connectionConfig) {\r\n\r\n console.info(logEventHeader, 'Start Streaming');\r\n\r\n if (connectionConfig) {\r\n\r\n instance.connectionConfig = connectionConfig;\r\n }\r\n\r\n initWebSocket(connectionUrl);\r\n };\r\n\r\n instance.remove = function () {\r\n\r\n instance.removing = true;\r\n\r\n // first release peer connection with ome\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 // release video, audio stream\r\n if (instance.stream) {\r\n\r\n instance.stream.getTracks().forEach(track => {\r\n\r\n track.stop();\r\n instance.stream.removeTrack(track);\r\n });\r\n\r\n if (instance.videoElement) {\r\n instance.videoElement.srcObject = null;\r\n }\r\n\r\n instance.stream = null;\r\n delete instance.stream;\r\n }\r\n\r\n // release websocket\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 instance.status = 'removed';\r\n\r\n console.info(logEventHeader, 'Removed');\r\n\r\n };\r\n}\r\n\r\n// static methods\r\nOvenLiveKit.create = function (options) {\r\n\r\n console.info(logEventHeader, 'Create WebRTC Input v1.0.2');\r\n\r\n let instance = {};\r\n\r\n instance.removing = 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 await getStreamForDeviceCheck();\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.0.4';\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') > -1 && lines[i].indexOf(format.toLowerCase()) > -1) {\r\n // parsing \"a=rtpmap:100 H264/90000\" line\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 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 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.connectionUrl = 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 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 // 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(connectionUrl) {\r\n\r\n if (!connectionUrl) {\r\n errorHandler('connectionUrl is required');\r\n return;\r\n }\r\n\r\n instance.connectionUrl = connectionUrl;\r\n\r\n let webSocket = null;\r\n\r\n try {\r\n\r\n webSocket = new WebSocket(connectionUrl);\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 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.connectionUrl);\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 peerConnectionConfig.iceTransportPolicy = 'relay';\r\n } else {\r\n // last priority using default ice servers.\r\n\r\n if (instance.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.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 // offer.sdp = appendOrientation(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 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, 'Iceconnection 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.startStreaming = function (connectionUrl, connectionConfig) {\r\n\r\n console.info(logEventHeader, 'Start Streaming');\r\n\r\n if (connectionConfig) {\r\n\r\n instance.connectionConfig = connectionConfig;\r\n }\r\n\r\n initWebSocket(connectionUrl);\r\n };\r\n\r\n instance.stopStreaming = function () {\r\n\r\n instance.webSocketClosedByUser = true;\r\n\r\n closeWebSocket();\r\n closePeerConnection();\r\n };\r\n\r\n instance.remove = function () {\r\n\r\n instance.webSocketClosedByUser = true;\r\n\r\n closeWebSocket();\r\n closePeerConnection();\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,(function(){return(()=>{"use strict";var e={d:(n,t)=>{for(var o in t)e.o(t,o)&&!e.o(n,o)&&Object.defineProperty(n,o,{enumerable:!0,get:t[o]})},o:(e,n)=>Object.prototype.hasOwnProperty.call(e,n)},n={};e.d(n,{default:()=>f});const t={},o="OvenLiveKit.js :",i="OvenLiveKit.js ====";function c(e,n){e&&e.send(JSON.stringify(n))}function r(e){let n,t="";return(n=e.match(/^(?:wss?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n\?\=]+)/im))&&(t=n[1]),t}function a(e){let n,t="";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")))&&(t=n[0]),t}function l(){var e=window.navigator.userAgent,n=e.indexOf("OS ");return(e.indexOf("iPhone")>-1||e.indexOf("iPad")>-1)&&n>-1?window.Number(e.substr(n+3,3).replace("_",".")):0}function s(e,n){const t=e.split("\n");let o=-1;for(let e=0;e<t.length-1;e++)if(t[e]=t[e].toLowerCase(),t[e].indexOf("a=rtpmap")>-1&&t[e].indexOf(n.toLowerCase())>-1){o=t[e].split(" ")[0].split(":")[1];break}return o}function d(e,n){let t=[],o=e.split("\n");for(let e=0;e<o.length;e++)0===o[e].indexOf("m=video")?t.push(o[e].replace(" "+n,"")):o[e].indexOf(n+"")>-1||t.push(o[e]);return t.join("\n")}function u(e){function n(n){e.callbacks.error&&e.callbacks.error(n)}function t(n){const t=e.connectionConfig.sdp.appendFmtp,o=n.split("\n"),i=[];for(let e=0;e<o.length;e++)if(0===o[e].indexOf("m=video")){let n=o[e].split(" ");for(let e=3;e<n.length;e++)i.push(n[e].replace("\r",""));break}for(let e=0;e<i.length;e++){let n=!1;for(let c=0;c<o.length;c++)0===o[c].indexOf("a=fmtp:"+i[e])&&(n=!0,o[c]+=";"+t);if(!n)for(let n=0;n<o.length;n++)0===o[n].indexOf("a=rtpmap:"+i[e])&&(o[n]+="\na=fmtp:"+i[e]+" "+t)}return o.join("\n")}e.attachMedia=function(n){e.videoElement=n},e.getUserMedia=function(t){return function(t){return t||(t={video:{deviceId:void 0},audio:{deviceId:void 0}}),console.info(o,"Requested Constraint To Input Devices",t),navigator.mediaDevices.getUserMedia(t).then((function(n){console.info(o,"Received Media Stream From Input Device",n),e.stream=n;let t=e.videoElement;return t&&(t.srcObject=n,t.onloadedmetadata=function(e){t.play()}),new Promise((function(e){e(n)}))})).catch((function(e){return console.error(o,"Can't Get Media Stream From Input Device",e),n(e),new Promise((function(n,t){t(e)}))}))}(t)},e.getDisplayMedia=function(t){return function(t){return t||(t={}),console.info(o,"Requested Constraint To Display",t),navigator.mediaDevices.getDisplayMedia(t).then((function(n){console.info(o,"Received Media Stream From Display",n),e.stream=n;let t=e.videoElement;return t&&(t.srcObject=n,t.onloadedmetadata=function(e){t.play()}),new Promise((function(e){e(n)}))})).catch((function(e){return console.error(o,"Can't Get Media Stream From Display",e),n(e),new Promise((function(n,t){t(e)}))}))}(t)},e.startStreaming=function(u,f){console.info(i,"Start Streaming"),f&&(e.connectionConfig=f),function(i){if(!i)return void n("connectionUrl is required");e.connectionUrl=i;let u=null;try{u=new WebSocket(i)}catch(e){n(e)}e.webSocket=u,u.onopen=function(){c(u,{command:"request_offer"})},u.onmessage=function(i){let u=JSON.parse(i.data);u.error&&(console.error("webSocket.onmessage",u.error),n(u.error)),"offer"===u.command&&function(i,u,f,p,m){let v={};if(e.connectionConfig.iceServers)v.iceServers=e.connectionConfig.iceServers,e.connectionConfig.iceTransportPolicy&&(v.iceTransportPolicy=e.connectionConfig.iceTransportPolicy);else if(m){v.iceServers=[];for(let n=0;n<m.length;n++){let t=m[n],o={};o.urls=t.urls;let i=!1,c=r(e.connectionUrl);for(let e=0;e<o.urls.length;e++)if(o.urls[e].indexOf(c)>-1){i=!0;break}if(!i&&o.urls.length>0){let e=o.urls[0],n=a(e);c&&n&&o.urls.push(e.replace(n,c))}o.username=t.user_name,o.credential=t.credential,v.iceServers.push(o)}v.iceTransportPolicy="relay"}else e.iceTransportPolicy&&(v.iceTransportPolicy=e.iceTransportPolicy);console.info(o,"Create Peer Connection With Config",v);let g=new RTCPeerConnection(v);if(e.peerConnection=g,e.stream.getTracks().forEach((function(n){console.info(o,"Add Track To Peer Connection",n),g.addTrack(n,e.stream)})),l()>=15){const e=s(f.sdp,"H264");e>0&&(f.sdp=d(f.sdp,e))}e.connectionConfig.maxVideoBitrate&&(f.sdp=function(e,n,t){let o=e.split("\n"),i=-1;for(let e=0;e<o.length;e++)if(0===o[e].indexOf("m=video")){i=e;break}if(-1===i)return e;for(i++;0===o[i].indexOf("i=")||0===o[i].indexOf("c=");)i++;if(0===o[i].indexOf("b"))return o[i]="b=AS:"+t,o.join("\n");let c=o.slice(0,i);return c.push("b=AS:"+t),c=c.concat(o.slice(i,o.length)),c.join("\n")}(f.sdp,0,e.connectionConfig.maxVideoBitrate)),e.connectionConfig.sdp&&e.connectionConfig.sdp.appendFmtp&&(f.sdp=t(f.sdp)),g.setRemoteDescription(new RTCSessionDescription(f)).then((function(){g.createAnswer().then((function(o){if(l()>=15){const e=s(o.sdp,"H264");e>0&&(o.sdp=d(o.sdp,e))}e.connectionConfig.sdp&&e.connectionConfig.sdp.appendFmtp&&(o.sdp=t(o.sdp)),g.setLocalDescription(o).then((function(){c(e.webSocket,{id:i,peer_id:u,command:"answer",sdp:o})})).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)})),p&&function(e,t){for(let o=0;o<t.length;o++)if(t[o]&&t[o].candidate){let i=t[o];e.addIceCandidate(new RTCIceCandidate(i)).then((function(){})).catch((function(e){console.error("peerConnection.addIceCandidate",e),n(e)}))}}(g,p),g.onicecandidate=function(n){n.candidate&&n.candidate.candidate&&(console.info(o,"Candidate Sent","\n",n.candidate.candidate,"\n",n),c(e.webSocket,{id:i,peer_id:u,command:"candidate",candidates:[n.candidate]}))},g.oniceconnectionstatechange=function(n){let t=g.iceConnectionState;e.callbacks.iceStateChange&&(console.info(o,"ICE State","["+t+"]"),e.callbacks.iceStateChange(t)),"connected"===t&&e.callbacks.connected&&(console.info(o,"Iceconnection Connected",n),e.callbacks.connected(n)),"failed"!==t&&"disconnected"!==t&&"closed"!==t||e.callbacks.connectionClosed&&(console.error(o,"Iceconnection Closed",n),e.callbacks.connectionClosed("ice",n))}}(u.id,u.peer_id,u.sdp,u.candidates,u.ice_servers)},u.onerror=function(e){console.error("webSocket.onerror",e),n(e)},u.onclose=function(n){e.removing||e.callbacks.connectionClosed&&e.callbacks.connectionClosed("websocket",n)}}(u)},e.remove=function(){e.removing=!0,e.peerConnection&&(e.peerConnection.getSenders().forEach((function(n){e.peerConnection.removeTrack(n)})),e.peerConnection.close(),e.peerConnection=null,delete e.peerConnection),e.stream&&(e.stream.getTracks().forEach((n=>{n.stop(),e.stream.removeTrack(n)})),e.videoElement&&(e.videoElement.srcObject=null),e.stream=null,delete e.stream),e.webSocket&&(e.webSocket.close(),e.webSocket=null,delete e.webSocket),e.status="removed",console.info(i,"Removed")}}t.create=function(e){console.info(i,"Create WebRTC Input v1.0.2");let n={removing:!1};return function(e,n){e.stream=null,e.webSocket=null,e.peerConnection=null,e.connectionConfig={},e.status="creating",e.videoElement=null,e.connectionUrl=null,n&&n.callbacks?e.callbacks=n.callbacks:e.callbacks={}}(n,e),u(n),n},t.getDevices=async function(){return await async function(){return await navigator.mediaDevices.getUserMedia({audio:{deviceId:void 0},video:{deviceId:void 0,width:1920,height:1080}})}(),function(e){let n={audioinput:[],audiooutput:[],videoinput:[],other:[]};for(let t=0;t!==e.length;++t){const o=e[t];let i={};i.deviceId=o.deviceId,"audioinput"===o.kind?(i.label=o.label||`microphone ${n.audioinput.length+1}`,n.audioinput.push(i)):"audiooutput"===o.kind?(i.label=o.label||`speaker ${n.audiooutput.length+1}`,n.audiooutput.push(i)):"videoinput"===o.kind?(i.label=o.label||`camera ${n.videoinput.length+1}`,n.videoinput.push(i)):(i.label=o.label||`other ${n.other.length+1}`,n.other.push(i))}return n}(await async function(){return await navigator.mediaDevices.enumerateDevices()}())};const f=t;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,(function(){return(()=>{"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:()=>d});const o={},t="OvenLiveKit.js :",i="OvenLiveKit.js ====";function c(e,n){e&&e.send(JSON.stringify(n))}function r(e){let n,o="";return(n=e.match(/^(?:wss?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n\?\=]+)/im))&&(o=n[1]),o}function a(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}async function l(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 s(e){function n(n){e.callbacks.error&&e.callbacks.error(n)}function o(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 l(){e.peerConnection&&(e.peerConnection.getSenders().forEach((function(n){e.peerConnection.removeTrack(n)})),e.peerConnection.close(),e.peerConnection=null,delete e.peerConnection)}function s(){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(t,"Request Stream To Input Devices With Constraints",o),navigator.mediaDevices.getUserMedia(o).then((function(n){console.info(t,"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(t,"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(t,"Request Stream To Display With Constraints",o),navigator.mediaDevices.getDisplayMedia(o).then((function(n){console.info(t,"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(t,"Can't Get Media Stream From Display",e),n(e),new Promise((function(n,o){o(e)}))}))}(o)},e.startStreaming=function(l,s){console.info(i,"Start Streaming"),s&&(e.connectionConfig=s),function(i){if(!i)return void n("connectionUrl is required");e.connectionUrl=i;let l=null;try{l=new WebSocket(i)}catch(e){n(e)}e.webSocket=l,l.onopen=function(){c(l,{command:"request_offer"})},l.onmessage=function(i){let l=JSON.parse(i.data);l.error&&(console.error("webSocket.onmessage",l.error),n(l.error)),"offer"===l.command&&function(i,l,s,d,u){let f={};if(e.connectionConfig.iceServers)f.iceServers=e.connectionConfig.iceServers,e.connectionConfig.iceTransportPolicy&&(f.iceTransportPolicy=e.connectionConfig.iceTransportPolicy);else if(u){f.iceServers=[];for(let n=0;n<u.length;n++){let o=u[n],t={};t.urls=o.urls;let i=!1,c=r(e.connectionUrl);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=a(e);c&&n&&t.urls.push(e.replace(n,c))}t.username=o.user_name,t.credential=o.credential,f.iceServers.push(t)}f.iceTransportPolicy="relay"}else e.iceTransportPolicy&&(f.iceTransportPolicy=e.iceTransportPolicy);console.info(t,"Create Peer Connection With Config",f);let p=new RTCPeerConnection(f);e.peerConnection=p,e.inputStream.getTracks().forEach((function(n){console.info(t,"Add Track To Peer Connection",n),p.addTrack(n,e.inputStream)})),e.connectionConfig.maxVideoBitrate&&(s.sdp=function(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=video")){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")}(s.sdp,0,e.connectionConfig.maxVideoBitrate)),e.connectionConfig.sdp&&e.connectionConfig.sdp.appendFmtp&&(s.sdp=o(s.sdp)),p.setRemoteDescription(new RTCSessionDescription(s)).then((function(){p.createAnswer().then((function(t){e.connectionConfig.sdp&&e.connectionConfig.sdp.appendFmtp&&(t.sdp=o(t.sdp)),p.setLocalDescription(t).then((function(){c(e.webSocket,{id:i,peer_id:l,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)})),d&&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)}))}}(p,d),p.onicecandidate=function(n){n.candidate&&n.candidate.candidate&&c(e.webSocket,{id:i,peer_id:l,command:"candidate",candidates:[n.candidate]})},p.oniceconnectionstatechange=function(n){let o=p.iceConnectionState;e.callbacks.iceStateChange&&(console.info(t,"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(t,"Iceconnection Closed",n),e.callbacks.connectionClosed("ice",n))}}(l.id,l.peer_id,l.sdp,l.candidates,l.ice_servers)},l.onerror=function(e){console.error("webSocket.onerror",e),n(e)},l.onclose=function(n){e.webSocketClosedByUser||e.callbacks.connectionClosed&&e.callbacks.connectionClosed("websocket",n)}}(l)},e.stopStreaming=function(){e.webSocketClosedByUser=!0,s(),l()},e.remove=function(){e.webSocketClosedByUser=!0,s(),l(),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(i,"Removed")}}o.getVersion=function(){return"1.0.4"},o.create=function(e){console.info(i,"Create WebRTC Input 1.0.4");let n={webSocketClosedByUser:!1};return function(e,n){e.inputStream=null,e.webSocket=null,e.peerConnection=null,e.connectionConfig={},e.videoElement=null,e.connectionUrl=null,n&&n.callbacks?e.callbacks=n.callbacks:e.callbacks={}}(n,e),s(n),n},o.getDevices=async function(){try{await l("both")}catch(e){console.warn(t,"Can not find Video and Audio devices",e);let n=null,o=null;try{n=await l("video")}catch(e){console.warn(t,"Can not find Video devices",e)}try{o=await l("audio")}catch(e){console.warn(t,"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 d=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, function() {\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 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 checkIOSVersion() {\r\n var agent = window.navigator.userAgent,\r\n start = agent.indexOf('OS ');\r\n if ((agent.indexOf('iPhone') > -1 || agent.indexOf('iPad') > -1) && start > -1) {\r\n return window.Number(agent.substr(start + 3, 3).replace('_', '.'));\r\n }\r\n return 0;\r\n}\r\n\r\nfunction getFormatNumber(sdp, format) {\r\n\r\n const lines = sdp.split('\\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') > -1 && lines[i].indexOf(format.toLowerCase()) > -1) {\r\n // parsing \"a=rtpmap:100 H264/90000\" line\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 removeFormat(sdp, formatNumber) {\r\n let newLines = [];\r\n let lines = sdp.split('\\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('\\n')\r\n}\r\n\r\nasync function getStreamForDeviceCheck() {\r\n\r\n // High resolution video constraints makes browser to get maximum resolution of video device.\r\n const constraints = {\r\n audio: { deviceId: undefined },\r\n video: { deviceId: undefined, width: 1920, height: 1080 }\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\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 instance.stream = null;\r\n instance.webSocket = null;\r\n instance.peerConnection = null;\r\n instance.connectionConfig = {};\r\n\r\n instance.status = 'creating';\r\n\r\n instance.videoElement = null;\r\n instance.connectionUrl = 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\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 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, 'Requested Constraint To Input Devices', 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.stream = 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, 'Requested Constraint To Display', 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.stream = 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 // From https://webrtchacks.com/limit-webrtc-bandwidth-sdp/\r\n function setBitrateLimit(sdp, media, bitrate) {\r\n\r\n let lines = sdp.split('\\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('\\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('\\n')\r\n }\r\n\r\n function initWebSocket(connectionUrl) {\r\n\r\n if (!connectionUrl) {\r\n errorHandler('connectionUrl is required');\r\n return;\r\n }\r\n\r\n instance.connectionUrl = connectionUrl;\r\n\r\n let webSocket = null;\r\n\r\n try {\r\n\r\n webSocket = new WebSocket(connectionUrl);\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.removing) {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n\r\n instance.callbacks.connectionClosed('websocket', e);\r\n }\r\n }\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('\\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].replace('\\r', ''));\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] += '\\na=fmtp:' + payloads[i] + ' ' + fmtpStr;\r\n }\r\n }\r\n }\r\n }\r\n\r\n return lines.join('\\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.connectionUrl);\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 peerConnectionConfig.iceTransportPolicy = 'relay';\r\n } else {\r\n // last priority using default ice servers.\r\n\r\n if (instance.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.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.stream.getTracks().forEach(function (track) {\r\n\r\n console.info(logHeader, 'Add Track To Peer Connection', track);\r\n peerConnection.addTrack(track, instance.stream);\r\n });\r\n\r\n\r\n if (checkIOSVersion() >= 15) {\r\n const formatNumber = getFormatNumber(offer.sdp, 'H264');\r\n\r\n if (formatNumber > 0) {\r\n offer.sdp = removeFormat(offer.sdp, formatNumber);\r\n }\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 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 (checkIOSVersion() >= 15) {\r\n\r\n const formatNumber = getFormatNumber(answer.sdp, 'H264');\r\n\r\n if (formatNumber > 0) {\r\n\r\n answer.sdp = removeFormat(answer.sdp, formatNumber);\r\n }\r\n }\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 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 console.info(logHeader, 'Candidate Sent', '\\n', e.candidate.candidate, '\\n', e);\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\r\n console.info(logHeader, 'Iceconnection Connected', e);\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\r\n console.error(logHeader, 'Iceconnection 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 // 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.startStreaming = function (connectionUrl, connectionConfig) {\r\n\r\n console.info(logEventHeader, 'Start Streaming');\r\n\r\n if (connectionConfig) {\r\n\r\n instance.connectionConfig = connectionConfig;\r\n }\r\n\r\n initWebSocket(connectionUrl);\r\n };\r\n\r\n instance.remove = function () {\r\n\r\n instance.removing = true;\r\n\r\n // first release peer connection with ome\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 // release video, audio stream\r\n if (instance.stream) {\r\n\r\n instance.stream.getTracks().forEach(track => {\r\n\r\n track.stop();\r\n instance.stream.removeTrack(track);\r\n });\r\n\r\n if (instance.videoElement) {\r\n instance.videoElement.srcObject = null;\r\n }\r\n\r\n instance.stream = null;\r\n delete instance.stream;\r\n }\r\n\r\n // release websocket\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 instance.status = 'removed';\r\n\r\n console.info(logEventHeader, 'Removed');\r\n\r\n };\r\n}\r\n\r\n// static methods\r\nOvenLiveKit.create = function (options) {\r\n\r\n console.info(logEventHeader, 'Create WebRTC Input v1.0.2');\r\n\r\n let instance = {};\r\n\r\n instance.removing = 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 await getStreamForDeviceCheck();\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","logHeader","logEventHeader","sendMessage","webSocket","message","send","JSON","stringify","generateDomainFromUrl","url","match","result","findIp","string","RegExp","checkIOSVersion","agent","window","navigator","userAgent","start","indexOf","Number","substr","replace","getFormatNumber","sdp","format","lines","split","formatNumber","i","length","toLowerCase","removeFormat","newLines","push","join","addMethod","instance","errorHandler","error","callbacks","appendFmtp","fmtpStr","connectionConfig","payloads","tokens","j","fmtpLineFound","attachMedia","videoElement","getUserMedia","constraints","video","deviceId","undefined","audio","console","info","mediaDevices","then","stream","elem","srcObject","onloadedmetadata","e","play","Promise","resolve","catch","reject","getDisplayMedia","startStreaming","connectionUrl","WebSocket","onopen","command","onmessage","parse","data","id","peerId","offer","candidates","iceServers","peerConnectionConfig","iceTransportPolicy","iceServer","regIceServer","urls","hasWebSocketUrl","webSocketUrl","cloneIceServer","ip","username","user_name","credential","peerConnection","RTCPeerConnection","getTracks","forEach","track","addTrack","maxVideoBitrate","media","bitrate","line","slice","concat","setBitrateLimit","setRemoteDescription","RTCSessionDescription","createAnswer","answer","setLocalDescription","peer_id","candidate","basicCandidate","addIceCandidate","RTCIceCandidate","onicecandidate","oniceconnectionstatechange","state","iceConnectionState","iceStateChange","connected","connectionClosed","createPeerConnection","ice_servers","onerror","onclose","removing","initWebSocket","remove","getSenders","sender","removeTrack","close","stop","status","create","options","initConfig","getDevices","async","width","height","getStreamForDeviceCheck","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, function() {\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.0.4';\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') > -1 && lines[i].indexOf(format.toLowerCase()) > -1) {\r\n // parsing \"a=rtpmap:100 H264/90000\" line\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 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 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.connectionUrl = 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 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 // 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(connectionUrl) {\r\n\r\n if (!connectionUrl) {\r\n errorHandler('connectionUrl is required');\r\n return;\r\n }\r\n\r\n instance.connectionUrl = connectionUrl;\r\n\r\n let webSocket = null;\r\n\r\n try {\r\n\r\n webSocket = new WebSocket(connectionUrl);\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 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.connectionUrl);\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 peerConnectionConfig.iceTransportPolicy = 'relay';\r\n } else {\r\n // last priority using default ice servers.\r\n\r\n if (instance.iceTransportPolicy) {\r\n\r\n peerConnectionConfig.iceTransportPolicy = instance.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 // offer.sdp = appendOrientation(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 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, 'Iceconnection 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.startStreaming = function (connectionUrl, connectionConfig) {\r\n\r\n console.info(logEventHeader, 'Start Streaming');\r\n\r\n if (connectionConfig) {\r\n\r\n instance.connectionConfig = connectionConfig;\r\n }\r\n\r\n initWebSocket(connectionUrl);\r\n };\r\n\r\n instance.stopStreaming = function () {\r\n\r\n instance.webSocketClosedByUser = true;\r\n\r\n closeWebSocket();\r\n closePeerConnection();\r\n };\r\n\r\n instance.remove = function () {\r\n\r\n instance.webSocketClosedByUser = true;\r\n\r\n closeWebSocket();\r\n closePeerConnection();\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","logHeader","logEventHeader","sendMessage","webSocket","message","send","JSON","stringify","generateDomainFromUrl","url","match","result","findIp","string","RegExp","async","getStreamForDeviceCheck","type","constraints","audio","video","navigator","mediaDevices","getUserMedia","addMethod","instance","errorHandler","error","callbacks","appendFmtp","sdp","fmtpStr","connectionConfig","lines","split","payloads","i","length","indexOf","tokens","j","push","fmtpLineFound","join","closePeerConnection","peerConnection","getSenders","forEach","sender","removeTrack","close","closeWebSocket","attachMedia","videoElement","deviceId","undefined","console","info","then","stream","inputStream","elem","srcObject","onloadedmetadata","e","play","Promise","resolve","catch","reject","getDisplayMedia","startStreaming","connectionUrl","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","media","bitrate","line","newLines","slice","concat","setBitrateLimit","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","stopStreaming","remove","stop","getVersion","create","options","initConfig","getDevices","warn","videoFound","audioFound","Error","deviceInfos","devices","deviceInfo","kind","label","audioinput","audiooutput","videoinput","other","gotDevices","enumerateDevices"],"sourceRoot":""}
package/package.json CHANGED
@@ -1,32 +1,32 @@
1
- {
2
- "name": "ovenlivekit",
3
- "version": "1.0.2",
4
- "description": "OvenLiveKit for Web is an open source JavaScript SDK suite for live streaming from web browsers to OvenMediaEngine.",
5
- "main": "dist/OvenLiveKit.min.js",
6
- "scripts": {
7
- "start": "webpack serve --open --config webpack.dev.js",
8
- "watch": "webpack watch --config webpack.dev.js",
9
- "build": "webpack --config webpack.prod.js"
10
- },
11
- "repository": {
12
- "type": "git",
13
- "url": "git+https://github.com/AirenSoft/OvenLiveKit-Web.git"
14
- },
15
- "keywords": [
16
- "Sub-Second Latency Streaming",
17
- "WebRTC",
18
- "OvenMediaEngine",
19
- "HTML5"
20
- ],
21
- "author": "AirenSoft Co., Ltd.",
22
- "license": "MIT",
23
- "bugs": {
24
- "url": "https://github.com/AirenSoft/OvenLiveKit-Web/issues"
25
- },
26
- "homepage": "https://github.com/AirenSoft/OvenLiveKit-Web#readme",
27
- "devDependencies": {
28
- "webpack": "^5.48.0",
29
- "webpack-cli": "^4.7.2",
30
- "webpack-dev-server": "^3.11.2"
31
- }
1
+ {
2
+ "name": "ovenlivekit",
3
+ "version": "1.0.4",
4
+ "description": "OvenLiveKit for Web is an open source JavaScript SDK suite for live streaming from web browsers to OvenMediaEngine.",
5
+ "main": "dist/OvenLiveKit.min.js",
6
+ "scripts": {
7
+ "start": "webpack serve --open --config webpack.dev.js",
8
+ "watch": "webpack watch --config webpack.dev.js",
9
+ "build": "webpack --config webpack.prod.js"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/AirenSoft/OvenLiveKit-Web.git"
14
+ },
15
+ "keywords": [
16
+ "Sub-Second Latency Streaming",
17
+ "WebRTC",
18
+ "OvenMediaEngine",
19
+ "HTML5"
20
+ ],
21
+ "author": "AirenSoft Co., Ltd.",
22
+ "license": "MIT",
23
+ "bugs": {
24
+ "url": "https://github.com/AirenSoft/OvenLiveKit-Web/issues"
25
+ },
26
+ "homepage": "https://github.com/AirenSoft/OvenLiveKit-Web#readme",
27
+ "devDependencies": {
28
+ "webpack": "^5.48.0",
29
+ "webpack-cli": "^4.7.2",
30
+ "webpack-dev-server": "^3.11.2"
31
+ }
32
32
  }
@@ -1,5 +1,6 @@
1
1
  const OvenLiveKit = {};
2
2
 
3
+ const version = '1.0.4';
3
4
  const logHeader = 'OvenLiveKit.js :';
4
5
  const logEventHeader = 'OvenLiveKit.js ====';
5
6
 
@@ -33,18 +34,9 @@ function findIp(string) {
33
34
  return result;
34
35
  }
35
36
 
36
- function checkIOSVersion() {
37
- var agent = window.navigator.userAgent,
38
- start = agent.indexOf('OS ');
39
- if ((agent.indexOf('iPhone') > -1 || agent.indexOf('iPad') > -1) && start > -1) {
40
- return window.Number(agent.substr(start + 3, 3).replace('_', '.'));
41
- }
42
- return 0;
43
- }
44
-
45
37
  function getFormatNumber(sdp, format) {
46
38
 
47
- const lines = sdp.split('\n');
39
+ const lines = sdp.split('\r\n');
48
40
  let formatNumber = -1;
49
41
 
50
42
  for (let i = 0; i < lines.length - 1; i++) {
@@ -63,7 +55,7 @@ function getFormatNumber(sdp, format) {
63
55
 
64
56
  function removeFormat(sdp, formatNumber) {
65
57
  let newLines = [];
66
- let lines = sdp.split('\n');
58
+ let lines = sdp.split('\r\n');
67
59
 
68
60
  for (let i = 0; i < lines.length; i++) {
69
61
 
@@ -76,25 +68,30 @@ function removeFormat(sdp, formatNumber) {
76
68
  }
77
69
  }
78
70
 
79
- return newLines.join('\n')
71
+ return newLines.join('\r\n')
80
72
  }
81
73
 
82
- async function getStreamForDeviceCheck() {
74
+ async function getStreamForDeviceCheck(type) {
83
75
 
84
76
  // High resolution video constraints makes browser to get maximum resolution of video device.
85
77
  const constraints = {
86
- audio: { deviceId: undefined },
87
- video: { deviceId: undefined, width: 1920, height: 1080 }
88
78
  };
89
79
 
80
+ if (type === 'both') {
81
+ constraints.audio = true;
82
+ constraints.video = true;
83
+ } else if (type === 'audio') {
84
+ constraints.audio = true;
85
+ } else if (type === 'video') {
86
+ constraints.video = true;
87
+ }
88
+
90
89
  return await navigator.mediaDevices.getUserMedia(constraints);
91
90
  }
92
91
 
93
92
  async function getDevices() {
94
93
 
95
94
  return await navigator.mediaDevices.enumerateDevices();
96
-
97
-
98
95
  }
99
96
 
100
97
  function gotDevices(deviceInfos) {
@@ -138,13 +135,11 @@ function gotDevices(deviceInfos) {
138
135
 
139
136
  function initConfig(instance, options) {
140
137
 
141
- instance.stream = null;
138
+ instance.inputStream = null;
142
139
  instance.webSocket = null;
143
140
  instance.peerConnection = null;
144
141
  instance.connectionConfig = {};
145
142
 
146
- instance.status = 'creating';
147
-
148
143
  instance.videoElement = null;
149
144
  instance.connectionUrl = null;
150
145
 
@@ -154,7 +149,6 @@ function initConfig(instance, options) {
154
149
  } else {
155
150
  instance.callbacks = {};
156
151
  }
157
-
158
152
  }
159
153
 
160
154
  function addMethod(instance) {
@@ -181,14 +175,14 @@ function addMethod(instance) {
181
175
  };
182
176
  }
183
177
 
184
- console.info(logHeader, 'Requested Constraint To Input Devices', constraints);
178
+ console.info(logHeader, 'Request Stream To Input Devices With Constraints', constraints);
185
179
 
186
180
  return navigator.mediaDevices.getUserMedia(constraints)
187
181
  .then(function (stream) {
188
182
 
189
183
  console.info(logHeader, 'Received Media Stream From Input Device', stream);
190
184
 
191
- instance.stream = stream;
185
+ instance.inputStream = stream;
192
186
 
193
187
  let elem = instance.videoElement;
194
188
 
@@ -225,14 +219,14 @@ function addMethod(instance) {
225
219
  constraints = {};
226
220
  }
227
221
 
228
- console.info(logHeader, 'Requested Constraint To Display', constraints);
222
+ console.info(logHeader, 'Request Stream To Display With Constraints', constraints);
229
223
 
230
224
  return navigator.mediaDevices.getDisplayMedia(constraints)
231
225
  .then(function (stream) {
232
226
 
233
227
  console.info(logHeader, 'Received Media Stream From Display', stream);
234
228
 
235
- instance.stream = stream;
229
+ instance.inputStream = stream;
236
230
 
237
231
  let elem = instance.videoElement;
238
232
 
@@ -266,7 +260,7 @@ function addMethod(instance) {
266
260
  // From https://webrtchacks.com/limit-webrtc-bandwidth-sdp/
267
261
  function setBitrateLimit(sdp, media, bitrate) {
268
262
 
269
- let lines = sdp.split('\n');
263
+ let lines = sdp.split('\r\n');
270
264
  let line = -1;
271
265
 
272
266
  for (let i = 0; i < lines.length; i++) {
@@ -294,7 +288,7 @@ function addMethod(instance) {
294
288
 
295
289
  lines[line] = 'b=AS:' + bitrate;
296
290
 
297
- return lines.join('\n');
291
+ return lines.join('\r\n');
298
292
  }
299
293
 
300
294
  // Add a new b line
@@ -303,7 +297,7 @@ function addMethod(instance) {
303
297
  newLines.push('b=AS:' + bitrate)
304
298
  newLines = newLines.concat(lines.slice(line, lines.length))
305
299
 
306
- return newLines.join('\n')
300
+ return newLines.join('\r\n')
307
301
  }
308
302
 
309
303
  function initWebSocket(connectionUrl) {
@@ -366,10 +360,9 @@ function addMethod(instance) {
366
360
 
367
361
  webSocket.onclose = function (e) {
368
362
 
369
- if (!instance.removing) {
363
+ if (!instance.webSocketClosedByUser) {
370
364
 
371
365
  if (instance.callbacks.connectionClosed) {
372
-
373
366
  instance.callbacks.connectionClosed('websocket', e);
374
367
  }
375
368
  }
@@ -381,7 +374,7 @@ function addMethod(instance) {
381
374
 
382
375
  const fmtpStr = instance.connectionConfig.sdp.appendFmtp;
383
376
 
384
- const lines = sdp.split('\n');
377
+ const lines = sdp.split('\r\n');
385
378
  const payloads = [];
386
379
 
387
380
  for (let i = 0; i < lines.length; i++) {
@@ -392,7 +385,7 @@ function addMethod(instance) {
392
385
 
393
386
  for (let j = 3; j < tokens.length; j++) {
394
387
 
395
- payloads.push(tokens[j].replace('\r', ''));
388
+ payloads.push(tokens[j]);
396
389
  }
397
390
 
398
391
  break;
@@ -417,13 +410,47 @@ function addMethod(instance) {
417
410
 
418
411
  if (lines[j].indexOf('a=rtpmap:' + payloads[i]) === 0) {
419
412
 
420
- lines[j] += '\na=fmtp:' + payloads[i] + ' ' + fmtpStr;
413
+ lines[j] += '\r\na=fmtp:' + payloads[i] + ' ' + fmtpStr;
421
414
  }
422
415
  }
423
416
  }
424
417
  }
425
418
 
426
- return lines.join('\n')
419
+ return lines.join('\r\n')
420
+ }
421
+
422
+ function appendOrientation(sdp) {
423
+
424
+ const lines = sdp.split('\r\n');
425
+ const payloads = [];
426
+
427
+ for (let i = 0; i < lines.length; i++) {
428
+
429
+ if (lines[i].indexOf('m=video') === 0) {
430
+
431
+ let tokens = lines[i].split(' ')
432
+
433
+ for (let j = 3; j < tokens.length; j++) {
434
+
435
+ payloads.push(tokens[j]);
436
+ }
437
+
438
+ break;
439
+ }
440
+ }
441
+
442
+ for (let i = 0; i < payloads.length; i++) {
443
+
444
+ for (let j = 0; j < lines.length; j++) {
445
+
446
+ if (lines[j].indexOf('a=rtpmap:' + payloads[i]) === 0) {
447
+
448
+ lines[j] += '\r\na=extmap:' + payloads[i] + ' urn:3gpp:video-orientation';
449
+ }
450
+ }
451
+ }
452
+
453
+ return lines.join('\r\n')
427
454
  }
428
455
 
429
456
  function createPeerConnection(id, peerId, offer, candidates, iceServers) {
@@ -526,21 +553,12 @@ function addMethod(instance) {
526
553
  instance.peerConnection = peerConnection;
527
554
 
528
555
  // set local stream
529
- instance.stream.getTracks().forEach(function (track) {
556
+ instance.inputStream.getTracks().forEach(function (track) {
530
557
 
531
558
  console.info(logHeader, 'Add Track To Peer Connection', track);
532
- peerConnection.addTrack(track, instance.stream);
559
+ peerConnection.addTrack(track, instance.inputStream);
533
560
  });
534
561
 
535
-
536
- if (checkIOSVersion() >= 15) {
537
- const formatNumber = getFormatNumber(offer.sdp, 'H264');
538
-
539
- if (formatNumber > 0) {
540
- offer.sdp = removeFormat(offer.sdp, formatNumber);
541
- }
542
- }
543
-
544
562
  if (instance.connectionConfig.maxVideoBitrate) {
545
563
 
546
564
  // if bandwith limit is set. modify sdp from ome to limit acceptable bandwidth of ome
@@ -552,22 +570,14 @@ function addMethod(instance) {
552
570
  offer.sdp = appendFmtp(offer.sdp);
553
571
  }
554
572
 
573
+ // offer.sdp = appendOrientation(offer.sdp);
574
+
555
575
  peerConnection.setRemoteDescription(new RTCSessionDescription(offer))
556
576
  .then(function () {
557
577
 
558
578
  peerConnection.createAnswer()
559
579
  .then(function (answer) {
560
580
 
561
- if (checkIOSVersion() >= 15) {
562
-
563
- const formatNumber = getFormatNumber(answer.sdp, 'H264');
564
-
565
- if (formatNumber > 0) {
566
-
567
- answer.sdp = removeFormat(answer.sdp, formatNumber);
568
- }
569
- }
570
-
571
581
  if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {
572
582
 
573
583
  answer.sdp = appendFmtp(answer.sdp);
@@ -610,8 +620,6 @@ function addMethod(instance) {
610
620
 
611
621
  if (e.candidate && e.candidate.candidate) {
612
622
 
613
- console.info(logHeader, 'Candidate Sent', '\n', e.candidate.candidate, '\n', e);
614
-
615
623
  sendMessage(instance.webSocket, {
616
624
  id: id,
617
625
  peer_id: peerId,
@@ -634,8 +642,6 @@ function addMethod(instance) {
634
642
  if (state === 'connected') {
635
643
 
636
644
  if (instance.callbacks.connected) {
637
-
638
- console.info(logHeader, 'Iceconnection Connected', e);
639
645
  instance.callbacks.connected(e);
640
646
  }
641
647
  }
@@ -643,7 +649,6 @@ function addMethod(instance) {
643
649
  if (state === 'failed' || state === 'disconnected' || state === 'closed') {
644
650
 
645
651
  if (instance.callbacks.connectionClosed) {
646
-
647
652
  console.error(logHeader, 'Iceconnection Closed', e);
648
653
  instance.callbacks.connectionClosed('ice', e);
649
654
  }
@@ -672,6 +677,49 @@ function addMethod(instance) {
672
677
  }
673
678
  }
674
679
 
680
+ function closePeerConnection() {
681
+ if (instance.peerConnection) {
682
+
683
+ // remove tracks from peer connection
684
+ instance.peerConnection.getSenders().forEach(function (sender) {
685
+ instance.peerConnection.removeTrack(sender);
686
+ });
687
+
688
+ instance.peerConnection.close();
689
+ instance.peerConnection = null;
690
+ delete instance.peerConnection;
691
+ }
692
+ }
693
+
694
+ function closeWebSocket() {
695
+
696
+ if (instance.webSocket) {
697
+
698
+ instance.webSocket.close();
699
+ instance.webSocket = null;
700
+ delete instance.webSocket;
701
+ }
702
+ }
703
+
704
+ function closeInputStream() {
705
+ // release video, audio stream
706
+ if (instance.inputStream) {
707
+
708
+ instance.inputStream.getTracks().forEach(track => {
709
+
710
+ track.stop();
711
+ instance.inputStream.removeTrack(track);
712
+ });
713
+
714
+ if (instance.videoElement) {
715
+ instance.videoElement.srcObject = null;
716
+ }
717
+
718
+ instance.inputStream = null;
719
+ delete instance.inputStream;
720
+ }
721
+ }
722
+
675
723
  // instance methods
676
724
  instance.attachMedia = function (videoElement) {
677
725
 
@@ -700,63 +748,39 @@ function addMethod(instance) {
700
748
  initWebSocket(connectionUrl);
701
749
  };
702
750
 
703
- instance.remove = function () {
704
-
705
- instance.removing = true;
706
-
707
- // first release peer connection with ome
708
- if (instance.peerConnection) {
709
-
710
- // remove tracks from peer connection
711
- instance.peerConnection.getSenders().forEach(function (sender) {
712
- instance.peerConnection.removeTrack(sender);
713
- });
714
-
715
- instance.peerConnection.close();
716
- instance.peerConnection = null;
717
- delete instance.peerConnection;
718
- }
719
-
720
- // release video, audio stream
721
- if (instance.stream) {
751
+ instance.stopStreaming = function () {
722
752
 
723
- instance.stream.getTracks().forEach(track => {
753
+ instance.webSocketClosedByUser = true;
724
754
 
725
- track.stop();
726
- instance.stream.removeTrack(track);
727
- });
755
+ closeWebSocket();
756
+ closePeerConnection();
757
+ };
728
758
 
729
- if (instance.videoElement) {
730
- instance.videoElement.srcObject = null;
731
- }
759
+ instance.remove = function () {
732
760
 
733
- instance.stream = null;
734
- delete instance.stream;
735
- }
761
+ instance.webSocketClosedByUser = true;
736
762
 
737
- // release websocket
738
- if (instance.webSocket) {
739
-
740
- instance.webSocket.close();
741
- instance.webSocket = null;
742
- delete instance.webSocket;
743
- }
744
-
745
- instance.status = 'removed';
763
+ closeWebSocket();
764
+ closePeerConnection();
765
+ closeInputStream();
746
766
 
747
767
  console.info(logEventHeader, 'Removed');
748
768
 
749
769
  };
750
770
  }
751
771
 
772
+ OvenLiveKit.getVersion = function () {
773
+ return version;
774
+ }
775
+
752
776
  // static methods
753
777
  OvenLiveKit.create = function (options) {
754
778
 
755
- console.info(logEventHeader, 'Create WebRTC Input v1.0.2');
779
+ console.info(logEventHeader, 'Create WebRTC Input ' + version);
756
780
 
757
781
  let instance = {};
758
782
 
759
- instance.removing = false;
783
+ instance.webSocketClosedByUser = false;
760
784
 
761
785
  initConfig(instance, options);
762
786
  addMethod(instance);
@@ -766,7 +790,33 @@ OvenLiveKit.create = function (options) {
766
790
 
767
791
  OvenLiveKit.getDevices = async function () {
768
792
 
769
- await getStreamForDeviceCheck();
793
+ try {
794
+ // First check both audio and video sources are available.
795
+ await getStreamForDeviceCheck('both');
796
+ } catch (e) {
797
+
798
+ console.warn(logHeader, 'Can not find Video and Audio devices', e);
799
+
800
+ let videoFound = null;
801
+ let audioFound = null;
802
+
803
+ try {
804
+ videoFound = await getStreamForDeviceCheck('video');
805
+ } catch (e) {
806
+ console.warn(logHeader, 'Can not find Video devices', e);
807
+ }
808
+
809
+ try {
810
+ audioFound = await getStreamForDeviceCheck('audio');
811
+ } catch (e) {
812
+ console.warn(logHeader, 'Can not find Audio devices', e);
813
+ }
814
+
815
+ if (!videoFound && !audioFound) {
816
+ throw new Error('No input devices were found.');
817
+ }
818
+ }
819
+
770
820
  const deviceInfos = await getDevices();
771
821
  return gotDevices(deviceInfos)
772
822
  };