@flashphoner/websdk 2.0.202 → 2.0.207

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 (51) hide show
  1. package/docTemplate/README.md +1 -1
  2. package/examples/demo/sip/phone/phone.js +7 -10
  3. package/examples/demo/streaming/hls-player/hls-player.html +1 -4
  4. package/examples/demo/streaming/hls-player/hls-player.js +19 -1
  5. package/examples/demo/streaming/hls-player/player-page.html +1 -1
  6. package/examples/demo/streaming/hls-player/video-js.css +142 -51
  7. package/examples/demo/streaming/hls-player/video.js +27294 -20390
  8. package/examples/demo/streaming/hls-player/video.min.js +27 -0
  9. package/examples/demo/streaming/media_devices_manager/manager.js +27 -1
  10. package/examples/demo/streaming/media_devices_manager/media_device_manager.html +7 -0
  11. package/examples/demo/streaming/stream-auto-restore/stream-auto-restore.css +23 -0
  12. package/examples/demo/streaming/stream-auto-restore/stream-auto-restore.html +76 -0
  13. package/examples/demo/streaming/stream-auto-restore/stream-auto-restore.js +356 -0
  14. package/examples/typescript/two-way-streaming-ts/.gitignore +23 -0
  15. package/examples/typescript/two-way-streaming-ts/README.md +36 -0
  16. package/examples/typescript/two-way-streaming-ts/package.json +45 -0
  17. package/examples/typescript/two-way-streaming-ts/public/favicon.ico +0 -0
  18. package/examples/typescript/two-way-streaming-ts/public/index.html +33 -0
  19. package/examples/typescript/two-way-streaming-ts/public/logo192.png +0 -0
  20. package/examples/typescript/two-way-streaming-ts/public/logo512.png +0 -0
  21. package/examples/typescript/two-way-streaming-ts/public/manifest.json +25 -0
  22. package/examples/typescript/two-way-streaming-ts/public/media/preloader.mp4 +0 -0
  23. package/examples/typescript/two-way-streaming-ts/public/robots.txt +3 -0
  24. package/examples/typescript/two-way-streaming-ts/src/TwoWayStreamingApp.css +23 -0
  25. package/examples/typescript/two-way-streaming-ts/src/TwoWayStreamingApp.tsx +371 -0
  26. package/examples/typescript/two-way-streaming-ts/src/fp-utils.ts +117 -0
  27. package/examples/typescript/two-way-streaming-ts/src/index.css +13 -0
  28. package/examples/typescript/two-way-streaming-ts/src/index.tsx +9 -0
  29. package/examples/typescript/two-way-streaming-ts/tsconfig.json +26 -0
  30. package/flashphoner-no-flash.js +19 -19
  31. package/flashphoner-no-flash.min.js +2 -2
  32. package/flashphoner-no-webrtc.js +18 -18
  33. package/flashphoner-no-webrtc.min.js +1 -1
  34. package/flashphoner-no-wsplayer.js +20 -20
  35. package/flashphoner-no-wsplayer.min.js +2 -2
  36. package/flashphoner-room-api.js +6 -6
  37. package/flashphoner-room-api.min.js +2 -2
  38. package/flashphoner-temasys-flash-websocket-without-adapterjs.js +20 -20
  39. package/flashphoner-temasys-flash-websocket.js +20 -20
  40. package/flashphoner-temasys-flash-websocket.min.js +1 -1
  41. package/flashphoner-webrtc-only.js +17 -17
  42. package/flashphoner-webrtc-only.min.js +1 -1
  43. package/flashphoner.js +20 -20
  44. package/flashphoner.min.js +2 -2
  45. package/package.json +2 -1
  46. package/src/constants.d.ts +1 -0
  47. package/src/flashphoner-core.d.ts +192 -0
  48. package/src/flashphoner-core.js +4 -4
  49. package/src/room-module.d.ts +29 -0
  50. package/src/webrtc-media-provider.js +2 -2
  51. package/examples/demo/streaming/hls-player/videojs-hls.min.js +0 -27
@@ -5,6 +5,7 @@ var STREAM_EVENT_TYPE = Flashphoner.constants.STREAM_EVENT_TYPE;
5
5
  var CONNECTION_QUALITY = Flashphoner.constants.CONNECTION_QUALITY;
6
6
  var MEDIA_DEVICE_KIND = Flashphoner.constants.MEDIA_DEVICE_KIND;
7
7
  var TRANSPORT_TYPE = Flashphoner.constants.TRANSPORT_TYPE;
8
+ var CONTENT_HINT_TYPE = Flashphoner.constants.CONTENT_HINT_TYPE;
8
9
  var CONNECTION_QUALITY_UPDATE_TIMEOUT_MS = 10000;
9
10
  var preloaderUrl = "../../dependencies/media/preloader.mp4";
10
11
  var Browser = Flashphoner.Browser;
@@ -412,6 +413,7 @@ function publish() {
412
413
  var mediaConnectionConstraints;
413
414
  var session = Flashphoner.getSessions()[0];
414
415
  var transportInput = $('#transportInput').val();
416
+ var contentHint = $('#contentHintInput').val();
415
417
  var cvo = $("#cvo").is(':checked');
416
418
  var strippedCodecs = $("#stripPublishCodecs").val();
417
419
 
@@ -434,7 +436,8 @@ function publish() {
434
436
  sdpHook: rewriteSdp,
435
437
  transport: transportInput,
436
438
  cvoExtension: cvo,
437
- stripCodecs: strippedCodecs
439
+ stripCodecs: strippedCodecs,
440
+ videoContentHint: contentHint
438
441
  }).on(STREAM_STATUS.PUBLISHING, function (stream) {
439
442
  $("#testBtn").prop('disabled', true);
440
443
  var video = document.getElementById(stream.id());
@@ -862,6 +865,29 @@ function readyControls() {
862
865
  option.value = transportType;
863
866
  transportOutput.appendChild(option);
864
867
  }
868
+
869
+ //init content hint form
870
+ var contentType;
871
+ var contentTypeValue;
872
+ var option;
873
+ var contentHintInput = document.getElementById("contentHintInput");
874
+ for (contentType in CONTENT_HINT_TYPE) {
875
+ option = document.createElement("option");
876
+ switch(contentType) {
877
+ case 'MOTION':
878
+ contentTypeValue = CONTENT_HINT_TYPE.MOTION;
879
+ break;
880
+ case 'DETAIL':
881
+ contentTypeValue = CONTENT_HINT_TYPE.DETAIL;
882
+ break;
883
+ case 'TEXT':
884
+ contentTypeValue = CONTENT_HINT_TYPE.TEXT;
885
+ break;
886
+ }
887
+ option.text = contentTypeValue;
888
+ option.value = contentTypeValue;
889
+ contentHintInput.appendChild(option);
890
+ }
865
891
  }
866
892
 
867
893
  // Stat
@@ -245,6 +245,13 @@
245
245
  <input type="checkbox" class="checkbox" id="cvo" value="false">
246
246
  </div>
247
247
  </div>
248
+ <div class="form-group" id="contentHintInputForm">
249
+ <label style="text-align: left" class="col-sm-4 control-label">Content Hint</label>
250
+ <div class="col-sm-6">
251
+ <select class="form-control" id="contentHintInput">
252
+ </select>
253
+ </div>
254
+ </div>
248
255
  <div class="form-group">
249
256
  <label style="text-align: left" class="col-sm-4 control-label">Mute</label>
250
257
  <div class="col-sm-6">
@@ -0,0 +1,23 @@
1
+ .fp-Video {
2
+ border: 1px double black;
3
+ width: 322px;
4
+ height: 242px;
5
+ text-align: center;
6
+ background: #c0c0c0;
7
+ margin: 0 auto 0 auto;
8
+ }
9
+
10
+ .display {
11
+ width: 100%;
12
+ height: 100%;
13
+ display: inline-block;
14
+ }
15
+
16
+ .display > video, object {
17
+ width: 100%;
18
+ height: 100%;
19
+ }
20
+
21
+ video:-webkit-full-screen {
22
+ border-radius: 1px;
23
+ }
@@ -0,0 +1,76 @@
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
+ <link rel="stylesheet" href="../../dependencies/bootstrap/css/bootstrap.css">
7
+ <link rel="stylesheet" href="../../dependencies/bootstrap/font-awesome/css/font-awesome.min.css">
8
+ <link rel="stylesheet" href="stream-auto-restore.css">
9
+ <title>Two-way Streaming</title>
10
+ <script type="text/javascript" src="../../../../flashphoner.js"></script>
11
+ <script type="text/javascript" src="../../dependencies/jquery/jquery-1.12.0.js"></script>
12
+ <script type="text/javascript" src="../../dependencies/js/utils.js"></script>
13
+
14
+ <script type="text/javascript" src="stream-auto-restore.js"></script>
15
+ </head>
16
+ <body onload="init_page()">
17
+ <div class="container">
18
+ <div class="row">
19
+
20
+ <h2 id="notifyFlash" class="text-danger"></h2>
21
+
22
+ <div class="col-sm-9 text-center">
23
+
24
+ <h2 class="text-center">Streaming Auto Restore</h2>
25
+
26
+ <div class="col-sm-6">
27
+ <div class="text-center text-muted">Local</div>
28
+ <div class="fp-Video">
29
+ <div id="localVideo" class="display"></div>
30
+ </div>
31
+ <div id="streamerForm" class="input-group col-sm-10" style="margin: 10px auto 0 auto;">
32
+ <input class="form-control" type="text" id="publishStream" placeholder="Stream Name">
33
+ <div class="input-group-btn">
34
+ <button id="publishBtn" type="button" class="btn btn-default">Publish</button>
35
+ </div>
36
+ </div>
37
+ <div class="text-center" style="margin-top: 20px">
38
+ <div id="publishStatus"></div>
39
+ <div id="publishInfo"></div>
40
+ </div>
41
+ </div>
42
+
43
+ <div class="col-sm-6">
44
+ <div class="text-center text-muted">Player</div>
45
+ <div class="fp-Video">
46
+ <div id="remoteVideo" class="display"></div>
47
+ </div>
48
+ <div id="playerForm" class="input-group col-sm-10" style="margin: 10px auto 0 auto;">
49
+ <input class="form-control" type="text" id="playStream" placeholder="Stream Name">
50
+ <div class="input-group-btn">
51
+ <button id="playBtn" type="button" class="btn btn-default">Play</button>
52
+ </div>
53
+ </div>
54
+ <div class="text-center" style="margin-top: 20px">
55
+ <div id="playStatus"></div>
56
+ <div id="playInfo"></div>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ <div class="row row-space">
62
+ <div class="col-sm-5 col-sm-offset-2">
63
+ <div id="connectionForm" class="input-group">
64
+ <input class="form-control" id="urlServer" type="text">
65
+ <div class="input-group-btn">
66
+ <button id="connectBtn" type="button" class="btn btn-default">Connect</button>
67
+ </div>
68
+ </div>
69
+ <div class="text-center">
70
+ <div id="connectStatus"></div>
71
+ </div>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </body>
76
+ </html>
@@ -0,0 +1,356 @@
1
+ var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS;
2
+ var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS;
3
+ var STREAM_EVENT = Flashphoner.constants.STREAM_EVENT;
4
+ var STREAM_EVENT_TYPE = Flashphoner.constants.STREAM_EVENT_TYPE;
5
+ var STREAM_STATUS_INFO = Flashphoner.constants.STREAM_STATUS_INFO;
6
+ var ERROR_INFO = Flashphoner.constants.ERROR_INFO;
7
+ var PRELOADER_URL = "../../dependencies/media/preloader.mp4";
8
+ var PUBLISH_FAILURE_DETECTOR_MAX_TRIES = 3;
9
+ var PUBLISH_FAILURE_DETECTOR_INTERVAL = 500;
10
+ var Browser = Flashphoner.Browser;
11
+ var localVideo;
12
+ var remoteVideo;
13
+ var publishFailureIntervalID;
14
+ // H264 publishing failure detection parameters using outgoing video stats in Chrome #WCS-3382
15
+ var statPublishFailureDetector = {
16
+ failed: false,
17
+ codec: "",
18
+ lastBytesSent: 0,
19
+ counter: {
20
+ value: 0,
21
+ threshold: PUBLISH_FAILURE_DETECTOR_MAX_TRIES
22
+ }
23
+ };
24
+
25
+ //////////////////////////////////
26
+ /////////////// Init /////////////
27
+
28
+ function init_page() {
29
+ //init api
30
+ try {
31
+ Flashphoner.init();
32
+ } catch (e) {
33
+ $("#notifyFlash").text("Your browser doesn't support WebRTC technology needed for this example");
34
+ return;
35
+ }
36
+
37
+ //local and remote displays
38
+ localVideo = document.getElementById("localVideo");
39
+ remoteVideo = document.getElementById("remoteVideo");
40
+
41
+ $("#urlServer").val(setURL());
42
+ var streamName = createUUID(4);
43
+ $("#publishStream").val(streamName);
44
+ $("#playStream").val(streamName);
45
+ onDisconnected();
46
+ onUnpublished();
47
+ onStopped();
48
+ }
49
+
50
+ function connect() {
51
+ var url = $('#urlServer').val();
52
+
53
+ //create session
54
+ console.log("Create new session with url " + url);
55
+ Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function (session) {
56
+ setStatus("#connectStatus", session.status());
57
+ onConnected(session);
58
+ }).on(SESSION_STATUS.DISCONNECTED, function () {
59
+ setStatus("#connectStatus", SESSION_STATUS.DISCONNECTED);
60
+ onDisconnected();
61
+ }).on(SESSION_STATUS.FAILED, function () {
62
+ setStatus("#connectStatus", SESSION_STATUS.FAILED);
63
+ onDisconnected();
64
+ });
65
+ }
66
+
67
+ function onConnected(session) {
68
+ $("#connectBtn").text("Disconnect").off('click').click(function () {
69
+ $(this).prop('disabled', true);
70
+ session.disconnect();
71
+ }).prop('disabled', false);
72
+ onUnpublished();
73
+ onStopped();
74
+ }
75
+
76
+ function onDisconnected() {
77
+ $("#connectBtn").text("Connect").off('click').click(function () {
78
+ if (validateForm("connectionForm")) {
79
+ $('#urlServer').prop('disabled', true);
80
+ $(this).prop('disabled', true);
81
+ connect();
82
+ }
83
+ }).prop('disabled', false);
84
+ $('#urlServer').prop('disabled', false);
85
+ onUnpublished();
86
+ onStopped();
87
+ }
88
+
89
+ function onPublishing(stream) {
90
+ $("#publishBtn").text("Stop").off('click').click(function () {
91
+ $(this).prop('disabled', true);
92
+ stream.stop();
93
+ }).prop('disabled', false);
94
+ $("#publishInfo").text("");
95
+ // Start publish failure detector in Chrome browser #WCS-3382
96
+ if(Browser.isChrome()) {
97
+ detectPublishFailure(stream, PUBLISH_FAILURE_DETECTOR_INTERVAL, PUBLISH_FAILURE_DETECTOR_MAX_TRIES);
98
+ }
99
+ }
100
+
101
+ function onUnpublished() {
102
+ $("#publishBtn").text("Publish").off('click').click(publishBtnClick);
103
+ if (Flashphoner.getSessions()[0] && Flashphoner.getSessions()[0].status() == SESSION_STATUS.ESTABLISHED) {
104
+ $("#publishBtn").prop('disabled', false);
105
+ $('#publishStream').prop('disabled', false);
106
+ } else {
107
+ $("#publishBtn").prop('disabled', true);
108
+ $('#publishStream').prop('disabled', true);
109
+ }
110
+ if (publishFailureIntervalID) {
111
+ clearInterval(publishFailureIntervalID);
112
+ publishFailureIntervalID = null;
113
+ }
114
+ checkPublishFailureConditions();
115
+ }
116
+
117
+ function publishBtnClick(stripCodecs) {
118
+ if (validateForm("streamerForm")) {
119
+ $('#publishStream').prop('disabled', true);
120
+ $(this).prop('disabled', true);
121
+ if (Browser.isSafariWebRTC()) {
122
+ Flashphoner.playFirstVideo(localVideo, true, PRELOADER_URL).then(function() {
123
+ publishStream(stripCodecs);
124
+ });
125
+ return;
126
+ }
127
+ publishStream(stripCodecs);
128
+ }
129
+ }
130
+
131
+ function onPlaying(stream) {
132
+ $("#playBtn").text("Stop").off('click').click(function () {
133
+ $(this).prop('disabled', true);
134
+ stream.stop();
135
+ }).prop('disabled', false);
136
+ $("#playInfo").text("");
137
+ }
138
+
139
+ function onStopped() {
140
+ $("#playBtn").text("Play").off('click').click(playBtnClick);
141
+ if (Flashphoner.getSessions()[0] && Flashphoner.getSessions()[0].status() == SESSION_STATUS.ESTABLISHED) {
142
+ $("#playBtn").prop('disabled', false);
143
+ $('#playStream').prop('disabled', false);
144
+ } else {
145
+ $("#playBtn").prop('disabled', true);
146
+ $('#playStream').prop('disabled', true);
147
+ }
148
+ }
149
+
150
+ function playBtnClick() {
151
+ if (validateForm("playerForm")) {
152
+ $('#playStream').prop('disabled', true);
153
+ $(this).prop('disabled', true);
154
+ if (Flashphoner.getMediaProviders()[0] === "WSPlayer") {
155
+ Flashphoner.playFirstSound();
156
+ } else if (Browser.isSafariWebRTC() || Flashphoner.getMediaProviders()[0] === "MSE") {
157
+ Flashphoner.playFirstVideo(remoteVideo, false, PRELOADER_URL).then(function () {
158
+ playStream();
159
+ });
160
+ return;
161
+ }
162
+ playStream();
163
+ }
164
+ }
165
+
166
+ function publishStream(stripCodecs) {
167
+ var session = Flashphoner.getSessions()[0];
168
+ var streamName = $('#publishStream').val();
169
+
170
+ session.createStream({
171
+ name: streamName,
172
+ display: localVideo,
173
+ cacheLocalResources: true,
174
+ receiveVideo: false,
175
+ receiveAudio: false,
176
+ stripCodecs: stripCodecs
177
+ }).on(STREAM_STATUS.PUBLISHING, function (stream) {
178
+ setStatus("#publishStatus", STREAM_STATUS.PUBLISHING);
179
+ onPublishing(stream);
180
+ }).on(STREAM_STATUS.UNPUBLISHED, function () {
181
+ setStatus("#publishStatus", STREAM_STATUS.UNPUBLISHED);
182
+ onUnpublished();
183
+ }).on(STREAM_STATUS.FAILED, function (stream) {
184
+ setStatus("#publishStatus", STREAM_STATUS.FAILED, stream);
185
+ onUnpublished();
186
+ }).publish();
187
+ }
188
+
189
+ function playStream() {
190
+ var session = Flashphoner.getSessions()[0];
191
+ var streamName = $('#playStream').val();
192
+
193
+ session.createStream({
194
+ name: streamName,
195
+ display: remoteVideo
196
+ }).on(STREAM_STATUS.PENDING, function (stream) {
197
+ var video = document.getElementById(stream.id());
198
+ if (!video.hasListeners) {
199
+ video.hasListeners = true;
200
+ video.addEventListener('resize', function (event) {
201
+ resizeVideo(event.target);
202
+ });
203
+ }
204
+ }).on(STREAM_STATUS.PLAYING, function (stream) {
205
+ setStatus("#playStatus", stream.status());
206
+ onPlaying(stream);
207
+ }).on(STREAM_STATUS.STOPPED, function () {
208
+ setStatus("#playStatus", STREAM_STATUS.STOPPED);
209
+ onStopped();
210
+ }).on(STREAM_STATUS.FAILED, function (stream) {
211
+ setStatus("#playStatus", STREAM_STATUS.FAILED, stream);
212
+ onStopped();
213
+ }).on(STREAM_EVENT, function(streamEvent) {
214
+ switch (streamEvent.type) {
215
+ case STREAM_EVENT_TYPE.DATA:
216
+ addPayload(streamEvent.payload);
217
+ break;
218
+ }
219
+ console.log("Received streamEvent ", streamEvent.type);
220
+ }).play();
221
+ }
222
+
223
+
224
+ //show connection, or local, or remote stream status
225
+ function setStatus(selector, status, stream) {
226
+ var statusField = $(selector);
227
+ statusField.text(status).removeClass();
228
+ if (status == "PLAYING" || status == "ESTABLISHED" || status == "PUBLISHING") {
229
+ statusField.attr("class", "text-success");
230
+ } else if (status == "DISCONNECTED" || status == "UNPUBLISHED" || status == "STOPPED") {
231
+ statusField.attr("class", "text-muted");
232
+ } else if (status == "FAILED") {
233
+ if (stream) {
234
+ if (stream.published()) {
235
+ switch(stream.getInfo()){
236
+ case STREAM_STATUS_INFO.STREAM_NAME_ALREADY_IN_USE:
237
+ $("#publishInfo").text("Server already has a publish stream with the same name, try using different one").attr("class", "text-muted");
238
+ break;
239
+ case ERROR_INFO.LOCAL_ERROR:
240
+ $("#publishInfo").text("Browser error detected: " + stream.getErrorInfo()).attr("class", "text-muted");
241
+ break;
242
+ default:
243
+ $("#publishInfo").text("Other: "+stream.getInfo()).attr("class", "text-muted");
244
+ break;
245
+ }
246
+ } else {
247
+ switch(stream.getInfo()){
248
+ case STREAM_STATUS_INFO.SESSION_DOES_NOT_EXIST:
249
+ $("#playInfo").text("Actual session does not exist").attr("class", "text-muted");
250
+ break;
251
+ case STREAM_STATUS_INFO.STOPPED_BY_PUBLISHER_STOP:
252
+ $("#playInfo").text("Related publisher stopped its stream or lost connection").attr("class", "text-muted");
253
+ break;
254
+ case STREAM_STATUS_INFO.SESSION_NOT_READY:
255
+ $("#playInfo").text("Session is not initialized or terminated on play ordinary stream").attr("class", "text-muted");
256
+ break;
257
+ case STREAM_STATUS_INFO.RTSP_STREAM_NOT_FOUND:
258
+ $("#playInfo").text("Rtsp stream not found where agent received '404-Not Found'").attr("class", "text-muted");
259
+ break;
260
+ case STREAM_STATUS_INFO.FAILED_TO_CONNECT_TO_RTSP_STREAM:
261
+ $("#playInfo").text("Failed to connect to rtsp stream").attr("class", "text-muted");
262
+ break;
263
+ case STREAM_STATUS_INFO.FILE_NOT_FOUND:
264
+ $("#playInfo").text("File does not exist, check filename").attr("class", "text-muted");
265
+ break;
266
+ case STREAM_STATUS_INFO.FILE_HAS_WRONG_FORMAT:
267
+ $("#playInfo").text("File has wrong format on play vod, this format is not supported").attr("class", "text-muted");
268
+ break;
269
+ case STREAM_STATUS_INFO.TRANSCODING_REQUIRED_BUT_DISABLED:
270
+ $("#playInfo").text("Transcoding required, but disabled in settings").attr("class", "text-muted");
271
+ break;
272
+ case STREAM_STATUS_INFO.NO_AVAILABLE_TRANSCODERS:
273
+ $("#playInfo").text("No available transcoders for stream").attr("class", "text-muted");
274
+ break;
275
+ default:
276
+ $("#playInfo").text("Other: "+stream.getInfo()).attr("class", "text-muted");
277
+ break;
278
+ }
279
+ }
280
+ }
281
+ statusField.attr("class", "text-danger");
282
+ }
283
+ }
284
+
285
+ // Detect publishing failure in Chrome using outgoing streaming stats #WCS-3382
286
+ function detectPublishFailure(stream, failureCheckInterval, maxBitrateDropsCount) {
287
+ statPublishFailureDetector = {
288
+ failed: false,
289
+ codec: "",
290
+ lastBytesSent: 0,
291
+ counter: {
292
+ value: 0,
293
+ threshold: maxBitrateDropsCount
294
+ }
295
+ }
296
+ publishFailureIntervalID = setInterval(function() {
297
+ stream.getStats(function(stat) {
298
+ let videoStats = stat.outboundStream.video;
299
+ if(!videoStats) {
300
+ return;
301
+ }
302
+ let codec = videoStats.codec;
303
+ let bytesSent = videoStats.bytesSent;
304
+ let bitrate = (bytesSent - statPublishFailureDetector.lastBytesSent) * 8;
305
+ if (bitrate == 0) {
306
+ statPublishFailureDetector.counter.value++;
307
+ console.log("Bitrate is 0 (" + statPublishFailureDetector.counter.value + ")");
308
+ if (statPublishFailureDetector.counter.value >= statPublishFailureDetector.counter.threshold) {
309
+ statPublishFailureDetector.failed = true;
310
+ console.log("Publishing seems to be failed, stop the stream");
311
+ stream.stop();
312
+ }
313
+ } else {
314
+ statPublishFailureDetector.counter.value = 0;
315
+ }
316
+ statPublishFailureDetector.lastBytesSent = bytesSent;
317
+ statPublishFailureDetector.codec = codec;
318
+ $("#publishInfo").text(statPublishFailureDetector.codec);
319
+ });
320
+ }, failureCheckInterval);
321
+ }
322
+
323
+ // Check if H264 publishing is failed in Chrome and restart as VP8 #WCS-3382
324
+ function checkPublishFailureConditions() {
325
+ if (statPublishFailureDetector.failed) {
326
+ $("#publishInfo").text("Failed to publish " + statPublishFailureDetector.codec);
327
+ if (statPublishFailureDetector.codec == "H264") {
328
+ console.log("H264 publishing seems to be failed, trying VP8 by stripping H264");
329
+ let stripCodecs = "H264";
330
+ publishBtnClick(stripCodecs);
331
+ } else if (statPublishFailureDetector.codec == "VP8") {
332
+ console.log("VP8 publishing seems to be failed, giving up");
333
+ }
334
+ }
335
+ }
336
+
337
+ function validateForm(formId) {
338
+ var valid = true;
339
+ $('#' + formId + ' :text').each(function () {
340
+ if (!$(this).val()) {
341
+ highlightInput($(this));
342
+ valid = false;
343
+ } else {
344
+ removeHighlight($(this));
345
+ }
346
+ });
347
+ return valid;
348
+
349
+ function highlightInput(input) {
350
+ input.closest('.input-group').addClass("has-error");
351
+ }
352
+
353
+ function removeHighlight(input) {
354
+ input.closest('.input-group').removeClass("has-error");
355
+ }
356
+ }
@@ -0,0 +1,23 @@
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.js
7
+
8
+ # testing
9
+ /coverage
10
+
11
+ # production
12
+ /build
13
+
14
+ # misc
15
+ .DS_Store
16
+ .env.local
17
+ .env.development.local
18
+ .env.test.local
19
+ .env.production.local
20
+
21
+ npm-debug.log*
22
+ yarn-debug.log*
23
+ yarn-error.log*
@@ -0,0 +1,36 @@
1
+ # Two Way Streaming React example application written in Typescript
2
+
3
+ This example shows how to integrate Flashphoner [WebCallServer](https://flashphoner.com) Javascript API ([WebSDK](https://www.npmjs.com/package/@flashphoner/websdk)) with Typescript typings to to React application
4
+
5
+ ## How to build
6
+
7
+ In the project directory, you can run:
8
+
9
+ ### `npm install`
10
+
11
+ Installs all the dependencies needed.
12
+
13
+ ### `npm start`
14
+
15
+ Runs the app in the development mode.\
16
+ Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
17
+
18
+ ### `npm run build`
19
+
20
+ Builds the app for production to the `build` folder.\
21
+ It correctly bundles React and WebSDK in production mode and optimizes the build for the best performance.
22
+
23
+ The build is minified and the filenames include the hashes.\
24
+ Your app is ready to be deployed!
25
+
26
+ ## How to deploy
27
+
28
+ Copy build folder content to a web server, for example
29
+ ```
30
+ mkdir -p /var/www/html/two-way-streaming-ts
31
+ cp -r build/* /var/www/html/two-way-streaming-ts
32
+ ```
33
+
34
+ Then you can open example page in browser `https://yourhost/two-way-streaming-ts/index.html`
35
+
36
+ Please note that you should open page via secure connection in browser for WebRTC to work, except localhost.
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "two-way-streaming-ts",
3
+ "description": "Two Way Streaming React appliction example written in Typescript using Flashphoner Web SDK",
4
+ "author": "Flashphoner",
5
+ "license": "MIT",
6
+ "version": "1.0.0",
7
+ "homepage": ".",
8
+ "private": true,
9
+ "dependencies": {
10
+ "@flashphoner/websdk": "^2.0.202",
11
+ "@types/jest": "^27.0.2",
12
+ "@types/node": "^16.10.3",
13
+ "@types/react": "^17.0.27",
14
+ "@types/react-dom": "^17.0.9",
15
+ "bootstrap": "^5.1.1",
16
+ "react": "^17.0.2",
17
+ "react-dom": "^17.0.2",
18
+ "react-scripts": "4.0.3",
19
+ "typescript": "^4.4.3"
20
+ },
21
+ "scripts": {
22
+ "start": "react-scripts start",
23
+ "build": "react-scripts build",
24
+ "test": "react-scripts test",
25
+ "eject": "react-scripts eject"
26
+ },
27
+ "eslintConfig": {
28
+ "extends": [
29
+ "react-app",
30
+ "react-app/jest"
31
+ ]
32
+ },
33
+ "browserslist": {
34
+ "production": [
35
+ ">0.2%",
36
+ "not dead",
37
+ "not op_mini all"
38
+ ],
39
+ "development": [
40
+ "last 1 chrome version",
41
+ "last 1 firefox version",
42
+ "last 1 safari version"
43
+ ]
44
+ }
45
+ }
@@ -0,0 +1,33 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <meta name="theme-color" content="#000000" />
8
+ <meta
9
+ name="description"
10
+ content="Two Way Streaming example created using create-react-app"
11
+ />
12
+ <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
13
+ <!--
14
+ manifest.json provides metadata used when your web app is installed on a
15
+ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
16
+ -->
17
+ <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
18
+ <!--
19
+ Notice the use of %PUBLIC_URL% in the tags above.
20
+ It will be replaced with the URL of the `public` folder during the build.
21
+ Only files inside the `public` folder can be referenced from the HTML.
22
+
23
+ Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
24
+ work correctly both with client-side routing and a non-root public URL.
25
+ Learn how to configure a non-root public URL by running `npm run build`.
26
+ -->
27
+ <title>TwoWay Streaming React App</title>
28
+ </head>
29
+ <body>
30
+ <noscript>You need to enable JavaScript to run this app.</noscript>
31
+ <div id="root"></div>
32
+ </body>
33
+ </html>