@flashphoner/sfusdk 1.0.40 → 1.0.47
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/docTemplate/README.md +1 -1
- package/package.json +6 -3
- package/src/examples/player/player.js +3 -0
- package/src/examples/two-way-streaming/two-way-streaming.js +88 -59
- package/src/sdk/constants.js +28 -2
- package/src/sdk/logger.js +16 -0
- package/src/sdk/room.js +280 -128
- package/src/sdk/sfu-extended.js +341 -11
- package/src/sdk/sfu.js +30 -1
- package/src/sdk/ws.js +4 -0
- package/src/tests/assets/1.jpeg +0 -0
- package/src/tests/lib/rtcaudiosourcesinewave.js +109 -0
- package/src/tests/lib/rtcvideosourcewrapper.js +28 -0
- package/src/tests/sdk/room.test.js +229 -0
- package/src/tests/sdk/sfu-extended.test.js +122 -8
- package/src/tests/sdk/sfu-extended.test.js_ +265 -0
- package/src/tests/util.js +48 -0
- package/src/sdk/messaging.js +0 -59
package/docTemplate/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flashphoner/sfusdk",
|
|
3
3
|
"description": "Official Flashphoner WebCallServer SFU SDK package",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.47",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "jest --runInBand"
|
|
7
7
|
},
|
|
@@ -17,13 +17,15 @@
|
|
|
17
17
|
"webrtc-adapter": "^7.2.6"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
+
"@mapbox/node-pre-gyp": "^1.0.8",
|
|
20
21
|
"grunt": "^1.0.1",
|
|
21
22
|
"grunt-browserify": "^5.0.0",
|
|
22
23
|
"grunt-contrib-clean": "^1.0.0",
|
|
23
24
|
"grunt-contrib-copy": "^1.0.0",
|
|
24
25
|
"grunt-jsdoc": "^2.4.0",
|
|
25
26
|
"grunt-run": "^0.8.1",
|
|
26
|
-
"jest": "^27.4.7"
|
|
27
|
+
"jest": "^27.4.7",
|
|
28
|
+
"wrtc": "^0.4.7"
|
|
27
29
|
},
|
|
28
30
|
"keywords": [
|
|
29
31
|
"Flashphoner",
|
|
@@ -33,6 +35,7 @@
|
|
|
33
35
|
"author": "Flashphoner",
|
|
34
36
|
"license": "MIT",
|
|
35
37
|
"jest" : {
|
|
36
|
-
"testEnvironment": "jsdom"
|
|
38
|
+
"testEnvironment": "jsdom",
|
|
39
|
+
"testRunner": "jest-jasmine2"
|
|
37
40
|
}
|
|
38
41
|
}
|
|
@@ -100,6 +100,9 @@ const connect = function(state) {
|
|
|
100
100
|
roomConfig.url = $("#url").val();
|
|
101
101
|
roomConfig.roomName = $("#roomName").val();
|
|
102
102
|
roomConfig.nickname = $("#" + state.inputId()).val();
|
|
103
|
+
// clean state display items
|
|
104
|
+
setStatus(state.statusId(), "");
|
|
105
|
+
setStatus(state.errInfoId(), "");
|
|
103
106
|
// connect to server and create a room if not
|
|
104
107
|
const session = sfu.createRoom(roomConfig);
|
|
105
108
|
session.on(constants.SFU_EVENT.CONNECTED, function(room) {
|
|
@@ -56,16 +56,37 @@ const CurrentState = function(prefix) {
|
|
|
56
56
|
pc: null,
|
|
57
57
|
session: null,
|
|
58
58
|
room: null,
|
|
59
|
+
timer: null,
|
|
59
60
|
set: function(pc, session, room) {
|
|
60
61
|
state.pc = pc;
|
|
61
62
|
state.session = session;
|
|
62
63
|
state.room = room;
|
|
63
64
|
},
|
|
64
65
|
clear: function() {
|
|
66
|
+
state.stopWaiting();
|
|
65
67
|
state.room = null;
|
|
66
68
|
state.session = null;
|
|
67
69
|
state.pc = null;
|
|
68
70
|
},
|
|
71
|
+
waitFor: function(div, timeout) {
|
|
72
|
+
state.stopWaiting();
|
|
73
|
+
state.timer = setTimeout(function () {
|
|
74
|
+
if (div.innerHTML !== "") {
|
|
75
|
+
// Enable stop button
|
|
76
|
+
$("#" + state.buttonId()).prop('disabled', false);
|
|
77
|
+
}
|
|
78
|
+
else if (state.isConnected()) {
|
|
79
|
+
setStatus(state.errInfoId(), "No media capturing started in " + timeout + " ms, stopping", "red");
|
|
80
|
+
onStopClick(state);
|
|
81
|
+
}
|
|
82
|
+
}, timeout);
|
|
83
|
+
},
|
|
84
|
+
stopWaiting: function() {
|
|
85
|
+
if (state.timer) {
|
|
86
|
+
clearTimeout(state.timer);
|
|
87
|
+
state.timer = null;
|
|
88
|
+
}
|
|
89
|
+
},
|
|
69
90
|
buttonId: function() {
|
|
70
91
|
return state.prefix + "Btn";
|
|
71
92
|
},
|
|
@@ -86,6 +107,12 @@ const CurrentState = function(prefix) {
|
|
|
86
107
|
},
|
|
87
108
|
is: function(value) {
|
|
88
109
|
return (prefix === value);
|
|
110
|
+
},
|
|
111
|
+
isActive: function() {
|
|
112
|
+
return (state.room && state.pc);
|
|
113
|
+
},
|
|
114
|
+
isConnected: function() {
|
|
115
|
+
return (state.session && state.session.state() == constants.SFU_STATE.CONNECTED);
|
|
89
116
|
}
|
|
90
117
|
};
|
|
91
118
|
return state;
|
|
@@ -165,14 +192,12 @@ const onConnected = function(state) {
|
|
|
165
192
|
// Add errors displaying
|
|
166
193
|
state.room.on(constants.SFU_ROOM_EVENT.FAILED, function(e) {
|
|
167
194
|
setStatus(state.errInfoId(), e, "red");
|
|
195
|
+
stopStreaming(state);
|
|
168
196
|
}).on(constants.SFU_ROOM_EVENT.OPERATION_FAILED, function (e) {
|
|
169
197
|
setStatus(state.errInfoId(), e.operation + " failed: " + e.error, "red");
|
|
198
|
+
stopStreaming(state);
|
|
170
199
|
});
|
|
171
|
-
|
|
172
|
-
publishStreams(state);
|
|
173
|
-
} else if (state.is(PLAY)) {
|
|
174
|
-
playStreams(state);
|
|
175
|
-
}
|
|
200
|
+
startStreaming(state);
|
|
176
201
|
}
|
|
177
202
|
|
|
178
203
|
const onDisconnected = function(state) {
|
|
@@ -204,65 +229,67 @@ const onStartClick = function(state) {
|
|
|
204
229
|
|
|
205
230
|
const onStopClick = function(state) {
|
|
206
231
|
$("#" + state.buttonId()).prop('disabled', true);
|
|
232
|
+
stopStreaming(state);
|
|
233
|
+
if (state.isConnected()) {
|
|
234
|
+
state.session.disconnect();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const startStreaming = function(state) {
|
|
207
239
|
if (state.is(PUBLISH)) {
|
|
208
|
-
|
|
240
|
+
publishStreams(state);
|
|
209
241
|
} else if (state.is(PLAY)) {
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
state.session.disconnect();
|
|
242
|
+
playStreams(state);
|
|
243
|
+
}
|
|
213
244
|
}
|
|
214
245
|
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
let streams = await getVideoStreams(mainConfig);
|
|
222
|
-
let audioStreams = await getAudioStreams(mainConfig);
|
|
223
|
-
//combine local video streams with audio streams
|
|
224
|
-
streams.push.apply(streams, audioStreams);
|
|
225
|
-
let config = {};
|
|
226
|
-
//add our local streams to the room (to PeerConnection)
|
|
227
|
-
streams.forEach(function (s) {
|
|
228
|
-
//add local stream to local display
|
|
229
|
-
localDisplay.add(s.stream.id, $("#" + state.inputId()).val(), s.stream);
|
|
230
|
-
//add each track to PeerConnection
|
|
231
|
-
s.stream.getTracks().forEach((track) => {
|
|
232
|
-
if (s.source === "screen") {
|
|
233
|
-
config[track.id] = s.source;
|
|
234
|
-
}
|
|
235
|
-
addTrackToPeerConnection(state.pc, s.stream, track, s.encodings);
|
|
236
|
-
subscribeTrackToEndedEvent(state.room, track, state.pc);
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
state.room.join(config);
|
|
240
|
-
// TODO: Use room state or promises to detect if publishing started to enable stop button
|
|
241
|
-
timerId = waitFor(document.getElementById("localVideo"), 3000, state);
|
|
242
|
-
} catch(e) {
|
|
243
|
-
console.error("Failed to capture streams: " + e);
|
|
244
|
-
setStatus(state.errInfoId(), e.name, "red");
|
|
245
|
-
if (timerId) {
|
|
246
|
-
clearTimeout(timerId);
|
|
247
|
-
timerId = null;
|
|
248
|
-
}
|
|
249
|
-
onStopClick(state);
|
|
246
|
+
const stopStreaming = function(state) {
|
|
247
|
+
state.stopWaiting();
|
|
248
|
+
if (state.is(PUBLISH)) {
|
|
249
|
+
unPublishStreams(state);
|
|
250
|
+
} else if (state.is(PLAY)) {
|
|
251
|
+
stopStreams(state);
|
|
250
252
|
}
|
|
251
253
|
}
|
|
252
254
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
255
|
+
const publishStreams = async function(state) {
|
|
256
|
+
if (state.isConnected()) {
|
|
257
|
+
//create local display item to show local streams
|
|
258
|
+
localDisplay = initLocalDisplay(document.getElementById("localVideo"));
|
|
259
|
+
try {
|
|
260
|
+
//get configured local video streams
|
|
261
|
+
let streams = await getVideoStreams(mainConfig);
|
|
262
|
+
let audioStreams = await getAudioStreams(mainConfig);
|
|
263
|
+
if (state.isConnected() && state.isActive()) {
|
|
264
|
+
//combine local video streams with audio streams
|
|
265
|
+
streams.push.apply(streams, audioStreams);
|
|
266
|
+
let config = {};
|
|
267
|
+
//add our local streams to the room (to PeerConnection)
|
|
268
|
+
streams.forEach(function (s) {
|
|
269
|
+
//add local stream to local display
|
|
270
|
+
localDisplay.add(s.stream.id, $("#" + state.inputId()).val(), s.stream);
|
|
271
|
+
//add each track to PeerConnection
|
|
272
|
+
s.stream.getTracks().forEach((track) => {
|
|
273
|
+
if (s.source === "screen") {
|
|
274
|
+
config[track.id] = s.source;
|
|
275
|
+
}
|
|
276
|
+
addTrackToPeerConnection(state.pc, s.stream, track, s.encodings);
|
|
277
|
+
subscribeTrackToEndedEvent(state.room, track, state.pc);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
state.room.join(config);
|
|
281
|
+
// TODO: Use room state or promises to detect if publishing started to enable stop button
|
|
282
|
+
state.waitFor(document.getElementById("localVideo"), 3000);
|
|
283
|
+
}
|
|
284
|
+
} catch(e) {
|
|
285
|
+
console.error("Failed to capture streams: " + e);
|
|
286
|
+
setStatus(state.errInfoId(), e.name, "red");
|
|
287
|
+
state.stopWaiting();
|
|
288
|
+
if (state.isConnected()) {
|
|
289
|
+
onStopClick(state);
|
|
290
|
+
}
|
|
263
291
|
}
|
|
264
|
-
}
|
|
265
|
-
return timerId;
|
|
292
|
+
}
|
|
266
293
|
}
|
|
267
294
|
|
|
268
295
|
const unPublishStreams = function(state) {
|
|
@@ -272,9 +299,11 @@ const unPublishStreams = function(state) {
|
|
|
272
299
|
}
|
|
273
300
|
|
|
274
301
|
const playStreams = function(state) {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
302
|
+
if (state.isConnected() && state.isActive()) {
|
|
303
|
+
//create remote display item to show remote streams
|
|
304
|
+
remoteDisplay = initRemoteDisplay(document.getElementById("remoteVideo"), state.room, state.pc);
|
|
305
|
+
state.room.join();
|
|
306
|
+
}
|
|
278
307
|
$("#" + state.buttonId()).prop('disabled', false);
|
|
279
308
|
}
|
|
280
309
|
|
package/src/sdk/constants.js
CHANGED
|
@@ -63,6 +63,10 @@ const constants = Object.freeze({
|
|
|
63
63
|
CHAT_DELETED: "CHAT_DELETED",
|
|
64
64
|
CHAT_UPDATED: "CHAT_UPDATED",
|
|
65
65
|
MESSAGE_STATE: "MESSAGE_STATE",
|
|
66
|
+
CONTACT_UPDATE: "CONTACT_UPDATE",
|
|
67
|
+
CONTACT_REMOVED: "CONTACT_REMOVED",
|
|
68
|
+
CONTACT_INVITE: "CONTACT_INVITE",
|
|
69
|
+
PUBLIC_CHANNELS: "PUBLIC_CHANNELS",
|
|
66
70
|
|
|
67
71
|
/**
|
|
68
72
|
* @typedef {Object} UserCalendar
|
|
@@ -291,6 +295,7 @@ const constants = Object.freeze({
|
|
|
291
295
|
* @memberof FlashphonerSFU.SFU_ROOM_EVENT
|
|
292
296
|
*/
|
|
293
297
|
WAITING_LIST: "SFU_WAITING_LIST",
|
|
298
|
+
WAITING_ROOM_UPDATE: "SFU_WAITING_ROOM_UPDATE",
|
|
294
299
|
/**
|
|
295
300
|
* Fires when mute/unmute state of one of the Room's participating tracks changes
|
|
296
301
|
* Event object {@link FlashphonerSFU.Room.TracksInfo}
|
|
@@ -388,7 +393,12 @@ const constants = Object.freeze({
|
|
|
388
393
|
* Room failed, something went wrong during room operations
|
|
389
394
|
* @memberOf FlashphonerSFU.SFU_ROOM_STATE
|
|
390
395
|
*/
|
|
391
|
-
FAILED: "FAILED"
|
|
396
|
+
FAILED: "FAILED",
|
|
397
|
+
/**
|
|
398
|
+
* Room disposed, leaveRoom or destroyRoom was called
|
|
399
|
+
* @memberOf FlashphonerSFU.SFU_ROOM_STATE
|
|
400
|
+
*/
|
|
401
|
+
DISPOSED: "DISPOSED"
|
|
392
402
|
}),
|
|
393
403
|
/**
|
|
394
404
|
* Internal api constants such as server side methods
|
|
@@ -409,16 +419,26 @@ const constants = Object.freeze({
|
|
|
409
419
|
USER_LIST: "SFU_USER_LIST",
|
|
410
420
|
USER_CALENDAR: "SFU_USER_CALENDAR",
|
|
411
421
|
USER_CHATS: "SFU_USER_CHATS",
|
|
422
|
+
PUBLIC_CHANNELS: "SFU_PUBLIC_CHANNELS",
|
|
412
423
|
CHAT_LOADED: "SFU_CHAT_LOADED",
|
|
413
424
|
NEW_CHAT: "SFU_NEW_CHAT",
|
|
414
425
|
CHAT_DELETED: "SFU_CHAT_DELETED",
|
|
415
426
|
CHAT_UPDATED: "SFU_UPDATE_CHAT",
|
|
427
|
+
CONTACT_UPDATED: "SFU_CONTACT_UPDATE",
|
|
428
|
+
CONTACT_INVITE: "SFU_CONTACT_INVITE",
|
|
429
|
+
CONTACT_REMOVED: "SFU_CONTACT_REMOVED",
|
|
416
430
|
GET_USER_LIST: "getUserList",
|
|
417
431
|
GET_USER_CALENDAR: "getUserCalendar",
|
|
418
432
|
ADD_CALENDAR_EVENT: "addCalendarEvent",
|
|
419
433
|
REMOVE_CALENDAR_EVENT: "removeCalendarEvent",
|
|
420
434
|
MUTE_TRACK: "muteTrack",
|
|
435
|
+
SEND_MESSAGE: "sendMessage",
|
|
421
436
|
SEND_CONTROL_MESSAGE: "sendControlMessage",
|
|
437
|
+
MARK_MESSAGE_READ: "markMessageRead",
|
|
438
|
+
MARK_MESSAGE_UNREAD: "markMessageUnread",
|
|
439
|
+
INVITE_CONTACT: "inviteContact",
|
|
440
|
+
REMOVE_CONTACT: "removeContact",
|
|
441
|
+
CONFIRM_CONTACT: "confirmContact",
|
|
422
442
|
ASSIGN_ROLE: "assignRole",
|
|
423
443
|
SUBSCRIBE_TO_WAITING_PARTICIPANT: "subscribeToWaitingParticipant",
|
|
424
444
|
UNSUBSCRIBE_FROM_WAITING_PARTICIPANT: "unsubscribeFromWaitingParticipant",
|
|
@@ -426,12 +446,18 @@ const constants = Object.freeze({
|
|
|
426
446
|
CONFIGURE_WAITING_ROOM: "configureWaitingRoom",
|
|
427
447
|
TRACK_CONTENT_HEADER: "a=content:",
|
|
428
448
|
GET_USER_CHATS: "getUserChats",
|
|
449
|
+
GET_PUBLIC_CHANNELS: "getPublicChannels",
|
|
429
450
|
LOAD_CHAT: "loadChat",
|
|
430
451
|
CREATE_CHAT: "createChat",
|
|
431
452
|
DELETE_CHAT: "deleteChat",
|
|
432
453
|
RENAME_CHAT: "renameChat",
|
|
433
454
|
ADD_MEMBER_TO_CHAT: "addMemberToChat",
|
|
434
|
-
REMOVE_MEMBER_FROM_CHAT: "removeMemberFromChat"
|
|
455
|
+
REMOVE_MEMBER_FROM_CHAT: "removeMemberFromChat",
|
|
456
|
+
UPDATE_CHANNEL_SEND_POLICY: "updateChannelSendPolicy",
|
|
457
|
+
ADD_CHANNEL_SEND_PERMISSION_LIST_MEMBER: "addChannelSendPermissionListMember",
|
|
458
|
+
REMOVE_CHANNEL_SEND_PERMISSION_LIST_MEMBER: "removeChannelSendPermissionListMember",
|
|
459
|
+
ADD_CHAT_TO_FAVOURITES: "addChatToFavourites",
|
|
460
|
+
REMOVE_CHAT_FROM_FAVOURITES: "removeChatFromFavourites"
|
|
435
461
|
}),
|
|
436
462
|
/**
|
|
437
463
|
* @namespace FlashphonerSFUExtended.SFU_OPERATIONS
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
let prefix = () => {};
|
|
2
|
+
const setPrefix = (func) => {
|
|
3
|
+
if (typeof func !== "function") {
|
|
4
|
+
throw new Error("Prefix must be a function");
|
|
5
|
+
}
|
|
6
|
+
prefix = func;
|
|
7
|
+
};
|
|
8
|
+
const log = (...args) => {
|
|
9
|
+
console.log(prefix(), ...args);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
setPrefix: setPrefix,
|
|
14
|
+
log: log,
|
|
15
|
+
info: log
|
|
16
|
+
};
|