@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
@@ -0,0 +1,220 @@
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>SFU Client</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 href="https://cdn.datatables.net/1.10.24/css/jquery.dataTables.min.css" rel="stylesheet"/>
15
+ <script src="https://cdn.datatables.net/1.10.24/js/jquery.dataTables.min.js"></script>
16
+ <link rel="stylesheet" href="main.css">
17
+ <script type="text/javascript" src="../sfu.js"></script>
18
+ <script type="text/javascript" src="chat.js"></script>
19
+ <script type="text/javascript" src="display.js"></script>
20
+ <script type="text/javascript" src="util.js"></script>
21
+ <script type="text/javascript" src="controls.js"></script>
22
+ <script type="text/javascript" src="main.js"></script>
23
+ </head>
24
+ <body onload="init()">
25
+ <div class="container-fluid" id="main">
26
+ <div id="errorMsg"></div>
27
+ <div class="row border border-primary">
28
+ </div>
29
+ <div class="row border border-primary">
30
+ <!-- ROW 2 config + chat -->
31
+ <div class="col border border-secondary">
32
+ <!-- Tracks console -->
33
+ <ul class="nav nav-tabs" id="tracksTablist" role="tablist">
34
+ <li class="nav-item" role="presentation">
35
+ <button class="nav-link active" id="videoTracksTab" data-bs-toggle="tab" data-bs-target="#videoTracks" type="button" role="tab" aria-controls="videoTracks" aria-selected="true">VideoTracks</button>
36
+ </li>
37
+ <li class="nav-item" role="presentation">
38
+ <button class="nav-link" id="audioTracksTab" data-bs-toggle="tab" data-bs-target="#audioTracks" type="button" role="tab" aria-controls="audioTracks" aria-selected="false">AudioTracks</button>
39
+ </li>
40
+ </ul>
41
+ <div class="tab-content" id="tracksTabContent">
42
+ <div class="tab-pane fade show active mt-2" id="videoTracks" role="tabpanel" aria-labelledby="raw-tab">
43
+ <table class="table" id="videoTracksTable">
44
+ <thead>
45
+ <tr>
46
+ <th></th>
47
+ <th>Source</th>
48
+ <th>Width</th>
49
+ <th>Height</th>
50
+ <th>Codec</th>
51
+ <th>Action</th>
52
+ </tr>
53
+ </thead>
54
+ <tfoot>
55
+ <tr>
56
+ <th></th>
57
+ <th>
58
+ <select class="form-select-sm" id="addVideoTrackSource">
59
+ <option value="camera" selected>camera</option>
60
+ <option value="screen">screen</option>
61
+ </select>
62
+ </th>
63
+ <th>
64
+ <input class="form-control-sm" id="addVideoTrackWidth" type="number" value="1280">
65
+ </th>
66
+ <th>
67
+ <input class="form-control-sm" id="addVideoTrackHeight" type="number" value="720">
68
+ </th>
69
+ <th>
70
+ <select class="form-select-sm" id="addVideoTrackCodec">
71
+ <option value="H264" selected>H264</option>
72
+ <option value="VP8">VP8</option>
73
+ </select>
74
+ </th>
75
+ <th>
76
+ <button class="btn btn-primary" id="addVideoTrack">Add</button>
77
+ </th>
78
+ </tr>
79
+ </tfoot>
80
+ <tbody id="videoTracksTableBody"></tbody>
81
+ </table>
82
+ <div class="row mt-4">
83
+ <div class="col-sm-1"></div>
84
+ <div class="col">
85
+ <table class="table" id="videoTrackEncodingsTable">
86
+ <thead>
87
+ <tr>
88
+ <th>RID</th>
89
+ <th>Active</th>
90
+ <th>MaxBitrate</th>
91
+ <th>ResolutionScale</th>
92
+ <th>Action</th>
93
+ </tr>
94
+ </thead>
95
+ <tfoot>
96
+ <tr>
97
+ <th>
98
+ <select class="form-select-sm" id="addVideoTrackEncodingRid">
99
+ <option value="h" selected>h</option>
100
+ <option value="m">m</option>
101
+ <option value="l">l</option>
102
+ </select>
103
+ </th>
104
+ <th>
105
+ <select class="form-select-sm" id="addVideoTrackEncodingActive">
106
+ <option value="true" selected>true</option>
107
+ <option value="false">false</option>
108
+ </select>
109
+ </th>
110
+ <th>
111
+ <input class="form-control-sm" id="addVideoTrackEncodingMaxBitrate" type="number" value="900000">
112
+ </th>
113
+ <th>
114
+ <select class="form-select-sm" id="addVideoTrackEncodingResolutionScale">
115
+ <option value="1" selected>1</option>
116
+ <option value="2">2</option>
117
+ <option value="4">4</option>
118
+ <option value="6">6</option>
119
+ </select>
120
+ </th>
121
+ <th>
122
+ <button class="btn btn-primary" id="addVideoTrackEncoding">Add</button>
123
+ </th>
124
+ </tr>
125
+ </tfoot>
126
+ <tbody id="videoTrackEncodingsTableBody"></tbody>
127
+ </table>
128
+ </div>
129
+ </div>
130
+ </div>
131
+ <div class="tab-pane fade" id="audioTracks" role="tabpanel" aria-labelledby="audioTracksTab">
132
+ <table class="table" id="audioTracksTable">
133
+ <caption class="caption-top">Audio tracks</caption>
134
+ <thead>
135
+ <tr>
136
+ <th>Source</th>
137
+ <th>Channels</th>
138
+ <th>Action</th>
139
+ </tr>
140
+ </thead>
141
+ <tfoot>
142
+ <tr>
143
+ <th>
144
+ <select class="form-select-sm" id="addAudioTrackSource">
145
+ <option value="mic" selected>mic</option>
146
+ </select>
147
+ </th>
148
+ <th>
149
+ <select class="form-select-sm" id="addAudioTrackChannels">
150
+ <option value="1" selected>1</option>
151
+ <option value="2">2</option>
152
+ </select>
153
+ </th>
154
+ <th>
155
+ <button id="addAudioTrack" class="btn btn-primary form-control-sm">Add</button>
156
+ </th>
157
+ </tr>
158
+ </tfoot>
159
+ <tbody></tbody>
160
+ </table>
161
+ </div>
162
+ </div>
163
+ </div>
164
+ <div class="col border border-secondary">
165
+ <div id="chat">
166
+ <!-- chat -->
167
+ <div id="messages" style="text-align: left"></div>
168
+ <input id="localMessage" type="text" value="">
169
+ <button id="sendMessage">send</button>
170
+ </div>
171
+ </div>
172
+ </div>
173
+ <div class="row border border-primary">
174
+ <!-- ROW3 local and remote displays -->
175
+ <div class="col border border-secondary">
176
+ <!-- Local display -->
177
+ <div id="localDisplay"></div>
178
+ </div>
179
+ <div class="col border border-secondary">
180
+ <!-- Remote display -->
181
+ <div id="display"></div>
182
+ </div>
183
+ </div>
184
+ </div>
185
+
186
+ <div class="modal" tabindex="-1" id="entranceModal">
187
+ <div class="modal-dialog">
188
+ <div class="modal-content">
189
+ <div class="modal-header">
190
+ <h5 class="modal-title">Room</h5>
191
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
192
+ </div>
193
+ <div class="modal-body">
194
+ <!-- ROW1 -->
195
+ <div class="row">
196
+ <label for="url">Server url</label>
197
+ <input id="url" type="text" class="form-control" value="ws://127.0.0.1:8080">
198
+ </div>
199
+ <div class="row">
200
+ <label for="roomName">Room name</label>
201
+ <input class="form-control" id="roomName" type="text" value="ROOM1">
202
+ </div>
203
+ <div class="row">
204
+ <label for="roomPin">PIN</label>
205
+ <input class="form-control" id="roomPin" type="text" value="1234">
206
+ </div>
207
+ <div class="row">
208
+ <label for="nickName">Nickname</label>
209
+ <input class="form-control" id="nickName" type="text" value="Bob">
210
+ </div>
211
+ </div>
212
+ <div class="modal-footer">
213
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
214
+ <button class="btn btn-primary" id="startButton" onclick="connect()">Enter</button>
215
+ </div>
216
+ </div>
217
+ </div>
218
+ </div>
219
+ </body>
220
+ </html>
@@ -0,0 +1,157 @@
1
+ const constants = SFU.constants;
2
+ const sfu = SFU;
3
+ let localDisplay;
4
+ let cControls;
5
+
6
+ const defaultConfig = {
7
+ room: {
8
+ url: "wss://127.0.0.1:8888",
9
+ name: "ROOM1",
10
+ pin: "1234",
11
+ nickName: "Alice"
12
+ },
13
+ media: {
14
+ audio: {
15
+ tracks: [
16
+ {
17
+ source: "mic",
18
+ channels: 1
19
+ }
20
+ ]
21
+ },
22
+ video: {
23
+ tracks: [
24
+ {
25
+ source: "camera",
26
+ width: 1280,
27
+ height: 720,
28
+ codec: "H264",
29
+ encodings: [
30
+ { rid: "h", active: true, maxBitrate: 900000 },
31
+ { rid: "m", active: true, maxBitrate: 300000, scaleResolutionDownBy: 2 }
32
+ ]
33
+ }
34
+ ]
35
+ }
36
+ }
37
+ };
38
+
39
+ /**
40
+ * load config and show entrance modal
41
+ */
42
+ const init = function() {
43
+ //read config
44
+ $.getJSON("config.json", function(config){
45
+ cControls = createControls(config);
46
+ }).fail(function(){
47
+ //use default config
48
+ cControls = createControls(defaultConfig);
49
+ });
50
+ //create local display to show local streams
51
+ localDisplay = initLocalDisplay(document.getElementById("localDisplay"));
52
+ //open entrance modal
53
+ $('#entranceModal').modal('show');
54
+ }
55
+
56
+ /**
57
+ * connect to server
58
+ */
59
+ function connect() {
60
+ //hide modal
61
+ $('#entranceModal').modal('hide');
62
+ //disable controls
63
+ cControls.muteInput();
64
+ //create peer connection
65
+ const pc = new RTCPeerConnection();
66
+ //get config object for room creation
67
+ const roomConfig = cControls.roomConfig();
68
+ roomConfig.pc = pc;
69
+ //kick off connect to server and local room creation
70
+ const session = sfu.createRoom(roomConfig);
71
+ session.on(constants.SFU_EVENT.CONNECTED, function() {
72
+ const room = session.room();
73
+ //connected to server
74
+ const chatDiv = document.getElementById('messages');
75
+ const chatInput = document.getElementById('localMessage');
76
+ const chatButton = document.getElementById('sendMessage');
77
+ //create and bind chat to the new room
78
+ createChat(room, chatDiv, chatInput, chatButton);
79
+
80
+ room.on(constants.SFU_ROOM_EVENT.FAILED, function(e) {
81
+ const errField = document.getElementById("errorMsg");
82
+ errField.style.color = "red";
83
+ errField.innerText = e;
84
+ }).on(constants.SFU_ROOM_EVENT.OPERATION_FAILED, function (e) {
85
+ const errField = document.getElementById("errorMsg");
86
+ errField.style.color = "red";
87
+ errField.innerText = e.operation + " failed: " + e.error;
88
+ })
89
+ //setup remote display for showing remote audio/video tracks
90
+ const remoteDisplay = document.getElementById("display");
91
+ initRemoteDisplay(room, remoteDisplay, pc);
92
+
93
+ //get configured local video streams
94
+ let streams = cControls.getVideoStreams();
95
+ //combine local video streams with audio streams
96
+ streams.push.apply(streams, cControls.getAudioStreams());
97
+ let config = {};
98
+ //add our local streams to the room (to PeerConnection)
99
+ streams.forEach(function (s) {
100
+ //add local stream to local display
101
+ localDisplay.add(s.stream.id, "local", s.stream);
102
+ //add each track to PeerConnection
103
+ s.stream.getTracks().forEach((track) => {
104
+ if (s.source === "screen") {
105
+ config[track.id] = s.source;
106
+ }
107
+ addTrackToPeerConnection(pc, s.stream, track, s.encodings);
108
+ subscribeTrackToEndedEvent(room, track, pc);
109
+ });
110
+ });
111
+ //add callback for the new local stream to the local controls
112
+ cControls.onTrack(function (s) {
113
+ let config = {};
114
+ //add local stream to local display
115
+ localDisplay.add(s.stream.id, "local", s.stream);
116
+ //add each track to PeerConnection
117
+ s.stream.getTracks().forEach((track) => {
118
+ if (s.source === "screen") {
119
+ config[track.id] = s.source;
120
+ }
121
+ addTrackToPeerConnection(pc, s.stream, track, s.encodings);
122
+ subscribeTrackToEndedEvent(room, track, pc);
123
+ });
124
+ //kickoff renegotiation
125
+ room.updateState(config);
126
+ });
127
+ //join room
128
+ room.join(config);
129
+ });
130
+ }
131
+
132
+ const subscribeTrackToEndedEvent = function(room, track, pc) {
133
+ track.addEventListener("ended", function() {
134
+ //track ended, see if we need to cleanup
135
+ let negotiate = false;
136
+ for (const sender of pc.getSenders()) {
137
+ if (sender.track === track) {
138
+ pc.removeTrack(sender);
139
+ //track found, set renegotiation flag
140
+ negotiate = true;
141
+ break;
142
+ }
143
+ }
144
+ if (negotiate) {
145
+ //kickoff renegotiation
146
+ room.updateState();
147
+ }
148
+ });
149
+ };
150
+
151
+ const addTrackToPeerConnection = function(pc, stream, track, encodings) {
152
+ pc.addTransceiver(track, {
153
+ direction: "sendonly",
154
+ streams: [stream],
155
+ sendEncodings: encodings ? encodings : [] //passing encoding types for video simulcast tracks
156
+ });
157
+ }
@@ -0,0 +1,67 @@
1
+ const stripCodecs = function(sdp, codecs) {
2
+ if (!codecs.length) return sdp;
3
+ var sdpArray = sdp.split("\n");
4
+ var codecsArray = codecs.split(",");
5
+
6
+ //search and delete codecs line
7
+ var pt = [];
8
+ var i;
9
+ for (var p = 0; p < codecsArray.length; p++) {
10
+ console.log("Searching for codec " + codecsArray[p]);
11
+ for (i = 0; i < sdpArray.length; i++) {
12
+ if (sdpArray[i].search(new RegExp(codecsArray[p],'i')) !== -1 && sdpArray[i].indexOf("a=rtpmap") === 0) {
13
+ console.log(codecsArray[p] + " detected");
14
+ pt.push(sdpArray[i].match(/[0-9]+/)[0]);
15
+ sdpArray[i] = "";
16
+ }
17
+ }
18
+ }
19
+ if (pt.length) {
20
+ //searching for fmtp
21
+ for (p = 0; p < pt.length; p++) {
22
+ for (i = 0; i < sdpArray.length; i++) {
23
+ if (sdpArray[i].search("a=fmtp:" + pt[p]) !== -1 || sdpArray[i].search("a=rtcp-fb:" + pt[p]) !== -1) {
24
+ sdpArray[i] = "";
25
+ }
26
+ }
27
+ }
28
+
29
+ //delete entries from m= line
30
+ for (i = 0; i < sdpArray.length; i++) {
31
+ if (sdpArray[i].search("m=audio") !== -1 || sdpArray[i].search("m=video") !== -1) {
32
+ var mLineSplitted = sdpArray[i].split(" ");
33
+ var newMLine = "";
34
+ for (var m = 0; m < mLineSplitted.length; m++) {
35
+ if (pt.indexOf(mLineSplitted[m].trim()) === -1 || m <= 2) {
36
+ newMLine += mLineSplitted[m];
37
+ if (m < mLineSplitted.length - 1) {
38
+ newMLine = newMLine + " ";
39
+ }
40
+ }
41
+ }
42
+ sdpArray[i] = newMLine;
43
+ }
44
+ }
45
+ }
46
+
47
+ //normalize sdp after modifications
48
+ var result = "";
49
+ for (i = 0; i < sdpArray.length; i++) {
50
+ if (sdpArray[i] !== "") {
51
+ result += sdpArray[i] + "\n";
52
+ }
53
+ }
54
+ return result;
55
+ }
56
+
57
+ const stripVideoCodecsExcept = function (sdp, codec) {
58
+ let actualStripCodec = "rtx";
59
+ if (codec === "VP8") {
60
+ actualStripCodec += ",H264";
61
+ } else if (codec === "H264") {
62
+ actualStripCodec += ",VP8";
63
+ } else {
64
+ return sdp;
65
+ }
66
+ return stripCodecs(sdp, actualStripCodec);
67
+ }
@@ -0,0 +1,81 @@
1
+ const getRoomConfig = function(config) {
2
+ let roomConfig = {
3
+ url: config.url || "ws://127.0.0.1:8080",
4
+ roomName: config.name || "ROOM1",
5
+ pin: config.pin || "1234",
6
+ nickname: config.nickName || "User1"
7
+ };
8
+ return roomConfig;
9
+ }
10
+
11
+ const getVideoStreams = async function(config) {
12
+ let streams = [];
13
+ if (config.media && config.media.video && config.media.video.tracks) {
14
+ streams = await getStreams(config.media.video.tracks);
15
+ }
16
+ return streams;
17
+ }
18
+
19
+ const getAudioStreams = async function(config) {
20
+ let streams = [];
21
+ if (config.media && config.media.audio && config.media.audio.tracks) {
22
+ streams = await getStreams(config.media.audio.tracks);
23
+ }
24
+ return streams;
25
+ }
26
+
27
+ const getStreams = async function(tracks) {
28
+ let streams = [];
29
+ for (let track of tracks) {
30
+ let stream = await getMedia(track);
31
+ if (stream) {
32
+ streams.push({
33
+ stream: stream,
34
+ encodings: track.encodings,
35
+ source: track.source
36
+ });
37
+ }
38
+ }
39
+ return streams;
40
+ }
41
+
42
+ const getMedia = async function(track) {
43
+ //convert to constraints
44
+ let screen = false;
45
+ const constraints= {};
46
+ if (track.source === "mic") {
47
+ //audio
48
+ constraints.audio = {};
49
+ if (track.constraints) {
50
+ constraints.audio = track.constraints;
51
+ }
52
+ if (track.channels && track.channels === 2) {
53
+ constraints.audio.echoCancellation = false;
54
+ constraints.audio.googEchoCancellation = false;
55
+ }
56
+ } else if (track.source === "camera") {
57
+ constraints.video = {};
58
+ if (track.constraints) {
59
+ constraints.video = track.constraints;
60
+ }
61
+ constraints.video.width = track.width;
62
+ constraints.video.height = track.height;
63
+ } else if (track.source === "screen") {
64
+ constraints.video = {};
65
+ if (track.constraints) {
66
+ constraints.video = track.constraints;
67
+ }
68
+ constraints.video.width = track.width;
69
+ constraints.video.height = track.height;
70
+ screen = true;
71
+ }
72
+
73
+ //get access to a/v
74
+ let stream;
75
+ if (screen) {
76
+ stream = await navigator.mediaDevices.getDisplayMedia(constraints);
77
+ } else {
78
+ stream = await navigator.mediaDevices.getUserMedia(constraints);
79
+ }
80
+ return stream;
81
+ }