forked from shaka-project/shaka-player
-
Notifications
You must be signed in to change notification settings - Fork 0
/
time_ranges_utils.js
204 lines (178 loc) · 5.29 KB
/
time_ranges_utils.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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
goog.provide('shaka.media.TimeRangesUtils');
/**
* @summary A set of utility functions for dealing with TimeRanges objects.
*/
shaka.media.TimeRangesUtils = class {
/**
* Gets the first timestamp in the buffer.
*
* @param {TimeRanges} b
* @return {?number} The first buffered timestamp, in seconds, if |buffered|
* is non-empty; otherwise, return null.
*/
static bufferStart(b) {
if (!b) {
return null;
}
// Workaround Safari bug: https://bit.ly/2trx6O8
if (b.length == 1 && b.end(0) - b.start(0) < 1e-6) {
return null;
}
// Workaround Edge bug: https://bit.ly/2JYLPeB
if (b.length == 1 && b.start(0) < 0) {
return 0;
}
return b.length ? b.start(0) : null;
}
/**
* Gets the last timestamp in the buffer.
*
* @param {TimeRanges} b
* @return {?number} The last buffered timestamp, in seconds, if |buffered|
* is non-empty; otherwise, return null.
*/
static bufferEnd(b) {
if (!b) {
return null;
}
// Workaround Safari bug: https://bit.ly/2trx6O8
if (b.length == 1 && b.end(0) - b.start(0) < 1e-6) {
return null;
}
return b.length ? b.end(b.length - 1) : null;
}
/**
* Determines if the given time is inside a buffered range.
*
* @param {TimeRanges} b
* @param {number} time Playhead time
* @return {boolean}
*/
static isBuffered(b, time) {
if (!b || !b.length) {
return false;
}
// Workaround Safari bug: https://bit.ly/2trx6O8
if (b.length == 1 && b.end(0) - b.start(0) < 1e-6) {
return false;
}
if (time > b.end(b.length - 1)) {
return false;
}
return time >= b.start(0);
}
/**
* Computes how far ahead of the given timestamp is buffered. To provide
* smooth playback while jumping gaps, we don't include the gaps when
* calculating this.
* This only includes the amount of content that is buffered.
*
* @param {TimeRanges} b
* @param {number} time
* @return {number} The number of seconds buffered, in seconds, ahead of the
* given time.
*/
static bufferedAheadOf(b, time) {
if (!b || !b.length) {
return 0;
}
// Workaround Safari bug: https://bit.ly/2trx6O8
if (b.length == 1 && b.end(0) - b.start(0) < 1e-6) {
return 0;
}
// We calculate the buffered amount by ONLY accounting for the content
// buffered (i.e. we ignore the times of the gaps). We also buffer through
// all gaps.
// Therefore, we start at the end and add up all buffers until |time|.
let result = 0;
for (const {start, end} of shaka.media.TimeRangesUtils.getBufferedInfo(b)) {
if (end > time) {
result += end - Math.max(start, time);
}
}
return result;
}
/**
* Determines if the given time is inside a gap between buffered ranges. If
* it is, this returns the index of the buffer that is *ahead* of the gap.
*
* @param {TimeRanges} b
* @param {number} time
* @param {number} threshold
* @return {?number} The index of the buffer after the gap, or null if not in
* a gap.
*/
static getGapIndex(b, time, threshold) {
const TimeRangesUtils = shaka.media.TimeRangesUtils;
if (!b || !b.length) {
return null;
}
// Workaround Safari bug: https://bit.ly/2trx6O8
if (b.length == 1 && b.end(0) - b.start(0) < 1e-6) {
return null;
}
const idx = TimeRangesUtils.getBufferedInfo(b).findIndex((item, i, arr) => {
return item.start > time &&
(i == 0 || arr[i - 1].end - time <= threshold);
});
return idx >= 0 ? idx : null;
}
/**
* @param {TimeRanges} b
* @return {!Array.<shaka.extern.BufferedRange>}
*/
static getBufferedInfo(b) {
if (!b) {
return [];
}
const ret = [];
for (let i = 0; i < b.length; i++) {
ret.push({start: b.start(i), end: b.end(i)});
}
return ret;
}
/**
* This operation can be potentially EXPENSIVE and should only be done in
* debug builds for debugging purposes.
*
* @param {TimeRanges} oldRanges
* @param {TimeRanges} newRanges
* @return {?shaka.extern.BufferedRange} The last added range,
* chronologically by presentation time.
*/
static computeAddedRange(oldRanges, newRanges) {
const TimeRangesUtils = shaka.media.TimeRangesUtils;
if (!oldRanges || !oldRanges.length) {
return null;
}
if (!newRanges || !newRanges.length) {
return TimeRangesUtils.getBufferedInfo(newRanges).pop();
}
const newRangesReversed =
TimeRangesUtils.getBufferedInfo(newRanges).reverse();
const oldRangesReversed =
TimeRangesUtils.getBufferedInfo(oldRanges).reverse();
for (const newRange of newRangesReversed) {
let foundOverlap = false;
for (const oldRange of oldRangesReversed) {
if (oldRange.end >= newRange.start && oldRange.end <= newRange.end) {
foundOverlap = true;
// If the new range goes beyond the corresponding old one, the
// difference is newly-added.
if (newRange.end > oldRange.end) {
return {start: oldRange.end, end: newRange.end};
}
}
}
if (!foundOverlap) {
return newRange;
}
}
return null;
}
};