Fix whitespace - tabs to spaces!
Showing
1 changed file
with
466 additions
and
466 deletions
... | @@ -87,378 +87,378 @@ const updateSegments = function(original, update, offset) { | ... | @@ -87,378 +87,378 @@ const updateSegments = function(original, update, offset) { |
87 | 87 | ||
88 | export default class PlaylistLoader extends Stream { | 88 | export default class PlaylistLoader extends Stream { |
89 | constructor(srcUrl, withCredentials) { | 89 | constructor(srcUrl, withCredentials) { |
90 | super(); | 90 | super(); |
91 | let loader = this; | 91 | let loader = this; |
92 | let dispose; | 92 | let dispose; |
93 | let mediaUpdateTimeout; | 93 | let mediaUpdateTimeout; |
94 | let request; | 94 | let request; |
95 | let playlistRequestError; | 95 | let playlistRequestError; |
96 | let haveMetadata; | 96 | let haveMetadata; |
97 | 97 | ||
98 | // a flag that disables "expired time"-tracking this setting has | 98 | // a flag that disables "expired time"-tracking this setting has |
99 | // no effect when not playing a live stream | 99 | // no effect when not playing a live stream |
100 | this.trackExpiredTime_ = false; | 100 | this.trackExpiredTime_ = false; |
101 | 101 | ||
102 | if (!srcUrl) { | 102 | if (!srcUrl) { |
103 | throw new Error('A non-empty playlist URL is required'); | 103 | throw new Error('A non-empty playlist URL is required'); |
104 | } | 104 | } |
105 | 105 | ||
106 | playlistRequestError = function(xhr, url, startingState) { | 106 | playlistRequestError = function(xhr, url, startingState) { |
107 | loader.setBandwidth(request || xhr); | 107 | loader.setBandwidth(request || xhr); |
108 | 108 | ||
109 | // any in-flight request is now finished | 109 | // any in-flight request is now finished |
110 | request = null; | 110 | request = null; |
111 | 111 | ||
112 | if (startingState) { | 112 | if (startingState) { |
113 | loader.state = startingState; | 113 | loader.state = startingState; |
114 | } | 114 | } |
115 | 115 | ||
116 | loader.error = { | 116 | loader.error = { |
117 | playlist: loader.master.playlists[url], | 117 | playlist: loader.master.playlists[url], |
118 | status: xhr.status, | 118 | status: xhr.status, |
119 | message: 'HLS playlist request error at URL: ' + url, | 119 | message: 'HLS playlist request error at URL: ' + url, |
120 | responseText: xhr.responseText, | 120 | responseText: xhr.responseText, |
121 | code: (xhr.status >= 500) ? 4 : 2 | 121 | code: (xhr.status >= 500) ? 4 : 2 |
122 | }; | 122 | }; |
123 | loader.trigger('error'); | 123 | loader.trigger('error'); |
124 | }; | 124 | }; |
125 | 125 | ||
126 | // update the playlist loader's state in response to a new or | 126 | // update the playlist loader's state in response to a new or |
127 | // updated playlist. | 127 | // updated playlist. |
128 | 128 | ||
129 | haveMetadata = function(xhr, url) { | 129 | haveMetadata = function(xhr, url) { |
130 | let parser; | 130 | let parser; |
131 | let refreshDelay; | 131 | let refreshDelay; |
132 | let update; | 132 | let update; |
133 | 133 | ||
134 | loader.setBandwidth(request || xhr); | 134 | loader.setBandwidth(request || xhr); |
135 | 135 | ||
136 | // any in-flight request is now finished | 136 | // any in-flight request is now finished |
137 | request = null; | 137 | request = null; |
138 | loader.state = 'HAVE_METADATA'; | 138 | loader.state = 'HAVE_METADATA'; |
139 | 139 | ||
140 | parser = new m3u8.Parser(); | 140 | parser = new m3u8.Parser(); |
141 | parser.push(xhr.responseText); | 141 | parser.push(xhr.responseText); |
142 | parser.end(); | 142 | parser.end(); |
143 | parser.manifest.uri = url; | 143 | parser.manifest.uri = url; |
144 | 144 | ||
145 | // merge this playlist into the master | 145 | // merge this playlist into the master |
146 | update = updateMaster(loader.master, parser.manifest); | 146 | update = updateMaster(loader.master, parser.manifest); |
147 | refreshDelay = (parser.manifest.targetDuration || 10) * 1000; | 147 | refreshDelay = (parser.manifest.targetDuration || 10) * 1000; |
148 | if (update) { | 148 | if (update) { |
149 | loader.master = update; | 149 | loader.master = update; |
150 | loader.updateMediaPlaylist_(parser.manifest); | 150 | loader.updateMediaPlaylist_(parser.manifest); |
151 | } else { | 151 | } else { |
152 | // if the playlist is unchanged since the last reload, | 152 | // if the playlist is unchanged since the last reload, |
153 | // try again after half the target duration | 153 | // try again after half the target duration |
154 | refreshDelay /= 2; | 154 | refreshDelay /= 2; |
155 | } | 155 | } |
156 | 156 | ||
157 | // refresh live playlists after a target duration passes | 157 | // refresh live playlists after a target duration passes |
158 | if (!loader.media().endList) { | 158 | if (!loader.media().endList) { |
159 | window.clearTimeout(mediaUpdateTimeout); | 159 | window.clearTimeout(mediaUpdateTimeout); |
160 | mediaUpdateTimeout = window.setTimeout(function() { | 160 | mediaUpdateTimeout = window.setTimeout(function() { |
161 | loader.trigger('mediaupdatetimeout'); | 161 | loader.trigger('mediaupdatetimeout'); |
162 | }, refreshDelay); | 162 | }, refreshDelay); |
163 | } | 163 | } |
164 | 164 | ||
165 | loader.trigger('loadedplaylist'); | 165 | loader.trigger('loadedplaylist'); |
166 | }; | 166 | }; |
167 | 167 | ||
168 | // initialize the loader state | 168 | // initialize the loader state |
169 | loader.state = 'HAVE_NOTHING'; | 169 | loader.state = 'HAVE_NOTHING'; |
170 | 170 | ||
171 | // track the time that has expired from the live window | 171 | // track the time that has expired from the live window |
172 | // this allows the seekable start range to be calculated even if | 172 | // this allows the seekable start range to be calculated even if |
173 | // all segments with timing information have expired | 173 | // all segments with timing information have expired |
174 | this.expired_ = 0; | 174 | this.expired_ = 0; |
175 | 175 | ||
176 | // capture the prototype dispose function | 176 | // capture the prototype dispose function |
177 | dispose = this.dispose; | 177 | dispose = this.dispose; |
178 | 178 | ||
179 | /** | 179 | /** |
180 | * Abort any outstanding work and clean up. | 180 | * Abort any outstanding work and clean up. |
181 | */ | 181 | */ |
182 | loader.dispose = function() { | 182 | loader.dispose = function() { |
183 | if (request) { | 183 | if (request) { |
184 | request.onreadystatechange = null; | 184 | request.onreadystatechange = null; |
185 | request.abort(); | 185 | request.abort(); |
186 | request = null; | 186 | request = null; |
187 | } | 187 | } |
188 | window.clearTimeout(mediaUpdateTimeout); | 188 | window.clearTimeout(mediaUpdateTimeout); |
189 | dispose.call(this); | 189 | dispose.call(this); |
190 | }; | 190 | }; |
191 | 191 | ||
192 | /** | 192 | /** |
193 | * When called without any arguments, returns the currently | 193 | * When called without any arguments, returns the currently |
194 | * active media playlist. When called with a single argument, | 194 | * active media playlist. When called with a single argument, |
195 | * triggers the playlist loader to asynchronously switch to the | 195 | * triggers the playlist loader to asynchronously switch to the |
196 | * specified media playlist. Calling this method while the | 196 | * specified media playlist. Calling this method while the |
197 | * loader is in the HAVE_NOTHING causes an error to be emitted | 197 | * loader is in the HAVE_NOTHING causes an error to be emitted |
198 | * but otherwise has no effect. | 198 | * but otherwise has no effect. |
199 | * @param playlist (optional) {object} the parsed media playlist | 199 | * @param playlist (optional) {object} the parsed media playlist |
200 | * object to switch to | 200 | * object to switch to |
201 | */ | 201 | */ |
202 | loader.media = function(playlist) { | 202 | loader.media = function(playlist) { |
203 | let startingState = loader.state; | 203 | let startingState = loader.state; |
204 | let mediaChange; | 204 | let mediaChange; |
205 | // getter | 205 | // getter |
206 | if (!playlist) { | 206 | if (!playlist) { |
207 | return loader.media_; | 207 | return loader.media_; |
208 | } | 208 | } |
209 | 209 | ||
210 | // setter | 210 | // setter |
211 | if (loader.state === 'HAVE_NOTHING') { | 211 | if (loader.state === 'HAVE_NOTHING') { |
212 | throw new Error('Cannot switch media playlist from ' + loader.state); | 212 | throw new Error('Cannot switch media playlist from ' + loader.state); |
213 | } | 213 | } |
214 | 214 | ||
215 | // find the playlist object if the target playlist has been | 215 | // find the playlist object if the target playlist has been |
216 | // specified by URI | 216 | // specified by URI |
217 | if (typeof playlist === 'string') { | 217 | if (typeof playlist === 'string') { |
218 | if (!loader.master.playlists[playlist]) { | 218 | if (!loader.master.playlists[playlist]) { |
219 | throw new Error('Unknown playlist URI: ' + playlist); | 219 | throw new Error('Unknown playlist URI: ' + playlist); |
220 | } | 220 | } |
221 | playlist = loader.master.playlists[playlist]; | 221 | playlist = loader.master.playlists[playlist]; |
222 | } | 222 | } |
223 | 223 | ||
224 | mediaChange = !loader.media_ || playlist.uri !== loader.media_.uri; | 224 | mediaChange = !loader.media_ || playlist.uri !== loader.media_.uri; |
225 | 225 | ||
226 | // switch to fully loaded playlists immediately | 226 | // switch to fully loaded playlists immediately |
227 | if (loader.master.playlists[playlist.uri].endList) { | 227 | if (loader.master.playlists[playlist.uri].endList) { |
228 | // abort outstanding playlist requests | 228 | // abort outstanding playlist requests |
229 | if (request) { | 229 | if (request) { |
230 | request.onreadystatechange = null; | 230 | request.onreadystatechange = null; |
231 | request.abort(); | 231 | request.abort(); |
232 | request = null; | 232 | request = null; |
233 | } | 233 | } |
234 | loader.state = 'HAVE_METADATA'; | 234 | loader.state = 'HAVE_METADATA'; |
235 | loader.media_ = playlist; | 235 | loader.media_ = playlist; |
236 | 236 | ||
237 | // trigger media change if the active media has been updated | 237 | // trigger media change if the active media has been updated |
238 | if (mediaChange) { | 238 | if (mediaChange) { |
239 | loader.trigger('mediachange'); | 239 | loader.trigger('mediachange'); |
240 | } | 240 | } |
241 | return; | 241 | return; |
242 | } | 242 | } |
243 | 243 | ||
244 | // switching to the active playlist is a no-op | 244 | // switching to the active playlist is a no-op |
245 | if (!mediaChange) { | 245 | if (!mediaChange) { |
246 | return; | 246 | return; |
247 | } | 247 | } |
248 | 248 | ||
249 | loader.state = 'SWITCHING_MEDIA'; | 249 | loader.state = 'SWITCHING_MEDIA'; |
250 | 250 | ||
251 | // there is already an outstanding playlist request | 251 | // there is already an outstanding playlist request |
252 | if (request) { | 252 | if (request) { |
253 | if (resolveUrl(loader.master.uri, playlist.uri) === request.url) { | 253 | if (resolveUrl(loader.master.uri, playlist.uri) === request.url) { |
254 | // requesting to switch to the same playlist multiple times | 254 | // requesting to switch to the same playlist multiple times |
255 | // has no effect after the first | 255 | // has no effect after the first |
256 | return; | 256 | return; |
257 | } | 257 | } |
258 | request.onreadystatechange = null; | 258 | request.onreadystatechange = null; |
259 | request.abort(); | 259 | request.abort(); |
260 | request = null; | 260 | request = null; |
261 | } | 261 | } |
262 | 262 | ||
263 | // request the new playlist | 263 | // request the new playlist |
264 | request = XhrModule({ | 264 | request = XhrModule({ |
265 | uri: resolveUrl(loader.master.uri, playlist.uri), | 265 | uri: resolveUrl(loader.master.uri, playlist.uri), |
266 | withCredentials | 266 | withCredentials |
267 | }, function(error, request) { | 267 | }, function(error, request) { |
268 | if (error) { | 268 | if (error) { |
269 | return playlistRequestError(request, playlist.uri, startingState); | 269 | return playlistRequestError(request, playlist.uri, startingState); |
270 | } | 270 | } |
271 | 271 | ||
272 | haveMetadata(request, playlist.uri); | 272 | haveMetadata(request, playlist.uri); |
273 | 273 | ||
274 | // fire loadedmetadata the first time a media playlist is loaded | 274 | // fire loadedmetadata the first time a media playlist is loaded |
275 | if (startingState === 'HAVE_MASTER') { | 275 | if (startingState === 'HAVE_MASTER') { |
276 | loader.trigger('loadedmetadata'); | 276 | loader.trigger('loadedmetadata'); |
277 | } else { | 277 | } else { |
278 | loader.trigger('mediachange'); | 278 | loader.trigger('mediachange'); |
279 | } | 279 | } |
280 | }); | 280 | }); |
281 | }; | 281 | }; |
282 | 282 | ||
283 | loader.setBandwidth = function(xhr) { | 283 | loader.setBandwidth = function(xhr) { |
284 | loader.bandwidth = xhr.bandwidth; | 284 | loader.bandwidth = xhr.bandwidth; |
285 | }; | 285 | }; |
286 | 286 | ||
287 | // In a live list, don't keep track of the expired time until | 287 | // In a live list, don't keep track of the expired time until |
288 | // HLS tells us that "first play" has commenced | 288 | // HLS tells us that "first play" has commenced |
289 | loader.on('firstplay', function() { | 289 | loader.on('firstplay', function() { |
290 | this.trackExpiredTime_ = true; | 290 | this.trackExpiredTime_ = true; |
291 | }); | 291 | }); |
292 | 292 | ||
293 | // live playlist staleness timeout | 293 | // live playlist staleness timeout |
294 | loader.on('mediaupdatetimeout', function() { | 294 | loader.on('mediaupdatetimeout', function() { |
295 | if (loader.state !== 'HAVE_METADATA') { | 295 | if (loader.state !== 'HAVE_METADATA') { |
296 | // only refresh the media playlist if no other activity is going on | 296 | // only refresh the media playlist if no other activity is going on |
297 | return; | 297 | return; |
298 | } | 298 | } |
299 | 299 | ||
300 | loader.state = 'HAVE_CURRENT_METADATA'; | 300 | loader.state = 'HAVE_CURRENT_METADATA'; |
301 | request = XhrModule({ | 301 | request = XhrModule({ |
302 | uri: resolveUrl(loader.master.uri, loader.media().uri), | 302 | uri: resolveUrl(loader.master.uri, loader.media().uri), |
303 | withCredentials | 303 | withCredentials |
304 | }, function(error, request) { | 304 | }, function(error, request) { |
305 | if (error) { | 305 | if (error) { |
306 | return playlistRequestError(request, loader.media().uri); | 306 | return playlistRequestError(request, loader.media().uri); |
307 | } | 307 | } |
308 | haveMetadata(request, loader.media().uri); | 308 | haveMetadata(request, loader.media().uri); |
309 | }); | 309 | }); |
310 | }); | 310 | }); |
311 | 311 | ||
312 | // request the specified URL | 312 | // request the specified URL |
313 | request = XhrModule({ | 313 | request = XhrModule({ |
314 | uri: srcUrl, | 314 | uri: srcUrl, |
315 | withCredentials | 315 | withCredentials |
316 | }, function(error, req) { | 316 | }, function(error, req) { |
317 | let parser; | 317 | let parser; |
318 | let i; | 318 | let i; |
319 | 319 | ||
320 | // clear the loader's request reference | 320 | // clear the loader's request reference |
321 | request = null; | 321 | request = null; |
322 | 322 | ||
323 | if (error) { | 323 | if (error) { |
324 | loader.error = { | 324 | loader.error = { |
325 | status: req.status, | 325 | status: req.status, |
326 | message: 'HLS playlist request error at URL: ' + srcUrl, | 326 | message: 'HLS playlist request error at URL: ' + srcUrl, |
327 | responseText: req.responseText, | 327 | responseText: req.responseText, |
328 | // MEDIA_ERR_NETWORK | 328 | // MEDIA_ERR_NETWORK |
329 | code: 2 | 329 | code: 2 |
330 | }; | 330 | }; |
331 | return loader.trigger('error'); | 331 | return loader.trigger('error'); |
332 | } | 332 | } |
333 | 333 | ||
334 | parser = new m3u8.Parser(); | 334 | parser = new m3u8.Parser(); |
335 | parser.push(req.responseText); | 335 | parser.push(req.responseText); |
336 | parser.end(); | 336 | parser.end(); |
337 | 337 | ||
338 | loader.state = 'HAVE_MASTER'; | 338 | loader.state = 'HAVE_MASTER'; |
339 | 339 | ||
340 | parser.manifest.uri = srcUrl; | 340 | parser.manifest.uri = srcUrl; |
341 | 341 | ||
342 | // loaded a master playlist | 342 | // loaded a master playlist |
343 | if (parser.manifest.playlists) { | 343 | if (parser.manifest.playlists) { |
344 | loader.master = parser.manifest; | 344 | loader.master = parser.manifest; |
345 | 345 | ||
346 | // setup by-URI lookups | 346 | // setup by-URI lookups |
347 | i = loader.master.playlists.length; | 347 | i = loader.master.playlists.length; |
348 | while (i--) { | 348 | while (i--) { |
349 | loader.master.playlists[loader.master.playlists[i].uri] = loader.master.playlists[i]; | 349 | loader.master.playlists[loader.master.playlists[i].uri] = loader.master.playlists[i]; |
350 | } | 350 | } |
351 | 351 | ||
352 | loader.trigger('loadedplaylist'); | 352 | loader.trigger('loadedplaylist'); |
353 | if (!request) { | 353 | if (!request) { |
354 | // no media playlist was specifically selected so start | 354 | // no media playlist was specifically selected so start |
355 | // from the first listed one | 355 | // from the first listed one |
356 | loader.media(parser.manifest.playlists[0]); | 356 | loader.media(parser.manifest.playlists[0]); |
357 | } | 357 | } |
358 | return; | 358 | return; |
359 | } | 359 | } |
360 | 360 | ||
361 | // loaded a media playlist | 361 | // loaded a media playlist |
362 | // infer a master playlist if none was previously requested | 362 | // infer a master playlist if none was previously requested |
363 | loader.master = { | 363 | loader.master = { |
364 | uri: window.location.href, | 364 | uri: window.location.href, |
365 | playlists: [{ | 365 | playlists: [{ |
366 | uri: srcUrl | 366 | uri: srcUrl |
367 | }] | 367 | }] |
368 | }; | 368 | }; |
369 | loader.master.playlists[srcUrl] = loader.master.playlists[0]; | 369 | loader.master.playlists[srcUrl] = loader.master.playlists[0]; |
370 | haveMetadata(req, srcUrl); | 370 | haveMetadata(req, srcUrl); |
371 | return loader.trigger('loadedmetadata'); | 371 | return loader.trigger('loadedmetadata'); |
372 | }); | 372 | }); |
373 | } | 373 | } |
374 | /** | 374 | /** |
375 | * Update the PlaylistLoader state to reflect the changes in an | 375 | * Update the PlaylistLoader state to reflect the changes in an |
376 | * update to the current media playlist. | 376 | * update to the current media playlist. |
377 | * @param update {object} the updated media playlist object | 377 | * @param update {object} the updated media playlist object |
378 | */ | 378 | */ |
379 | updateMediaPlaylist_(update) { | 379 | updateMediaPlaylist_(update) { |
380 | let outdated; | 380 | let outdated; |
381 | let i; | 381 | let i; |
382 | let segment; | 382 | let segment; |
383 | 383 | ||
384 | outdated = this.media_; | 384 | outdated = this.media_; |
385 | this.media_ = this.master.playlists[update.uri]; | 385 | this.media_ = this.master.playlists[update.uri]; |
386 | 386 | ||
387 | if (!outdated) { | 387 | if (!outdated) { |
388 | return; | 388 | return; |
389 | } | 389 | } |
390 | 390 | ||
391 | // don't track expired time until this flag is truthy | 391 | // don't track expired time until this flag is truthy |
392 | if (!this.trackExpiredTime_) { | 392 | if (!this.trackExpiredTime_) { |
393 | return; | 393 | return; |
394 | } | 394 | } |
395 | 395 | ||
396 | // if the update was the result of a rendition switch do not | 396 | // if the update was the result of a rendition switch do not |
397 | // attempt to calculate expired_ since media-sequences need not | 397 | // attempt to calculate expired_ since media-sequences need not |
398 | // correlate between renditions/variants | 398 | // correlate between renditions/variants |
399 | if (update.uri !== outdated.uri) { | 399 | if (update.uri !== outdated.uri) { |
400 | return; | 400 | return; |
401 | } | 401 | } |
402 | 402 | ||
403 | // try using precise timing from first segment of the updated | 403 | // try using precise timing from first segment of the updated |
404 | // playlist | 404 | // playlist |
405 | if (update.segments.length) { | 405 | if (update.segments.length) { |
406 | if (update.segments[0].start !== undefined) { | 406 | if (update.segments[0].start !== undefined) { |
407 | this.expired_ = update.segments[0].start; | 407 | this.expired_ = update.segments[0].start; |
408 | return; | 408 | return; |
409 | } else if (update.segments[0].end !== undefined) { | 409 | } else if (update.segments[0].end !== undefined) { |
410 | this.expired_ = update.segments[0].end - update.segments[0].duration; | 410 | this.expired_ = update.segments[0].end - update.segments[0].duration; |
411 | return; | 411 | return; |
412 | } | 412 | } |
413 | } | 413 | } |
414 | 414 | ||
415 | // calculate expired by walking the outdated playlist | 415 | // calculate expired by walking the outdated playlist |
416 | i = update.mediaSequence - outdated.mediaSequence - 1; | 416 | i = update.mediaSequence - outdated.mediaSequence - 1; |
417 | 417 | ||
418 | for (; i >= 0; i--) { | 418 | for (; i >= 0; i--) { |
419 | segment = outdated.segments[i]; | 419 | segment = outdated.segments[i]; |
420 | 420 | ||
421 | if (!segment) { | 421 | if (!segment) { |
422 | // we missed information on this segment completely between | 422 | // we missed information on this segment completely between |
423 | // playlist updates so we'll have to take an educated guess | 423 | // playlist updates so we'll have to take an educated guess |
424 | // once we begin buffering again, any error we introduce can | 424 | // once we begin buffering again, any error we introduce can |
425 | // be corrected | 425 | // be corrected |
426 | this.expired_ += outdated.targetDuration || 10; | 426 | this.expired_ += outdated.targetDuration || 10; |
427 | continue; | 427 | continue; |
428 | } | 428 | } |
429 | 429 | ||
430 | if (segment.end !== undefined) { | 430 | if (segment.end !== undefined) { |
431 | this.expired_ = segment.end; | 431 | this.expired_ = segment.end; |
432 | return; | 432 | return; |
433 | } | 433 | } |
434 | if (segment.start !== undefined) { | 434 | if (segment.start !== undefined) { |
435 | this.expired_ = segment.start + segment.duration; | 435 | this.expired_ = segment.start + segment.duration; |
436 | return; | 436 | return; |
437 | } | 437 | } |
438 | this.expired_ += segment.duration; | 438 | this.expired_ += segment.duration; |
439 | } | 439 | } |
440 | } | 440 | } |
441 | 441 | ||
442 | /** | 442 | /** |
443 | * Determine the index of the segment that contains a specified | 443 | * Determine the index of the segment that contains a specified |
444 | * playback position in the current media playlist. Early versions | 444 | * playback position in the current media playlist. Early versions |
445 | * of the HLS specification require segment durations to be rounded | 445 | * of the HLS specification require segment durations to be rounded |
446 | * to the nearest integer which means it may not be possible to | 446 | * to the nearest integer which means it may not be possible to |
447 | * determine the correct segment for a playback position if that | 447 | * determine the correct segment for a playback position if that |
448 | * position is within .5 seconds of the segment duration. This | 448 | * position is within .5 seconds of the segment duration. This |
449 | * function will always return the lower of the two possible indices | 449 | * function will always return the lower of the two possible indices |
450 | * in those cases. | 450 | * in those cases. |
451 | * | 451 | * |
452 | * @param time {number} The number of seconds since the earliest | 452 | * @param time {number} The number of seconds since the earliest |
453 | * possible position to determine the containing segment for | 453 | * possible position to determine the containing segment for |
454 | * @returns {number} The number of the media segment that contains | 454 | * @returns {number} The number of the media segment that contains |
455 | * that time position. If the specified playback position is outside | 455 | * that time position. If the specified playback position is outside |
456 | * the time range of the current set of media segments, the return | 456 | * the time range of the current set of media segments, the return |
457 | * value will be clamped to the index of the segment containing the | 457 | * value will be clamped to the index of the segment containing the |
458 | * closest playback position that is currently available. | 458 | * closest playback position that is currently available. |
459 | */ | 459 | */ |
460 | getMediaIndexForTime_(time) { | 460 | getMediaIndexForTime_(time) { |
461 | let i; | 461 | let i; |
462 | let segment; | 462 | let segment; |
463 | let originalTime = time; | 463 | let originalTime = time; |
464 | let numSegments = this.media_.segments.length; | 464 | let numSegments = this.media_.segments.length; |
... | @@ -468,99 +468,99 @@ export default class PlaylistLoader extends Stream { | ... | @@ -468,99 +468,99 @@ export default class PlaylistLoader extends Stream { |
468 | let knownStart; | 468 | let knownStart; |
469 | let knownEnd; | 469 | let knownEnd; |
470 | 470 | ||
471 | if (!this.media_) { | 471 | if (!this.media_) { |
472 | return 0; | 472 | return 0; |
473 | } | 473 | } |
474 | 474 | ||
475 | // when the requested position is earlier than the current set of | 475 | // when the requested position is earlier than the current set of |
476 | // segments, return the earliest segment index | 476 | // segments, return the earliest segment index |
477 | if (time < 0) { | 477 | if (time < 0) { |
478 | return 0; | 478 | return 0; |
479 | } | 479 | } |
480 | 480 | ||
481 | // find segments with known timing information that bound the | 481 | // find segments with known timing information that bound the |
482 | // target time | 482 | // target time |
483 | for (i = 0; i < numSegments; i++) { | 483 | for (i = 0; i < numSegments; i++) { |
484 | segment = this.media_.segments[i]; | 484 | segment = this.media_.segments[i]; |
485 | if (segment.end) { | 485 | if (segment.end) { |
486 | if (segment.end > time) { | 486 | if (segment.end > time) { |
487 | knownEnd = segment.end; | 487 | knownEnd = segment.end; |
488 | endIndex = i; | 488 | endIndex = i; |
489 | break; | 489 | break; |
490 | } else { | 490 | } else { |
491 | knownStart = segment.end; | 491 | knownStart = segment.end; |
492 | startIndex = i + 1; | 492 | startIndex = i + 1; |
493 | } | 493 | } |
494 | } | 494 | } |
495 | } | 495 | } |
496 | 496 | ||
497 | // use the bounds we just found and playlist information to | 497 | // use the bounds we just found and playlist information to |
498 | // estimate the segment that contains the time we are looking for | 498 | // estimate the segment that contains the time we are looking for |
499 | if (startIndex !== undefined) { | 499 | if (startIndex !== undefined) { |
500 | // We have a known-start point that is before our desired time so | 500 | // We have a known-start point that is before our desired time so |
501 | // walk from that point forwards | 501 | // walk from that point forwards |
502 | time = time - knownStart; | 502 | time = time - knownStart; |
503 | for (i = startIndex; i < (endIndex || numSegments); i++) { | 503 | for (i = startIndex; i < (endIndex || numSegments); i++) { |
504 | segment = this.media_.segments[i]; | 504 | segment = this.media_.segments[i]; |
505 | time -= segment.duration; | 505 | time -= segment.duration; |
506 | 506 | ||
507 | if (time < 0) { | 507 | if (time < 0) { |
508 | return i; | 508 | return i; |
509 | } | 509 | } |
510 | } | 510 | } |
511 | 511 | ||
512 | if (i >= endIndex) { | 512 | if (i >= endIndex) { |
513 | // We haven't found a segment but we did hit a known end point | 513 | // We haven't found a segment but we did hit a known end point |
514 | // so fallback to interpolating between the segment index | 514 | // so fallback to interpolating between the segment index |
515 | // based on the known span of the timeline we are dealing with | 515 | // based on the known span of the timeline we are dealing with |
516 | // and the number of segments inside that span | 516 | // and the number of segments inside that span |
517 | return startIndex + Math.floor( | 517 | return startIndex + Math.floor( |
518 | ((originalTime - knownStart) / (knownEnd - knownStart)) * | 518 | ((originalTime - knownStart) / (knownEnd - knownStart)) * |
519 | (endIndex - startIndex)); | 519 | (endIndex - startIndex)); |
520 | } | 520 | } |
521 | 521 | ||
522 | // We _still_ haven't found a segment so load the last one | 522 | // We _still_ haven't found a segment so load the last one |
523 | return lastSegment; | 523 | return lastSegment; |
524 | } else if (endIndex !== undefined) { | 524 | } else if (endIndex !== undefined) { |
525 | // We _only_ have a known-end point that is after our desired time so | 525 | // We _only_ have a known-end point that is after our desired time so |
526 | // walk from that point backwards | 526 | // walk from that point backwards |
527 | time = knownEnd - time; | 527 | time = knownEnd - time; |
528 | for (i = endIndex; i >= 0; i--) { | 528 | for (i = endIndex; i >= 0; i--) { |
529 | segment = this.media_.segments[i]; | 529 | segment = this.media_.segments[i]; |
530 | time -= segment.duration; | 530 | time -= segment.duration; |
531 | 531 | ||
532 | if (time < 0) { | 532 | if (time < 0) { |
533 | return i; | 533 | return i; |
534 | } | 534 | } |
535 | } | 535 | } |
536 | 536 | ||
537 | // We haven't found a segment so load the first one if time is zero | 537 | // We haven't found a segment so load the first one if time is zero |
538 | if (time === 0) { | 538 | if (time === 0) { |
539 | return 0; | 539 | return 0; |
540 | } else { | 540 | } else { |
541 | return -1; | 541 | return -1; |
542 | } | 542 | } |
543 | } else { | 543 | } else { |
544 | // We known nothing so walk from the front of the playlist, | 544 | // We known nothing so walk from the front of the playlist, |
545 | // subtracting durations until we find a segment that contains | 545 | // subtracting durations until we find a segment that contains |
546 | // time and return it | 546 | // time and return it |
547 | time = time - this.expired_; | 547 | time = time - this.expired_; |
548 | 548 | ||
549 | if (time < 0) { | 549 | if (time < 0) { |
550 | return -1; | 550 | return -1; |
551 | } | 551 | } |
552 | 552 | ||
553 | for (i = 0; i < numSegments; i++) { | 553 | for (i = 0; i < numSegments; i++) { |
554 | segment = this.media_.segments[i]; | 554 | segment = this.media_.segments[i]; |
555 | time -= segment.duration; | 555 | time -= segment.duration; |
556 | if (time < 0) { | 556 | if (time < 0) { |
557 | return i; | 557 | return i; |
558 | } | 558 | } |
559 | } | 559 | } |
560 | // We are out of possible candidates so load the last one... | 560 | // We are out of possible candidates so load the last one... |
561 | // The last one is the least likely to overlap a buffer and therefore | 561 | // The last one is the least likely to overlap a buffer and therefore |
562 | // the one most likely to tell us something about the timeline | 562 | // the one most likely to tell us something about the timeline |
563 | return lastSegment; | 563 | return lastSegment; |
564 | } | 564 | } |
565 | } | 565 | } |
566 | } | 566 | } | ... | ... |
-
Please register or sign in to post a comment