@flashphoner/sfusdk-examples 2.0.271 → 2.0.273

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flashphoner/sfusdk-examples",
3
- "version": "2.0.271",
3
+ "version": "2.0.273",
4
4
  "description": "Official Flashphoner WebCallServer SFU SDK usage examples",
5
5
  "main": "dist/sfu.js",
6
6
  "types": "src/sfu.ts",
@@ -0,0 +1,26 @@
1
+ video, object {
2
+ width: 100%;
3
+ height: 100%;
4
+ }
5
+
6
+ .local-video-display {
7
+ border: 1px solid rgba(0, 0, 0, 0.8);
8
+ text-align: center;
9
+ width: 200px;
10
+ height: auto;
11
+ }
12
+
13
+ .display {
14
+ width: 100%;
15
+ height: 100%;
16
+ display: inline-block;
17
+ }
18
+
19
+ .display > video, object {
20
+ width: 100%;
21
+ height: 100%;
22
+ }
23
+
24
+ video:-webkit-full-screen {
25
+ border-radius: 1px;
26
+ }
@@ -0,0 +1,47 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Bitrate test</title>
7
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet"
8
+ integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous">
9
+ <!-- JavaScript Bundle with Popper -->
10
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.bundle.min.js"
11
+ integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf"
12
+ crossorigin="anonymous"></script>
13
+ <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
14
+ <link rel="stylesheet" href="bitrate_test.css">
15
+ <script type="text/javascript" src="../sfu.js"></script>
16
+ <script type="text/javascript" src="../commons/js/util.js"></script>
17
+ <script type="text/javascript" src="../commons/js/config.js"></script>
18
+ <script type="text/javascript" src="bitrate_test.js"></script>
19
+ </head>
20
+ <body onload="init()">
21
+ <div class="container" id="main">
22
+ <div class="col-sm-12">
23
+ <h2 class="text-center">Bitrate test</h2>
24
+
25
+ <div class="row col-sm-12 justify-content-center">
26
+ <div id="connectionForm" class="col-sm-6 text-center">
27
+ <label for="url" class="control-label">Server url</label>
28
+ <input class="form-control" id="url" type="text">
29
+ <label for="bitrateTestDuration" class="control-label">Test duration, ms</label>
30
+ <input class="form-control" id="bitrateTestDuration" type="text">
31
+ </div>
32
+ </div>
33
+ <div class="row col-sm-12 justify-content-center" style="margin-top: 20px;">
34
+ <div class="text-center" style="margin-top: 20px">
35
+ <button id="bitrateTestBtn" type="button" style="height: 30px; width: auto;">Start</button>
36
+ <div class="text-center" style="margin-top: 20px">
37
+ <div id="bitrateTestStatus"></div>
38
+ <div id="bitrateTestErrorInfo"></div>
39
+ </div>
40
+ <div class="text-center" style="margin-top: 20px">
41
+ <output id="bitrateTestCurrentState">
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </body>
47
+ </html>
@@ -0,0 +1,251 @@
1
+ const constants = SFU.constants;
2
+ const sfu = SFU;
3
+ let bitrateTestState;
4
+
5
+ const BITRATE_TEST = "bitrateTest";
6
+ const TEST_DURATION = 30000;
7
+
8
+ /**
9
+ * Default publishing config
10
+ */
11
+ const defaultConfig = {
12
+ room: {
13
+ url: "ws://localhost:8080",
14
+ name: "ROOM1",
15
+ pin: "1234",
16
+ nickName: "User1",
17
+ failedProbesThreshold: 5,
18
+ pingInterval: 5000
19
+ }
20
+ };
21
+
22
+ /**
23
+ * Current state object
24
+ */
25
+ const CurrentState = function (prefix) {
26
+ let state = {
27
+ prefix: prefix,
28
+ pc: null,
29
+ session: null,
30
+ room: null,
31
+ bitrateController: null,
32
+ set: function (pc, session, room) {
33
+ state.pc = pc;
34
+ state.session = session;
35
+ state.room = room;
36
+ },
37
+ clear: function () {
38
+ state.room = null;
39
+ state.session = null;
40
+ state.pc = null;
41
+ state.bitrateController = null;
42
+ },
43
+ durationId: function () {
44
+ return state.prefix + "Duration";
45
+ },
46
+ buttonId: function () {
47
+ return state.prefix + "Btn";
48
+ },
49
+ statusId: function () {
50
+ return state.prefix + "Status";
51
+ },
52
+ errInfoId: function () {
53
+ return state.prefix + "ErrorInfo";
54
+ },
55
+ currentStateId: function () {
56
+ return state.prefix + "CurrentState";
57
+ },
58
+ getBitrateController: function () {
59
+ return state.bitrateController;
60
+ },
61
+ setBitrateController: function (controller) {
62
+ state.bitrateController = controller;
63
+ },
64
+ isConnected: function () {
65
+ return (state.session && state.session.state() === constants.SFU_STATE.CONNECTED);
66
+ }
67
+ };
68
+ return state;
69
+ }
70
+
71
+ /**
72
+ * load config and set default values
73
+ */
74
+ const init = function () {
75
+ bitrateTestState = CurrentState(BITRATE_TEST);
76
+ $("#" + bitrateTestState.buttonId()).prop('disabled', true);
77
+ $("#url").prop('disabled', true);
78
+ onDisconnected(bitrateTestState);
79
+ $("#url").val(setURL());
80
+ $("#" + bitrateTestState.durationId()).val(TEST_DURATION);
81
+ }
82
+
83
+ /**
84
+ * connect to server
85
+ */
86
+ const connect = async function (state) {
87
+ //create peer connection
88
+ const pc = new RTCPeerConnection();
89
+ //get config object for room creation
90
+ const roomConfig = getRoomConfig(defaultConfig);
91
+ roomConfig.url = $("#url").val();
92
+ roomConfig.roomName = "ROOM1-" + createUUID(4);
93
+ roomConfig.nickname = "User1" + createUUID(4);
94
+ // clean status display items
95
+ setStatus(state.statusId(), " ");
96
+ setStatus(state.errInfoId(), " ");
97
+ // clean bitrate display item
98
+ $("#" + state.currentStateId()).val("");
99
+ // connect to server and create a room if not
100
+ try {
101
+ const session = await sfu.createRoom(roomConfig);
102
+ // Set up session ending events
103
+ session.on(constants.SFU_EVENT.DISCONNECTED, function () {
104
+ onStopClick(state);
105
+ onDisconnected(state);
106
+ setStatus(state.statusId(), "DISCONNECTED", "green");
107
+ }).on(constants.SFU_EVENT.FAILED, function (e) {
108
+ onStopClick(state);
109
+ onDisconnected(state);
110
+ setStatus(state.statusId(), "FAILED", "red");
111
+ if (e.status && e.statusText) {
112
+ setStatus(state.errInfoId(), e.status + " " + e.statusText, "red");
113
+ } else if (e.type && e.info) {
114
+ setStatus(state.errInfoId(), e.type + ": " + e.info, "red");
115
+ }
116
+ });
117
+ // Connected successfully
118
+ onConnected(state, pc, session);
119
+ setStatus(state.statusId(), "ESTABLISHED", "green");
120
+ } catch (e) {
121
+ onDisconnected(state);
122
+ setStatus(state.statusId(), "FAILED", "red");
123
+ setStatus(state.errInfoId(), e, "red");
124
+ }
125
+ }
126
+
127
+ const onConnected = function (state, pc, session) {
128
+ state.set(pc, session, session.room());
129
+ $("#" + state.buttonId()).text("Stop").off('click').click(function () {
130
+ onStopClick(state);
131
+ }).prop('disabled', false);
132
+
133
+ $('#url').prop('disabled', true);
134
+ $("#" + bitrateTestState.durationId()).prop('disabled', true);
135
+ // Add errors displaying
136
+ state.room.on(constants.SFU_ROOM_EVENT.FAILED, function (e) {
137
+ setStatus(state.errInfoId(), e, "red");
138
+ onStopClick(state);
139
+ }).on(constants.SFU_ROOM_EVENT.OPERATION_FAILED, function (e) {
140
+ onOperationFailed(state, e);
141
+ }).on(constants.SFU_ROOM_EVENT.ENDED, function () {
142
+ setStatus(state.errInfoId(), "Room " + state.room.name() + " has ended", "red");
143
+ onStopClick(state);
144
+ }).on(constants.SFU_ROOM_EVENT.DROPPED, function () {
145
+ setStatus(state.errInfoId(), "Dropped from the room " + state.room.name() + " due to network issues", "red");
146
+ onStopClick(state);
147
+ });
148
+ startBitrateTest(state);
149
+ }
150
+
151
+ const onDisconnected = function (state) {
152
+ state.clear();
153
+ $("#" + state.buttonId()).text("Start").off('click').click(function () {
154
+ onStartClick(state);
155
+ }).prop('disabled', false);
156
+ $('#url').prop('disabled', false);
157
+ $("#" + bitrateTestState.durationId()).prop('disabled', false);
158
+ }
159
+
160
+ const onStartClick = function (state) {
161
+ if (validateForm("connectionForm", state.errInfoId())) {
162
+ $("#" + state.buttonId()).prop('disabled', true);
163
+ connect(state);
164
+ }
165
+ }
166
+
167
+ const startBitrateTest = async function (state) {
168
+ if (state.room) {
169
+ await state.room.join(state.pc, null, {});
170
+ const stateSelector = $("#" + state.currentStateId());
171
+ stateSelector.attr("style", "display:inline-block;margin-left: 10px");
172
+ try {
173
+ const bitrateTest = state.room.getBitrateTest();
174
+ state.setBitrateController(bitrateTest);
175
+ bitrateTest.setListener({
176
+ onStatusUpdate(bitrateKbps) {
177
+ stateSelector.text("Current bitrate: " + bitrateKbps + " kbps");
178
+ }
179
+ });
180
+ bitrateTest.test($("#" + bitrateTestState.durationId()).val()).then((bitrateKbps) => {
181
+ stateSelector.text("Test is finished, last measured bitrate: " + bitrateKbps + " kbps");
182
+ state.setBitrateController(null);
183
+ onStopClick(state);
184
+ });
185
+ } catch (e) {
186
+ if (e.type === constants.SFU_ROOM_EVENT.OPERATION_FAILED) {
187
+ onOperationFailed(state, e);
188
+ } else {
189
+ console.error("Failed to start bitrate test: " + e);
190
+ setStatus(state.errInfoId(), e.name, "red");
191
+ onStopClick(state);
192
+ }
193
+ }
194
+ }
195
+ }
196
+
197
+ const stopBitrateTest = function (state) {
198
+ const controller = state.getBitrateController();
199
+ if (controller) {
200
+ controller.stop();
201
+ }
202
+ }
203
+
204
+ const onOperationFailed = function (state, event) {
205
+ if (event.operation && event.error) {
206
+ setStatus(state.errInfoId(), event.operation + " failed: " + event.error, "red");
207
+ } else {
208
+ setStatus(state.errInfoId(), event, "red");
209
+ }
210
+ onStopClick(state);
211
+ }
212
+
213
+ const onStopClick = async function (state) {
214
+ if (state.isConnected()) {
215
+ stopBitrateTest(state);
216
+ await state.session.disconnect();
217
+ onDisconnected(state);
218
+ }
219
+ }
220
+
221
+ const setStatus = function (status, text, color) {
222
+ const field = document.getElementById(status);
223
+ if (color) {
224
+ field.style.color = color;
225
+ }
226
+ field.innerText = text;
227
+ }
228
+
229
+ const validateForm = function (formId, errorInfoId) {
230
+ let valid = true;
231
+ // Validate empty fields
232
+ $('#' + formId + ' :text').each(function () {
233
+ if (!$(this).val()) {
234
+ highlightInput($(this));
235
+ valid = false;
236
+ setStatus(errorInfoId, "Fields cannot be empty", "red");
237
+ } else {
238
+ removeHighlight($(this));
239
+ setStatus(errorInfoId, "");
240
+ }
241
+ });
242
+ return valid;
243
+
244
+ function highlightInput(input) {
245
+ input.closest('.input-group').addClass("has-error");
246
+ }
247
+
248
+ function removeHighlight(input) {
249
+ input.closest('.input-group').removeClass("has-error");
250
+ }
251
+ }