ovenlivekit 1.0.3 → 1.0.5
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.
- package/README.md +42 -28
- package/assets/05_OvenSpace_230214.png +0 -0
- package/dist/OvenLiveKit.js +1 -1
- package/dist/OvenLiveKit.min.js +1 -1
- package/dist/OvenLiveKit.min.js.map +1 -1
- package/package.json +1 -1
- package/src/OvenLiveKit.js +111 -87
package/README.md
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# OvenLiveKit for Web
|
|
2
2
|
|
|
3
3
|
## What is OvenLiveKit for Web?
|
|
4
|
-
|
|
4
|
+
OvenLiveKit for Web is a JavaScript-based Live Streaming Encoder that supports WebRTC optimized for [OvenMediaEngine](https://github.com/AirenSoft/OvenMediaEngine), Sub-Second Latency Streaming Server. OvenLiveKit for Web relies on the browser's WebRTC API and wraps it to make it easy for you to broadcast WebRTC streams to OvenMediaEngine.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
## Demo
|
|
7
|
+
<img src="./assets/05_OvenSpace_230214.png" style="max-width: 100%; height: auto;">
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
## What is the goal of this project?
|
|
11
|
-
AirenSoft aims to make it easier for you to build a stable broadcasting/streaming service with Sub-Second Latency.
|
|
12
|
-
Therefore, we will continue developing and providing the most optimized tools for smooth Sub-Second Latency Streaming.
|
|
13
|
-
|
|
14
|
-
Would you please click on each link below for details:
|
|
15
|
-
* ["Live Streaming Encoder for Mobile" **OvenLiveKit SDK**](https://www.airensoft.com/olk)
|
|
16
|
-
* ["Sub-Second Latency Streaming Server" **OvenMediaEngine**](https://www.ovenmediaengine.com/ome)
|
|
17
|
-
* ["HTML5 Player" **OvenPlayer**](https://www.ovenmediaengine.com/ovenplayer)
|
|
9
|
+
OvenSpace is a sub-second latency streaming demo service using [OvenMediaEngine](https://github.com/AirenSoft/OvenMediaEngine), [OvenPlayer](https://github.com/AirenSoft/OvenPlayer) and [OvenLiveKit](https://github.com/AirenSoft/OvenLiveKit-Web). You can experience OvenLiveKit in the **[OvenSpace Demo](https://space.ovenplayer.com/)** and see examples of how it can be applied in the [OvenSpace Repository](https://github.com/AirenSoft/OvenSpace).
|
|
18
10
|
|
|
19
11
|
## Features
|
|
20
12
|
* Streaming to OvenMediaEngine's WebRTC Provider.
|
|
@@ -23,6 +15,10 @@ Would you please click on each link below for details:
|
|
|
23
15
|
* Set the Quality of the Input Stream.
|
|
24
16
|
|
|
25
17
|
## Quick Start
|
|
18
|
+
|
|
19
|
+
### OvenLiveKit Demo
|
|
20
|
+
* https://demo.ovenplayer.com/demo_input.html
|
|
21
|
+
|
|
26
22
|
### Installation
|
|
27
23
|
#### OveliveKit CDN
|
|
28
24
|
```html
|
|
@@ -40,7 +36,7 @@ import OvenLiveKit from 'ovenlivekit'
|
|
|
40
36
|
This is the simplest example of sending a device media stream such as a webcam to OvenMediaEngine's WebRTC Provider.
|
|
41
37
|
```JavaScript
|
|
42
38
|
// Initialize OvenLiveKit
|
|
43
|
-
|
|
39
|
+
const ovenLivekit = OvenLiveKit.create();
|
|
44
40
|
|
|
45
41
|
// Get media stream from user device
|
|
46
42
|
ovenLivekit.getUserMedia().then(function () {
|
|
@@ -104,7 +100,7 @@ var config = {
|
|
|
104
100
|
}
|
|
105
101
|
|
|
106
102
|
// Initialize ovenLivekit instance
|
|
107
|
-
|
|
103
|
+
const ovenLivekit = OvenLiveKit.create(config);
|
|
108
104
|
|
|
109
105
|
// Release all resources and destroy the instance
|
|
110
106
|
ovenLivekit.remove();
|
|
@@ -130,7 +126,7 @@ To make the library lightweight and easy to use, only callback options are imple
|
|
|
130
126
|
- parameters
|
|
131
127
|
- event: event object of iceconnectionstatechange
|
|
132
128
|
- This is a callback that occurs when the `RTCPeerConnection.iceConnectionState` becomes `connected`.
|
|
133
|
-
- It means that the media stream is
|
|
129
|
+
- It means that the media stream is being transmitted normally to OvenMediaEngine's WebRTC Provider.
|
|
134
130
|
|
|
135
131
|
##### `callbacks.connectionClosed`
|
|
136
132
|
- type
|
|
@@ -219,7 +215,7 @@ OvenLiveKit also provides APIs to control a media stream from a user device.
|
|
|
219
215
|
```
|
|
220
216
|
```JavaScript
|
|
221
217
|
// Create instance
|
|
222
|
-
|
|
218
|
+
const ovenLivekit = OvenLiveKit.create();
|
|
223
219
|
|
|
224
220
|
// Attaching video element for playing device stream
|
|
225
221
|
ovenLivekit.attachMedia(document.getElementById('myVideo'));
|
|
@@ -254,7 +250,7 @@ ovenLivekit.getUserMedia(constraints).then(function (stream) {
|
|
|
254
250
|
Congrats on getting the media stream from the user device and then ready to stream into OvenMediaEngine.
|
|
255
251
|
```JavaScript
|
|
256
252
|
// Create instance
|
|
257
|
-
|
|
253
|
+
const ovenLivekit = OvenLiveKit.create();
|
|
258
254
|
|
|
259
255
|
ovenLivekit.getUserMedia().then(function () {
|
|
260
256
|
|
|
@@ -275,6 +271,11 @@ ovenLivekit.getUserMedia().then(function () {
|
|
|
275
271
|
- When this API is called, the media stream starts to be streamed according to OvenMediaEngine's signaling protocol.
|
|
276
272
|
|
|
277
273
|
#### `ConnectionConfig`
|
|
274
|
+
|
|
275
|
+
##### `preferredVideoFormat`
|
|
276
|
+
- type
|
|
277
|
+
- String: Video codec name (eq. H256, VP8)
|
|
278
|
+
- If set the specified codec will be preferred if available.
|
|
278
279
|
##### `iceServers`
|
|
279
280
|
- type
|
|
280
281
|
- [`RTCConfiguration.iceServers`](https://developer.mozilla.org/en-US/docs/Web/API/RTCConfiguration/iceServers)
|
|
@@ -293,19 +294,32 @@ ovenLivekit.getUserMedia().then(function () {
|
|
|
293
294
|
- String: String you want to append to a=fmtp of SDP.
|
|
294
295
|
- If set video format is appended to the a=fmtp sections of SDP.
|
|
295
296
|
|
|
297
|
+
|
|
298
|
+
#### `instance.stopStreaming()`
|
|
299
|
+
- Close peer connection and websocket associated with OvenMediaEngine.
|
|
300
|
+
|
|
296
301
|
## For more information
|
|
297
|
-
* [
|
|
298
|
-
*
|
|
299
|
-
* [OvenMediaEngine Website](https://ovenmediaengine.com)
|
|
300
|
-
* Basic Information, FAQ, and Benchmark about OvenMediaEngine.
|
|
302
|
+
* [AirenSoft Website](https://airensoft.com)
|
|
303
|
+
* About OvenMediaEngine, OvenMediaEngine Enterprise, OvenVideo, AirenBlog and more
|
|
301
304
|
* [OvenMediaEngine GitHub](https://github.com/AirenSoft/OvenMediaEngine)
|
|
302
|
-
*
|
|
303
|
-
* [OvenMediaEngine
|
|
304
|
-
*
|
|
305
|
+
* Sub-Second Latency Streaming Server with LLHLS and WebRTC
|
|
306
|
+
* [OvenMediaEngine Getting Started](https://airensoft.gitbook.io/ovenmediaengine/)
|
|
307
|
+
* User guide for OvenMediaEngine Configuration, ABR, Clustering, and more
|
|
305
308
|
* [OvenPlayer GitHub](https://github.com/AirenSoft/OvenPlayer)
|
|
306
|
-
*
|
|
307
|
-
* [OvenPlayer
|
|
308
|
-
*
|
|
309
|
+
* JavaScript-based Player with LLHLS and WebRTC
|
|
310
|
+
* [OvenPlayer Getting Started](https://airensoft.gitbook.io/ovenplayer)
|
|
311
|
+
* User guide for OvenPlayer UI Customize, API Reference, Examples, and more
|
|
312
|
+
* [OvenSpace Demo](https://space.ovenplayer.com/)
|
|
313
|
+
* Sub-Second Latency Streaming Demo Service
|
|
309
314
|
|
|
310
315
|
## License
|
|
311
316
|
OvenLiveKit for Web is licensed under the [MIT](./LICENSE) license.
|
|
317
|
+
|
|
318
|
+
## About AirenSoft
|
|
319
|
+
AirenSoft aims to make it easier for you to build a stable broadcasting/streaming service with Sub-Second Latency.
|
|
320
|
+
Therefore, we will continue developing and providing the most optimized tools for smooth Sub-Second Latency Streaming.
|
|
321
|
+
|
|
322
|
+
Would you please click on each link below for details:
|
|
323
|
+
* ["JavaScript-based Live Streaming Encoder" **OvenLiveKit**](https://github.com/AirenSoft/OvenLiveKit-Web)
|
|
324
|
+
* ["Sub-Second Latency Streaming Server with LLHLS and WebRTC" **OvenMediaEngine**](https://github.com/AirenSoft/OvenMediaEngine)
|
|
325
|
+
* ["JavaScript-based Player with LLHLS and WebRTC" **OvenPlayer**](https://github.com/AirenSoft/OvenPlayer)
|
|
Binary file
|
package/dist/OvenLiveKit.js
CHANGED
|
@@ -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('\\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.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('\\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.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('\\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.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 // 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 (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 try {\r\n // First check both audio and video sources are available.\r\n await getStreamForDeviceCheck('both');\r\n } catch (e) {\r\n\r\n console.warn(logHeader, 'Can not find Video and Audio devices', e);\r\n\r\n let videoFound = null;\r\n let audioFound = null;\r\n\r\n try {\r\n videoFound = await getStreamForDeviceCheck('video');\r\n } catch (e) {\r\n console.warn(logHeader, 'Can not find Video devices', e);\r\n }\r\n\r\n try {\r\n audioFound = await getStreamForDeviceCheck('audio');\r\n } catch (e) {\r\n console.warn(logHeader, 'Can not find Audio devices', e);\r\n }\r\n\r\n if (!videoFound && !audioFound) {\r\n throw new Error('No input devices were found.');\r\n }\r\n }\r\n\r\n const deviceInfos = await getDevices();\r\n return gotDevices(deviceInfos)\r\n};\r\n\r\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (OvenLiveKit);\n\n//# sourceURL=webpack://OvenLiveKit/./src/OvenLiveKit.js?");
|
|
29
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\nconst OvenLiveKit = {};\r\n\r\nconst version = '1.0.5';\r\nconst logHeader = 'OvenLiveKit.js :';\r\nconst logEventHeader = 'OvenLiveKit.js ====';\r\n\r\n// private methods\r\nfunction sendMessage(webSocket, message) {\r\n\r\n if (webSocket) {\r\n webSocket.send(JSON.stringify(message));\r\n }\r\n}\r\n\r\nfunction generateDomainFromUrl(url) {\r\n let result = '';\r\n let match;\r\n if (match = url.match(/^(?:wss?:\\/\\/)?(?:[^@\\n]+@)?(?:www\\.)?([^:\\/\\n\\?\\=]+)/im)) {\r\n result = match[1];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction findIp(string) {\r\n\r\n let result = '';\r\n let match;\r\n\r\n if (match = string.match(new RegExp('\\\\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\b', 'gi'))) {\r\n result = match[0];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction getFormatNumber(sdp, format) {\r\n\r\n const lines = sdp.split('\\r\\n');\r\n let formatNumber = -1;\r\n\r\n for (let i = 0; i < lines.length - 1; i++) {\r\n\r\n lines[i] = lines[i].toLowerCase();\r\n\r\n if (lines[i].indexOf('a=rtpmap') === 0 && lines[i].indexOf(format.toLowerCase()) > -1) {\r\n // parsing \"a=rtpmap:100 H264/90000\" line\r\n // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters >]\r\n formatNumber = lines[i].split(' ')[0].split(':')[1];\r\n break;\r\n }\r\n }\r\n\r\n return formatNumber;\r\n}\r\n\r\nfunction setPreferredVideoFormat(sdp, formatName) {\r\n\r\n const formatNumber = getFormatNumber(sdp, formatName);\r\n\r\n if (formatNumber === -1) {\r\n return sdp;\r\n }\r\n\r\n let newLines = [];\r\n const lines = sdp.split('\\r\\n');\r\n\r\n for (let i = 0; i < lines.length - 1; i++) {\r\n\r\n const line = lines[i];\r\n\r\n if (line.indexOf('m=video') === 0) {\r\n\r\n // m=<media> <port>/<number of ports> <transport> <fmt list>\r\n const others = line.split(' ').slice(0, 3);\r\n const formats = line.split(' ').slice(3);\r\n formats.sort(function (x, y) { return x == formatNumber ? -1 : y == formatNumber ? 1 : 0; });\r\n newLines.push(others.concat(formats).join(' '));\r\n } else {\r\n newLines.push(line);\r\n }\r\n\r\n }\r\n\r\n return newLines.join('\\r\\n') + '\\r\\n';\r\n}\r\n\r\nfunction removeFormat(sdp, formatNumber) {\r\n let newLines = [];\r\n let lines = sdp.split('\\r\\n');\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n\r\n if (lines[i].indexOf('m=video') === 0) {\r\n newLines.push(lines[i].replace(' ' + formatNumber + '', ''));\r\n } else if (lines[i].indexOf(formatNumber + '') > -1) {\r\n\r\n } else {\r\n newLines.push(lines[i]);\r\n }\r\n }\r\n\r\n return newLines.join('\\r\\n')\r\n}\r\n\r\nasync function getStreamForDeviceCheck(type) {\r\n\r\n // High resolution video constraints makes browser to get maximum resolution of video device.\r\n const constraints = {\r\n };\r\n\r\n if (type === 'both') {\r\n constraints.audio = true;\r\n constraints.video = true;\r\n } else if (type === 'audio') {\r\n constraints.audio = true;\r\n } else if (type === 'video') {\r\n constraints.video = true;\r\n }\r\n\r\n return await navigator.mediaDevices.getUserMedia(constraints);\r\n}\r\n\r\nasync function getDevices() {\r\n\r\n return await navigator.mediaDevices.enumerateDevices();\r\n}\r\n\r\nfunction gotDevices(deviceInfos) {\r\n\r\n let devices = {\r\n 'audioinput': [],\r\n 'audiooutput': [],\r\n 'videoinput': [],\r\n 'other': [],\r\n };\r\n\r\n for (let i = 0; i !== deviceInfos.length; ++i) {\r\n\r\n const deviceInfo = deviceInfos[i];\r\n\r\n let info = {};\r\n\r\n info.deviceId = deviceInfo.deviceId;\r\n\r\n if (deviceInfo.kind === 'audioinput') {\r\n\r\n info.label = deviceInfo.label || `microphone ${devices.audioinput.length + 1}`;\r\n devices.audioinput.push(info);\r\n } else if (deviceInfo.kind === 'audiooutput') {\r\n\r\n info.label = deviceInfo.label || `speaker ${devices.audiooutput.length + 1}`;\r\n devices.audiooutput.push(info);\r\n } else if (deviceInfo.kind === 'videoinput') {\r\n\r\n info.label = deviceInfo.label || `camera ${devices.videoinput.length + 1}`;\r\n devices.videoinput.push(info);\r\n } else {\r\n\r\n info.label = deviceInfo.label || `other ${devices.other.length + 1}`;\r\n devices.other.push(info);\r\n }\r\n }\r\n\r\n return devices;\r\n}\r\n\r\nfunction initConfig(instance, options) {\r\n\r\n 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 if (instance.connectionConfig.preferredVideoFormat) {\r\n offer.sdp = setPreferredVideoFormat(offer.sdp, instance.connectionConfig.preferredVideoFormat)\r\n }\r\n\r\n\r\n // offer.sdp = appendOrientation(offer.sdp);\r\n console.info(logHeader, 'Modified offer sdp\\n\\n' + offer.sdp);\r\n\r\n peerConnection.setRemoteDescription(new RTCSessionDescription(offer))\r\n .then(function () {\r\n\r\n peerConnection.createAnswer()\r\n .then(function (answer) {\r\n\r\n if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {\r\n\r\n answer.sdp = appendFmtp(answer.sdp);\r\n }\r\n\r\n answer.sdp = setPreferredVideoFormat(answer.sdp, instance.connectionConfig.preferredVideoFormat)\r\n console.info(logHeader, 'Modified answer sdp\\n\\n' + answer.sdp);\r\n\r\n peerConnection.setLocalDescription(answer)\r\n .then(function () {\r\n\r\n sendMessage(instance.webSocket, {\r\n id: id,\r\n peer_id: peerId,\r\n command: 'answer',\r\n sdp: answer\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.setLocalDescription', error);\r\n errorHandler(error);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.createAnswer', error);\r\n errorHandler(error);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.setRemoteDescription', error);\r\n errorHandler(error);\r\n });\r\n\r\n if (candidates) {\r\n\r\n addIceCandidate(peerConnection, candidates);\r\n }\r\n\r\n peerConnection.onicecandidate = function (e) {\r\n\r\n if (e.candidate && e.candidate.candidate) {\r\n\r\n sendMessage(instance.webSocket, {\r\n id: id,\r\n peer_id: peerId,\r\n command: 'candidate',\r\n candidates: [e.candidate]\r\n });\r\n }\r\n };\r\n\r\n peerConnection.oniceconnectionstatechange = function (e) {\r\n\r\n let state = peerConnection.iceConnectionState;\r\n\r\n if (instance.callbacks.iceStateChange) {\r\n\r\n console.info(logHeader, 'ICE State', '[' + state + ']');\r\n instance.callbacks.iceStateChange(state);\r\n }\r\n\r\n if (state === 'connected') {\r\n\r\n if (instance.callbacks.connected) {\r\n instance.callbacks.connected(e);\r\n }\r\n }\r\n\r\n if (state === 'failed' || state === 'disconnected' || state === 'closed') {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.error(logHeader, '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 with connectionConfig', connectionConfig);\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
|
|
package/dist/OvenLiveKit.min.js
CHANGED
|
@@ -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,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:()=>
|
|
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:()=>u});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}function l(e,n){const o=function(e,n){const o=e.split("\r\n");let t=-1;for(let e=0;e<o.length-1;e++)if(o[e]=o[e].toLowerCase(),0===o[e].indexOf("a=rtpmap")&&o[e].indexOf(n.toLowerCase())>-1){t=o[e].split(" ")[0].split(":")[1];break}return t}(e,n);if(-1===o)return e;let t=[];const i=e.split("\r\n");for(let e=0;e<i.length-1;e++){const n=i[e];if(0===n.indexOf("m=video")){const e=n.split(" ").slice(0,3),i=n.split(" ").slice(3);i.sort((function(e,n){return e==o?-1:n==o?1:0})),t.push(e.concat(i).join(" "))}else t.push(n)}return t.join("\r\n")+"\r\n"}async function s(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 d(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 s(){e.peerConnection&&(e.peerConnection.getSenders().forEach((function(n){e.peerConnection.removeTrack(n)})),e.peerConnection.close(),e.peerConnection=null,delete e.peerConnection)}function d(){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(s,d){console.info(i,"Start Streaming with connectionConfig",d),d&&(e.connectionConfig=d),function(i){if(!i)return void n("connectionUrl is required");e.connectionUrl=i;let s=null;try{s=new WebSocket(i)}catch(e){n(e)}e.webSocket=s,s.onopen=function(){c(s,{command:"request_offer"})},s.onmessage=function(i){let s=JSON.parse(i.data);s.error&&(console.error("webSocket.onmessage",s.error),n(s.error)),"offer"===s.command&&function(i,s,d,u,f){let p={};if(e.connectionConfig.iceServers)p.iceServers=e.connectionConfig.iceServers,e.connectionConfig.iceTransportPolicy&&(p.iceTransportPolicy=e.connectionConfig.iceTransportPolicy);else if(f){p.iceServers=[];for(let n=0;n<f.length;n++){let o=f[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,p.iceServers.push(t)}p.iceTransportPolicy="relay"}else e.iceTransportPolicy&&(p.iceTransportPolicy=e.iceTransportPolicy);console.info(t,"Create Peer Connection With Config",p);let m=new RTCPeerConnection(p);e.peerConnection=m,e.inputStream.getTracks().forEach((function(n){console.info(t,"Add Track To Peer Connection",n),m.addTrack(n,e.inputStream)})),e.connectionConfig.maxVideoBitrate&&(d.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")}(d.sdp,0,e.connectionConfig.maxVideoBitrate)),e.connectionConfig.sdp&&e.connectionConfig.sdp.appendFmtp&&(d.sdp=o(d.sdp)),e.connectionConfig.preferredVideoFormat&&(d.sdp=l(d.sdp,e.connectionConfig.preferredVideoFormat)),console.info(t,"Modified offer sdp\n\n"+d.sdp),m.setRemoteDescription(new RTCSessionDescription(d)).then((function(){m.createAnswer().then((function(r){e.connectionConfig.sdp&&e.connectionConfig.sdp.appendFmtp&&(r.sdp=o(r.sdp)),r.sdp=l(r.sdp,e.connectionConfig.preferredVideoFormat),console.info(t,"Modified answer sdp\n\n"+r.sdp),m.setLocalDescription(r).then((function(){c(e.webSocket,{id:i,peer_id:s,command:"answer",sdp:r})})).catch((function(e){console.error("peerConnection.setLocalDescription",e),n(e)}))})).catch((function(e){console.error("peerConnection.createAnswer",e),n(e)}))})).catch((function(e){console.error("peerConnection.setRemoteDescription",e),n(e)})),u&&function(e,o){for(let t=0;t<o.length;t++)if(o[t]&&o[t].candidate){let i=o[t];e.addIceCandidate(new RTCIceCandidate(i)).then((function(){})).catch((function(e){console.error("peerConnection.addIceCandidate",e),n(e)}))}}(m,u),m.onicecandidate=function(n){n.candidate&&n.candidate.candidate&&c(e.webSocket,{id:i,peer_id:s,command:"candidate",candidates:[n.candidate]})},m.oniceconnectionstatechange=function(n){let o=m.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))}}(s.id,s.peer_id,s.sdp,s.candidates,s.ice_servers)},s.onerror=function(e){console.error("webSocket.onerror",e),n(e)},s.onclose=function(n){e.webSocketClosedByUser||e.callbacks.connectionClosed&&e.callbacks.connectionClosed("websocket",n)}}(s)},e.stopStreaming=function(){e.webSocketClosedByUser=!0,d(),s()},e.remove=function(){e.webSocketClosedByUser=!0,d(),s(),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.5"},o.create=function(e){console.info(i,"Create WebRTC Input 1.0.5");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),d(n),n},o.getDevices=async function(){try{await s("both")}catch(e){console.warn(t,"Can not find Video and Audio devices",e);let n=null,o=null;try{n=await s("video")}catch(e){console.warn(t,"Can not find Video devices",e)}try{o=await s("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 u=o;return n.default})()}));
|
|
2
2
|
//# sourceMappingURL=OvenLiveKit.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OvenLiveKit.min.js","mappings":"CAAA,SAA2CA,EAAMC,GAC1B,iBAAZC,SAA0C,iBAAXC,OACxCA,OAAOD,QAAUD,IACQ,mBAAXG,QAAyBA,OAAOC,IAC9CD,OAAO,GAAIH,GACe,iBAAZC,QACdA,QAAqB,YAAID,IAEzBD,EAAkB,YAAIC,IARxB,CASGK,MAAM,WACT,M,mBCTA,IAAIC,EAAsB,CCA1B,EAAwB,CAACL,EAASM,KACjC,IAAI,IAAIC,KAAOD,EACXD,EAAoBG,EAAEF,EAAYC,KAASF,EAAoBG,EAAER,EAASO,IAC5EE,OAAOC,eAAeV,EAASO,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,MCJ3E,EAAwB,CAACM,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,I,4BCAlF,MAAMI,EAAc,GAEdC,EAAY,mBACZC,EAAiB,sBAGvB,SAASC,EAAYC,EAAWC,GAExBD,GACAA,EAAUE,KAAKC,KAAKC,UAAUH,IAItC,SAASI,EAAsBC,GAC3B,IACIC,EADAC,EAAS,GAMb,OAJID,EAAQD,EAAIC,MAAM,8DAClBC,EAASD,EAAM,IAGZC,EAGX,SAASC,EAAOC,GAEZ,IACIH,EADAC,EAAS,GAOb,OAJID,EAAQG,EAAOH,MAAM,IAAII,OAAO,0KAA2K,UAC3MH,EAASD,EAAM,IAGZC,EAGX,SAASI,IACL,IAAIC,EAAQC,OAAOC,UAAUC,UACzBC,EAAQJ,EAAMK,QAAQ,OAC1B,OAAKL,EAAMK,QAAQ,WAAa,GAAKL,EAAMK,QAAQ,SAAW,IAAMD,GAAS,EAClEH,OAAOK,OAAON,EAAMO,OAAOH,EAAQ,EAAG,GAAGI,QAAQ,IAAK,MAE1D,EAGX,SAASC,EAAgBC,EAAKC,GAE1B,MAAMC,EAAQF,EAAIG,MAAM,QACxB,IAAIC,GAAgB,EAEpB,IAAK,IAAIC,EAAI,EAAGA,EAAIH,EAAMI,OAAS,EAAGD,IAIlC,GAFAH,EAAMG,GAAKH,EAAMG,GAAGE,cAEhBL,EAAMG,GAAGV,QAAQ,aAAe,GAAKO,EAAMG,GAAGV,QAAQM,EAAOM,gBAAkB,EAAG,CAElFH,EAAeF,EAAMG,GAAGF,MAAM,KAAK,GAAGA,MAAM,KAAK,GACjD,MAIR,OAAOC,EAGX,SAASI,EAAaR,EAAKI,GACvB,IAAIK,EAAW,GACXP,EAAQF,EAAIG,MAAM,QAEtB,IAAK,IAAIE,EAAI,EAAGA,EAAIH,EAAMI,OAAQD,IAEM,IAAhCH,EAAMG,GAAGV,QAAQ,WACjBc,EAASC,KAAKR,EAAMG,GAAGP,QAAQ,IAAMM,EAAmB,KACjDF,EAAMG,GAAGV,QAAQS,EAAe,KAAO,GAG9CK,EAASC,KAAKR,EAAMG,IAI5B,OAAOI,EAASE,KAAK,QAGzBC,eAAeC,EAAwBC,GAGnC,MAAMC,EAAc,GAYpB,MATa,SAATD,GACAC,EAAYC,OAAQ,EACpBD,EAAYE,OAAQ,GACJ,UAATH,EACPC,EAAYC,OAAQ,EACJ,UAATF,IACPC,EAAYE,OAAQ,SAGXzB,UAAU0B,aAAaC,aAAaJ,GAoErD,SAASK,EAAUC,GAEf,SAASC,EAAaC,GAEdF,EAASG,UAAUD,OAEnBF,EAASG,UAAUD,MAAMA,GAsNjC,SAASE,EAAWzB,GAEhB,MAAM0B,EAAUL,EAASM,iBAAiB3B,IAAIyB,WAExCvB,EAAQF,EAAIG,MAAM,QAClByB,EAAW,GAEjB,IAAK,IAAIvB,EAAI,EAAGA,EAAIH,EAAMI,OAAQD,IAE9B,GAAoC,IAAhCH,EAAMG,GAAGV,QAAQ,WAAkB,CAEnC,IAAIkC,EAAS3B,EAAMG,GAAGF,MAAM,KAE5B,IAAK,IAAI2B,EAAI,EAAGA,EAAID,EAAOvB,OAAQwB,IAE/BF,EAASlB,KAAKmB,EAAOC,IAGzB,MAIR,IAAK,IAAIzB,EAAI,EAAGA,EAAIuB,EAAStB,OAAQD,IAAK,CAEtC,IAAI0B,GAAgB,EAEpB,IAAK,IAAID,EAAI,EAAGA,EAAI5B,EAAMI,OAAQwB,IAEoB,IAA9C5B,EAAM4B,GAAGnC,QAAQ,UAAYiC,EAASvB,MACtC0B,GAAgB,EAChB7B,EAAM4B,IAAM,IAAMJ,GAI1B,IAAKK,EAED,IAAK,IAAID,EAAI,EAAGA,EAAI5B,EAAMI,OAAQwB,IAEsB,IAAhD5B,EAAM4B,GAAGnC,QAAQ,YAAciC,EAASvB,MAExCH,EAAM4B,IAAM,cAAgBF,EAASvB,GAAK,IAAMqB,GAMhE,OAAOxB,EAAMS,KAAK,QA8RtBU,EAASW,YAAc,SAAUC,GAE7BZ,EAASY,aAAeA,GAG5BZ,EAASF,aAAe,SAAUJ,GAE9B,OAriBJ,SAAsBA,GAgBlB,OAdKA,IAEDA,EAAc,CACVE,MAAO,CACHiB,cAAUC,GAEdnB,MAAO,CACHkB,cAAUC,KAKtBC,QAAQC,KAAK/D,EAAW,wCAAyCyC,GAE1DvB,UAAU0B,aAAaC,aAAaJ,GACtCuB,MAAK,SAAUC,GAEZH,QAAQC,KAAK/D,EAAW,0CAA2CiE,GAEnElB,EAASkB,OAASA,EAElB,IAAIC,EAAOnB,EAASY,aAapB,OAVIO,IAEAA,EAAKC,UAAYF,EAEjBC,EAAKE,iBAAmB,SAAUC,GAE9BH,EAAKI,SAIN,IAAIC,SAAQ,SAAUC,GAEzBA,EAAQP,SAGfQ,OAAM,SAAUxB,GAKb,OAHAa,QAAQb,MAAMjD,EAAW,2CAA6CiD,GACtED,EAAaC,GAEN,IAAIsB,SAAQ,SAAUC,EAASE,GAClCA,EAAOzB,SAsfZJ,CAAaJ,IAGxBM,EAAS4B,gBAAkB,SAAUlC,GAEjC,OAtfJ,SAAyBA,GAQrB,OANKA,IACDA,EAAc,IAGlBqB,QAAQC,KAAK/D,EAAW,kCAAmCyC,GAEpDvB,UAAU0B,aAAa+B,gBAAgBlC,GACzCuB,MAAK,SAAUC,GAEZH,QAAQC,KAAK/D,EAAW,qCAAsCiE,GAE9DlB,EAASkB,OAASA,EAElB,IAAIC,EAAOnB,EAASY,aAapB,OAVIO,IAEAA,EAAKC,UAAYF,EAEjBC,EAAKE,iBAAmB,SAAUC,GAE9BH,EAAKI,SAIN,IAAIC,SAAQ,SAAUC,GAEzBA,EAAQP,SAGfQ,OAAM,SAAUxB,GAKb,OAHAa,QAAQb,MAAMjD,EAAW,sCAAwCiD,GACjED,EAAaC,GAEN,IAAIsB,SAAQ,SAAUC,EAASE,GAClCA,EAAOzB,SA+cZ0B,CAAgBlC,IAG3BM,EAAS6B,eAAiB,SAAUC,EAAexB,GAE/CS,QAAQC,KAAK9D,EAAgB,mBAEzBoD,IAEAN,EAASM,iBAAmBA,GAxapC,SAAuBwB,GAEnB,IAAKA,EAED,YADA7B,EAAa,6BAIjBD,EAAS8B,cAAgBA,EAEzB,IAAI1E,EAAY,KAEhB,IAEIA,EAAY,IAAI2E,UAAUD,GAC5B,MAAO5B,GAELD,EAAaC,GAIjBF,EAAS5C,UAAYA,EAErBA,EAAU4E,OAAS,WAGf7E,EAAYC,EAAW,CACnB6E,QAAS,mBAIjB7E,EAAU8E,UAAY,SAAUZ,GAE5B,IAAIjE,EAAUE,KAAK4E,MAAMb,EAAEc,MAEvB/E,EAAQ6C,QACRa,QAAQb,MAAM,sBAAuB7C,EAAQ6C,OAC7CD,EAAa5C,EAAQ6C,QAGD,UAApB7C,EAAQ4E,SAmHpB,SAA8BI,EAAIC,EAAQC,EAAOC,EAAYC,GAEzD,IAAIC,EAAuB,GAE3B,GAAI1C,EAASM,iBAAiBmC,WAG1BC,EAAqBD,WAAazC,EAASM,iBAAiBmC,WAExDzC,EAASM,iBAAiBqC,qBAE1BD,EAAqBC,mBAAqB3C,EAASM,iBAAiBqC,yBAErE,GAAIF,EAAY,CAGnBC,EAAqBD,WAAa,GAElC,IAAK,IAAIzD,EAAI,EAAGA,EAAIyD,EAAWxD,OAAQD,IAAK,CAExC,IAAI4D,EAAYH,EAAWzD,GAEvB6D,EAAe,GAEnBA,EAAaC,KAAOF,EAAUE,KAE9B,IAAIC,GAAkB,EAClBC,EAAevF,EAAsBuC,EAAS8B,eAElD,IAAK,IAAIrB,EAAI,EAAGA,EAAIoC,EAAaC,KAAK7D,OAAQwB,IAI1C,GAFgBoC,EAAaC,KAAKrC,GAEpBnC,QAAQ0E,IAAiB,EAAG,CACtCD,GAAkB,EAClB,MAIR,IAAKA,GAEGF,EAAaC,KAAK7D,OAAS,EAAG,CAE9B,IAAIgE,EAAiBJ,EAAaC,KAAK,GACnCI,EAAKrF,EAAOoF,GAEZD,GAAgBE,GAChBL,EAAaC,KAAKzD,KAAK4D,EAAexE,QAAQyE,EAAIF,IAK9DH,EAAaM,SAAWP,EAAUQ,UAClCP,EAAaQ,WAAaT,EAAUS,WAEpCX,EAAqBD,WAAWpD,KAAKwD,GAGzCH,EAAqBC,mBAAqB,aAItC3C,EAAS2C,qBAETD,EAAqBC,mBAAqB3C,EAAS2C,oBA6B3D5B,QAAQC,KAAK/D,EAAW,qCAAsCyF,GAE9D,IAAIY,EAAiB,IAAIC,kBAAkBb,GAY3C,GAVA1C,EAASsD,eAAiBA,EAG1BtD,EAASkB,OAAOsC,YAAYC,SAAQ,SAAUC,GAE1C3C,QAAQC,KAAK/D,EAAW,+BAAgCyG,GACxDJ,EAAeK,SAASD,EAAO1D,EAASkB,WAIxClD,KAAqB,GAAI,CACzB,MAAMe,EAAeL,EAAgB6D,EAAM5D,IAAK,QAE5CI,EAAe,IACfwD,EAAM5D,IAAMQ,EAAaoD,EAAM5D,IAAKI,IAIxCiB,EAASM,iBAAiBsD,kBAG1BrB,EAAM5D,IA1Td,SAAyBA,EAAKkF,EAAOC,GAEjC,IAAIjF,EAAQF,EAAIG,MAAM,QAClBiF,GAAQ,EAEZ,IAAK,IAAI/E,EAAI,EAAGA,EAAIH,EAAMI,OAAQD,IAC9B,GAAuC,IAAnCH,EAAMG,GAAGV,QAAQ,WAAqB,CACtCyF,EAAO/E,EACP,MAGR,IAAc,IAAV+E,EAEA,OAAOpF,EAOX,IAHAoF,IAGqC,IAA9BlF,EAAMkF,GAAMzF,QAAQ,OAA6C,IAA9BO,EAAMkF,GAAMzF,QAAQ,OAE1DyF,IAIJ,GAAiC,IAA7BlF,EAAMkF,GAAMzF,QAAQ,KAIpB,OAFAO,EAAMkF,GAAQ,QAAUD,EAEjBjF,EAAMS,KAAK,QAItB,IAAIF,EAAWP,EAAMmF,MAAM,EAAGD,GAK9B,OAHA3E,EAASC,KAAK,QAAUyE,GACxB1E,EAAWA,EAAS6E,OAAOpF,EAAMmF,MAAMD,EAAMlF,EAAMI,SAE5CG,EAASE,KAAK,QAmRL4E,CAAgB3B,EAAM5D,IAAK,EAASqB,EAASM,iBAAiBsD,kBAG1E5D,EAASM,iBAAiB3B,KAAOqB,EAASM,iBAAiB3B,IAAIyB,aAE/DmC,EAAM5D,IAAMyB,EAAWmC,EAAM5D,MAKjC2E,EAAea,qBAAqB,IAAIC,sBAAsB7B,IACzDtB,MAAK,WAEFqC,EAAee,eACVpD,MAAK,SAAUqD,GAEZ,GAAItG,KAAqB,GAAI,CAEzB,MAAMe,EAAeL,EAAgB4F,EAAO3F,IAAK,QAE7CI,EAAe,IAEfuF,EAAO3F,IAAMQ,EAAamF,EAAO3F,IAAKI,IAI1CiB,EAASM,iBAAiB3B,KAAOqB,EAASM,iBAAiB3B,IAAIyB,aAE/DkE,EAAO3F,IAAMyB,EAAWkE,EAAO3F,MAGnC2E,EAAeiB,oBAAoBD,GAC9BrD,MAAK,WAEF9D,EAAY6C,EAAS5C,UAAW,CAC5BiF,GAAIA,EACJmC,QAASlC,EACTL,QAAS,SACTtD,IAAK2F,OAGZ5C,OAAM,SAAUxB,GAEba,QAAQb,MAAM,qCAAsCA,GACpDD,EAAaC,SAGxBwB,OAAM,SAAUxB,GAEba,QAAQb,MAAM,8BAA+BA,GAC7CD,EAAaC,SAGxBwB,OAAM,SAAUxB,GAEba,QAAQb,MAAM,sCAAuCA,GACrDD,EAAaC,MAGjBsC,GAkDR,SAAyBc,EAAgBd,GAErC,IAAK,IAAIxD,EAAI,EAAGA,EAAIwD,EAAWvD,OAAQD,IAEnC,GAAIwD,EAAWxD,IAAMwD,EAAWxD,GAAGyF,UAAW,CAE1C,IAAIC,EAAiBlC,EAAWxD,GAEhCsE,EAAeqB,gBAAgB,IAAIC,gBAAgBF,IAC9CzD,MAAK,eAGLS,OAAM,SAAUxB,GAEba,QAAQb,MAAM,iCAAkCA,GAChDD,EAAaC,OA/DzByE,CAAgBrB,EAAgBd,GAGpCc,EAAeuB,eAAiB,SAAUvD,GAElCA,EAAEmD,WAAanD,EAAEmD,UAAUA,YAE3B1D,QAAQC,KAAK/D,EAAW,iBAAkB,KAAMqE,EAAEmD,UAAUA,UAAW,KAAMnD,GAE7EnE,EAAY6C,EAAS5C,UAAW,CAC5BiF,GAAIA,EACJmC,QAASlC,EACTL,QAAS,YACTO,WAAY,CAAClB,EAAEmD,eAK3BnB,EAAewB,2BAA6B,SAAUxD,GAElD,IAAIyD,EAAQzB,EAAe0B,mBAEvBhF,EAASG,UAAU8E,iBAEnBlE,QAAQC,KAAK/D,EAAW,YAAa,IAAM8H,EAAQ,KACnD/E,EAASG,UAAU8E,eAAeF,IAGxB,cAAVA,GAEI/E,EAASG,UAAU+E,YAEnBnE,QAAQC,KAAK/D,EAAW,0BAA2BqE,GACnDtB,EAASG,UAAU+E,UAAU5D,IAIvB,WAAVyD,GAAgC,iBAAVA,GAAsC,WAAVA,GAE9C/E,EAASG,UAAUgF,mBAEnBpE,QAAQb,MAAMjD,EAAW,uBAAwBqE,GACjDtB,EAASG,UAAUgF,iBAAiB,MAAO7D,KA7U/C8D,CACI/H,EAAQgF,GACRhF,EAAQmH,QACRnH,EAAQsB,IACRtB,EAAQmF,WACRnF,EAAQgI,cAKpBjI,EAAUkI,QAAU,SAAUpF,GAE1Ba,QAAQb,MAAM,oBAAqBA,GACnCD,EAAaC,IAGjB9C,EAAUmI,QAAU,SAAUjE,GAErBtB,EAASwF,UAENxF,EAASG,UAAUgF,kBAEnBnF,EAASG,UAAUgF,iBAAiB,YAAa7D,IA2W7DmE,CAAc3D,IAGlB9B,EAAS0F,OAAS,WAEd1F,EAASwF,UAAW,EAGhBxF,EAASsD,iBAGTtD,EAASsD,eAAeqC,aAAalC,SAAQ,SAAUmC,GACnD5F,EAASsD,eAAeuC,YAAYD,MAGxC5F,EAASsD,eAAewC,QACxB9F,EAASsD,eAAiB,YACnBtD,EAASsD,gBAIhBtD,EAASkB,SAETlB,EAASkB,OAAOsC,YAAYC,SAAQC,IAEhCA,EAAMqC,OACN/F,EAASkB,OAAO2E,YAAYnC,MAG5B1D,EAASY,eACTZ,EAASY,aAAaQ,UAAY,MAGtCpB,EAASkB,OAAS,YACXlB,EAASkB,QAIhBlB,EAAS5C,YAET4C,EAAS5C,UAAU0I,QACnB9F,EAAS5C,UAAY,YACd4C,EAAS5C,WAGpB4C,EAASgG,OAAS,UAElBjF,QAAQC,KAAK9D,EAAgB,YAMrCF,EAAYiJ,OAAS,SAAUC,GAE3BnF,QAAQC,KAAK9D,EAAgB,8BAE7B,IAAI8C,EAAW,CAEf,UAAoB,GAKpB,OArpBJ,SAAoBA,EAAUkG,GAE1BlG,EAASkB,OAAS,KAClBlB,EAAS5C,UAAY,KACrB4C,EAASsD,eAAiB,KAC1BtD,EAASM,iBAAmB,GAE5BN,EAASgG,OAAS,WAElBhG,EAASY,aAAe,KACxBZ,EAAS8B,cAAgB,KAErBoE,GAAWA,EAAQ/F,UAEnBH,EAASG,UAAY+F,EAAQ/F,UAE7BH,EAASG,UAAY,GAkoBzBgG,CAAWnG,EAAUkG,GACrBnG,EAAUC,GAEHA,GAGXhD,EAAYoJ,WAAa7G,iBAErB,UAEUC,EAAwB,QAChC,MAAO8B,GAELP,QAAQsF,KAAKpJ,EAAW,uCAAwCqE,GAEhE,IAAIgF,EAAa,KACbC,EAAa,KAEjB,IACID,QAAmB9G,EAAwB,SAC7C,MAAO8B,GACLP,QAAQsF,KAAKpJ,EAAW,6BAA8BqE,GAG1D,IACIiF,QAAmB/G,EAAwB,SAC7C,MAAO8B,GACLP,QAAQsF,KAAKpJ,EAAW,6BAA8BqE,GAG1D,IAAKgF,IAAeC,EAChB,MAAM,IAAIC,MAAM,gCAKxB,OA7tBJ,SAAoBC,GAEhB,IAAIC,EAAU,CACV,WAAc,GACd,YAAe,GACf,WAAc,GACd,MAAS,IAGb,IAAK,IAAI1H,EAAI,EAAGA,IAAMyH,EAAYxH,SAAUD,EAAG,CAE3C,MAAM2H,EAAaF,EAAYzH,GAE/B,IAAIgC,EAAO,GAEXA,EAAKH,SAAW8F,EAAW9F,SAEH,eAApB8F,EAAWC,MAEX5F,EAAK6F,MAAQF,EAAWE,OAAS,cAAcH,EAAQI,WAAW7H,OAAS,IAC3EyH,EAAQI,WAAWzH,KAAK2B,IACG,gBAApB2F,EAAWC,MAElB5F,EAAK6F,MAAQF,EAAWE,OAAS,WAAWH,EAAQK,YAAY9H,OAAS,IACzEyH,EAAQK,YAAY1H,KAAK2B,IACE,eAApB2F,EAAWC,MAElB5F,EAAK6F,MAAQF,EAAWE,OAAS,UAAUH,EAAQM,WAAW/H,OAAS,IACvEyH,EAAQM,WAAW3H,KAAK2B,KAGxBA,EAAK6F,MAAQF,EAAWE,OAAS,SAASH,EAAQO,MAAMhI,OAAS,IACjEyH,EAAQO,MAAM5H,KAAK2B,IAI3B,OAAO0F,EAyrBAQ,OAluBX3H,iBAEI,aAAapB,UAAU0B,aAAasH,mBA+tBVf,KAI9B,U","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('\\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.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('\\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.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('\\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.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 // 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 (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 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","checkIOSVersion","agent","window","navigator","userAgent","start","indexOf","Number","substr","replace","getFormatNumber","sdp","format","lines","split","formatNumber","i","length","toLowerCase","removeFormat","newLines","push","join","async","getStreamForDeviceCheck","type","constraints","audio","video","mediaDevices","getUserMedia","addMethod","instance","errorHandler","error","callbacks","appendFmtp","fmtpStr","connectionConfig","payloads","tokens","j","fmtpLineFound","attachMedia","videoElement","deviceId","undefined","console","info","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","warn","videoFound","audioFound","Error","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.5';\r\nconst logHeader = 'OvenLiveKit.js :';\r\nconst logEventHeader = 'OvenLiveKit.js ====';\r\n\r\n// private methods\r\nfunction sendMessage(webSocket, message) {\r\n\r\n if (webSocket) {\r\n webSocket.send(JSON.stringify(message));\r\n }\r\n}\r\n\r\nfunction generateDomainFromUrl(url) {\r\n let result = '';\r\n let match;\r\n if (match = url.match(/^(?:wss?:\\/\\/)?(?:[^@\\n]+@)?(?:www\\.)?([^:\\/\\n\\?\\=]+)/im)) {\r\n result = match[1];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction findIp(string) {\r\n\r\n let result = '';\r\n let match;\r\n\r\n if (match = string.match(new RegExp('\\\\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\b', 'gi'))) {\r\n result = match[0];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction getFormatNumber(sdp, format) {\r\n\r\n const lines = sdp.split('\\r\\n');\r\n let formatNumber = -1;\r\n\r\n for (let i = 0; i < lines.length - 1; i++) {\r\n\r\n lines[i] = lines[i].toLowerCase();\r\n\r\n if (lines[i].indexOf('a=rtpmap') === 0 && lines[i].indexOf(format.toLowerCase()) > -1) {\r\n // parsing \"a=rtpmap:100 H264/90000\" line\r\n // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters >]\r\n formatNumber = lines[i].split(' ')[0].split(':')[1];\r\n break;\r\n }\r\n }\r\n\r\n return formatNumber;\r\n}\r\n\r\nfunction setPreferredVideoFormat(sdp, formatName) {\r\n\r\n const formatNumber = getFormatNumber(sdp, formatName);\r\n\r\n if (formatNumber === -1) {\r\n return sdp;\r\n }\r\n\r\n let newLines = [];\r\n const lines = sdp.split('\\r\\n');\r\n\r\n for (let i = 0; i < lines.length - 1; i++) {\r\n\r\n const line = lines[i];\r\n\r\n if (line.indexOf('m=video') === 0) {\r\n\r\n // m=<media> <port>/<number of ports> <transport> <fmt list>\r\n const others = line.split(' ').slice(0, 3);\r\n const formats = line.split(' ').slice(3);\r\n formats.sort(function (x, y) { return x == formatNumber ? -1 : y == formatNumber ? 1 : 0; });\r\n newLines.push(others.concat(formats).join(' '));\r\n } else {\r\n newLines.push(line);\r\n }\r\n\r\n }\r\n\r\n return newLines.join('\\r\\n') + '\\r\\n';\r\n}\r\n\r\nfunction removeFormat(sdp, formatNumber) {\r\n let newLines = [];\r\n let lines = sdp.split('\\r\\n');\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n\r\n if (lines[i].indexOf('m=video') === 0) {\r\n newLines.push(lines[i].replace(' ' + formatNumber + '', ''));\r\n } else if (lines[i].indexOf(formatNumber + '') > -1) {\r\n\r\n } else {\r\n newLines.push(lines[i]);\r\n }\r\n }\r\n\r\n return newLines.join('\\r\\n')\r\n}\r\n\r\nasync function getStreamForDeviceCheck(type) {\r\n\r\n // High resolution video constraints makes browser to get maximum resolution of video device.\r\n const constraints = {\r\n };\r\n\r\n if (type === 'both') {\r\n constraints.audio = true;\r\n constraints.video = true;\r\n } else if (type === 'audio') {\r\n constraints.audio = true;\r\n } else if (type === 'video') {\r\n constraints.video = true;\r\n }\r\n\r\n return await navigator.mediaDevices.getUserMedia(constraints);\r\n}\r\n\r\nasync function getDevices() {\r\n\r\n return await navigator.mediaDevices.enumerateDevices();\r\n}\r\n\r\nfunction gotDevices(deviceInfos) {\r\n\r\n let devices = {\r\n 'audioinput': [],\r\n 'audiooutput': [],\r\n 'videoinput': [],\r\n 'other': [],\r\n };\r\n\r\n for (let i = 0; i !== deviceInfos.length; ++i) {\r\n\r\n const deviceInfo = deviceInfos[i];\r\n\r\n let info = {};\r\n\r\n info.deviceId = deviceInfo.deviceId;\r\n\r\n if (deviceInfo.kind === 'audioinput') {\r\n\r\n info.label = deviceInfo.label || `microphone ${devices.audioinput.length + 1}`;\r\n devices.audioinput.push(info);\r\n } else if (deviceInfo.kind === 'audiooutput') {\r\n\r\n info.label = deviceInfo.label || `speaker ${devices.audiooutput.length + 1}`;\r\n devices.audiooutput.push(info);\r\n } else if (deviceInfo.kind === 'videoinput') {\r\n\r\n info.label = deviceInfo.label || `camera ${devices.videoinput.length + 1}`;\r\n devices.videoinput.push(info);\r\n } else {\r\n\r\n info.label = deviceInfo.label || `other ${devices.other.length + 1}`;\r\n devices.other.push(info);\r\n }\r\n }\r\n\r\n return devices;\r\n}\r\n\r\nfunction initConfig(instance, options) {\r\n\r\n 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 if (instance.connectionConfig.preferredVideoFormat) {\r\n offer.sdp = setPreferredVideoFormat(offer.sdp, instance.connectionConfig.preferredVideoFormat)\r\n }\r\n\r\n\r\n // offer.sdp = appendOrientation(offer.sdp);\r\n console.info(logHeader, 'Modified offer sdp\\n\\n' + offer.sdp);\r\n\r\n peerConnection.setRemoteDescription(new RTCSessionDescription(offer))\r\n .then(function () {\r\n\r\n peerConnection.createAnswer()\r\n .then(function (answer) {\r\n\r\n if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {\r\n\r\n answer.sdp = appendFmtp(answer.sdp);\r\n }\r\n\r\n answer.sdp = setPreferredVideoFormat(answer.sdp, instance.connectionConfig.preferredVideoFormat)\r\n console.info(logHeader, 'Modified answer sdp\\n\\n' + answer.sdp);\r\n\r\n peerConnection.setLocalDescription(answer)\r\n .then(function () {\r\n\r\n sendMessage(instance.webSocket, {\r\n id: id,\r\n peer_id: peerId,\r\n command: 'answer',\r\n sdp: answer\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.setLocalDescription', error);\r\n errorHandler(error);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.createAnswer', error);\r\n errorHandler(error);\r\n });\r\n })\r\n .catch(function (error) {\r\n\r\n console.error('peerConnection.setRemoteDescription', error);\r\n errorHandler(error);\r\n });\r\n\r\n if (candidates) {\r\n\r\n addIceCandidate(peerConnection, candidates);\r\n }\r\n\r\n peerConnection.onicecandidate = function (e) {\r\n\r\n if (e.candidate && e.candidate.candidate) {\r\n\r\n sendMessage(instance.webSocket, {\r\n id: id,\r\n peer_id: peerId,\r\n command: 'candidate',\r\n candidates: [e.candidate]\r\n });\r\n }\r\n };\r\n\r\n peerConnection.oniceconnectionstatechange = function (e) {\r\n\r\n let state = peerConnection.iceConnectionState;\r\n\r\n if (instance.callbacks.iceStateChange) {\r\n\r\n console.info(logHeader, 'ICE State', '[' + state + ']');\r\n instance.callbacks.iceStateChange(state);\r\n }\r\n\r\n if (state === 'connected') {\r\n\r\n if (instance.callbacks.connected) {\r\n instance.callbacks.connected(e);\r\n }\r\n }\r\n\r\n if (state === 'failed' || state === 'disconnected' || state === 'closed') {\r\n\r\n if (instance.callbacks.connectionClosed) {\r\n console.error(logHeader, '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 with connectionConfig', connectionConfig);\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","setPreferredVideoFormat","sdp","formatName","formatNumber","format","lines","split","i","length","toLowerCase","indexOf","getFormatNumber","newLines","line","others","slice","formats","sort","x","y","push","concat","join","async","getStreamForDeviceCheck","type","constraints","audio","video","navigator","mediaDevices","getUserMedia","addMethod","instance","errorHandler","error","callbacks","appendFmtp","fmtpStr","connectionConfig","payloads","tokens","j","fmtpLineFound","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","setBitrateLimit","preferredVideoFormat","setRemoteDescription","RTCSessionDescription","createAnswer","answer","setLocalDescription","peer_id","candidate","basicCandidate","addIceCandidate","RTCIceCandidate","onicecandidate","oniceconnectionstatechange","state","iceConnectionState","iceStateChange","connected","connectionClosed","createPeerConnection","ice_servers","onerror","onclose","webSocketClosedByUser","initWebSocket","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
package/src/OvenLiveKit.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const OvenLiveKit = {};
|
|
2
2
|
|
|
3
|
+
const version = '1.0.5';
|
|
3
4
|
const logHeader = 'OvenLiveKit.js :';
|
|
4
5
|
const logEventHeader = 'OvenLiveKit.js ====';
|
|
5
6
|
|
|
@@ -33,15 +34,6 @@ 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
39
|
const lines = sdp.split('\r\n');
|
|
@@ -51,8 +43,9 @@ function getFormatNumber(sdp, format) {
|
|
|
51
43
|
|
|
52
44
|
lines[i] = lines[i].toLowerCase();
|
|
53
45
|
|
|
54
|
-
if (lines[i].indexOf('a=rtpmap')
|
|
46
|
+
if (lines[i].indexOf('a=rtpmap') === 0 && lines[i].indexOf(format.toLowerCase()) > -1) {
|
|
55
47
|
// parsing "a=rtpmap:100 H264/90000" line
|
|
48
|
+
// a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters >]
|
|
56
49
|
formatNumber = lines[i].split(' ')[0].split(':')[1];
|
|
57
50
|
break;
|
|
58
51
|
}
|
|
@@ -61,6 +54,37 @@ function getFormatNumber(sdp, format) {
|
|
|
61
54
|
return formatNumber;
|
|
62
55
|
}
|
|
63
56
|
|
|
57
|
+
function setPreferredVideoFormat(sdp, formatName) {
|
|
58
|
+
|
|
59
|
+
const formatNumber = getFormatNumber(sdp, formatName);
|
|
60
|
+
|
|
61
|
+
if (formatNumber === -1) {
|
|
62
|
+
return sdp;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let newLines = [];
|
|
66
|
+
const lines = sdp.split('\r\n');
|
|
67
|
+
|
|
68
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
|
69
|
+
|
|
70
|
+
const line = lines[i];
|
|
71
|
+
|
|
72
|
+
if (line.indexOf('m=video') === 0) {
|
|
73
|
+
|
|
74
|
+
// m=<media> <port>/<number of ports> <transport> <fmt list>
|
|
75
|
+
const others = line.split(' ').slice(0, 3);
|
|
76
|
+
const formats = line.split(' ').slice(3);
|
|
77
|
+
formats.sort(function (x, y) { return x == formatNumber ? -1 : y == formatNumber ? 1 : 0; });
|
|
78
|
+
newLines.push(others.concat(formats).join(' '));
|
|
79
|
+
} else {
|
|
80
|
+
newLines.push(line);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return newLines.join('\r\n') + '\r\n';
|
|
86
|
+
}
|
|
87
|
+
|
|
64
88
|
function removeFormat(sdp, formatNumber) {
|
|
65
89
|
let newLines = [];
|
|
66
90
|
let lines = sdp.split('\r\n');
|
|
@@ -143,13 +167,11 @@ function gotDevices(deviceInfos) {
|
|
|
143
167
|
|
|
144
168
|
function initConfig(instance, options) {
|
|
145
169
|
|
|
146
|
-
instance.
|
|
170
|
+
instance.inputStream = null;
|
|
147
171
|
instance.webSocket = null;
|
|
148
172
|
instance.peerConnection = null;
|
|
149
173
|
instance.connectionConfig = {};
|
|
150
174
|
|
|
151
|
-
instance.status = 'creating';
|
|
152
|
-
|
|
153
175
|
instance.videoElement = null;
|
|
154
176
|
instance.connectionUrl = null;
|
|
155
177
|
|
|
@@ -159,7 +181,6 @@ function initConfig(instance, options) {
|
|
|
159
181
|
} else {
|
|
160
182
|
instance.callbacks = {};
|
|
161
183
|
}
|
|
162
|
-
|
|
163
184
|
}
|
|
164
185
|
|
|
165
186
|
function addMethod(instance) {
|
|
@@ -186,14 +207,14 @@ function addMethod(instance) {
|
|
|
186
207
|
};
|
|
187
208
|
}
|
|
188
209
|
|
|
189
|
-
console.info(logHeader, '
|
|
210
|
+
console.info(logHeader, 'Request Stream To Input Devices With Constraints', constraints);
|
|
190
211
|
|
|
191
212
|
return navigator.mediaDevices.getUserMedia(constraints)
|
|
192
213
|
.then(function (stream) {
|
|
193
214
|
|
|
194
215
|
console.info(logHeader, 'Received Media Stream From Input Device', stream);
|
|
195
216
|
|
|
196
|
-
instance.
|
|
217
|
+
instance.inputStream = stream;
|
|
197
218
|
|
|
198
219
|
let elem = instance.videoElement;
|
|
199
220
|
|
|
@@ -230,14 +251,14 @@ function addMethod(instance) {
|
|
|
230
251
|
constraints = {};
|
|
231
252
|
}
|
|
232
253
|
|
|
233
|
-
console.info(logHeader, '
|
|
254
|
+
console.info(logHeader, 'Request Stream To Display With Constraints', constraints);
|
|
234
255
|
|
|
235
256
|
return navigator.mediaDevices.getDisplayMedia(constraints)
|
|
236
257
|
.then(function (stream) {
|
|
237
258
|
|
|
238
259
|
console.info(logHeader, 'Received Media Stream From Display', stream);
|
|
239
260
|
|
|
240
|
-
instance.
|
|
261
|
+
instance.inputStream = stream;
|
|
241
262
|
|
|
242
263
|
let elem = instance.videoElement;
|
|
243
264
|
|
|
@@ -371,10 +392,9 @@ function addMethod(instance) {
|
|
|
371
392
|
|
|
372
393
|
webSocket.onclose = function (e) {
|
|
373
394
|
|
|
374
|
-
if (!instance.
|
|
395
|
+
if (!instance.webSocketClosedByUser) {
|
|
375
396
|
|
|
376
397
|
if (instance.callbacks.connectionClosed) {
|
|
377
|
-
|
|
378
398
|
instance.callbacks.connectionClosed('websocket', e);
|
|
379
399
|
}
|
|
380
400
|
}
|
|
@@ -565,21 +585,12 @@ function addMethod(instance) {
|
|
|
565
585
|
instance.peerConnection = peerConnection;
|
|
566
586
|
|
|
567
587
|
// set local stream
|
|
568
|
-
instance.
|
|
588
|
+
instance.inputStream.getTracks().forEach(function (track) {
|
|
569
589
|
|
|
570
590
|
console.info(logHeader, 'Add Track To Peer Connection', track);
|
|
571
|
-
peerConnection.addTrack(track, instance.
|
|
591
|
+
peerConnection.addTrack(track, instance.inputStream);
|
|
572
592
|
});
|
|
573
593
|
|
|
574
|
-
|
|
575
|
-
if (checkIOSVersion() >= 15) {
|
|
576
|
-
const formatNumber = getFormatNumber(offer.sdp, 'H264');
|
|
577
|
-
|
|
578
|
-
if (formatNumber > 0) {
|
|
579
|
-
offer.sdp = removeFormat(offer.sdp, formatNumber);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
|
|
583
594
|
if (instance.connectionConfig.maxVideoBitrate) {
|
|
584
595
|
|
|
585
596
|
// if bandwith limit is set. modify sdp from ome to limit acceptable bandwidth of ome
|
|
@@ -591,7 +602,13 @@ function addMethod(instance) {
|
|
|
591
602
|
offer.sdp = appendFmtp(offer.sdp);
|
|
592
603
|
}
|
|
593
604
|
|
|
605
|
+
if (instance.connectionConfig.preferredVideoFormat) {
|
|
606
|
+
offer.sdp = setPreferredVideoFormat(offer.sdp, instance.connectionConfig.preferredVideoFormat)
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
|
|
594
610
|
// offer.sdp = appendOrientation(offer.sdp);
|
|
611
|
+
console.info(logHeader, 'Modified offer sdp\n\n' + offer.sdp);
|
|
595
612
|
|
|
596
613
|
peerConnection.setRemoteDescription(new RTCSessionDescription(offer))
|
|
597
614
|
.then(function () {
|
|
@@ -599,21 +616,14 @@ function addMethod(instance) {
|
|
|
599
616
|
peerConnection.createAnswer()
|
|
600
617
|
.then(function (answer) {
|
|
601
618
|
|
|
602
|
-
if (checkIOSVersion() >= 15) {
|
|
603
|
-
|
|
604
|
-
const formatNumber = getFormatNumber(answer.sdp, 'H264');
|
|
605
|
-
|
|
606
|
-
if (formatNumber > 0) {
|
|
607
|
-
|
|
608
|
-
answer.sdp = removeFormat(answer.sdp, formatNumber);
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
|
|
612
619
|
if (instance.connectionConfig.sdp && instance.connectionConfig.sdp.appendFmtp) {
|
|
613
620
|
|
|
614
621
|
answer.sdp = appendFmtp(answer.sdp);
|
|
615
622
|
}
|
|
616
623
|
|
|
624
|
+
answer.sdp = setPreferredVideoFormat(answer.sdp, instance.connectionConfig.preferredVideoFormat)
|
|
625
|
+
console.info(logHeader, 'Modified answer sdp\n\n' + answer.sdp);
|
|
626
|
+
|
|
617
627
|
peerConnection.setLocalDescription(answer)
|
|
618
628
|
.then(function () {
|
|
619
629
|
|
|
@@ -651,8 +661,6 @@ function addMethod(instance) {
|
|
|
651
661
|
|
|
652
662
|
if (e.candidate && e.candidate.candidate) {
|
|
653
663
|
|
|
654
|
-
console.info(logHeader, 'Candidate Sent', '\n', e.candidate.candidate, '\n', e);
|
|
655
|
-
|
|
656
664
|
sendMessage(instance.webSocket, {
|
|
657
665
|
id: id,
|
|
658
666
|
peer_id: peerId,
|
|
@@ -675,8 +683,6 @@ function addMethod(instance) {
|
|
|
675
683
|
if (state === 'connected') {
|
|
676
684
|
|
|
677
685
|
if (instance.callbacks.connected) {
|
|
678
|
-
|
|
679
|
-
console.info(logHeader, 'Iceconnection Connected', e);
|
|
680
686
|
instance.callbacks.connected(e);
|
|
681
687
|
}
|
|
682
688
|
}
|
|
@@ -684,7 +690,6 @@ function addMethod(instance) {
|
|
|
684
690
|
if (state === 'failed' || state === 'disconnected' || state === 'closed') {
|
|
685
691
|
|
|
686
692
|
if (instance.callbacks.connectionClosed) {
|
|
687
|
-
|
|
688
693
|
console.error(logHeader, 'Iceconnection Closed', e);
|
|
689
694
|
instance.callbacks.connectionClosed('ice', e);
|
|
690
695
|
}
|
|
@@ -713,6 +718,49 @@ function addMethod(instance) {
|
|
|
713
718
|
}
|
|
714
719
|
}
|
|
715
720
|
|
|
721
|
+
function closePeerConnection() {
|
|
722
|
+
if (instance.peerConnection) {
|
|
723
|
+
|
|
724
|
+
// remove tracks from peer connection
|
|
725
|
+
instance.peerConnection.getSenders().forEach(function (sender) {
|
|
726
|
+
instance.peerConnection.removeTrack(sender);
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
instance.peerConnection.close();
|
|
730
|
+
instance.peerConnection = null;
|
|
731
|
+
delete instance.peerConnection;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function closeWebSocket() {
|
|
736
|
+
|
|
737
|
+
if (instance.webSocket) {
|
|
738
|
+
|
|
739
|
+
instance.webSocket.close();
|
|
740
|
+
instance.webSocket = null;
|
|
741
|
+
delete instance.webSocket;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function closeInputStream() {
|
|
746
|
+
// release video, audio stream
|
|
747
|
+
if (instance.inputStream) {
|
|
748
|
+
|
|
749
|
+
instance.inputStream.getTracks().forEach(track => {
|
|
750
|
+
|
|
751
|
+
track.stop();
|
|
752
|
+
instance.inputStream.removeTrack(track);
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
if (instance.videoElement) {
|
|
756
|
+
instance.videoElement.srcObject = null;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
instance.inputStream = null;
|
|
760
|
+
delete instance.inputStream;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
716
764
|
// instance methods
|
|
717
765
|
instance.attachMedia = function (videoElement) {
|
|
718
766
|
|
|
@@ -731,7 +779,7 @@ function addMethod(instance) {
|
|
|
731
779
|
|
|
732
780
|
instance.startStreaming = function (connectionUrl, connectionConfig) {
|
|
733
781
|
|
|
734
|
-
console.info(logEventHeader, 'Start Streaming');
|
|
782
|
+
console.info(logEventHeader, 'Start Streaming with connectionConfig', connectionConfig);
|
|
735
783
|
|
|
736
784
|
if (connectionConfig) {
|
|
737
785
|
|
|
@@ -741,63 +789,39 @@ function addMethod(instance) {
|
|
|
741
789
|
initWebSocket(connectionUrl);
|
|
742
790
|
};
|
|
743
791
|
|
|
744
|
-
instance.
|
|
745
|
-
|
|
746
|
-
instance.removing = true;
|
|
747
|
-
|
|
748
|
-
// first release peer connection with ome
|
|
749
|
-
if (instance.peerConnection) {
|
|
750
|
-
|
|
751
|
-
// remove tracks from peer connection
|
|
752
|
-
instance.peerConnection.getSenders().forEach(function (sender) {
|
|
753
|
-
instance.peerConnection.removeTrack(sender);
|
|
754
|
-
});
|
|
755
|
-
|
|
756
|
-
instance.peerConnection.close();
|
|
757
|
-
instance.peerConnection = null;
|
|
758
|
-
delete instance.peerConnection;
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
// release video, audio stream
|
|
762
|
-
if (instance.stream) {
|
|
792
|
+
instance.stopStreaming = function () {
|
|
763
793
|
|
|
764
|
-
|
|
794
|
+
instance.webSocketClosedByUser = true;
|
|
765
795
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
if (instance.videoElement) {
|
|
771
|
-
instance.videoElement.srcObject = null;
|
|
772
|
-
}
|
|
796
|
+
closeWebSocket();
|
|
797
|
+
closePeerConnection();
|
|
798
|
+
};
|
|
773
799
|
|
|
774
|
-
|
|
775
|
-
delete instance.stream;
|
|
776
|
-
}
|
|
800
|
+
instance.remove = function () {
|
|
777
801
|
|
|
778
|
-
|
|
779
|
-
if (instance.webSocket) {
|
|
802
|
+
instance.webSocketClosedByUser = true;
|
|
780
803
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
instance.status = 'removed';
|
|
804
|
+
closeWebSocket();
|
|
805
|
+
closePeerConnection();
|
|
806
|
+
closeInputStream();
|
|
787
807
|
|
|
788
808
|
console.info(logEventHeader, 'Removed');
|
|
789
809
|
|
|
790
810
|
};
|
|
791
811
|
}
|
|
792
812
|
|
|
813
|
+
OvenLiveKit.getVersion = function () {
|
|
814
|
+
return version;
|
|
815
|
+
}
|
|
816
|
+
|
|
793
817
|
// static methods
|
|
794
818
|
OvenLiveKit.create = function (options) {
|
|
795
819
|
|
|
796
|
-
console.info(logEventHeader, 'Create WebRTC Input
|
|
820
|
+
console.info(logEventHeader, 'Create WebRTC Input ' + version);
|
|
797
821
|
|
|
798
822
|
let instance = {};
|
|
799
823
|
|
|
800
|
-
instance.
|
|
824
|
+
instance.webSocketClosedByUser = false;
|
|
801
825
|
|
|
802
826
|
initConfig(instance, options);
|
|
803
827
|
addMethod(instance);
|