forked from video-dev/hls.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cap-level-controller.js
145 lines (124 loc) · 4.31 KB
/
cap-level-controller.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
* cap stream level to media size dimension controller
*/
import Event from '../events';
import EventHandler from '../event-handler';
class CapLevelController extends EventHandler {
constructor (hls) {
super(hls,
Event.FPS_DROP_LEVEL_CAPPING,
Event.MEDIA_ATTACHING,
Event.MANIFEST_PARSED);
}
destroy () {
if (this.hls.config.capLevelToPlayerSize) {
this.media = this.restrictedLevels = null;
this.autoLevelCapping = Number.POSITIVE_INFINITY;
if (this.timer) {
this.timer = clearInterval(this.timer);
}
}
}
onFpsDropLevelCapping (data) {
// Don't add a restricted level more than once
if (CapLevelController.isLevelAllowed(data.droppedLevel, this.restrictedLevels)) {
this.restrictedLevels.push(data.droppedLevel);
}
}
onMediaAttaching (data) {
this.media = data.media instanceof HTMLVideoElement ? data.media : null;
}
onManifestParsed (data) {
const hls = this.hls;
this.restrictedLevels = [];
// Only fire getMaxLevel or detectPlayerSize if video is expected in the manifest
if (hls.config.capLevelToPlayerSize && (data.video || (data.levels.length && data.altAudio))) {
this.autoLevelCapping = Number.POSITIVE_INFINITY;
this.levels = data.levels;
hls.firstLevel = this.getMaxLevel(data.firstLevel);
clearInterval(this.timer);
this.timer = setInterval(this.detectPlayerSize.bind(this), 1000);
this.detectPlayerSize();
}
}
detectPlayerSize () {
if (this.media) {
let levelsLength = this.levels ? this.levels.length : 0;
if (levelsLength) {
const hls = this.hls;
hls.autoLevelCapping = this.getMaxLevel(levelsLength - 1);
if (hls.autoLevelCapping > this.autoLevelCapping) {
// if auto level capping has a higher value for the previous one, flush the buffer using nextLevelSwitch
// usually happen when the user go to the fullscreen mode.
hls.streamController.nextLevelSwitch();
}
this.autoLevelCapping = hls.autoLevelCapping;
}
}
}
/*
* returns level should be the one with the dimensions equal or greater than the media (player) dimensions (so the video will be downscaled)
*/
getMaxLevel (capLevelIndex) {
if (!this.levels) {
return -1;
}
const validLevels = this.levels.filter((level, index) =>
CapLevelController.isLevelAllowed(index, this.restrictedLevels) && index <= capLevelIndex
);
return CapLevelController.getMaxLevelByMediaSize(validLevels, this.mediaWidth, this.mediaHeight);
}
get mediaWidth () {
let width;
const media = this.media;
if (media) {
width = media.width || media.clientWidth || media.offsetWidth;
width *= CapLevelController.contentScaleFactor;
}
return width;
}
get mediaHeight () {
let height;
const media = this.media;
if (media) {
height = media.height || media.clientHeight || media.offsetHeight;
height *= CapLevelController.contentScaleFactor;
}
return height;
}
static get contentScaleFactor () {
let pixelRatio = 1;
try {
pixelRatio = window.devicePixelRatio;
} catch (e) {}
return pixelRatio;
}
static isLevelAllowed (level, restrictedLevels = []) {
return restrictedLevels.indexOf(level) === -1;
}
static getMaxLevelByMediaSize (levels, width, height) {
if (!levels || (levels && !levels.length)) {
return -1;
}
// Levels can have the same dimensions but differing bandwidths - since levels are ordered, we can look to the next
// to determine whether we've chosen the greatest bandwidth for the media's dimensions
const atGreatestBandiwdth = (curLevel, nextLevel) => {
if (!nextLevel) {
return true;
}
return curLevel.width !== nextLevel.width || curLevel.height !== nextLevel.height;
};
// If we run through the loop without breaking, the media's dimensions are greater than every level, so default to
// the max level
let maxLevelIndex = levels.length - 1;
for (let i = 0; i < levels.length; i += 1) {
const level = levels[i];
if ((level.width >= width || level.height >= height) && atGreatestBandiwdth(level, levels[i + 1])) {
maxLevelIndex = i;
break;
}
}
return maxLevelIndex;
}
}
export default CapLevelController;