@flashphoner/websdk 2.0.205 → 2.0.209

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.
@@ -0,0 +1,744 @@
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 RESTART_MAX_TRIES = 100;
11
+ var RESTART_TIMEOUT = 3000;
12
+ var MAX_PINGS_MISSING = 10;
13
+ var PING_CHECK_TIMEOUT = 5000;
14
+ var Browser = Flashphoner.Browser;
15
+ var localVideo;
16
+ var remoteVideo;
17
+ var h264PublishFailureDetector;
18
+ var currentSession;
19
+ var streamPublishing;
20
+ var streamPlaying;
21
+ var streamingRestarter;
22
+ var connection;
23
+ var connectionType;
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
+ // 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
+
59
+ $("#urlServer").val(setURL());
60
+ var streamName = createUUID(4);
61
+ $("#publishStream").val(streamName);
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);
69
+ onDisconnected();
70
+ onUnpublished();
71
+ onStopped();
72
+ }
73
+
74
+ function connect() {
75
+ var restoreConnection = $("#restoreConnection").is(':checked');
76
+ var url = $('#urlServer').val();
77
+ var receiveProbes = restoreConnection ? $("#maxPingsMissing").val() : 0;
78
+ var probesInterval = restoreConnection ? $("#pingsPeriod").val() : 0;
79
+
80
+ //create session
81
+ console.log("Create new session with url " + url);
82
+ Flashphoner.createSession({
83
+ urlServer: url,
84
+ receiveProbes: receiveProbes,
85
+ probesInterval: probesInterval
86
+ }).on(SESSION_STATUS.ESTABLISHED, function (session) {
87
+ setStatus("#connectStatus", session.status());
88
+ currentSession.set(url, session);
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
+ }
101
+ }).on(SESSION_STATUS.DISCONNECTED, function () {
102
+ setStatus("#connectStatus", SESSION_STATUS.DISCONNECTED);
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
+ }
111
+ }).on(SESSION_STATUS.FAILED, function () {
112
+ setStatus("#connectStatus", SESSION_STATUS.FAILED);
113
+ onDisconnected();
114
+ if(restoreConnection
115
+ && (streamPublishing.wasActive || streamPlaying.wasActive)) {
116
+ streamingRestarter.restart($("#restoreTimeout").val(), $("#restoreMaxTries").val());
117
+ }
118
+ });
119
+ }
120
+
121
+ function onConnected(session) {
122
+ $("#connectBtn").text("Disconnect").off('click').click(function () {
123
+ $(this).prop('disabled', true);
124
+ currentSession.isManuallyDisconnected = true;
125
+ session.disconnect();
126
+ }).prop('disabled', false);
127
+ onUnpublished();
128
+ onStopped();
129
+ }
130
+
131
+ function onDisconnected() {
132
+ $("#connectBtn").text("Connect").off('click').click(function () {
133
+ if (validateForm()) {
134
+ $('#urlServer').prop('disabled', true);
135
+ $(this).prop('disabled', true);
136
+ disableForm('reconnectForm', true);
137
+ connect();
138
+ }
139
+ }).prop('disabled', false);
140
+ $('#urlServer').prop('disabled', false);
141
+ onUnpublished();
142
+ onStopped();
143
+ disableForm('bitrateForm', false);
144
+ disableForm('reconnectForm', false);
145
+ }
146
+
147
+ function onPublishing(stream) {
148
+ $("#publishBtn").text("Stop").off('click').click(function () {
149
+ $(this).prop('disabled', true);
150
+ streamPublishing.isManuallyStopped = true;
151
+ stream.stop();
152
+ }).prop('disabled', false);
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
+ }
158
+ }
159
+
160
+ function onUnpublished() {
161
+ $("#publishBtn").text("Publish").off('click').click(publishBtnClick);
162
+ if (currentSession.getStatus() == SESSION_STATUS.ESTABLISHED) {
163
+ $("#publishBtn").prop('disabled', false);
164
+ $('#publishStream').prop('disabled', false);
165
+ } else {
166
+ $("#publishBtn").prop('disabled', true);
167
+ $('#publishStream').prop('disabled', true);
168
+ }
169
+ h264PublishFailureDetector.stopDetection(streamPublishing.isManuallyStopped || currentSession.isManuallyDisconnected);
170
+ disableForm('bitrateForm', false);
171
+ }
172
+
173
+ function publishBtnClick(stripCodecs) {
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()) {
180
+ $('#publishStream').prop('disabled', true);
181
+ $(this).prop('disabled', true);
182
+ disableForm('bitrateForm', true);
183
+ if (Browser.isSafariWebRTC()) {
184
+ Flashphoner.playFirstVideo(localVideo, true, PRELOADER_URL).then(function() {
185
+ publishStream(stripCodecs);
186
+ });
187
+ return;
188
+ }
189
+ publishStream(stripCodecs);
190
+ }
191
+ }
192
+
193
+ function onPlaying(stream) {
194
+ $("#playBtn").text("Stop").off('click').click(function () {
195
+ $(this).prop('disabled', true);
196
+ stream.stop();
197
+ }).prop('disabled', false);
198
+ $("#playInfo").text("");
199
+ }
200
+
201
+ function onStopped() {
202
+ $("#playBtn").text("Play").off('click').click(playBtnClick);
203
+ if (Flashphoner.getSessions()[0] && Flashphoner.getSessions()[0].status() == SESSION_STATUS.ESTABLISHED) {
204
+ $("#playBtn").prop('disabled', false);
205
+ $('#playStream').prop('disabled', false);
206
+ } else {
207
+ $("#playBtn").prop('disabled', true);
208
+ $('#playStream').prop('disabled', true);
209
+ }
210
+ }
211
+
212
+ function playBtnClick() {
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()) {
219
+ $('#playStream').prop('disabled', true);
220
+ $(this).prop('disabled', true);
221
+ if (Flashphoner.getMediaProviders()[0] === "WSPlayer") {
222
+ Flashphoner.playFirstSound();
223
+ } else if (Browser.isSafariWebRTC() || Flashphoner.getMediaProviders()[0] === "MSE") {
224
+ Flashphoner.playFirstVideo(remoteVideo, false, PRELOADER_URL).then(function () {
225
+ playStream();
226
+ });
227
+ return;
228
+ }
229
+ playStream();
230
+ }
231
+ }
232
+
233
+ function publishStream(stripCodecs) {
234
+ var session = Flashphoner.getSessions()[0];
235
+ var streamName = $('#publishStream').val();
236
+
237
+ session.createStream({
238
+ name: streamName,
239
+ display: localVideo,
240
+ cacheLocalResources: true,
241
+ receiveVideo: false,
242
+ receiveAudio: false,
243
+ stripCodecs: stripCodecs
244
+ }).on(STREAM_STATUS.PUBLISHING, function (stream) {
245
+ setStatus("#publishStatus", STREAM_STATUS.PUBLISHING);
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
+ }
254
+ }).on(STREAM_STATUS.UNPUBLISHED, function () {
255
+ setStatus("#publishStatus", STREAM_STATUS.UNPUBLISHED);
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();
265
+ }).on(STREAM_STATUS.FAILED, function (stream) {
266
+ setStatus("#publishStatus", STREAM_STATUS.FAILED, stream);
267
+ onUnpublished();
268
+ if ($("#restoreConnection").is(':checked') && stream.getInfo() != ERROR_INFO.LOCAL_ERROR) {
269
+ streamingRestarter.restart($("#restoreTimeout").val(), $("#restoreMaxTries").val());
270
+ }
271
+ }).publish();
272
+ }
273
+
274
+ function playStream() {
275
+ var session = Flashphoner.getSessions()[0];
276
+ var streamName = $('#playStream').val();
277
+
278
+ session.createStream({
279
+ name: streamName,
280
+ display: remoteVideo
281
+ }).on(STREAM_STATUS.PENDING, function (stream) {
282
+ var video = document.getElementById(stream.id());
283
+ if (!video.hasListeners) {
284
+ video.hasListeners = true;
285
+ video.addEventListener('resize', function (event) {
286
+ resizeVideo(event.target);
287
+ });
288
+ }
289
+ }).on(STREAM_STATUS.PLAYING, function (stream) {
290
+ setStatus("#playStatus", stream.status());
291
+ onPlaying(stream);
292
+ streamingRestarter.reset();
293
+ streamPlaying.set(streamName, stream);
294
+ }).on(STREAM_STATUS.STOPPED, function () {
295
+ setStatus("#playStatus", STREAM_STATUS.STOPPED);
296
+ onStopped();
297
+ streamingRestarter.reset();
298
+ streamPlaying.clear();
299
+ }).on(STREAM_STATUS.FAILED, function (stream) {
300
+ setStatus("#playStatus", STREAM_STATUS.FAILED, stream);
301
+ onStopped();
302
+ if ($("#restoreConnection").is(':checked')) {
303
+ streamingRestarter.restart($("#restoreTimeout").val(), $("#restoreMaxTries").val());
304
+ }
305
+ }).play();
306
+ }
307
+
308
+
309
+ //show connection, or local, or remote stream status
310
+ function setStatus(selector, status, stream) {
311
+ var statusField = $(selector);
312
+ statusField.text(status).removeClass();
313
+ if (status == "PLAYING" || status == "ESTABLISHED" || status == "PUBLISHING") {
314
+ statusField.attr("class", "text-success");
315
+ } else if (status == "DISCONNECTED" || status == "UNPUBLISHED" || status == "STOPPED") {
316
+ statusField.attr("class", "text-muted");
317
+ } else if (status == "FAILED") {
318
+ if (stream) {
319
+ if (stream.published()) {
320
+ switch(stream.getInfo()){
321
+ case STREAM_STATUS_INFO.STREAM_NAME_ALREADY_IN_USE:
322
+ $("#publishInfo").text("Server already has a publish stream with the same name, try using different one").attr("class", "text-muted");
323
+ break;
324
+ case ERROR_INFO.LOCAL_ERROR:
325
+ $("#publishInfo").text("Browser error detected: " + stream.getErrorInfo()).attr("class", "text-muted");
326
+ break;
327
+ default:
328
+ $("#publishInfo").text("Other: "+stream.getInfo()).attr("class", "text-muted");
329
+ break;
330
+ }
331
+ } else {
332
+ switch(stream.getInfo()){
333
+ case STREAM_STATUS_INFO.SESSION_DOES_NOT_EXIST:
334
+ $("#playInfo").text("Actual session does not exist").attr("class", "text-muted");
335
+ break;
336
+ case STREAM_STATUS_INFO.STOPPED_BY_PUBLISHER_STOP:
337
+ $("#playInfo").text("Related publisher stopped its stream or lost connection").attr("class", "text-muted");
338
+ break;
339
+ case STREAM_STATUS_INFO.SESSION_NOT_READY:
340
+ $("#playInfo").text("Session is not initialized or terminated on play ordinary stream").attr("class", "text-muted");
341
+ break;
342
+ case STREAM_STATUS_INFO.RTSP_STREAM_NOT_FOUND:
343
+ $("#playInfo").text("Rtsp stream not found where agent received '404-Not Found'").attr("class", "text-muted");
344
+ break;
345
+ case STREAM_STATUS_INFO.FAILED_TO_CONNECT_TO_RTSP_STREAM:
346
+ $("#playInfo").text("Failed to connect to rtsp stream").attr("class", "text-muted");
347
+ break;
348
+ case STREAM_STATUS_INFO.FILE_NOT_FOUND:
349
+ $("#playInfo").text("File does not exist, check filename").attr("class", "text-muted");
350
+ break;
351
+ case STREAM_STATUS_INFO.FILE_HAS_WRONG_FORMAT:
352
+ $("#playInfo").text("File has wrong format on play vod, this format is not supported").attr("class", "text-muted");
353
+ break;
354
+ case STREAM_STATUS_INFO.TRANSCODING_REQUIRED_BUT_DISABLED:
355
+ $("#playInfo").text("Transcoding required, but disabled in settings").attr("class", "text-muted");
356
+ break;
357
+ case STREAM_STATUS_INFO.NO_AVAILABLE_TRANSCODERS:
358
+ $("#playInfo").text("No available transcoders for stream").attr("class", "text-muted");
359
+ break;
360
+ default:
361
+ $("#playInfo").text("Other: "+stream.getInfo()).attr("class", "text-muted");
362
+ break;
363
+ }
364
+ }
365
+ }
366
+ statusField.attr("class", "text-danger");
367
+ }
368
+ }
369
+
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 = {
400
+ failed: false,
401
+ codec: "",
402
+ lastBytesSent: 0,
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");
483
+ return;
484
+ }
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;
491
+ }
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;
502
+ }
503
+ restarter.counter.reset();
504
+ }
505
+ };
506
+ restarter.init();
507
+
508
+ return(restarter);
509
+ }
510
+
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");
545
+ }
546
+ }
547
+ }
548
+
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;
564
+ } else {
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
+ }
580
+ }
581
+ if (clickButton) {
582
+ click("playBtn");
583
+ }
584
+ }
585
+ }
586
+
587
+ // Helper function to click a button
588
+ function click(buttonId) {
589
+ let selector = "#" + buttonId;
590
+ if (!$(selector).prop('disabled')) {
591
+ $(selector).click();
592
+ }
593
+ }
594
+
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
+ }
742
+ }
743
+ return(false);
744
+ }