@flashphoner/websdk 2.0.208 → 2.0.212

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 (38) hide show
  1. package/docTemplate/README.md +1 -1
  2. package/examples/demo/streaming/2players/2players.js +0 -5
  3. package/examples/demo/streaming/canvas_streaming/canvas_streaming.js +0 -12
  4. package/examples/demo/streaming/conference/conference.html +6 -0
  5. package/examples/demo/streaming/conference/conference.js +83 -23
  6. package/examples/demo/streaming/embed_player/player.js +127 -198
  7. package/examples/demo/streaming/firewall-traversal-streaming/firewall-traversal-streaming.js +0 -12
  8. package/examples/demo/streaming/mcu_client/mcu_client.js +0 -8
  9. package/examples/demo/streaming/media_devices_manager/manager.js +0 -12
  10. package/examples/demo/streaming/player/player.js +10 -9
  11. package/examples/demo/streaming/stream-auto-restore/stream-auto-restore.html +77 -1
  12. package/examples/demo/streaming/stream-auto-restore/stream-auto-restore.js +472 -84
  13. package/examples/demo/streaming/stream-diagnostic/stream-diagnostic.js +0 -8
  14. package/examples/demo/streaming/stream-local-snapshot/stream-local-snapshot.js +0 -6
  15. package/examples/demo/streaming/stream-snapshot/stream-snapshot.js +0 -6
  16. package/examples/demo/streaming/stream_recording/recording.js +0 -6
  17. package/examples/demo/streaming/streamer/streamer.js +0 -8
  18. package/examples/demo/streaming/two_way_streaming/two_way_streaming.js +0 -11
  19. package/examples/demo/streaming/webrtc-as-rtmp-republishing/webrtc-as-rtmp-republishing.js +0 -6
  20. package/flashphoner-no-flash.js +118 -14
  21. package/flashphoner-no-flash.min.js +2 -2
  22. package/flashphoner-no-webrtc.js +89 -5
  23. package/flashphoner-no-webrtc.min.js +1 -1
  24. package/flashphoner-no-wsplayer.js +118 -14
  25. package/flashphoner-no-wsplayer.min.js +2 -2
  26. package/flashphoner-room-api.js +104 -9
  27. package/flashphoner-room-api.min.js +2 -2
  28. package/flashphoner-temasys-flash-websocket-without-adapterjs.js +89 -5
  29. package/flashphoner-temasys-flash-websocket.js +89 -5
  30. package/flashphoner-temasys-flash-websocket.min.js +1 -1
  31. package/flashphoner-webrtc-only.js +118 -14
  32. package/flashphoner-webrtc-only.min.js +1 -1
  33. package/flashphoner.js +118 -14
  34. package/flashphoner.min.js +2 -2
  35. package/package.json +1 -1
  36. package/src/flashphoner-core.d.ts +22 -5
  37. package/src/flashphoner-core.js +79 -3
  38. package/src/webrtc-media-provider.js +25 -6
@@ -7,20 +7,20 @@ var ERROR_INFO = Flashphoner.constants.ERROR_INFO;
7
7
  var PRELOADER_URL = "../../dependencies/media/preloader.mp4";
8
8
  var PUBLISH_FAILURE_DETECTOR_MAX_TRIES = 3;
9
9
  var PUBLISH_FAILURE_DETECTOR_INTERVAL = 500;
10
+ var RESTART_MAX_TRIES = 100;
11
+ var RESTART_TIMEOUT = 3000;
12
+ var MAX_PINGS_MISSING = 10;
13
+ var PING_CHECK_TIMEOUT = 5000;
10
14
  var Browser = Flashphoner.Browser;
11
15
  var localVideo;
12
16
  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
- };
17
+ var h264PublishFailureDetector;
18
+ var currentSession;
19
+ var streamPublishing;
20
+ var streamPlaying;
21
+ var streamingRestarter;
22
+ var connection;
23
+ var connectionType;
24
24
 
25
25
  //////////////////////////////////
26
26
  /////////////// Init /////////////
@@ -38,35 +38,90 @@ function init_page() {
38
38
  localVideo = document.getElementById("localVideo");
39
39
  remoteVideo = document.getElementById("remoteVideo");
40
40
 
41
+ // session and streams state objects
42
+ currentSession = sessionState();
43
+ streamPublishing = streamState();
44
+ streamPlaying = streamState();
45
+ // Publish failure detector object #WCS-3382
46
+ h264PublishFailureDetector = codecPublishingFailureDetector();
47
+ // Publishing/playback restarter object #WCS-3410
48
+ streamingRestarter = streamRestarter(function() {
49
+ if (streamPublishing.wasActive) {
50
+ onPublishRestart();
51
+ }
52
+ if (streamPlaying.wasActive && streamPlaying.name != streamPublishing.name) {
53
+ onPlayRestart();
54
+ }
55
+ });
56
+ // Start network change detection #WCS-3410
57
+ networkChangeDetector();
58
+
41
59
  $("#urlServer").val(setURL());
42
60
  var streamName = createUUID(4);
43
61
  $("#publishStream").val(streamName);
44
62
  $("#playStream").val(streamName);
63
+ $("#bitrateInteval").val(PUBLISH_FAILURE_DETECTOR_INTERVAL);
64
+ $("#bitrateMaxTries").val(PUBLISH_FAILURE_DETECTOR_MAX_TRIES);
65
+ $("#restoreTimeout").val(RESTART_TIMEOUT);
66
+ $("#restoreMaxTries").val(RESTART_MAX_TRIES);
67
+ $("#maxPingsMissing").val(MAX_PINGS_MISSING);
68
+ $("#pingsPeriod").val(PING_CHECK_TIMEOUT);
45
69
  onDisconnected();
46
70
  onUnpublished();
47
71
  onStopped();
48
72
  }
49
73
 
50
74
  function connect() {
75
+ var restoreConnection = $("#restoreConnection").is(':checked');
51
76
  var url = $('#urlServer').val();
77
+ var receiveProbes = restoreConnection ? $("#maxPingsMissing").val() : 0;
78
+ var probesInterval = restoreConnection ? $("#pingsPeriod").val() : 0;
52
79
 
53
80
  //create session
54
81
  console.log("Create new session with url " + url);
55
- Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function (session) {
82
+ Flashphoner.createSession({
83
+ urlServer: url,
84
+ receiveProbes: receiveProbes,
85
+ probesInterval: probesInterval
86
+ }).on(SESSION_STATUS.ESTABLISHED, function (session) {
56
87
  setStatus("#connectStatus", session.status());
88
+ currentSession.set(url, session);
57
89
  onConnected(session);
90
+ if(restoreConnection) {
91
+ if(streamPublishing.wasActive) {
92
+ console.log("A stream was published before disconnection, restart publishing");
93
+ onPublishRestart();
94
+ return;
95
+ }
96
+ if(streamPlaying.wasActive) {
97
+ console.log("A stream was played before disconnection, restart playback");
98
+ onPlayRestart();
99
+ }
100
+ }
58
101
  }).on(SESSION_STATUS.DISCONNECTED, function () {
59
102
  setStatus("#connectStatus", SESSION_STATUS.DISCONNECTED);
60
103
  onDisconnected();
104
+ // Prevent streaming restart if session is manually disconnected
105
+ if (currentSession.isManuallyDisconnected) {
106
+ streamPublishing.clear();
107
+ streamPlaying.clear();
108
+ streamingRestarter.reset();
109
+ currentSession.clear();
110
+ }
61
111
  }).on(SESSION_STATUS.FAILED, function () {
62
112
  setStatus("#connectStatus", SESSION_STATUS.FAILED);
63
113
  onDisconnected();
114
+ if(restoreConnection
115
+ && (streamPublishing.wasActive || streamPlaying.wasActive)) {
116
+ streamingRestarter.restart($("#restoreTimeout").val(), $("#restoreMaxTries").val());
117
+ }
64
118
  });
65
119
  }
66
120
 
67
121
  function onConnected(session) {
68
122
  $("#connectBtn").text("Disconnect").off('click').click(function () {
69
123
  $(this).prop('disabled', true);
124
+ currentSession.isManuallyDisconnected = true;
70
125
  session.disconnect();
71
126
  }).prop('disabled', false);
72
127
  onUnpublished();
@@ -75,49 +130,56 @@ function onConnected(session) {
75
130
 
76
131
  function onDisconnected() {
77
132
  $("#connectBtn").text("Connect").off('click').click(function () {
78
- if (validateForm("connectionForm")) {
133
+ if (validateForm()) {
79
134
  $('#urlServer').prop('disabled', true);
80
135
  $(this).prop('disabled', true);
136
+ disableForm('reconnectForm', true);
81
137
  connect();
82
138
  }
83
139
  }).prop('disabled', false);
84
140
  $('#urlServer').prop('disabled', false);
85
141
  onUnpublished();
86
142
  onStopped();
143
+ disableForm('bitrateForm', false);
144
+ disableForm('reconnectForm', false);
87
145
  }
88
146
 
89
147
  function onPublishing(stream) {
90
148
  $("#publishBtn").text("Stop").off('click').click(function () {
91
149
  $(this).prop('disabled', true);
150
+ streamPublishing.isManuallyStopped = true;
92
151
  stream.stop();
93
152
  }).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
- }
153
+ $("#publishInfo").text("");
154
+ // Start publish failure detector by bitrate #WCS-3382
155
+ if($("#checkBitrate").is(':checked')) {
156
+ h264PublishFailureDetector.startDetection(stream, $("#bitrateInteval").val(), $("#bitrateMaxTries").val());
157
+ }
99
158
  }
100
159
 
101
160
  function onUnpublished() {
102
161
  $("#publishBtn").text("Publish").off('click').click(publishBtnClick);
103
- if (Flashphoner.getSessions()[0] && Flashphoner.getSessions()[0].status() == SESSION_STATUS.ESTABLISHED) {
162
+ if (currentSession.getStatus() == SESSION_STATUS.ESTABLISHED) {
104
163
  $("#publishBtn").prop('disabled', false);
105
164
  $('#publishStream').prop('disabled', false);
106
165
  } else {
107
166
  $("#publishBtn").prop('disabled', true);
108
167
  $('#publishStream').prop('disabled', true);
109
168
  }
110
- if (publishFailureIntervalID) {
111
- clearInterval(publishFailureIntervalID);
112
- publishFailureIntervalID = null;
113
- }
114
- checkPublishFailureConditions();
169
+ h264PublishFailureDetector.stopDetection(streamPublishing.isManuallyStopped || currentSession.isManuallyDisconnected);
170
+ disableForm('bitrateForm', false);
115
171
  }
116
172
 
117
173
  function publishBtnClick(stripCodecs) {
118
- if (validateForm("streamerForm")) {
174
+ if (currentSession.getStatus() != SESSION_STATUS.ESTABLISHED) {
175
+ // Prevent stream publishing if session is in wrong state
176
+ console.error("Can't publish, session is not established");
177
+ return;
178
+ }
179
+ if (validateForm()) {
119
180
  $('#publishStream').prop('disabled', true);
120
181
  $(this).prop('disabled', true);
182
+ disableForm('bitrateForm', true);
121
183
  if (Browser.isSafariWebRTC()) {
122
184
  Flashphoner.playFirstVideo(localVideo, true, PRELOADER_URL).then(function() {
123
185
  publishStream(stripCodecs);
@@ -148,7 +210,12 @@ function onStopped() {
148
210
  }
149
211
 
150
212
  function playBtnClick() {
151
- if (validateForm("playerForm")) {
213
+ if (currentSession.getStatus() != SESSION_STATUS.ESTABLISHED) {
214
+ // Prevent stream publishing if session is in wrong state
215
+ console.error("Can't play, session is not established");
216
+ return;
217
+ }
218
+ if (validateForm()) {
152
219
  $('#playStream').prop('disabled', true);
153
220
  $(this).prop('disabled', true);
154
221
  if (Flashphoner.getMediaProviders()[0] === "WSPlayer") {
@@ -177,12 +244,30 @@ function publishStream(stripCodecs) {
177
244
  }).on(STREAM_STATUS.PUBLISHING, function (stream) {
178
245
  setStatus("#publishStatus", STREAM_STATUS.PUBLISHING);
179
246
  onPublishing(stream);
247
+ streamPublishing.set(streamName, stream);
248
+ streamingRestarter.reset();
249
+ if ($("#restoreConnection").is(':checked')
250
+ && streamPlaying.wasActive) {
251
+ console.log("A stream was played before, restart playback");
252
+ onPlayRestart();
253
+ }
180
254
  }).on(STREAM_STATUS.UNPUBLISHED, function () {
181
255
  setStatus("#publishStatus", STREAM_STATUS.UNPUBLISHED);
182
256
  onUnpublished();
257
+ if (!streamPlaying.wasActive) {
258
+ // No stream playback< we don't need restart any more
259
+ streamingRestarter.reset();
260
+ } else if (streamPlaying.wasActive && streamPlaying.name == streamPublishing.name) {
261
+ // Prevent playback restart for the same stream
262
+ streamingRestarter.reset();
263
+ }
264
+ streamPublishing.clear();
183
265
  }).on(STREAM_STATUS.FAILED, function (stream) {
184
266
  setStatus("#publishStatus", STREAM_STATUS.FAILED, stream);
185
267
  onUnpublished();
268
+ if ($("#restoreConnection").is(':checked') && stream.getInfo() != ERROR_INFO.LOCAL_ERROR) {
269
+ streamingRestarter.restart($("#restoreTimeout").val(), $("#restoreMaxTries").val());
270
+ }
186
271
  }).publish();
187
272
  }
188
273
 
@@ -204,19 +289,19 @@ function playStream() {
204
289
  }).on(STREAM_STATUS.PLAYING, function (stream) {
205
290
  setStatus("#playStatus", stream.status());
206
291
  onPlaying(stream);
292
+ streamingRestarter.reset();
293
+ streamPlaying.set(streamName, stream);
207
294
  }).on(STREAM_STATUS.STOPPED, function () {
208
295
  setStatus("#playStatus", STREAM_STATUS.STOPPED);
209
296
  onStopped();
297
+ streamingRestarter.reset();
298
+ streamPlaying.clear();
210
299
  }).on(STREAM_STATUS.FAILED, function (stream) {
211
300
  setStatus("#playStatus", STREAM_STATUS.FAILED, stream);
212
301
  onStopped();
213
- }).on(STREAM_EVENT, function(streamEvent) {
214
- switch (streamEvent.type) {
215
- case STREAM_EVENT_TYPE.DATA:
216
- addPayload(streamEvent.payload);
217
- break;
302
+ if ($("#restoreConnection").is(':checked')) {
303
+ streamingRestarter.restart($("#restoreTimeout").val(), $("#restoreMaxTries").val());
218
304
  }
219
- console.log("Received streamEvent ", streamEvent.type);
220
305
  }).play();
221
306
  }
222
307
 
@@ -282,75 +367,378 @@ function setStatus(selector, status, stream) {
282
367
  }
283
368
  }
284
369
 
285
- // Detect publishing failure in Chrome using outgoing streaming stats #WCS-3382
286
- function detectPublishFailure(stream, failureCheckInterval, maxBitrateDropsCount) {
287
- statPublishFailureDetector = {
370
+ function validateForm() {
371
+ var valid = true;
372
+ $(':text').each(function () {
373
+ if (!$(this).val()) {
374
+ highlightInput($(this));
375
+ valid = false;
376
+ } else {
377
+ removeHighlight($(this));
378
+ }
379
+ });
380
+ return valid;
381
+
382
+ function highlightInput(input) {
383
+ input.closest('.input-group').addClass("has-error");
384
+ }
385
+
386
+ function removeHighlight(input) {
387
+ input.closest('.input-group').removeClass("has-error");
388
+ }
389
+ }
390
+
391
+ function disableForm(formId, disable) {
392
+ $('#' + formId + ' :input').each(function () {
393
+ $(this).prop('disabled', disable);
394
+ });
395
+ }
396
+
397
+ // H264 publishing failure detector using outgoing video stats in Chrome #WCS-3382
398
+ function codecPublishingFailureDetector() {
399
+ var detector = {
288
400
  failed: false,
289
401
  codec: "",
290
402
  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) {
403
+ counter: null,
404
+ publishFailureIntervalID: null,
405
+ startDetection: function(stream, failureCheckInterval, maxBitrateDropsCount) {
406
+ detector.failed = false;
407
+ detector.lastBytesSent = 0;
408
+ detector.counter = counterWithThreshold(maxBitrateDropsCount);
409
+ detector.publishFailureIntervalID = setInterval(function() {
410
+ // Detect publishing failure in Chrome using outgoing streaming stats #WCS-3382
411
+ stream.getStats(function(stat) {
412
+ let videoStats = stat.outboundStream.video;
413
+ if(!videoStats) {
414
+ return;
415
+ }
416
+ let stats_codec = videoStats.codec;
417
+ let bytesSent = videoStats.bytesSent;
418
+ let bitrate = (bytesSent - detector.lastBytesSent) * 8;
419
+ if (bitrate == 0) {
420
+ detector.counter.inc();
421
+ console.log("Bitrate is 0 (" + detector.counter.getCurrent() + ")");
422
+ if (detector.counter.exceeded()) {
423
+ detector.failed = true;
424
+ console.log("Publishing seems to be failed, stop the stream");
425
+ stream.stop();
426
+ }
427
+ } else {
428
+ detector.counter.reset();
429
+ }
430
+ detector.lastBytesSent = bytesSent;
431
+ detector.codec = stats_codec;
432
+ $("#publishInfo").text(detector.codec);
433
+ });
434
+ }, failureCheckInterval);
435
+ },
436
+ stopDetection: function(isManuallyStopped) {
437
+ if (detector.publishFailureIntervalID) {
438
+ clearInterval(detector.publishFailureIntervalID);
439
+ detector.publishFailureIntervalID = null;
440
+ }
441
+ // Clear failed state if streaming is stopped manually
442
+ if (isManuallyStopped) {
443
+ detector.failed = false;
444
+ }
445
+ // Check if bitrate is constantly 0 #WCS-3382
446
+ if (detector.failed) {
447
+ $("#publishInfo").text("Failed to publish " + detector.codec);
448
+ if($("#changeCodec").is(':checked')) {
449
+ // Try to change codec from H264 to VP8 #WCS-3382
450
+ if (detector.codec == "H264") {
451
+ console.log("H264 publishing seems to be failed, trying VP8 by stripping H264");
452
+ let stripCodecs = "H264";
453
+ publishBtnClick(stripCodecs);
454
+ } else if (detector.codec == "VP8") {
455
+ console.log("VP8 publishing seems to be failed, giving up");
456
+ }
457
+ } else {
458
+ // Try to republish with the same codec #WCS-3410
459
+ publishBtnClick();
460
+ }
461
+ }
462
+ }
463
+ };
464
+
465
+ return(detector);
466
+ }
467
+
468
+ // Restart publishing or playback automatically #WCS-3410
469
+ function streamRestarter(onRestart) {
470
+ let logger = Flashphoner.getLogger();
471
+ var restarter = {
472
+ counter: null,
473
+ restartTimerId: null,
474
+ init: function() {
475
+ restarter.counter = counterWithThreshold(RESTART_MAX_TRIES);
476
+ },
477
+ restart: function(restartTimeout, restartMaxTimes) {
478
+ if (restarter.restartTimerId) {
479
+ return;
480
+ }
481
+ if (restartMaxTimes < 1) {
482
+ console.log("Streaming will not be restarted");
300
483
  return;
301
484
  }
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();
485
+ restarter.counter.set(restartMaxTimes);
486
+ restarter.restartTimerId = setInterval(function(){
487
+ if (restarter.counter.exceeded()) {
488
+ logger.info("Tried to restart for " + restartMaxTimes + " times with " +restartTimeout + " ms interval, cancelled");
489
+ restarter.reset();
490
+ return;
312
491
  }
313
- } else {
314
- statPublishFailureDetector.counter.value = 0;
492
+ onRestart();
493
+ restarter.counter.inc();
494
+ }, restartTimeout);
495
+ logger.info("Timer " + restarter.restartTimerId + " started to restart streaming after " + restartTimeout + " ms interval");
496
+ },
497
+ reset: function() {
498
+ if (restarter.restartTimerId) {
499
+ clearInterval(restarter.restartTimerId);
500
+ logger.info("Timer " + restarter.restartTimerId + " stopped");
501
+ restarter.restartTimerId = null;
315
502
  }
316
- statPublishFailureDetector.lastBytesSent = bytesSent;
317
- statPublishFailureDetector.codec = codec;
318
- $("#publishInfo").text(statPublishFailureDetector.codec);
319
- });
320
- }, failureCheckInterval);
503
+ restarter.counter.reset();
504
+ }
505
+ };
506
+ restarter.init();
507
+
508
+ return(restarter);
321
509
  }
322
510
 
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");
511
+ // Function to invoke when publishing restart timeout is fired
512
+ function onPublishRestart() {
513
+ let logger = Flashphoner.getLogger();
514
+ let sessions = Flashphoner.getSessions();
515
+ if (!sessions.length || sessions[0].status() == SESSION_STATUS.FAILED) {
516
+ logger.info("Restart session to publish");
517
+ click("connectBtn");
518
+ } else {
519
+ let streams = sessions[0].getStreams();
520
+ let stream = null;
521
+ let clickButton = false;
522
+ if (streams.length == 0) {
523
+ // No streams in session, try to restart publishing
524
+ logger.info("No streams in session, restart publishing");
525
+ clickButton = true;
526
+ } else {
527
+ // If there is already a stream, check its state and restart publishing if needed
528
+ for (let i = 0; i < streams.length; i++) {
529
+ if (streams[i].name() === $('#publishStream').val()) {
530
+ stream = streams[i];
531
+ if (!isStreamPublishing(stream)) {
532
+ logger.info("Restart stream " + stream.name() + " publishing");
533
+ clickButton = true;
534
+ }
535
+ break;
536
+ }
537
+ }
538
+ if (!stream) {
539
+ logger.info("Restart stream publishing");
540
+ clickButton = true;
541
+ }
542
+ }
543
+ if (clickButton) {
544
+ click("publishBtn");
333
545
  }
334
546
  }
335
547
  }
336
548
 
337
- function validateForm(formId) {
338
- var valid = true;
339
- $('#' + formId + ' :text').each(function () {
340
- if (!$(this).val()) {
341
- highlightInput($(this));
342
- valid = false;
549
+ // Function to invoke when playing restart timeout is fired
550
+ function onPlayRestart() {
551
+ let logger = Flashphoner.getLogger();
552
+ let sessions = Flashphoner.getSessions();
553
+ if (!sessions.length || sessions[0].status() == SESSION_STATUS.FAILED) {
554
+ logger.info("Restart session to play");
555
+ click("connectBtn");
556
+ } else {
557
+ let streams = sessions[0].getStreams();
558
+ let stream = null;
559
+ let clickButton = false;
560
+ if (streams.length == 0) {
561
+ // No streams in session, try to restart playing
562
+ logger.info("No streams in session, restart playback");
563
+ clickButton = true;
343
564
  } else {
344
- removeHighlight($(this));
565
+ // If there is already a stream, check its state and restart playing if needed
566
+ for (let i = 0; i < streams.length; i++) {
567
+ if (streams[i].name() === $('#playStream').val()) {
568
+ stream = streams[i];
569
+ if (!isStreamPlaying(stream)) {
570
+ logger.info("Restart stream " + stream.name() + " playback");
571
+ clickButton = true;
572
+ }
573
+ break;
574
+ }
575
+ }
576
+ if (!stream) {
577
+ logger.info("Restart stream playback");
578
+ clickButton = true;
579
+ }
345
580
  }
346
- });
347
- return valid;
581
+ if (clickButton) {
582
+ click("playBtn");
583
+ }
584
+ }
585
+ }
348
586
 
349
- function highlightInput(input) {
350
- input.closest('.input-group').addClass("has-error");
587
+ // Helper function to click a button
588
+ function click(buttonId) {
589
+ let selector = "#" + buttonId;
590
+ if (!$(selector).prop('disabled')) {
591
+ $(selector).click();
351
592
  }
593
+ }
352
594
 
353
- function removeHighlight(input) {
354
- input.closest('.input-group').removeClass("has-error");
595
+ // Stream publishing status helper function
596
+ function isStreamPublishing(stream) {
597
+ switch(stream.status()) {
598
+ case STREAM_STATUS.PENDING:
599
+ case STREAM_STATUS.PUBLISHING:
600
+ return true;
601
+ default:
602
+ return false;
603
+ }
604
+ }
605
+
606
+ // Stream status helper function
607
+ function isStreamPlaying(stream) {
608
+ switch(stream.status()) {
609
+ case STREAM_STATUS.PENDING:
610
+ case STREAM_STATUS.PLAYING:
611
+ case STREAM_STATUS.RESIZE:
612
+ case STREAM_STATUS.SNAPSHOT_COMPLETE:
613
+ case STREAM_STATUS.NOT_ENOUGH_BANDWIDTH:
614
+ return true;
615
+ default:
616
+ return false;
617
+ }
618
+ }
619
+
620
+ // Helper counter with threshold
621
+ function counterWithThreshold(threshold) {
622
+ var counter = {
623
+ value: 0,
624
+ threshold: threshold,
625
+ set: function(newThreshold) {
626
+ counter.threshold = newThreshold;
627
+ },
628
+ inc: function() {
629
+ counter.value++;
630
+ },
631
+ reset: function() {
632
+ counter.value = 0;
633
+ },
634
+ exceeded: function() {
635
+ return(counter.value >= counter.threshold);
636
+ },
637
+ getCurrent: function() {
638
+ return(counter.value);
639
+ }
640
+ };
641
+
642
+ return(counter);
643
+ }
644
+
645
+ // Session state object
646
+ function sessionState() {
647
+ var session = {
648
+ url: "",
649
+ isManuallyDisconnected: false,
650
+ sdkSession: null,
651
+ set: function(url, sdkSession) {
652
+ session.url = url;
653
+ session.sdkSession = sdkSession;
654
+ session.isManuallyDisconnected = false;
655
+ },
656
+ clear: function() {
657
+ session.url = "";
658
+ session.sdkSession = null;
659
+ session.isManuallyDisconnected = false;
660
+ },
661
+ getStatus: function() {
662
+ if (session.sdkSession) {
663
+ return(session.sdkSession.status());
664
+ }
665
+ return(SESSION_STATUS.DISCONNECTED);
666
+ }
667
+ };
668
+
669
+ return(session);
670
+ }
671
+
672
+ // Stream state object
673
+ function streamState() {
674
+ var stream = {
675
+ name: "",
676
+ wasActive: false,
677
+ isManuallyStopped: false,
678
+ sdkStream: null,
679
+ set: function(name, sdkStream) {
680
+ stream.name = name;
681
+ stream.sdkStream = sdkStream;
682
+ stream.isManuallyStopped = false;
683
+ stream.wasActive = true;
684
+ },
685
+ clear: function() {
686
+ stream.name = "";
687
+ stream.sdkStream = null;
688
+ stream.isManuallyStopped = false;
689
+ stream.wasActive = false;
690
+ }
691
+ };
692
+
693
+ return(stream);
694
+ }
695
+
696
+ // Network change detection using Network Information API #WCS-3410
697
+ function networkChangeDetector() {
698
+ // The API is supported in Chromium browsers and Firefox for Android
699
+ if (Browser.isSafariWebRTC()) {
700
+ return;
701
+ }
702
+ if (Browser.isChrome() || (Browser.isFirefox() && Browser.isAndroid())) {
703
+ connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
704
+ if (connection) {
705
+ connectionType = connection.type;
706
+ if (Browser.isFirefox()) {
707
+ connection.ontypechange = onNetworkChange;
708
+ } else {
709
+ connection.onchange = onNetworkChange;
710
+ }
711
+ }
712
+ }
713
+ }
714
+
715
+ function onNetworkChange() {
716
+ if (connection) {
717
+ if (connection.type === undefined) {
718
+ return;
719
+ }
720
+ // If network type is changed, close the session
721
+ console.log("connectionType = " + connectionType + ", connection.type = " + connection.type);
722
+ if (isNetworkConnected() && connection.type != connectionType) {
723
+ if (currentSession.getStatus() == SESSION_STATUS.ESTABLISHED) {
724
+ let logger = Flashphoner.getLogger();
725
+ logger.info("Close session due to network change from " + connectionType + " to " + connection.type);
726
+ currentSession.sdkSession.disconnect();
727
+ }
728
+ }
729
+ connectionType = connection.type;
730
+ }
731
+ }
732
+
733
+ function isNetworkConnected() {
734
+ if (connection) {
735
+ switch (connection.type) {
736
+ case "cellular":
737
+ case "ethernet":
738
+ case "wifi":
739
+ case "wimax":
740
+ return(true);
741
+ }
355
742
  }
743
+ return(false);
356
744
  }