@flashphoner/websdk 2.0.203 → 2.0.208

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 (34) 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 +29 -6
  10. package/examples/demo/streaming/media_devices_manager/media_device_manager.html +7 -0
  11. package/examples/demo/streaming/screen-sharing/screen-sharing.html +1 -1
  12. package/examples/demo/streaming/screen-sharing/screen-sharing.js +1 -1
  13. package/examples/demo/streaming/stream-auto-restore/stream-auto-restore.css +23 -0
  14. package/examples/demo/streaming/stream-auto-restore/stream-auto-restore.html +76 -0
  15. package/examples/demo/streaming/stream-auto-restore/stream-auto-restore.js +356 -0
  16. package/flashphoner-no-flash.js +20 -20
  17. package/flashphoner-no-flash.min.js +2 -2
  18. package/flashphoner-no-webrtc.js +18 -18
  19. package/flashphoner-no-webrtc.min.js +1 -1
  20. package/flashphoner-no-wsplayer.js +21 -21
  21. package/flashphoner-no-wsplayer.min.js +2 -2
  22. package/flashphoner-room-api.js +7 -7
  23. package/flashphoner-room-api.min.js +2 -2
  24. package/flashphoner-temasys-flash-websocket-without-adapterjs.js +20 -20
  25. package/flashphoner-temasys-flash-websocket.js +20 -20
  26. package/flashphoner-temasys-flash-websocket.min.js +1 -1
  27. package/flashphoner-webrtc-only.js +18 -18
  28. package/flashphoner-webrtc-only.min.js +1 -1
  29. package/flashphoner.js +21 -21
  30. package/flashphoner.min.js +2 -2
  31. package/package.json +1 -1
  32. package/src/flashphoner-core.js +4 -4
  33. package/src/webrtc-media-provider.js +3 -3
  34. 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;
@@ -61,12 +62,9 @@ function init_page() {
61
62
  localVideo = document.getElementById("localVideo");
62
63
  remoteVideo = document.getElementById("remoteVideo");
63
64
 
64
- if(!Browser.isChrome() && !Browser.isFirefox()) {
65
+ if(Browser.isAndroid() || Browser.isiOS()) {
65
66
  $('#screenShareForm').hide();
66
67
  }
67
- if (!Browser.isFirefox()) {
68
- $('#mediaSourceForm').hide();
69
- }
70
68
 
71
69
  Flashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.OUTPUT).then(function (list) {
72
70
  list.audio.forEach(function (device) {
@@ -412,6 +410,7 @@ function publish() {
412
410
  var mediaConnectionConstraints;
413
411
  var session = Flashphoner.getSessions()[0];
414
412
  var transportInput = $('#transportInput').val();
413
+ var contentHint = $('#contentHintInput').val();
415
414
  var cvo = $("#cvo").is(':checked');
416
415
  var strippedCodecs = $("#stripPublishCodecs").val();
417
416
 
@@ -434,7 +433,8 @@ function publish() {
434
433
  sdpHook: rewriteSdp,
435
434
  transport: transportInput,
436
435
  cvoExtension: cvo,
437
- stripCodecs: strippedCodecs
436
+ stripCodecs: strippedCodecs,
437
+ videoContentHint: contentHint
438
438
  }).on(STREAM_STATUS.PUBLISHING, function (stream) {
439
439
  $("#testBtn").prop('disabled', true);
440
440
  var video = document.getElementById(stream.id());
@@ -641,7 +641,7 @@ function switchToScreen() {
641
641
  if (publishStream) {
642
642
  $('#switchBtn').prop('disabled', true);
643
643
  $('#videoInput').prop('disabled', true);
644
- publishStream.switchToScreen($('#mediaSource').val()).catch(function () {
644
+ publishStream.switchToScreen($('#mediaSource').val(), true).catch(function () {
645
645
  $("#screenShareToggle").removeAttr("checked");
646
646
  $('#switchBtn').prop('disabled', false);
647
647
  $('#videoInput').prop('disabled', false);
@@ -862,6 +862,29 @@ function readyControls() {
862
862
  option.value = transportType;
863
863
  transportOutput.appendChild(option);
864
864
  }
865
+
866
+ //init content hint form
867
+ var contentType;
868
+ var contentTypeValue;
869
+ var option;
870
+ var contentHintInput = document.getElementById("contentHintInput");
871
+ for (contentType in CONTENT_HINT_TYPE) {
872
+ option = document.createElement("option");
873
+ switch(contentType) {
874
+ case 'MOTION':
875
+ contentTypeValue = CONTENT_HINT_TYPE.MOTION;
876
+ break;
877
+ case 'DETAIL':
878
+ contentTypeValue = CONTENT_HINT_TYPE.DETAIL;
879
+ break;
880
+ case 'TEXT':
881
+ contentTypeValue = CONTENT_HINT_TYPE.TEXT;
882
+ break;
883
+ }
884
+ option.text = contentTypeValue;
885
+ option.value = contentTypeValue;
886
+ contentHintInput.appendChild(option);
887
+ }
865
888
  }
866
889
 
867
890
  // 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">
@@ -29,7 +29,7 @@
29
29
  <h2 class="modal-title text-center text-danger">Warning!</h2>
30
30
  </div>
31
31
  <div class="modal-body text-center">
32
- <h2 class="text-primary">Screen sharing works only in Chrome and Firefox browsers on PC</h2>
32
+ <h2 class="text-primary">Screen sharing works only in desktop browsers</h2>
33
33
  </div>
34
34
  <div class="modal-footer">
35
35
  <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
@@ -16,7 +16,7 @@ function init_page() {
16
16
  }
17
17
 
18
18
  var interval;
19
- if (Browser.isFirefox()) {
19
+ if (Browser.isFirefox() && !Browser.isAndroid() && !Browser.isiOS()) {
20
20
  $("#installExtensionButton").show();
21
21
  interval = setInterval(function() {
22
22
  if (Flashphoner.firefoxScreenSharingExtensionInstalled) {
@@ -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
+ }