@flashphoner/sfusdk-examples 2.0.56

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.
Files changed (40) hide show
  1. package/README.md +43 -0
  2. package/package.json +32 -0
  3. package/src/client/chat.js +67 -0
  4. package/src/client/config.json +26 -0
  5. package/src/client/controls.js +314 -0
  6. package/src/client/display.js +502 -0
  7. package/src/client/main.css +45 -0
  8. package/src/client/main.html +220 -0
  9. package/src/client/main.js +157 -0
  10. package/src/client/resources/details_close.png +0 -0
  11. package/src/client/resources/details_open.png +0 -0
  12. package/src/client/util.js +67 -0
  13. package/src/commons/js/config.js +81 -0
  14. package/src/commons/js/display.js +484 -0
  15. package/src/commons/js/util.js +202 -0
  16. package/src/commons/media/silence.mp3 +0 -0
  17. package/src/controller/dependencies/sigma/sigma.renderers.edgeLabels.min.js +1 -0
  18. package/src/controller/dependencies/sigma/sigma.renderers.parallelEdges.min.js +1 -0
  19. package/src/controller/dependencies/sigma/sigma.require.js +12076 -0
  20. package/src/controller/graph-view.js +32 -0
  21. package/src/controller/main.css +45 -0
  22. package/src/controller/main.html +79 -0
  23. package/src/controller/main.js +65 -0
  24. package/src/controller/parser.js +202 -0
  25. package/src/controller/resources/details_close.png +0 -0
  26. package/src/controller/resources/details_open.png +0 -0
  27. package/src/controller/rest.js +56 -0
  28. package/src/controller/table-view.js +64 -0
  29. package/src/controller/test-data.js +382 -0
  30. package/src/player/config.json +8 -0
  31. package/src/player/player.css +19 -0
  32. package/src/player/player.html +54 -0
  33. package/src/player/player.js +209 -0
  34. package/src/sfu.ts +28 -0
  35. package/src/two-way-streaming/config.json +34 -0
  36. package/src/two-way-streaming/two-way-streaming.css +26 -0
  37. package/src/two-way-streaming/two-way-streaming.html +72 -0
  38. package/src/two-way-streaming/two-way-streaming.js +375 -0
  39. package/tsconfig.json +15 -0
  40. package/webpack.config.js +40 -0
package/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # @flashphoner/sfusdk
2
+
3
+ # Flashphoner WebCallServer SFU API (SFU SDK) usage examples
4
+
5
+ Flashphoner [WebCallServer](https://flashphoner.com) [SFU](https://docs.flashphoner.com/display/WCS52EN/SFU+functions+with+Simulcast) API (SFU SDK) usage examples.
6
+
7
+ ## Get the sources
8
+
9
+ Install as NPM package
10
+ ```
11
+ npm init
12
+ npm install @flashphoner/sfusdk-examples
13
+ cd node_modules/@flashphoner/sfusdk-examples
14
+ ```
15
+
16
+ or download from GitHub
17
+ ```
18
+ git clone https://github.com/flashphoner/sfu_sdk.git
19
+ cd sfu_sdk/examples
20
+ ```
21
+
22
+ ## Build
23
+ ```
24
+ npm install && npm run build
25
+ ```
26
+
27
+ ### Deploy to web server
28
+ ```
29
+ mkdir -p /var/www/html/flashphoner-sfu-test
30
+ cp -r dist/* /var/www/html/flashphoner-sfu-test
31
+ ```
32
+
33
+ ## Documentation
34
+
35
+ [SFU functions description](https://docs.flashphoner.com/display/WCS52EN/SFU+functions+with+Simulcast)
36
+
37
+ [SFU SDK documentation](https://docs.flashphoner.com/display/SS1E/SFU+SDK+1.0+-+EN)
38
+
39
+ [SFU examples description](https://docs.flashphoner.com/display/SS1E/SFU+SDK+Examples)
40
+
41
+ [API documentation](http://flashphoner.com/docs/api/WCS5/client/sfu-sdk/latest)
42
+
43
+
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@flashphoner/sfusdk-examples",
3
+ "version": "2.0.56",
4
+ "description": "Official Flashphoner WebCallServer SFU SDK usage examples",
5
+ "main": "dist/sfu.js",
6
+ "types": "src/sfu.ts",
7
+ "files": [
8
+ "/dist"
9
+ ],
10
+ "scripts": {
11
+ "clean": "rm -rf dist/*",
12
+ "build": "webpack --mode=production"
13
+ },
14
+ "keywords": [
15
+ "Flashphoner",
16
+ "WebCallServer",
17
+ "SFU SDK"
18
+ ],
19
+ "author": "Flashphoner",
20
+ "license": "MIT",
21
+ "devDependencies": {
22
+ "@mapbox/node-pre-gyp": "^1.0.8",
23
+ "copy-webpack-plugin": "^10.2.4",
24
+ "ts-loader": "^9.2.8",
25
+ "typescript": "^4.6.3",
26
+ "webpack": "^5.72.0",
27
+ "webpack-cli": "^4.9.2"
28
+ },
29
+ "dependencies": {
30
+ "@flashphoner/sfusdk": "^2.0.48"
31
+ }
32
+ }
@@ -0,0 +1,67 @@
1
+ const createChat = function(room, messages, input, sendButton) {
2
+ const constants = SFU.constants;
3
+ const chatSelfColour = "green";
4
+ const chatTextColour = "black";
5
+ const chatOtherColour = "red";
6
+ const chatEventColour = "navy";
7
+
8
+ room.on(constants.SFU_ROOM_EVENT.MESSAGE, function(e) {
9
+ appendMessage(e, chatOtherColour, chatTextColour);
10
+ }).on(constants.SFU_ROOM_EVENT.JOINED, function(e) {
11
+ appendMessage({
12
+ nickName: e.name,
13
+ message: e.type
14
+ }, chatOtherColour, chatEventColour);
15
+ }).on(constants.SFU_ROOM_EVENT.LEFT, function(e) {
16
+ appendMessage({
17
+ nickName: e.name,
18
+ message: e.type
19
+ }, chatOtherColour, chatEventColour);
20
+ });
21
+
22
+ const sendMessage = function() {
23
+ let message = input.value;
24
+ input.value = "";
25
+ room.sendMessage(message);
26
+ appendMessage({
27
+ nickName: nickName.value,
28
+ message: message
29
+ }, chatSelfColour, chatTextColour);
30
+ }
31
+
32
+ sendButton.addEventListener("click", sendMessage);
33
+ input.onkeyup = function(e) {
34
+ if (e.keyCode === 13) {
35
+ if (e.shiftKey) {
36
+
37
+ } else {
38
+ sendMessage();
39
+ }
40
+ return false;
41
+ }
42
+ }
43
+
44
+ const appendMessage = function(msg, nickColour, msgColour) {
45
+ let message = document.createElement('div');
46
+ message.setAttribute("class","message");
47
+ messages.appendChild(message);
48
+ let nickDiv = document.createElement('div');
49
+ nickDiv.style.color = nickColour;
50
+ nickDiv.innerText = getChatTimestamp() + " " + msg.nickName + ":";
51
+ message.appendChild(nickDiv);
52
+ let msgDiv = document.createElement('div');
53
+ msgDiv.style.color = msgColour;
54
+ msgDiv.innerText = msg.message;
55
+ message.appendChild(msgDiv);
56
+ scrollToBottom();
57
+ }
58
+
59
+ const scrollToBottom = function() {
60
+ messages.scrollTop = messages.scrollHeight;
61
+ }
62
+
63
+ const getChatTimestamp = function() {
64
+ let currentdate = new Date();
65
+ return currentdate.getHours() + ":" + currentdate.getMinutes() + ":" + currentdate.getSeconds();
66
+ }
67
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "room": {
3
+ "url": "ws://127.0.0.1:8080",
4
+ "name": "ROOM1",
5
+ "pin": "1234",
6
+ "nickName": "Bob"
7
+ },
8
+ "media": {
9
+ "audio": {
10
+ "tracks": [
11
+ ]
12
+ },
13
+ "video": {
14
+ "tracks": [{
15
+ "source": "camera",
16
+ "width": 1280,
17
+ "height": 720,
18
+ "codec": "H264",
19
+ "encodings": [
20
+ { "rid": "h", "active": true, "maxBitrate": 900000 },
21
+ { "rid": "m", "active": true, "maxBitrate": 300000, "scaleResolutionDownBy": 2 }
22
+ ]
23
+ }]
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,314 @@
1
+ const createControls = function(config) {
2
+
3
+ let trackCallback = function(){};
4
+
5
+ const controls = {
6
+ entrance: {
7
+ url: document.getElementById("url"),
8
+ roomName: document.getElementById("roomName"),
9
+ roomPin: document.getElementById("roomPin"),
10
+ nickName: document.getElementById("nickName"),
11
+ enter: document.getElementById("startButton")
12
+ },
13
+ addVideoTrack: {
14
+ source: document.getElementById("addVideoTrackSource"),
15
+ width: document.getElementById("addVideoTrackWidth"),
16
+ height: document.getElementById("addVideoTrackHeight"),
17
+ codec: document.getElementById("addVideoTrackCodec")
18
+ },
19
+ addAudioTrack: {
20
+ source: document.getElementById("addAudioTrackSource"),
21
+ channels: document.getElementById("addAudioTrackChannels")
22
+ },
23
+ addVideoEncoding: {
24
+ rid: document.getElementById("addVideoTrackEncodingRid"),
25
+ active: document.getElementById("addVideoTrackEncodingActive"),
26
+ maxBitrate: document.getElementById("addVideoTrackEncodingMaxBitrate"),
27
+ resolutionScale: document.getElementById("addVideoTrackEncodingResolutionScale")
28
+ },
29
+ tables: {
30
+ video: $('#videoTracksTable').DataTable({
31
+ "sDom": 't',
32
+ "columns": [
33
+ {
34
+ "className": 'details-control',
35
+ "orderable": false,
36
+ "data": null,
37
+ "defaultContent": ''
38
+ },
39
+ {"data": "source"},
40
+ {"data": "width"},
41
+ {"data": "height"},
42
+ {"data": "codec"},
43
+ {"data": "action"}
44
+ ]
45
+ }),
46
+ audio: $('#audioTracksTable').DataTable({
47
+ "sDom": 't',
48
+ "columns": [
49
+ {"data": "source"},
50
+ {"data": "channels"},
51
+ {"data": "action"}
52
+ ]
53
+ }),
54
+ encodings: $('#videoTrackEncodingsTable').DataTable({
55
+ "sDom": 't',
56
+ "columns": [
57
+ {"data": "rid"},
58
+ {"data": "active"},
59
+ {"data": "maxBitrate"},
60
+ {"data": "resolutionScale"},
61
+ {"data": "action"}
62
+ ]
63
+ })
64
+ }
65
+ }
66
+
67
+ //apply room config
68
+ controls.entrance.url.value = config.room.url;
69
+ controls.entrance.roomName.value = config.room.name;
70
+ controls.entrance.roomPin.value = config.room.pin;
71
+ controls.entrance.nickName.value = config.room.nickName;
72
+
73
+ const addAudioTrackRow = function(track) {
74
+ getMedia([track]).then(function(stream){
75
+ let button = '<button id="' + stream.id + '-button" class="btn btn-primary">Delete</button>';
76
+ const row = controls.tables.audio.row.add({
77
+ source: track.source,
78
+ channels: track.channels,
79
+ action: button,
80
+ stream: stream
81
+ }).node();
82
+ controls.tables.audio.draw();
83
+
84
+ $('#' + stream.id + "-button").on('click', function(){
85
+ //terminate stream
86
+ console.log("terminate stream " + stream.id);
87
+ let track = stream.getAudioTracks()[0];
88
+ track.stop();
89
+ track.dispatchEvent(new Event("ended"));
90
+ });
91
+ stream.getTracks()[0].onended = function() {
92
+ controls.tables.audio.row(row).remove().draw();
93
+ }
94
+ trackCallback({
95
+ stream: stream,
96
+ encodings: track.encodings,
97
+ source: track.source
98
+ });
99
+ });
100
+ }
101
+
102
+ const addVideoTrackRow = function(track) {
103
+ getMedia([track]).then(function(stream){
104
+ let button = '<button id="' + stream.id + '-button" class="btn btn-primary">Delete</button>';
105
+ const row = controls.tables.video.row.add({
106
+ source: track.source,
107
+ width: track.width,
108
+ height: track.height,
109
+ codec: track.codec,
110
+ action: button,
111
+ stream: stream,
112
+ encodings: track.encodings
113
+ }).node();
114
+ controls.tables.video.draw();
115
+
116
+ $('#' + stream.id + "-button").on('click', function(){
117
+ //terminate stream
118
+ console.log("terminate stream " + stream.id);
119
+ let track = stream.getVideoTracks()[0];
120
+ track.stop();
121
+ track.dispatchEvent(new Event("ended"));
122
+ });
123
+ stream.getTracks()[0].addEventListener("ended", function() {
124
+ controls.tables.video.row(row).remove().draw();
125
+ });
126
+ trackCallback({
127
+ stream: stream,
128
+ encodings: track.encodings,
129
+ source: track.source
130
+ });
131
+ });
132
+ }
133
+
134
+ const format = function(d) {
135
+ if (!d.encodings) {
136
+ return;
137
+ }
138
+ let details = '<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">';
139
+ d.encodings.forEach(function(encoding){
140
+ details += '<tr>';
141
+ for (const [key, value] of Object.entries(encoding)) {
142
+ details += '<td>'+ key + '</td>'+
143
+ '<td>'+ value + '</td>';
144
+ }
145
+ details += '</tr>';
146
+ });
147
+ details +='</table>';
148
+ return details;
149
+ }
150
+
151
+ // Add event listener for opening and closing details
152
+ $('#videoTracksTableBody').on('click', 'td.details-control', function () {
153
+ let tr = $(this).closest('tr');
154
+ let row = controls.tables.video.row(tr);
155
+ if (row.child.isShown()) {
156
+ // This row is already open - close it
157
+ row.child.hide();
158
+ tr.removeClass('shown');
159
+ } else {
160
+ // Open this row
161
+ row.child(format(row.data())).show();
162
+ tr.addClass('shown');
163
+ }
164
+ });
165
+
166
+ config.media.audio.tracks.forEach(function(track){
167
+ addAudioTrackRow(track);
168
+ })
169
+ config.media.video.tracks.forEach(function(track){
170
+ addVideoTrackRow(track);
171
+ })
172
+
173
+ const muteForm = function(form) {
174
+ for (const [key, value] of Object.entries(form)) {
175
+ value.disabled = true;
176
+ }
177
+ }
178
+
179
+ const unmuteForm = function(form) {
180
+ for (const [key, value] of Object.entries(form)) {
181
+ value.disabled = false;
182
+ }
183
+ }
184
+
185
+ const muteInput = function() {
186
+ muteForm(controls.entrance);
187
+ }
188
+
189
+ const roomConfig = function() {
190
+ return {
191
+ url: controls.entrance.url.value,
192
+ roomName: controls.entrance.roomName.value,
193
+ pin: controls.entrance.roomPin.value,
194
+ nickname: controls.entrance.nickName.value
195
+ }
196
+ }
197
+
198
+ const getVideoStreams = function() {
199
+ let streams = [];
200
+ controls.tables.video.rows().every(function(rowIdx, tableLoop, rowLoop) {
201
+ let data = this.data();
202
+ streams.push({
203
+ stream: data.stream,
204
+ encodings: data.encodings,
205
+ source: data.source
206
+ });
207
+ });
208
+ return streams;
209
+ }
210
+ const getAudioStreams = function() {
211
+ let streams = [];
212
+ controls.tables.audio.rows().every(function(rowIdx, tableLoop, rowLoop) {
213
+ let data = this.data();
214
+ streams.push({
215
+ stream: data.stream,
216
+ encodings: [],
217
+ source: data.source
218
+ });
219
+ });
220
+ return streams;
221
+ }
222
+
223
+ document.getElementById("addVideoTrack").addEventListener("click", function(e){
224
+ let encodings = [];
225
+ controls.tables.encodings.rows().every(function() {
226
+ let encoding = this.data();
227
+ encodings.push({
228
+ rid: encoding.rid,
229
+ active: encoding.active,
230
+ maxBitrate: encoding.maxBitrate,
231
+ scaleResolutionDownBy: encoding.resolutionScale
232
+ })
233
+ });
234
+ let track = {
235
+ source: controls.addVideoTrack.source.value,
236
+ width: controls.addVideoTrack.width.value,
237
+ height: controls.addVideoTrack.height.value,
238
+ codec: controls.addVideoTrack.codec.value,
239
+ encodings: encodings
240
+ }
241
+ addVideoTrackRow(track);
242
+ });
243
+
244
+ $("#videoTrackEncodingsTable").on("click", ".remove", function(){
245
+ controls.tables.encodings.row($(this).parents('tr')).remove().draw();
246
+ });
247
+
248
+ document.getElementById("addVideoTrackEncoding").addEventListener("click", function(){
249
+ let button = '<button class="btn btn-primary remove">Delete</button>';
250
+ controls.tables.encodings.row.add({
251
+ rid: controls.addVideoEncoding.rid.value,
252
+ active: controls.addVideoEncoding.active.value,
253
+ maxBitrate: controls.addVideoEncoding.maxBitrate.value,
254
+ resolutionScale: controls.addVideoEncoding.resolutionScale.value,
255
+ action: button
256
+ }).draw();
257
+ });
258
+
259
+ document.getElementById("addAudioTrack").addEventListener("click", function(e){
260
+ let encodings = [];
261
+ let track = {
262
+ source: controls.addAudioTrack.source.value,
263
+ channels: controls.addAudioTrack.channels.value,
264
+ encodings: encodings
265
+ }
266
+ addAudioTrackRow(track);
267
+ });
268
+
269
+ const onTrack = function(callback) {
270
+ trackCallback = callback;
271
+ }
272
+
273
+
274
+ return {
275
+ muteInput: muteInput,
276
+ roomConfig: roomConfig,
277
+ getAudioStreams: getAudioStreams,
278
+ getVideoStreams: getVideoStreams,
279
+ onTrack: onTrack
280
+ }
281
+ }
282
+
283
+ const getMedia = async function(tracks) {
284
+ //convert to constraints
285
+ let screen = false;
286
+ const constraints= {};
287
+ tracks.forEach(function(track){
288
+ if (track.source === "mic") {
289
+ //audio
290
+ constraints.audio = {};
291
+ constraints.audio.stereo = track.channels !== 1
292
+ } else if (track.source === "camera") {
293
+ constraints.video = {
294
+ width: track.width,
295
+ height: track.height
296
+ };
297
+ } else if (track.source === "screen") {
298
+ constraints.video = {
299
+ width: track.width,
300
+ height: track.height
301
+ };
302
+ screen = true;
303
+ }
304
+ });
305
+
306
+ //get access to a/v
307
+ let stream;
308
+ if (screen) {
309
+ stream = await navigator.mediaDevices.getDisplayMedia(constraints);
310
+ } else {
311
+ stream = await navigator.mediaDevices.getUserMedia(constraints);
312
+ }
313
+ return stream;
314
+ }