@flashphoner/websdk 2.0.242 → 2.0.243
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/dependencies/js/stats.js +105 -0
- package/examples/demo/dependencies/js/utils.js +95 -6
- package/examples/demo/streaming/hls-js-player/hls-js-player.html +1 -4
- package/examples/demo/streaming/hls-js-player/hls-js-player.js +147 -55
- package/examples/demo/streaming/hls-native/hls-native.html +1 -4
- package/examples/demo/streaming/hls-native/hls-native.js +148 -56
- package/examples/demo/streaming/hls-player/hls-player.html +1 -4
- package/examples/demo/streaming/hls-player/hls-player.js +55 -4
- package/examples/demo/streaming/hls-player/player-page.html +33 -3
- package/package.json +1 -1
package/docTemplate/README.md
CHANGED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML5 video playback statistics
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* HTML5 statistics item
|
|
7
|
+
*
|
|
8
|
+
* @returns {object}
|
|
9
|
+
* @constructor
|
|
10
|
+
*/
|
|
11
|
+
const HTML5StatsItem = function() {
|
|
12
|
+
const statsItem = {
|
|
13
|
+
value: 0,
|
|
14
|
+
lastData: 0,
|
|
15
|
+
timestamp: 0,
|
|
16
|
+
init: function(value, data) {
|
|
17
|
+
statsItem.setValue(value);
|
|
18
|
+
statsItem.setLastData(data);
|
|
19
|
+
},
|
|
20
|
+
setValue: function(value) {
|
|
21
|
+
statsItem.value = value;
|
|
22
|
+
},
|
|
23
|
+
getValue: function() {
|
|
24
|
+
return statsItem.value;
|
|
25
|
+
},
|
|
26
|
+
setLastData: function(data, timestamp) {
|
|
27
|
+
statsItem.lastData = data;
|
|
28
|
+
statsItem.setTimestamp(timestamp);
|
|
29
|
+
},
|
|
30
|
+
getLastData: function() {
|
|
31
|
+
return statsItem.lastData;
|
|
32
|
+
},
|
|
33
|
+
setTimestamp: function(timestamp) {
|
|
34
|
+
statsItem.timestamp = timestamp || Date.now();
|
|
35
|
+
},
|
|
36
|
+
getTimestamp: function() {
|
|
37
|
+
return statsItem.timestamp;
|
|
38
|
+
},
|
|
39
|
+
elapsedSecs: function(timestamp) {
|
|
40
|
+
let now = timestamp || Date.now();
|
|
41
|
+
return (now - statsItem.timestamp) / 1000;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
return statsItem;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const HTML5Stats = function(video) {
|
|
48
|
+
const stats = {
|
|
49
|
+
video: video || null,
|
|
50
|
+
width: 0,
|
|
51
|
+
height: 0,
|
|
52
|
+
bitrate: HTML5StatsItem(),
|
|
53
|
+
fps: HTML5StatsItem(),
|
|
54
|
+
init: function(video) {
|
|
55
|
+
stats.video = video;
|
|
56
|
+
stats.width = 0;
|
|
57
|
+
stats.height = 0;
|
|
58
|
+
stats.bitrate.init(0);
|
|
59
|
+
stats.fps.init(0);
|
|
60
|
+
},
|
|
61
|
+
collect: function() {
|
|
62
|
+
if (stats.video) {
|
|
63
|
+
let now = Date.now();
|
|
64
|
+
let quality = video.getVideoPlaybackQuality();
|
|
65
|
+
|
|
66
|
+
if (quality) {
|
|
67
|
+
let played = quality.totalVideoFrames - quality.droppedVideoFrames;
|
|
68
|
+
if (played !== stats.fps.getLastData()) {
|
|
69
|
+
stats.fps.setValue((played - stats.fps.getLastData()) / stats.fps.elapsedSecs(now));
|
|
70
|
+
stats.fps.setLastData(played, now);
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
stats.fps.setValue(undefined);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (stats.video.webkitVideoDecodedByteCount !== undefined) {
|
|
77
|
+
let decoded = stats.video.webkitVideoDecodedByteCount;
|
|
78
|
+
stats.bitrate.setValue(Math.round((decoded - stats.bitrate.getLastData()) * 8 / stats.bitrate.elapsedSecs(now)));
|
|
79
|
+
stats.bitrate.setLastData(decoded, now);
|
|
80
|
+
} else {
|
|
81
|
+
stats.bitrate.setValue(undefined);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
stats.width = stats.video.videoWidth;
|
|
85
|
+
stats.height = stats.video.videoHeight;
|
|
86
|
+
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
},
|
|
91
|
+
getWidth: function() {
|
|
92
|
+
return stats.width;
|
|
93
|
+
},
|
|
94
|
+
getHeight: function() {
|
|
95
|
+
return stats.height;
|
|
96
|
+
},
|
|
97
|
+
getFps: function() {
|
|
98
|
+
return stats.fps.getValue();
|
|
99
|
+
},
|
|
100
|
+
getBitrate: function() {
|
|
101
|
+
return stats.bitrate.getValue();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
return stats;
|
|
105
|
+
}
|
|
@@ -457,7 +457,12 @@ function canWebkitFullScreen(video) {
|
|
|
457
457
|
return canFullscreen;
|
|
458
458
|
}
|
|
459
459
|
|
|
460
|
-
|
|
460
|
+
/**
|
|
461
|
+
* Helper function to set item text
|
|
462
|
+
*
|
|
463
|
+
* @param id
|
|
464
|
+
* @param text
|
|
465
|
+
*/
|
|
461
466
|
const setText = function (id, text) {
|
|
462
467
|
let item = document.getElementById(id);
|
|
463
468
|
if (item) {
|
|
@@ -465,7 +470,12 @@ const setText = function (id, text) {
|
|
|
465
470
|
}
|
|
466
471
|
}
|
|
467
472
|
|
|
468
|
-
|
|
473
|
+
/**
|
|
474
|
+
* Helper function to set an item value
|
|
475
|
+
*
|
|
476
|
+
* @param id
|
|
477
|
+
* @param value
|
|
478
|
+
*/
|
|
469
479
|
const setValue = function (id, value) {
|
|
470
480
|
let item = document.getElementById(id);
|
|
471
481
|
if (item) {
|
|
@@ -473,6 +483,12 @@ const setValue = function (id, value) {
|
|
|
473
483
|
}
|
|
474
484
|
}
|
|
475
485
|
|
|
486
|
+
/**
|
|
487
|
+
* Helper function to get an item value
|
|
488
|
+
*
|
|
489
|
+
* @param id
|
|
490
|
+
* @returns value
|
|
491
|
+
*/
|
|
476
492
|
const getValue = function (id) {
|
|
477
493
|
let item = document.getElementById(id);
|
|
478
494
|
if (item) {
|
|
@@ -481,7 +497,39 @@ const getValue = function (id) {
|
|
|
481
497
|
return null;
|
|
482
498
|
}
|
|
483
499
|
|
|
484
|
-
|
|
500
|
+
/**
|
|
501
|
+
* Helper function to set/unset a checkbox
|
|
502
|
+
*
|
|
503
|
+
* @param id
|
|
504
|
+
* @param value
|
|
505
|
+
*/
|
|
506
|
+
const setCheckbox = function (id, value) {
|
|
507
|
+
let item = document.getElementById(id);
|
|
508
|
+
if (item) {
|
|
509
|
+
item.checked = value;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Helper function to get a checkbox state
|
|
515
|
+
*
|
|
516
|
+
* @param id
|
|
517
|
+
* @returns value
|
|
518
|
+
*/
|
|
519
|
+
const getCheckbox = function (id) {
|
|
520
|
+
let item = document.getElementById(id);
|
|
521
|
+
if (item) {
|
|
522
|
+
return item.checked;
|
|
523
|
+
}
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Helper function to display an item
|
|
530
|
+
*
|
|
531
|
+
* @param id
|
|
532
|
+
*/
|
|
485
533
|
const showItem = function(id) {
|
|
486
534
|
let item = document.getElementById(id);
|
|
487
535
|
if (item) {
|
|
@@ -489,6 +537,11 @@ const showItem = function(id) {
|
|
|
489
537
|
}
|
|
490
538
|
}
|
|
491
539
|
|
|
540
|
+
/**
|
|
541
|
+
* Helper function to hide an item
|
|
542
|
+
*
|
|
543
|
+
* @param id
|
|
544
|
+
*/
|
|
492
545
|
const hideItem = function(id) {
|
|
493
546
|
let item = document.getElementById(id);
|
|
494
547
|
if (item) {
|
|
@@ -496,7 +549,11 @@ const hideItem = function(id) {
|
|
|
496
549
|
}
|
|
497
550
|
}
|
|
498
551
|
|
|
499
|
-
|
|
552
|
+
/**
|
|
553
|
+
* Helper function to disable an item
|
|
554
|
+
*
|
|
555
|
+
* @param id
|
|
556
|
+
*/
|
|
500
557
|
const disableItem = function(id) {
|
|
501
558
|
let item = document.getElementById(id);
|
|
502
559
|
if (item) {
|
|
@@ -504,6 +561,11 @@ const disableItem = function(id) {
|
|
|
504
561
|
}
|
|
505
562
|
}
|
|
506
563
|
|
|
564
|
+
/**
|
|
565
|
+
* Helper function to enable an item
|
|
566
|
+
*
|
|
567
|
+
* @param id
|
|
568
|
+
*/
|
|
507
569
|
const enableItem = function(id) {
|
|
508
570
|
let item = document.getElementById(id);
|
|
509
571
|
if (item) {
|
|
@@ -511,7 +573,14 @@ const enableItem = function(id) {
|
|
|
511
573
|
}
|
|
512
574
|
}
|
|
513
575
|
|
|
514
|
-
|
|
576
|
+
/**
|
|
577
|
+
* Set an event handler
|
|
578
|
+
*
|
|
579
|
+
* @param id
|
|
580
|
+
* @param event
|
|
581
|
+
* @param handler
|
|
582
|
+
* @param previous
|
|
583
|
+
*/
|
|
515
584
|
const setHandler = function (id, event, handler, previous = null) {
|
|
516
585
|
let item = document.getElementById(id);
|
|
517
586
|
if (item) {
|
|
@@ -522,7 +591,13 @@ const setHandler = function (id, event, handler, previous = null) {
|
|
|
522
591
|
}
|
|
523
592
|
}
|
|
524
593
|
|
|
525
|
-
|
|
594
|
+
/**
|
|
595
|
+
* Find a closest item
|
|
596
|
+
*
|
|
597
|
+
* @param id
|
|
598
|
+
* @param selector
|
|
599
|
+
* @returns {null}
|
|
600
|
+
*/
|
|
526
601
|
const closest = function (id, selector) {
|
|
527
602
|
let currentElement = document.getElementById(id);
|
|
528
603
|
let returnElement = null;
|
|
@@ -534,3 +609,17 @@ const closest = function (id, selector) {
|
|
|
534
609
|
|
|
535
610
|
return returnElement;
|
|
536
611
|
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Display object properties fro debugging purposes
|
|
615
|
+
*
|
|
616
|
+
* @param object
|
|
617
|
+
*/
|
|
618
|
+
const showProps = function (object) {
|
|
619
|
+
console.log("-------------------------------object begin");
|
|
620
|
+
for (const property in object) {
|
|
621
|
+
console.log(`${property}: ${object[property]}`);
|
|
622
|
+
}
|
|
623
|
+
console.log("-------------------------------object end");
|
|
624
|
+
|
|
625
|
+
}
|
|
@@ -8,12 +8,9 @@
|
|
|
8
8
|
<title>HLS.JS Player Minimal</title>
|
|
9
9
|
<script type="text/javascript" src="../../../../flashphoner.js"></script>
|
|
10
10
|
<script type="text/javascript" src="hls.min.js"></script>
|
|
11
|
-
<script type="text/javascript" src="../../dependencies/jquery/jquery-1.12.0.js"></script>
|
|
12
|
-
<script type="text/javascript" src="../../dependencies/jquery/jquery-ui.js"></script>
|
|
13
11
|
<script type="text/javascript" src="../../dependencies/js/utils.js"></script>
|
|
12
|
+
<script type="text/javascript" src="../../dependencies/js/stats.js"></script>
|
|
14
13
|
<script type="text/javascript" src="hls-js-player.js"></script>
|
|
15
|
-
<!-- Bootstrap JS -->
|
|
16
|
-
<script src="../../dependencies/bootstrap/js/bootstrap.js"></script>
|
|
17
14
|
</head>
|
|
18
15
|
<body onload="loadPlayerPage()">
|
|
19
16
|
<div id="playerPage" class="container">
|
|
@@ -1,35 +1,54 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
const Browser = Flashphoner.Browser;
|
|
2
|
+
const STATS_INTERVAL = 1000;
|
|
3
|
+
let remoteVideo = null;
|
|
4
|
+
let hlsPlayer = null;
|
|
5
|
+
let playbackStats = null;
|
|
6
|
+
|
|
7
|
+
const loadPlayerPage = function() {
|
|
8
|
+
loadPage("../hls-player/player-page.html", "playerPage", initPage );
|
|
9
|
+
}
|
|
4
10
|
|
|
5
|
-
function
|
|
6
|
-
|
|
11
|
+
const loadPage = function(page, containerId, onLoad) {
|
|
12
|
+
fetch(page).then(function (response) {
|
|
13
|
+
if (response.ok) {
|
|
14
|
+
return response.text();
|
|
15
|
+
}
|
|
16
|
+
throw response;
|
|
17
|
+
}).then(function (text) {
|
|
18
|
+
let container = document.getElementById(containerId);
|
|
19
|
+
container.innerHTML = text;
|
|
20
|
+
onLoad();
|
|
21
|
+
});
|
|
7
22
|
}
|
|
8
23
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
24
|
+
const initPage = function() {
|
|
25
|
+
setText("header", "HLS.JS Player Minimal");
|
|
26
|
+
setValue("urlServer", getHLSUrl());
|
|
27
|
+
setText("applyBtn", "Play");
|
|
28
|
+
setHandler("applyBtn", "click", playBtnClick);
|
|
29
|
+
remoteVideo = document.getElementById('remoteVideo');
|
|
30
|
+
remoteVideo.style ="background-color: lightgrey;";
|
|
13
31
|
if (Hls.isSupported()) {
|
|
14
32
|
console.log("Using HLS.JS " + Hls.version);
|
|
15
|
-
|
|
33
|
+
enableItem("applyBtn");
|
|
34
|
+
showItem("llHlsMode");
|
|
35
|
+
playbackStats = PlaybackStats(STATS_INTERVAL);
|
|
16
36
|
} else {
|
|
17
|
-
|
|
37
|
+
setText("notifyFlash", "Your browser doesn't support MSE technology required to play video");
|
|
38
|
+
disableItem("applyBtn");
|
|
39
|
+
toggleInputs(false);
|
|
18
40
|
}
|
|
19
|
-
remoteVideo = document.getElementById('remoteVideo');
|
|
20
|
-
remoteVideo.style ="background-color: lightgrey;";
|
|
21
|
-
$('#llHlsMode').show();
|
|
22
41
|
}
|
|
23
42
|
|
|
24
43
|
|
|
25
|
-
|
|
44
|
+
const playBtnClick = function() {
|
|
26
45
|
if (validateForm()) {
|
|
27
|
-
|
|
28
|
-
|
|
46
|
+
let llHlsEnabled = getCheckbox("llHlsEnabled");
|
|
47
|
+
let streamName = getValue("playStream");
|
|
29
48
|
streamName = encodeURIComponent(streamName);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
49
|
+
let videoSrc = getValue("urlServer") + '/' + streamName + '/' + streamName + '.m3u8';
|
|
50
|
+
let key = getValue('key');
|
|
51
|
+
let token = getValue("token");
|
|
33
52
|
if (key.length > 0 && token.length > 0) {
|
|
34
53
|
videoSrc += "?" + key + "=" + token;
|
|
35
54
|
}
|
|
@@ -45,8 +64,8 @@ function playBtnClick() {
|
|
|
45
64
|
}
|
|
46
65
|
|
|
47
66
|
|
|
48
|
-
|
|
49
|
-
|
|
67
|
+
const getHlsConfig = function(llHlsEnabled) {
|
|
68
|
+
let config = {
|
|
50
69
|
lowLatencyMode: false,
|
|
51
70
|
enableWorker: true,
|
|
52
71
|
backBufferLength: 90,
|
|
@@ -71,7 +90,7 @@ function getHlsConfig(llHlsEnabled) {
|
|
|
71
90
|
}
|
|
72
91
|
|
|
73
92
|
|
|
74
|
-
|
|
93
|
+
const stopBtnClick = function() {
|
|
75
94
|
if (hlsPlayer != null) {
|
|
76
95
|
console.log("Stop HLS segments loading");
|
|
77
96
|
hlsPlayer.stopLoad();
|
|
@@ -88,53 +107,126 @@ function stopBtnClick() {
|
|
|
88
107
|
}
|
|
89
108
|
|
|
90
109
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
$('#llHlsEnabled').prop('disabled', true);
|
|
98
|
-
$("#applyBtn").prop('disabled', false).text("Stop").off('click').click(stopBtnClick);
|
|
110
|
+
const onStarted = function() {
|
|
111
|
+
toggleInputs(false);
|
|
112
|
+
enableItem("applyBtn");
|
|
113
|
+
setText("applyBtn", "Stop");
|
|
114
|
+
setHandler("applyBtn", "click", stopBtnClick, playBtnClick);
|
|
115
|
+
playbackStats.start();
|
|
99
116
|
}
|
|
100
117
|
|
|
101
118
|
|
|
102
119
|
function onStopped() {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
$('#llHlsEnabled').prop('disabled', false);
|
|
109
|
-
$("#applyBtn").prop('disabled', false).text("Play").off('click').click(playBtnClick);
|
|
120
|
+
toggleInputs(true);
|
|
121
|
+
enableItem("applyBtn");
|
|
122
|
+
setText("applyBtn", "Play");
|
|
123
|
+
setHandler("applyBtn", "click", playBtnClick, stopBtnClick);
|
|
124
|
+
playbackStats.stop();
|
|
110
125
|
}
|
|
111
126
|
|
|
112
127
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (
|
|
116
|
-
|
|
117
|
-
valid = false;
|
|
118
|
-
} else {
|
|
119
|
-
removeHighlight($("#urlServer"));
|
|
128
|
+
const validateForm = function() {
|
|
129
|
+
let valid = validateInput("urlServer");
|
|
130
|
+
if (valid) {
|
|
131
|
+
valid = validateInput("playStream");
|
|
120
132
|
}
|
|
121
|
-
|
|
122
|
-
|
|
133
|
+
return valid;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const validateInput = function(id) {
|
|
137
|
+
let value = getValue(id);
|
|
138
|
+
let valid = true;
|
|
139
|
+
if (!value || !value.length) {
|
|
140
|
+
highlightInput(id);
|
|
123
141
|
valid = false;
|
|
124
142
|
} else {
|
|
125
|
-
removeHighlight(
|
|
143
|
+
removeHighlight(id);
|
|
126
144
|
}
|
|
127
|
-
|
|
128
145
|
return valid;
|
|
129
|
-
|
|
130
146
|
}
|
|
131
147
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
148
|
+
const highlightInput = function(input) {
|
|
149
|
+
let item = document.getElementById(input);
|
|
150
|
+
if (item) {
|
|
151
|
+
let parent = closest(input,'.form-group');
|
|
152
|
+
if (parent) {
|
|
153
|
+
parent.classList.add("has-error");
|
|
154
|
+
}
|
|
155
|
+
}
|
|
135
156
|
}
|
|
136
157
|
|
|
137
158
|
|
|
138
|
-
|
|
139
|
-
|
|
159
|
+
const removeHighlight = function(input) {
|
|
160
|
+
let item = document.getElementById(input);
|
|
161
|
+
if (item) {
|
|
162
|
+
let parent = closest(input,'.form-group');
|
|
163
|
+
if (parent) {
|
|
164
|
+
parent.classList.remove("has-error");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const toggleInputs = function(enable) {
|
|
170
|
+
if (enable) {
|
|
171
|
+
enableItem("urlServer");
|
|
172
|
+
enableItem("playStream");
|
|
173
|
+
enableItem("key");
|
|
174
|
+
enableItem("token");
|
|
175
|
+
enableItem("player");
|
|
176
|
+
enableItem("llHlsEnabled");
|
|
177
|
+
} else {
|
|
178
|
+
disableItem("urlServer");
|
|
179
|
+
disableItem("playStream");
|
|
180
|
+
disableItem("key");
|
|
181
|
+
disableItem("token");
|
|
182
|
+
disableItem("player");
|
|
183
|
+
disableItem("llHlsEnabled");
|
|
184
|
+
}
|
|
140
185
|
}
|
|
186
|
+
|
|
187
|
+
const PlaybackStats = function(interval) {
|
|
188
|
+
const playbackStats = {
|
|
189
|
+
interval: interval || STATS_INTERVAL,
|
|
190
|
+
timer: null,
|
|
191
|
+
stats: null,
|
|
192
|
+
start: function() {
|
|
193
|
+
let video = remoteVideo;
|
|
194
|
+
|
|
195
|
+
playbackStats.stop();
|
|
196
|
+
stats = HTML5Stats(video);
|
|
197
|
+
playbackStats.timer = setInterval(playbackStats.displayStats, playbackStats.interval);
|
|
198
|
+
setText("videoWidth", "N/A");
|
|
199
|
+
setText("videoHeight", "N/A");
|
|
200
|
+
setText("videoRate", "N/A");
|
|
201
|
+
setText("videoFps", "N/A");
|
|
202
|
+
showItem("stats");
|
|
203
|
+
},
|
|
204
|
+
stop: function() {
|
|
205
|
+
if (playbackStats.timer) {
|
|
206
|
+
clearInterval(playbackStats.timer);
|
|
207
|
+
playbackStats.timer = null;
|
|
208
|
+
}
|
|
209
|
+
playbackStats.stats = null;
|
|
210
|
+
hideItem("stats");
|
|
211
|
+
},
|
|
212
|
+
displayStats: function() {
|
|
213
|
+
if (stats.collect()) {
|
|
214
|
+
let width = stats.getWidth();
|
|
215
|
+
let height = stats.getHeight();
|
|
216
|
+
let bitrate = stats.getBitrate();
|
|
217
|
+
let fps = stats.getFps();
|
|
218
|
+
|
|
219
|
+
setText("videoWidth", width);
|
|
220
|
+
setText("videoHeight", height);
|
|
221
|
+
|
|
222
|
+
if (bitrate !== undefined) {
|
|
223
|
+
setText("videoRate", Math.round(bitrate));
|
|
224
|
+
}
|
|
225
|
+
if (fps !== undefined) {
|
|
226
|
+
setText("videoFps", fps.toFixed(1));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
return playbackStats;
|
|
232
|
+
}
|
|
@@ -7,12 +7,9 @@
|
|
|
7
7
|
<link rel="stylesheet" href="hls-native.css">
|
|
8
8
|
<title>HLS Native Player Minimal</title>
|
|
9
9
|
<script type="text/javascript" src="../../../../flashphoner.js"></script>
|
|
10
|
-
<script type="text/javascript" src="../../dependencies/jquery/jquery-1.12.0.js"></script>
|
|
11
|
-
<script type="text/javascript" src="../../dependencies/jquery/jquery-ui.js"></script>
|
|
12
10
|
<script type="text/javascript" src="../../dependencies/js/utils.js"></script>
|
|
11
|
+
<script type="text/javascript" src="../../dependencies/js/stats.js"></script>
|
|
13
12
|
<script type="text/javascript" src="hls-native.js"></script>
|
|
14
|
-
<!-- Bootstrap JS -->
|
|
15
|
-
<script src="../../dependencies/bootstrap/js/bootstrap.js"></script>
|
|
16
13
|
</head>
|
|
17
14
|
<body onload="loadPlayerPage()">
|
|
18
15
|
<div id="playerPage" class="container">
|
|
@@ -1,48 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const Browser = Flashphoner.Browser;
|
|
2
|
+
const STATS_INTERVAL = 1000;
|
|
3
|
+
let remoteVideo = null;
|
|
4
|
+
let playbackStats = null;
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
const loadPlayerPage = function() {
|
|
7
|
+
loadPage("../hls-player/player-page.html", "playerPage", initPage );
|
|
6
8
|
}
|
|
7
9
|
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const loadPage = function(page, containerId, onLoad) {
|
|
11
|
+
fetch(page).then(function (response) {
|
|
12
|
+
if (response.ok) {
|
|
13
|
+
return response.text();
|
|
14
|
+
}
|
|
15
|
+
throw response;
|
|
16
|
+
}).then(function (text) {
|
|
17
|
+
let container = document.getElementById(containerId);
|
|
18
|
+
container.innerHTML = text;
|
|
19
|
+
onLoad();
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const initPage = function() {
|
|
24
|
+
setText("header", "HLS Native Player Minimal");
|
|
25
|
+
setValue("urlServer", getHLSUrl());
|
|
26
|
+
setText("applyBtn", "Play");
|
|
27
|
+
setHandler("applyBtn", "click", playBtnClick);
|
|
12
28
|
remoteVideo = document.getElementById('remoteVideo');
|
|
13
29
|
remoteVideo.style ="background-color: lightgrey;";
|
|
14
30
|
if (Browser.isSafariWebRTC() && Browser.isiOS()) {
|
|
15
31
|
// iOS hack when using standard controls to leave fullscreen mode
|
|
16
32
|
setWebkitFullscreenHandlers(remoteVideo);
|
|
17
33
|
}
|
|
34
|
+
if (remoteVideo.canPlayType('application/vnd.apple.mpegurl') && Browser.isSafariWebRTC()) {
|
|
35
|
+
enableItem("applyBtn");
|
|
36
|
+
playbackStats = PlaybackStats(STATS_INTERVAL);
|
|
37
|
+
} else {
|
|
38
|
+
setText("notifyFlash", "Your browser doesn't support native HLS playback");
|
|
39
|
+
disableItem("applyBtn");
|
|
40
|
+
toggleInputs(false);
|
|
41
|
+
}
|
|
18
42
|
}
|
|
19
43
|
|
|
20
|
-
|
|
44
|
+
const playBtnClick = function() {
|
|
21
45
|
if (validateForm()) {
|
|
22
|
-
|
|
46
|
+
let streamName = getValue("playStream");
|
|
23
47
|
streamName = encodeURIComponent(streamName);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
48
|
+
let videoSrc = getValue("urlServer") + '/' + streamName + '/' + streamName + '.m3u8';
|
|
49
|
+
let key = getValue('key');
|
|
50
|
+
let token = getValue("token");
|
|
27
51
|
if (key.length > 0 && token.length > 0) {
|
|
28
52
|
videoSrc += "?" + key + "=" + token;
|
|
29
53
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
$("#notifyFlash").text("Your browser doesn't support native HLS playback");
|
|
40
|
-
}
|
|
54
|
+
remoteVideo.src = videoSrc;
|
|
55
|
+
remoteVideo.addEventListener('loadedmetadata', function() {
|
|
56
|
+
console.log("Play native HLS");
|
|
57
|
+
remoteVideo.play();
|
|
58
|
+
onStarted();
|
|
59
|
+
});
|
|
41
60
|
}
|
|
42
61
|
}
|
|
43
62
|
|
|
44
63
|
|
|
45
|
-
|
|
64
|
+
const stopBtnClick = function() {
|
|
46
65
|
if (remoteVideo != null) {
|
|
47
66
|
console.log("Stop HTML5 player");
|
|
48
67
|
remoteVideo.pause();
|
|
@@ -54,51 +73,124 @@ function stopBtnClick() {
|
|
|
54
73
|
}
|
|
55
74
|
|
|
56
75
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
$("#applyBtn").prop('disabled', false).text("Stop").off('click').click(stopBtnClick);
|
|
76
|
+
const onStarted = function() {
|
|
77
|
+
toggleInputs(false);
|
|
78
|
+
enableItem("applyBtn");
|
|
79
|
+
setText("applyBtn", "Stop");
|
|
80
|
+
setHandler("applyBtn", "click", stopBtnClick, playBtnClick);
|
|
81
|
+
playbackStats.start();
|
|
64
82
|
}
|
|
65
83
|
|
|
66
84
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
$("#applyBtn").prop('disabled', false).text("Play").off('click').click(playBtnClick);
|
|
85
|
+
const onStopped = function() {
|
|
86
|
+
toggleInputs(true);
|
|
87
|
+
enableItem("applyBtn");
|
|
88
|
+
setText("applyBtn", "Play");
|
|
89
|
+
setHandler("applyBtn", "click", playBtnClick, stopBtnClick);
|
|
90
|
+
playbackStats.stop();
|
|
74
91
|
}
|
|
75
92
|
|
|
76
93
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
valid = false;
|
|
82
|
-
} else {
|
|
83
|
-
removeHighlight($("#urlServer"));
|
|
94
|
+
const validateForm = function() {
|
|
95
|
+
let valid = validateInput("urlServer");
|
|
96
|
+
if (valid) {
|
|
97
|
+
valid = validateInput("playStream");
|
|
84
98
|
}
|
|
85
|
-
|
|
86
|
-
|
|
99
|
+
return valid;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const validateInput = function(id) {
|
|
103
|
+
let value = getValue(id);
|
|
104
|
+
let valid = true;
|
|
105
|
+
if (!value || !value.length) {
|
|
106
|
+
highlightInput(id);
|
|
87
107
|
valid = false;
|
|
88
108
|
} else {
|
|
89
|
-
removeHighlight(
|
|
109
|
+
removeHighlight(id);
|
|
90
110
|
}
|
|
91
|
-
|
|
92
111
|
return valid;
|
|
93
|
-
|
|
94
112
|
}
|
|
95
113
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
114
|
+
const highlightInput = function(input) {
|
|
115
|
+
let item = document.getElementById(input);
|
|
116
|
+
if (item) {
|
|
117
|
+
let parent = closest(input,'.form-group');
|
|
118
|
+
if (parent) {
|
|
119
|
+
parent.classList.add("has-error");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
99
122
|
}
|
|
100
123
|
|
|
101
124
|
|
|
102
|
-
|
|
103
|
-
|
|
125
|
+
const removeHighlight = function(input) {
|
|
126
|
+
let item = document.getElementById(input);
|
|
127
|
+
if (item) {
|
|
128
|
+
let parent = closest(input,'.form-group');
|
|
129
|
+
if (parent) {
|
|
130
|
+
parent.classList.remove("has-error");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const toggleInputs = function(enable) {
|
|
136
|
+
if (enable) {
|
|
137
|
+
enableItem("urlServer");
|
|
138
|
+
enableItem("playStream");
|
|
139
|
+
enableItem("key");
|
|
140
|
+
enableItem("token");
|
|
141
|
+
enableItem("player");
|
|
142
|
+
} else {
|
|
143
|
+
disableItem("urlServer");
|
|
144
|
+
disableItem("playStream");
|
|
145
|
+
disableItem("key");
|
|
146
|
+
disableItem("token");
|
|
147
|
+
disableItem("player");
|
|
148
|
+
}
|
|
104
149
|
}
|
|
150
|
+
|
|
151
|
+
const PlaybackStats = function(interval) {
|
|
152
|
+
const playbackStats = {
|
|
153
|
+
interval: interval || STATS_INTERVAL,
|
|
154
|
+
timer: null,
|
|
155
|
+
stats: null,
|
|
156
|
+
start: function() {
|
|
157
|
+
let video = remoteVideo;
|
|
158
|
+
|
|
159
|
+
playbackStats.stop();
|
|
160
|
+
stats = HTML5Stats(video);
|
|
161
|
+
playbackStats.timer = setInterval(playbackStats.displayStats, playbackStats.interval);
|
|
162
|
+
setText("videoWidth", "N/A");
|
|
163
|
+
setText("videoHeight", "N/A");
|
|
164
|
+
setText("videoRate", "N/A");
|
|
165
|
+
setText("videoFps", "N/A");
|
|
166
|
+
showItem("stats");
|
|
167
|
+
},
|
|
168
|
+
stop: function() {
|
|
169
|
+
if (playbackStats.timer) {
|
|
170
|
+
clearInterval(playbackStats.timer);
|
|
171
|
+
playbackStats.timer = null;
|
|
172
|
+
}
|
|
173
|
+
playbackStats.stats = null;
|
|
174
|
+
hideItem("stats");
|
|
175
|
+
},
|
|
176
|
+
displayStats: function() {
|
|
177
|
+
if (stats.collect()) {
|
|
178
|
+
let width = stats.getWidth();
|
|
179
|
+
let height = stats.getHeight();
|
|
180
|
+
let bitrate = stats.getBitrate();
|
|
181
|
+
let fps = stats.getFps();
|
|
182
|
+
|
|
183
|
+
setText("videoWidth", width);
|
|
184
|
+
setText("videoHeight", height);
|
|
185
|
+
|
|
186
|
+
if (bitrate !== undefined) {
|
|
187
|
+
setText("videoRate", Math.round(bitrate));
|
|
188
|
+
}
|
|
189
|
+
if (fps !== undefined) {
|
|
190
|
+
setText("videoFps", fps.toFixed(1));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
return playbackStats;
|
|
196
|
+
}
|
|
@@ -5,14 +5,11 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<link rel="stylesheet" href="../../dependencies/bootstrap/css/bootstrap.css">
|
|
7
7
|
<link rel="stylesheet" href="hls-player.css">
|
|
8
|
-
<!-- <link rel="stylesheet" href="video-js.css"> -->
|
|
9
8
|
<title>HLS VideoJS Player Minimal</title>
|
|
10
9
|
<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/jquery/jquery-ui.js"></script> -->
|
|
13
10
|
<script type="text/javascript" src="../../dependencies/js/utils.js"></script>
|
|
11
|
+
<script type="text/javascript" src="../../dependencies/js/stats.js"></script>
|
|
14
12
|
<script type="text/javascript" src="hls-player.js"></script>
|
|
15
|
-
<!-- <script type="text/javascript" src="video.js"></script> -->
|
|
16
13
|
</head>
|
|
17
14
|
<body onload="loadPlayerPage()">
|
|
18
15
|
<div class="row form-group" id="videojsInputForm">
|
|
@@ -6,9 +6,11 @@ const VIDEOJS_VERSION_TYPE = {
|
|
|
6
6
|
const LIVE_THRESHOLD = 5;
|
|
7
7
|
const LIVE_TOLERANCE = 5;
|
|
8
8
|
const LIVE_UI_INTERVAL = 1000;
|
|
9
|
+
const STATS_INTERVAL = 1000;
|
|
9
10
|
let player = null;
|
|
10
11
|
let liveUITimer = null;
|
|
11
12
|
let videojsVersion = getUrlParam("version");
|
|
13
|
+
let playbackStats = null;
|
|
12
14
|
|
|
13
15
|
const loadPlayerPage = function() {
|
|
14
16
|
if (videojsVersion) {
|
|
@@ -127,6 +129,7 @@ const initPage = function() {
|
|
|
127
129
|
let remoteVideo = document.getElementById('remoteVideo');
|
|
128
130
|
remoteVideo.className = "video-js vjs-default-skin";
|
|
129
131
|
player = initVideoJsPlayer(remoteVideo);
|
|
132
|
+
playbackStats = PlaybackStats(STATS_INTERVAL);
|
|
130
133
|
}
|
|
131
134
|
|
|
132
135
|
const playBtnClick = function() {
|
|
@@ -234,6 +237,7 @@ const onStarted = function() {
|
|
|
234
237
|
toggleBackButtons(true);
|
|
235
238
|
setText("applyBtn", "Stop");
|
|
236
239
|
setHandler("applyBtn", "click", stopBtnClick, playBtnClick);
|
|
240
|
+
playbackStats.start();
|
|
237
241
|
}
|
|
238
242
|
|
|
239
243
|
|
|
@@ -243,6 +247,7 @@ const onStopped = function() {
|
|
|
243
247
|
hideItem("backward");
|
|
244
248
|
setText("applyBtn", "Play");
|
|
245
249
|
setHandler("applyBtn", "click", playBtnClick, stopBtnClick);
|
|
250
|
+
playbackStats.stop();
|
|
246
251
|
if(!document.getElementById('remoteVideo')) {
|
|
247
252
|
createRemoteVideo(document.getElementById('videoContainer'));
|
|
248
253
|
}
|
|
@@ -250,10 +255,8 @@ const onStopped = function() {
|
|
|
250
255
|
|
|
251
256
|
|
|
252
257
|
const createRemoteVideo = function(parent) {
|
|
253
|
-
remoteVideo = document.createElement("video");
|
|
258
|
+
let remoteVideo = document.createElement("video");
|
|
254
259
|
remoteVideo.id = "remoteVideo";
|
|
255
|
-
remoteVideo.width=852;
|
|
256
|
-
remoteVideo.height=480;
|
|
257
260
|
remoteVideo.controls="controls";
|
|
258
261
|
remoteVideo.autoplay="autoplay";
|
|
259
262
|
remoteVideo.type="application/vnd.apple.mpegurl";
|
|
@@ -314,7 +317,8 @@ const initVideoJsPlayer = function(video) {
|
|
|
314
317
|
liveTracker: {
|
|
315
318
|
trackingThreshold: LIVE_THRESHOLD,
|
|
316
319
|
liveTolerance: LIVE_TOLERANCE
|
|
317
|
-
}
|
|
320
|
+
},
|
|
321
|
+
fill: true
|
|
318
322
|
});
|
|
319
323
|
console.log("Using VideoJs " + videojs.VERSION);
|
|
320
324
|
if (Browser.isSafariWebRTC() && Browser.isiOS()) {
|
|
@@ -361,4 +365,51 @@ const toggleInputs = function(enable) {
|
|
|
361
365
|
disableItem("token");
|
|
362
366
|
disableItem("player");
|
|
363
367
|
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const PlaybackStats = function(interval) {
|
|
371
|
+
const playbackStats = {
|
|
372
|
+
interval: interval || STATS_INTERVAL,
|
|
373
|
+
timer: null,
|
|
374
|
+
stats: null,
|
|
375
|
+
start: function() {
|
|
376
|
+
let video = getActualVideoTag();
|
|
377
|
+
|
|
378
|
+
playbackStats.stop();
|
|
379
|
+
stats = HTML5Stats(video);
|
|
380
|
+
playbackStats.timer = setInterval(playbackStats.displayStats, playbackStats.interval);
|
|
381
|
+
setText("videoWidth", "N/A");
|
|
382
|
+
setText("videoHeight", "N/A");
|
|
383
|
+
setText("videoRate", "N/A");
|
|
384
|
+
setText("videoFps", "N/A");
|
|
385
|
+
showItem("stats");
|
|
386
|
+
},
|
|
387
|
+
stop: function() {
|
|
388
|
+
if (playbackStats.timer) {
|
|
389
|
+
clearInterval(playbackStats.timer);
|
|
390
|
+
playbackStats.timer = null;
|
|
391
|
+
}
|
|
392
|
+
playbackStats.stats = null;
|
|
393
|
+
hideItem("stats");
|
|
394
|
+
},
|
|
395
|
+
displayStats: function() {
|
|
396
|
+
if (stats.collect()) {
|
|
397
|
+
let width = stats.getWidth();
|
|
398
|
+
let height = stats.getHeight();
|
|
399
|
+
let bitrate = stats.getBitrate();
|
|
400
|
+
let fps = stats.getFps();
|
|
401
|
+
|
|
402
|
+
setText("videoWidth", width);
|
|
403
|
+
setText("videoHeight", height);
|
|
404
|
+
|
|
405
|
+
if (bitrate !== undefined) {
|
|
406
|
+
setText("videoRate", Math.round(bitrate));
|
|
407
|
+
}
|
|
408
|
+
if (fps !== undefined) {
|
|
409
|
+
setText("videoFps", fps.toFixed(1));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
return playbackStats;
|
|
364
415
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<div class="row">
|
|
2
|
-
<div class="col-sm-12 text-center" style="max-width:
|
|
2
|
+
<div class="col-sm-12 text-center" style="max-width: 500px">
|
|
3
3
|
|
|
4
4
|
<h2 id="header"></h2>
|
|
5
5
|
|
|
@@ -69,12 +69,42 @@
|
|
|
69
69
|
</div>
|
|
70
70
|
</div>
|
|
71
71
|
<div class="row">
|
|
72
|
-
<div id="videoContainer" class="col-sm-12 text-center" style="margin-top: 20px; max-width: 852px">
|
|
73
|
-
<video id="remoteVideo"
|
|
72
|
+
<div id="videoContainer" class="col-sm-12 text-center" style="margin-top: 20px; max-width: 852px; width: available; max-height: 480px; height: 56.25vw">
|
|
73
|
+
<video id="remoteVideo"
|
|
74
|
+
width="100%"
|
|
75
|
+
height="100%"
|
|
74
76
|
controls="controls"
|
|
75
77
|
autoplay="autoplay"
|
|
76
78
|
playsinline="playsinline"
|
|
77
79
|
webkit-playsinline="webkit-playsinline"
|
|
78
80
|
type="application/vnd.apple.mpegurl"></video>
|
|
79
81
|
</div>
|
|
82
|
+
</div>
|
|
83
|
+
<div id="stats" class="row" style="display: none;">
|
|
84
|
+
<div class="col-sm-12">
|
|
85
|
+
<div class="row" style="margin-top: 20px">
|
|
86
|
+
<div class="col-sm-2">
|
|
87
|
+
<label class="form-check-label" for="videoWidth">Video width</label>
|
|
88
|
+
</div>
|
|
89
|
+
<div class="col-sm-2" id="videoWidth"></div>
|
|
90
|
+
</div>
|
|
91
|
+
<div class="row" style="margin-top: 10px">
|
|
92
|
+
<div class="col-sm-2">
|
|
93
|
+
<label class="form-check-label" for="videoHeight">Video height</label>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="col-sm-2" id="videoHeight"></div>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="row" style="margin-top: 10px">
|
|
98
|
+
<div class="col-sm-2">
|
|
99
|
+
<label class="form-check-label" for="videoRate">Bitrate, bps</label>
|
|
100
|
+
</div>
|
|
101
|
+
<div class="col-sm-2" id="videoRate"></div>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="row" style="margin-top: 10px">
|
|
104
|
+
<div class="col-sm-2">
|
|
105
|
+
<label class="form-check-label" for="videoFps">Framerate</label>
|
|
106
|
+
</div>
|
|
107
|
+
<div class="col-sm-2" id="videoFps"></div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
80
110
|
</div>
|