@flashphoner/websdk 2.0.202 → 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.
- package/docTemplate/README.md +1 -1
- package/examples/demo/sip/phone/phone.js +7 -10
- package/examples/demo/streaming/hls-player/hls-player.html +1 -4
- package/examples/demo/streaming/hls-player/hls-player.js +19 -1
- package/examples/demo/streaming/hls-player/player-page.html +1 -1
- package/examples/demo/streaming/hls-player/video-js.css +142 -51
- package/examples/demo/streaming/hls-player/video.js +27294 -20390
- package/examples/demo/streaming/hls-player/video.min.js +27 -0
- package/examples/demo/streaming/media_devices_manager/manager.js +27 -1
- package/examples/demo/streaming/media_devices_manager/media_device_manager.html +7 -0
- package/examples/demo/streaming/stream-auto-restore/stream-auto-restore.css +23 -0
- package/examples/demo/streaming/stream-auto-restore/stream-auto-restore.html +76 -0
- package/examples/demo/streaming/stream-auto-restore/stream-auto-restore.js +356 -0
- package/examples/typescript/two-way-streaming-ts/.gitignore +23 -0
- package/examples/typescript/two-way-streaming-ts/README.md +36 -0
- package/examples/typescript/two-way-streaming-ts/package.json +45 -0
- package/examples/typescript/two-way-streaming-ts/public/favicon.ico +0 -0
- package/examples/typescript/two-way-streaming-ts/public/index.html +33 -0
- package/examples/typescript/two-way-streaming-ts/public/logo192.png +0 -0
- package/examples/typescript/two-way-streaming-ts/public/logo512.png +0 -0
- package/examples/typescript/two-way-streaming-ts/public/manifest.json +25 -0
- package/examples/typescript/two-way-streaming-ts/public/media/preloader.mp4 +0 -0
- package/examples/typescript/two-way-streaming-ts/public/robots.txt +3 -0
- package/examples/typescript/two-way-streaming-ts/src/TwoWayStreamingApp.css +23 -0
- package/examples/typescript/two-way-streaming-ts/src/TwoWayStreamingApp.tsx +371 -0
- package/examples/typescript/two-way-streaming-ts/src/fp-utils.ts +117 -0
- package/examples/typescript/two-way-streaming-ts/src/index.css +13 -0
- package/examples/typescript/two-way-streaming-ts/src/index.tsx +9 -0
- package/examples/typescript/two-way-streaming-ts/tsconfig.json +26 -0
- package/flashphoner-no-flash.js +19 -19
- package/flashphoner-no-flash.min.js +2 -2
- package/flashphoner-no-webrtc.js +18 -18
- package/flashphoner-no-webrtc.min.js +1 -1
- package/flashphoner-no-wsplayer.js +20 -20
- package/flashphoner-no-wsplayer.min.js +2 -2
- package/flashphoner-room-api.js +6 -6
- package/flashphoner-room-api.min.js +2 -2
- package/flashphoner-temasys-flash-websocket-without-adapterjs.js +20 -20
- package/flashphoner-temasys-flash-websocket.js +20 -20
- package/flashphoner-temasys-flash-websocket.min.js +1 -1
- package/flashphoner-webrtc-only.js +17 -17
- package/flashphoner-webrtc-only.min.js +1 -1
- package/flashphoner.js +20 -20
- package/flashphoner.min.js +2 -2
- package/package.json +2 -1
- package/src/constants.d.ts +1 -0
- package/src/flashphoner-core.d.ts +192 -0
- package/src/flashphoner-core.js +4 -4
- package/src/room-module.d.ts +29 -0
- package/src/webrtc-media-provider.js +2 -2
- 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;
|
|
@@ -412,6 +413,7 @@ function publish() {
|
|
|
412
413
|
var mediaConnectionConstraints;
|
|
413
414
|
var session = Flashphoner.getSessions()[0];
|
|
414
415
|
var transportInput = $('#transportInput').val();
|
|
416
|
+
var contentHint = $('#contentHintInput').val();
|
|
415
417
|
var cvo = $("#cvo").is(':checked');
|
|
416
418
|
var strippedCodecs = $("#stripPublishCodecs").val();
|
|
417
419
|
|
|
@@ -434,7 +436,8 @@ function publish() {
|
|
|
434
436
|
sdpHook: rewriteSdp,
|
|
435
437
|
transport: transportInput,
|
|
436
438
|
cvoExtension: cvo,
|
|
437
|
-
stripCodecs: strippedCodecs
|
|
439
|
+
stripCodecs: strippedCodecs,
|
|
440
|
+
videoContentHint: contentHint
|
|
438
441
|
}).on(STREAM_STATUS.PUBLISHING, function (stream) {
|
|
439
442
|
$("#testBtn").prop('disabled', true);
|
|
440
443
|
var video = document.getElementById(stream.id());
|
|
@@ -862,6 +865,29 @@ function readyControls() {
|
|
|
862
865
|
option.value = transportType;
|
|
863
866
|
transportOutput.appendChild(option);
|
|
864
867
|
}
|
|
868
|
+
|
|
869
|
+
//init content hint form
|
|
870
|
+
var contentType;
|
|
871
|
+
var contentTypeValue;
|
|
872
|
+
var option;
|
|
873
|
+
var contentHintInput = document.getElementById("contentHintInput");
|
|
874
|
+
for (contentType in CONTENT_HINT_TYPE) {
|
|
875
|
+
option = document.createElement("option");
|
|
876
|
+
switch(contentType) {
|
|
877
|
+
case 'MOTION':
|
|
878
|
+
contentTypeValue = CONTENT_HINT_TYPE.MOTION;
|
|
879
|
+
break;
|
|
880
|
+
case 'DETAIL':
|
|
881
|
+
contentTypeValue = CONTENT_HINT_TYPE.DETAIL;
|
|
882
|
+
break;
|
|
883
|
+
case 'TEXT':
|
|
884
|
+
contentTypeValue = CONTENT_HINT_TYPE.TEXT;
|
|
885
|
+
break;
|
|
886
|
+
}
|
|
887
|
+
option.text = contentTypeValue;
|
|
888
|
+
option.value = contentTypeValue;
|
|
889
|
+
contentHintInput.appendChild(option);
|
|
890
|
+
}
|
|
865
891
|
}
|
|
866
892
|
|
|
867
893
|
// 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">
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
2
|
+
|
|
3
|
+
# dependencies
|
|
4
|
+
/node_modules
|
|
5
|
+
/.pnp
|
|
6
|
+
.pnp.js
|
|
7
|
+
|
|
8
|
+
# testing
|
|
9
|
+
/coverage
|
|
10
|
+
|
|
11
|
+
# production
|
|
12
|
+
/build
|
|
13
|
+
|
|
14
|
+
# misc
|
|
15
|
+
.DS_Store
|
|
16
|
+
.env.local
|
|
17
|
+
.env.development.local
|
|
18
|
+
.env.test.local
|
|
19
|
+
.env.production.local
|
|
20
|
+
|
|
21
|
+
npm-debug.log*
|
|
22
|
+
yarn-debug.log*
|
|
23
|
+
yarn-error.log*
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Two Way Streaming React example application written in Typescript
|
|
2
|
+
|
|
3
|
+
This example shows how to integrate Flashphoner [WebCallServer](https://flashphoner.com) Javascript API ([WebSDK](https://www.npmjs.com/package/@flashphoner/websdk)) with Typescript typings to to React application
|
|
4
|
+
|
|
5
|
+
## How to build
|
|
6
|
+
|
|
7
|
+
In the project directory, you can run:
|
|
8
|
+
|
|
9
|
+
### `npm install`
|
|
10
|
+
|
|
11
|
+
Installs all the dependencies needed.
|
|
12
|
+
|
|
13
|
+
### `npm start`
|
|
14
|
+
|
|
15
|
+
Runs the app in the development mode.\
|
|
16
|
+
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
|
17
|
+
|
|
18
|
+
### `npm run build`
|
|
19
|
+
|
|
20
|
+
Builds the app for production to the `build` folder.\
|
|
21
|
+
It correctly bundles React and WebSDK in production mode and optimizes the build for the best performance.
|
|
22
|
+
|
|
23
|
+
The build is minified and the filenames include the hashes.\
|
|
24
|
+
Your app is ready to be deployed!
|
|
25
|
+
|
|
26
|
+
## How to deploy
|
|
27
|
+
|
|
28
|
+
Copy build folder content to a web server, for example
|
|
29
|
+
```
|
|
30
|
+
mkdir -p /var/www/html/two-way-streaming-ts
|
|
31
|
+
cp -r build/* /var/www/html/two-way-streaming-ts
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Then you can open example page in browser `https://yourhost/two-way-streaming-ts/index.html`
|
|
35
|
+
|
|
36
|
+
Please note that you should open page via secure connection in browser for WebRTC to work, except localhost.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "two-way-streaming-ts",
|
|
3
|
+
"description": "Two Way Streaming React appliction example written in Typescript using Flashphoner Web SDK",
|
|
4
|
+
"author": "Flashphoner",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"version": "1.0.0",
|
|
7
|
+
"homepage": ".",
|
|
8
|
+
"private": true,
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@flashphoner/websdk": "^2.0.202",
|
|
11
|
+
"@types/jest": "^27.0.2",
|
|
12
|
+
"@types/node": "^16.10.3",
|
|
13
|
+
"@types/react": "^17.0.27",
|
|
14
|
+
"@types/react-dom": "^17.0.9",
|
|
15
|
+
"bootstrap": "^5.1.1",
|
|
16
|
+
"react": "^17.0.2",
|
|
17
|
+
"react-dom": "^17.0.2",
|
|
18
|
+
"react-scripts": "4.0.3",
|
|
19
|
+
"typescript": "^4.4.3"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"start": "react-scripts start",
|
|
23
|
+
"build": "react-scripts build",
|
|
24
|
+
"test": "react-scripts test",
|
|
25
|
+
"eject": "react-scripts eject"
|
|
26
|
+
},
|
|
27
|
+
"eslintConfig": {
|
|
28
|
+
"extends": [
|
|
29
|
+
"react-app",
|
|
30
|
+
"react-app/jest"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"browserslist": {
|
|
34
|
+
"production": [
|
|
35
|
+
">0.2%",
|
|
36
|
+
"not dead",
|
|
37
|
+
"not op_mini all"
|
|
38
|
+
],
|
|
39
|
+
"development": [
|
|
40
|
+
"last 1 chrome version",
|
|
41
|
+
"last 1 firefox version",
|
|
42
|
+
"last 1 safari version"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<meta name="theme-color" content="#000000" />
|
|
8
|
+
<meta
|
|
9
|
+
name="description"
|
|
10
|
+
content="Two Way Streaming example created using create-react-app"
|
|
11
|
+
/>
|
|
12
|
+
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
13
|
+
<!--
|
|
14
|
+
manifest.json provides metadata used when your web app is installed on a
|
|
15
|
+
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
|
16
|
+
-->
|
|
17
|
+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
|
18
|
+
<!--
|
|
19
|
+
Notice the use of %PUBLIC_URL% in the tags above.
|
|
20
|
+
It will be replaced with the URL of the `public` folder during the build.
|
|
21
|
+
Only files inside the `public` folder can be referenced from the HTML.
|
|
22
|
+
|
|
23
|
+
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
|
24
|
+
work correctly both with client-side routing and a non-root public URL.
|
|
25
|
+
Learn how to configure a non-root public URL by running `npm run build`.
|
|
26
|
+
-->
|
|
27
|
+
<title>TwoWay Streaming React App</title>
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
31
|
+
<div id="root"></div>
|
|
32
|
+
</body>
|
|
33
|
+
</html>
|
|
Binary file
|
|
Binary file
|