@flashphoner/websdk 2.0.208 → 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.
- package/docTemplate/README.md +1 -1
- package/examples/demo/streaming/stream-auto-restore/stream-auto-restore.html +77 -1
- package/examples/demo/streaming/stream-auto-restore/stream-auto-restore.js +472 -84
- package/flashphoner-no-flash.js +84 -3
- package/flashphoner-no-flash.min.js +1 -1
- package/flashphoner-no-webrtc.js +84 -3
- package/flashphoner-no-webrtc.min.js +1 -1
- package/flashphoner-no-wsplayer.js +84 -3
- package/flashphoner-no-wsplayer.min.js +1 -1
- package/flashphoner-room-api.js +74 -1
- package/flashphoner-room-api.min.js +1 -1
- package/flashphoner-temasys-flash-websocket-without-adapterjs.js +84 -3
- package/flashphoner-temasys-flash-websocket.js +84 -3
- package/flashphoner-temasys-flash-websocket.min.js +1 -1
- package/flashphoner-webrtc-only.js +84 -3
- package/flashphoner-webrtc-only.min.js +1 -1
- package/flashphoner.js +84 -3
- package/flashphoner.min.js +1 -1
- package/package.json +1 -1
- package/src/flashphoner-core.d.ts +4 -1
- package/src/flashphoner-core.js +74 -1
|
@@ -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
|
|
14
|
-
|
|
15
|
-
var
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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({
|
|
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(
|
|
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
|
|
96
|
-
if(
|
|
97
|
-
|
|
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 (
|
|
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
|
-
|
|
111
|
-
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
214
|
-
|
|
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
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
314
|
-
|
|
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
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
503
|
+
restarter.counter.reset();
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
restarter.init();
|
|
507
|
+
|
|
508
|
+
return(restarter);
|
|
321
509
|
}
|
|
322
510
|
|
|
323
|
-
//
|
|
324
|
-
function
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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
|
-
|
|
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
|
-
|
|
581
|
+
if (clickButton) {
|
|
582
|
+
click("playBtn");
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
348
586
|
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
354
|
-
|
|
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
|
}
|