@flashphoner/websdk 2.0.206 → 2.0.207

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- Web SDK - 2.0.206
1
+ Web SDK - 2.0.207
2
2
 
3
3
  [Download builds](https://docs.flashphoner.com/display/WEBSDK2EN/Web+SDK+release+notes)
4
4
 
@@ -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
+ }