Expose the ability to set an `beforeRequest` function on the xhr object in order…
… to modify the options used to create the request object * Make Xhr a factory function that returns a unique instance of the xhr function and expose an instance of it on each player's xhr object * Keep the returned function is backward compatible with the previous xhr * Add a `beforeRequest` function to the XHR that allows you to override options before the request * The `beforeRequest` function can be specified on the global `videojs.Hls.xhr` function and it'll be used for all players unless overridden on a per-player level
Showing
5 changed files
with
157 additions
and
53 deletions
... | @@ -6,7 +6,6 @@ | ... | @@ -6,7 +6,6 @@ |
6 | * | 6 | * |
7 | */ | 7 | */ |
8 | import resolveUrl from './resolve-url'; | 8 | import resolveUrl from './resolve-url'; |
9 | import XhrModule from './xhr'; | ||
10 | import {mergeOptions} from 'video.js'; | 9 | import {mergeOptions} from 'video.js'; |
11 | import Stream from './stream'; | 10 | import Stream from './stream'; |
12 | import m3u8 from './m3u8'; | 11 | import m3u8 from './m3u8'; |
... | @@ -86,7 +85,7 @@ const updateSegments = function(original, update, offset) { | ... | @@ -86,7 +85,7 @@ const updateSegments = function(original, update, offset) { |
86 | }; | 85 | }; |
87 | 86 | ||
88 | export default class PlaylistLoader extends Stream { | 87 | export default class PlaylistLoader extends Stream { |
89 | constructor(srcUrl, withCredentials) { | 88 | constructor(srcUrl, hls, withCredentials) { |
90 | super(); | 89 | super(); |
91 | let loader = this; | 90 | let loader = this; |
92 | let dispose; | 91 | let dispose; |
... | @@ -95,6 +94,8 @@ export default class PlaylistLoader extends Stream { | ... | @@ -95,6 +94,8 @@ export default class PlaylistLoader extends Stream { |
95 | let playlistRequestError; | 94 | let playlistRequestError; |
96 | let haveMetadata; | 95 | let haveMetadata; |
97 | 96 | ||
97 | this.hls_ = hls; | ||
98 | |||
98 | // a flag that disables "expired time"-tracking this setting has | 99 | // a flag that disables "expired time"-tracking this setting has |
99 | // no effect when not playing a live stream | 100 | // no effect when not playing a live stream |
100 | this.trackExpiredTime_ = false; | 101 | this.trackExpiredTime_ = false; |
... | @@ -261,7 +262,7 @@ export default class PlaylistLoader extends Stream { | ... | @@ -261,7 +262,7 @@ export default class PlaylistLoader extends Stream { |
261 | } | 262 | } |
262 | 263 | ||
263 | // request the new playlist | 264 | // request the new playlist |
264 | request = XhrModule({ | 265 | request = this.hls_.xhr({ |
265 | uri: resolveUrl(loader.master.uri, playlist.uri), | 266 | uri: resolveUrl(loader.master.uri, playlist.uri), |
266 | withCredentials | 267 | withCredentials |
267 | }, function(error, request) { | 268 | }, function(error, request) { |
... | @@ -298,7 +299,7 @@ export default class PlaylistLoader extends Stream { | ... | @@ -298,7 +299,7 @@ export default class PlaylistLoader extends Stream { |
298 | } | 299 | } |
299 | 300 | ||
300 | loader.state = 'HAVE_CURRENT_METADATA'; | 301 | loader.state = 'HAVE_CURRENT_METADATA'; |
301 | request = XhrModule({ | 302 | request = this.hls_.xhr({ |
302 | uri: resolveUrl(loader.master.uri, loader.media().uri), | 303 | uri: resolveUrl(loader.master.uri, loader.media().uri), |
303 | withCredentials | 304 | withCredentials |
304 | }, function(error, request) { | 305 | }, function(error, request) { |
... | @@ -310,7 +311,7 @@ export default class PlaylistLoader extends Stream { | ... | @@ -310,7 +311,7 @@ export default class PlaylistLoader extends Stream { |
310 | }); | 311 | }); |
311 | 312 | ||
312 | // request the specified URL | 313 | // request the specified URL |
313 | request = XhrModule({ | 314 | request = this.hls_.xhr({ |
314 | uri: srcUrl, | 315 | uri: srcUrl, |
315 | withCredentials | 316 | withCredentials |
316 | }, function(error, req) { | 317 | }, function(error, req) { | ... | ... |
... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
5 | */ | 5 | */ |
6 | import PlaylistLoader from './playlist-loader'; | 6 | import PlaylistLoader from './playlist-loader'; |
7 | import Playlist from './playlist'; | 7 | import Playlist from './playlist'; |
8 | import xhr from './xhr'; | 8 | import xhrFactory from './xhr'; |
9 | import {Decrypter, AsyncStream, decrypt} from './decrypter'; | 9 | import {Decrypter, AsyncStream, decrypt} from './decrypter'; |
10 | import utils from './bin-utils'; | 10 | import utils from './bin-utils'; |
11 | import {MediaSource, URL} from 'videojs-contrib-media-sources'; | 11 | import {MediaSource, URL} from 'videojs-contrib-media-sources'; |
... | @@ -20,7 +20,7 @@ const Hls = { | ... | @@ -20,7 +20,7 @@ const Hls = { |
20 | AsyncStream, | 20 | AsyncStream, |
21 | decrypt, | 21 | decrypt, |
22 | utils, | 22 | utils, |
23 | xhr | 23 | xhr: xhrFactory() |
24 | }; | 24 | }; |
25 | 25 | ||
26 | // the desired length of video to maintain in the buffer, in seconds | 26 | // the desired length of video to maintain in the buffer, in seconds |
... | @@ -416,6 +416,7 @@ export default class HlsHandler extends Component { | ... | @@ -416,6 +416,7 @@ export default class HlsHandler extends Component { |
416 | this.options_.withCredentials = videojs.options.hls.withCredentials; | 416 | this.options_.withCredentials = videojs.options.hls.withCredentials; |
417 | } | 417 | } |
418 | this.playlists = new Hls.PlaylistLoader(this.source_.src, | 418 | this.playlists = new Hls.PlaylistLoader(this.source_.src, |
419 | this.tech_.hls, | ||
419 | this.options_.withCredentials); | 420 | this.options_.withCredentials); |
420 | 421 | ||
421 | this.tech_.one('canplay', this.setupFirstPlay.bind(this)); | 422 | this.tech_.one('canplay', this.setupFirstPlay.bind(this)); |
... | @@ -1224,7 +1225,7 @@ export default class HlsHandler extends Component { | ... | @@ -1224,7 +1225,7 @@ export default class HlsHandler extends Component { |
1224 | } | 1225 | } |
1225 | 1226 | ||
1226 | // request the next segment | 1227 | // request the next segment |
1227 | this.segmentXhr_ = Hls.xhr({ | 1228 | this.segmentXhr_ = this.tech_.hls.xhr({ |
1228 | uri: segmentInfo.uri, | 1229 | uri: segmentInfo.uri, |
1229 | responseType: 'arraybuffer', | 1230 | responseType: 'arraybuffer', |
1230 | withCredentials: this.source_.withCredentials, | 1231 | withCredentials: this.source_.withCredentials, |
... | @@ -1504,7 +1505,7 @@ export default class HlsHandler extends Component { | ... | @@ -1504,7 +1505,7 @@ export default class HlsHandler extends Component { |
1504 | 1505 | ||
1505 | // request the key if the retry limit hasn't been reached | 1506 | // request the key if the retry limit hasn't been reached |
1506 | if (!key.bytes && !keyFailed(key)) { | 1507 | if (!key.bytes && !keyFailed(key)) { |
1507 | this.keyXhr_ = Hls.xhr({ | 1508 | this.keyXhr_ = this.tech_.hls.xhr({ |
1508 | uri: this.playlistUriToUrl(key.uri), | 1509 | uri: this.playlistUriToUrl(key.uri), |
1509 | responseType: 'arraybuffer', | 1510 | responseType: 'arraybuffer', |
1510 | withCredentials: settings.withCredentials | 1511 | withCredentials: settings.withCredentials |
... | @@ -1563,6 +1564,14 @@ const HlsSourceHandler = function(mode) { | ... | @@ -1563,6 +1564,14 @@ const HlsSourceHandler = function(mode) { |
1563 | source, | 1564 | source, |
1564 | mode | 1565 | mode |
1565 | }); | 1566 | }); |
1567 | |||
1568 | tech.hls.xhr = xhrFactory(); | ||
1569 | // Use a global `before` function if specified on videojs.Hls.xhr | ||
1570 | // but still allow for a per-player override | ||
1571 | if (videojs.Hls.xhr.beforeRequest) { | ||
1572 | tech.hls.xhr.beforeRequest = videojs.Hls.xhr.beforeRequest; | ||
1573 | } | ||
1574 | |||
1566 | tech.hls.src(source.src); | 1575 | tech.hls.src(source.src); |
1567 | return tech.hls; | 1576 | return tech.hls; |
1568 | }, | 1577 | }, | ... | ... |
... | @@ -2,12 +2,25 @@ | ... | @@ -2,12 +2,25 @@ |
2 | * A wrapper for videojs.xhr that tracks bandwidth. | 2 | * A wrapper for videojs.xhr that tracks bandwidth. |
3 | */ | 3 | */ |
4 | import {xhr as videojsXHR, mergeOptions} from 'video.js'; | 4 | import {xhr as videojsXHR, mergeOptions} from 'video.js'; |
5 | const xhr = function(options, callback) { | 5 | |
6 | const xhrFactory = function() { | ||
7 | const xhr = function XhrFunction(options, callback) { | ||
6 | // Add a default timeout for all hls requests | 8 | // Add a default timeout for all hls requests |
7 | options = mergeOptions({ | 9 | options = mergeOptions({ |
8 | timeout: 45e3 | 10 | timeout: 45e3 |
9 | }, options); | 11 | }, options); |
10 | 12 | ||
13 | // Allow an optional user-specified function to modify the option | ||
14 | // object before we construct the xhr request | ||
15 | if (XhrFunction.beforeRequest && | ||
16 | typeof XhrFunction.beforeRequest === 'function') { | ||
17 | let newOptions = XhrFunction.beforeRequest(options); | ||
18 | |||
19 | if (newOptions) { | ||
20 | options = newOptions; | ||
21 | } | ||
22 | } | ||
23 | |||
11 | let request = videojsXHR(options, function(error, response) { | 24 | let request = videojsXHR(options, function(error, response) { |
12 | if (!error && request.response) { | 25 | if (!error && request.response) { |
13 | request.responseTime = (new Date()).getTime(); | 26 | request.responseTime = (new Date()).getTime(); |
... | @@ -44,6 +57,9 @@ const xhr = function(options, callback) { | ... | @@ -44,6 +57,9 @@ const xhr = function(options, callback) { |
44 | 57 | ||
45 | request.requestTime = (new Date()).getTime(); | 58 | request.requestTime = (new Date()).getTime(); |
46 | return request; | 59 | return request; |
60 | }; | ||
61 | |||
62 | return xhr; | ||
47 | }; | 63 | }; |
48 | 64 | ||
49 | export default xhr; | 65 | export default xhrFactory; | ... | ... |
... | @@ -2,6 +2,7 @@ import sinon from 'sinon'; | ... | @@ -2,6 +2,7 @@ import sinon from 'sinon'; |
2 | import QUnit from 'qunit'; | 2 | import QUnit from 'qunit'; |
3 | import PlaylistLoader from '../src/playlist-loader'; | 3 | import PlaylistLoader from '../src/playlist-loader'; |
4 | import videojs from 'video.js'; | 4 | import videojs from 'video.js'; |
5 | import xhrFactory from '../src/xhr'; | ||
5 | // Attempts to produce an absolute URL to a given relative path | 6 | // Attempts to produce an absolute URL to a given relative path |
6 | // based on window.location.href | 7 | // based on window.location.href |
7 | const urlTo = function(path) { | 8 | const urlTo = function(path) { |
... | @@ -27,6 +28,10 @@ QUnit.module('Playlist Loader', { | ... | @@ -27,6 +28,10 @@ QUnit.module('Playlist Loader', { |
27 | // fake timers | 28 | // fake timers |
28 | this.clock = sinon.useFakeTimers(); | 29 | this.clock = sinon.useFakeTimers(); |
29 | videojs.xhr.XMLHttpRequest = this.sinonXhr; | 30 | videojs.xhr.XMLHttpRequest = this.sinonXhr; |
31 | |||
32 | this.fakeHls = { | ||
33 | xhr: xhrFactory() | ||
34 | }; | ||
30 | }, | 35 | }, |
31 | afterEach() { | 36 | afterEach() { |
32 | this.sinonXhr.restore(); | 37 | this.sinonXhr.restore(); |
... | @@ -45,13 +50,13 @@ QUnit.test('throws if the playlist url is empty or undefined', function() { | ... | @@ -45,13 +50,13 @@ QUnit.test('throws if the playlist url is empty or undefined', function() { |
45 | }); | 50 | }); |
46 | 51 | ||
47 | QUnit.test('starts without any metadata', function() { | 52 | QUnit.test('starts without any metadata', function() { |
48 | let loader = new PlaylistLoader('master.m3u8'); | 53 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
49 | 54 | ||
50 | QUnit.strictEqual(loader.state, 'HAVE_NOTHING', 'no metadata has loaded yet'); | 55 | QUnit.strictEqual(loader.state, 'HAVE_NOTHING', 'no metadata has loaded yet'); |
51 | }); | 56 | }); |
52 | 57 | ||
53 | QUnit.test('starts with no expired time', function() { | 58 | QUnit.test('starts with no expired time', function() { |
54 | let loader = new PlaylistLoader('media.m3u8'); | 59 | let loader = new PlaylistLoader('media.m3u8', this.fakeHls); |
55 | 60 | ||
56 | this.requests.pop().respond(200, null, | 61 | this.requests.pop().respond(200, null, |
57 | '#EXTM3U\n' + | 62 | '#EXTM3U\n' + |
... | @@ -64,7 +69,7 @@ QUnit.test('starts with no expired time', function() { | ... | @@ -64,7 +69,7 @@ QUnit.test('starts with no expired time', function() { |
64 | 69 | ||
65 | QUnit.test('requests the initial playlist immediately', function() { | 70 | QUnit.test('requests the initial playlist immediately', function() { |
66 | /* eslint-disable no-unused-vars */ | 71 | /* eslint-disable no-unused-vars */ |
67 | let loader = new PlaylistLoader('master.m3u8'); | 72 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
68 | /* eslint-enable no-unused-vars */ | 73 | /* eslint-enable no-unused-vars */ |
69 | 74 | ||
70 | QUnit.strictEqual(this.requests.length, 1, 'made a request'); | 75 | QUnit.strictEqual(this.requests.length, 1, 'made a request'); |
... | @@ -74,7 +79,7 @@ QUnit.test('requests the initial playlist immediately', function() { | ... | @@ -74,7 +79,7 @@ QUnit.test('requests the initial playlist immediately', function() { |
74 | }); | 79 | }); |
75 | 80 | ||
76 | QUnit.test('moves to HAVE_MASTER after loading a master playlist', function() { | 81 | QUnit.test('moves to HAVE_MASTER after loading a master playlist', function() { |
77 | let loader = new PlaylistLoader('master.m3u8'); | 82 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
78 | let state; | 83 | let state; |
79 | 84 | ||
80 | loader.on('loadedplaylist', function() { | 85 | loader.on('loadedplaylist', function() { |
... | @@ -90,7 +95,7 @@ QUnit.test('moves to HAVE_MASTER after loading a master playlist', function() { | ... | @@ -90,7 +95,7 @@ QUnit.test('moves to HAVE_MASTER after loading a master playlist', function() { |
90 | 95 | ||
91 | QUnit.test('jumps to HAVE_METADATA when initialized with a media playlist', function() { | 96 | QUnit.test('jumps to HAVE_METADATA when initialized with a media playlist', function() { |
92 | let loadedmetadatas = 0; | 97 | let loadedmetadatas = 0; |
93 | let loader = new PlaylistLoader('media.m3u8'); | 98 | let loader = new PlaylistLoader('media.m3u8', this.fakeHls); |
94 | 99 | ||
95 | loader.on('loadedmetadata', function() { | 100 | loader.on('loadedmetadata', function() { |
96 | loadedmetadatas++; | 101 | loadedmetadatas++; |
... | @@ -110,7 +115,7 @@ QUnit.test('jumps to HAVE_METADATA when initialized with a media playlist', func | ... | @@ -110,7 +115,7 @@ QUnit.test('jumps to HAVE_METADATA when initialized with a media playlist', func |
110 | 115 | ||
111 | QUnit.test('jumps to HAVE_METADATA when initialized with a live media playlist', | 116 | QUnit.test('jumps to HAVE_METADATA when initialized with a live media playlist', |
112 | function() { | 117 | function() { |
113 | let loader = new PlaylistLoader('media.m3u8'); | 118 | let loader = new PlaylistLoader('media.m3u8', this.fakeHls); |
114 | 119 | ||
115 | this.requests.pop().respond(200, null, | 120 | this.requests.pop().respond(200, null, |
116 | '#EXTM3U\n' + | 121 | '#EXTM3U\n' + |
... | @@ -124,7 +129,7 @@ function() { | ... | @@ -124,7 +129,7 @@ function() { |
124 | QUnit.test('moves to HAVE_METADATA after loading a media playlist', function() { | 129 | QUnit.test('moves to HAVE_METADATA after loading a media playlist', function() { |
125 | let loadedPlaylist = 0; | 130 | let loadedPlaylist = 0; |
126 | let loadedMetadata = 0; | 131 | let loadedMetadata = 0; |
127 | let loader = new PlaylistLoader('master.m3u8'); | 132 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
128 | 133 | ||
129 | loader.on('loadedplaylist', function() { | 134 | loader.on('loadedplaylist', function() { |
130 | loadedPlaylist++; | 135 | loadedPlaylist++; |
... | @@ -157,7 +162,7 @@ QUnit.test('moves to HAVE_METADATA after loading a media playlist', function() { | ... | @@ -157,7 +162,7 @@ QUnit.test('moves to HAVE_METADATA after loading a media playlist', function() { |
157 | }); | 162 | }); |
158 | 163 | ||
159 | QUnit.test('moves to HAVE_CURRENT_METADATA when refreshing the playlist', function() { | 164 | QUnit.test('moves to HAVE_CURRENT_METADATA when refreshing the playlist', function() { |
160 | let loader = new PlaylistLoader('live.m3u8'); | 165 | let loader = new PlaylistLoader('live.m3u8', this.fakeHls); |
161 | 166 | ||
162 | this.requests.pop().respond(200, null, | 167 | this.requests.pop().respond(200, null, |
163 | '#EXTM3U\n' + | 168 | '#EXTM3U\n' + |
... | @@ -173,7 +178,7 @@ QUnit.test('moves to HAVE_CURRENT_METADATA when refreshing the playlist', functi | ... | @@ -173,7 +178,7 @@ QUnit.test('moves to HAVE_CURRENT_METADATA when refreshing the playlist', functi |
173 | }); | 178 | }); |
174 | 179 | ||
175 | QUnit.test('returns to HAVE_METADATA after refreshing the playlist', function() { | 180 | QUnit.test('returns to HAVE_METADATA after refreshing the playlist', function() { |
176 | let loader = new PlaylistLoader('live.m3u8'); | 181 | let loader = new PlaylistLoader('live.m3u8', this.fakeHls); |
177 | 182 | ||
178 | this.requests.pop().respond(200, null, | 183 | this.requests.pop().respond(200, null, |
179 | '#EXTM3U\n' + | 184 | '#EXTM3U\n' + |
... | @@ -190,7 +195,7 @@ QUnit.test('returns to HAVE_METADATA after refreshing the playlist', function() | ... | @@ -190,7 +195,7 @@ QUnit.test('returns to HAVE_METADATA after refreshing the playlist', function() |
190 | 195 | ||
191 | QUnit.test('does not increment expired seconds before firstplay is triggered', | 196 | QUnit.test('does not increment expired seconds before firstplay is triggered', |
192 | function() { | 197 | function() { |
193 | let loader = new PlaylistLoader('live.m3u8'); | 198 | let loader = new PlaylistLoader('live.m3u8', this.fakeHls); |
194 | 199 | ||
195 | this.requests.pop().respond(200, null, | 200 | this.requests.pop().respond(200, null, |
196 | '#EXTM3U\n' + | 201 | '#EXTM3U\n' + |
... | @@ -220,7 +225,7 @@ function() { | ... | @@ -220,7 +225,7 @@ function() { |
220 | }); | 225 | }); |
221 | 226 | ||
222 | QUnit.test('increments expired seconds after a segment is removed', function() { | 227 | QUnit.test('increments expired seconds after a segment is removed', function() { |
223 | let loader = new PlaylistLoader('live.m3u8'); | 228 | let loader = new PlaylistLoader('live.m3u8', this.fakeHls); |
224 | 229 | ||
225 | loader.trigger('firstplay'); | 230 | loader.trigger('firstplay'); |
226 | this.requests.pop().respond(200, null, | 231 | this.requests.pop().respond(200, null, |
... | @@ -251,7 +256,7 @@ QUnit.test('increments expired seconds after a segment is removed', function() { | ... | @@ -251,7 +256,7 @@ QUnit.test('increments expired seconds after a segment is removed', function() { |
251 | }); | 256 | }); |
252 | 257 | ||
253 | QUnit.test('increments expired seconds after a discontinuity', function() { | 258 | QUnit.test('increments expired seconds after a discontinuity', function() { |
254 | let loader = new PlaylistLoader('live.m3u8'); | 259 | let loader = new PlaylistLoader('live.m3u8', this.fakeHls); |
255 | 260 | ||
256 | loader.trigger('firstplay'); | 261 | loader.trigger('firstplay'); |
257 | this.requests.pop().respond(200, null, | 262 | this.requests.pop().respond(200, null, |
... | @@ -299,7 +304,7 @@ QUnit.test('increments expired seconds after a discontinuity', function() { | ... | @@ -299,7 +304,7 @@ QUnit.test('increments expired seconds after a discontinuity', function() { |
299 | 304 | ||
300 | QUnit.test('tracks expired seconds properly when two discontinuities expire at once', | 305 | QUnit.test('tracks expired seconds properly when two discontinuities expire at once', |
301 | function() { | 306 | function() { |
302 | let loader = new PlaylistLoader('live.m3u8'); | 307 | let loader = new PlaylistLoader('live.m3u8', this.fakeHls); |
303 | 308 | ||
304 | loader.trigger('firstplay'); | 309 | loader.trigger('firstplay'); |
305 | this.requests.pop().respond(200, null, | 310 | this.requests.pop().respond(200, null, |
... | @@ -327,7 +332,7 @@ function() { | ... | @@ -327,7 +332,7 @@ function() { |
327 | 332 | ||
328 | QUnit.test('estimates expired if an entire window elapses between live playlist updates', | 333 | QUnit.test('estimates expired if an entire window elapses between live playlist updates', |
329 | function() { | 334 | function() { |
330 | let loader = new PlaylistLoader('live.m3u8'); | 335 | let loader = new PlaylistLoader('live.m3u8', this.fakeHls); |
331 | 336 | ||
332 | loader.trigger('firstplay'); | 337 | loader.trigger('firstplay'); |
333 | this.requests.pop().respond(200, null, | 338 | this.requests.pop().respond(200, null, |
... | @@ -354,7 +359,7 @@ function() { | ... | @@ -354,7 +359,7 @@ function() { |
354 | 359 | ||
355 | QUnit.test('emits an error when an initial playlist request fails', function() { | 360 | QUnit.test('emits an error when an initial playlist request fails', function() { |
356 | let errors = []; | 361 | let errors = []; |
357 | let loader = new PlaylistLoader('master.m3u8'); | 362 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
358 | 363 | ||
359 | loader.on('error', function() { | 364 | loader.on('error', function() { |
360 | errors.push(loader.error); | 365 | errors.push(loader.error); |
... | @@ -367,7 +372,7 @@ QUnit.test('emits an error when an initial playlist request fails', function() { | ... | @@ -367,7 +372,7 @@ QUnit.test('emits an error when an initial playlist request fails', function() { |
367 | 372 | ||
368 | QUnit.test('errors when an initial media playlist request fails', function() { | 373 | QUnit.test('errors when an initial media playlist request fails', function() { |
369 | let errors = []; | 374 | let errors = []; |
370 | let loader = new PlaylistLoader('master.m3u8'); | 375 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
371 | 376 | ||
372 | loader.on('error', function() { | 377 | loader.on('error', function() { |
373 | errors.push(loader.error); | 378 | errors.push(loader.error); |
... | @@ -389,7 +394,7 @@ QUnit.test('errors when an initial media playlist request fails', function() { | ... | @@ -389,7 +394,7 @@ QUnit.test('errors when an initial media playlist request fails', function() { |
389 | QUnit.test('halves the refresh timeout if a playlist is unchanged since the last reload', | 394 | QUnit.test('halves the refresh timeout if a playlist is unchanged since the last reload', |
390 | function() { | 395 | function() { |
391 | /* eslint-disable no-unused-vars */ | 396 | /* eslint-disable no-unused-vars */ |
392 | let loader = new PlaylistLoader('live.m3u8'); | 397 | let loader = new PlaylistLoader('live.m3u8', this.fakeHls); |
393 | /* eslint-enable no-unused-vars */ | 398 | /* eslint-enable no-unused-vars */ |
394 | 399 | ||
395 | this.requests.pop().respond(200, null, | 400 | this.requests.pop().respond(200, null, |
... | @@ -414,7 +419,7 @@ function() { | ... | @@ -414,7 +419,7 @@ function() { |
414 | }); | 419 | }); |
415 | 420 | ||
416 | QUnit.test('preserves segment metadata across playlist refreshes', function() { | 421 | QUnit.test('preserves segment metadata across playlist refreshes', function() { |
417 | let loader = new PlaylistLoader('live.m3u8'); | 422 | let loader = new PlaylistLoader('live.m3u8', this.fakeHls); |
418 | let segment; | 423 | let segment; |
419 | 424 | ||
420 | this.requests.pop().respond(200, null, | 425 | this.requests.pop().respond(200, null, |
... | @@ -446,7 +451,7 @@ QUnit.test('preserves segment metadata across playlist refreshes', function() { | ... | @@ -446,7 +451,7 @@ QUnit.test('preserves segment metadata across playlist refreshes', function() { |
446 | }); | 451 | }); |
447 | 452 | ||
448 | QUnit.test('clears the update timeout when switching quality', function() { | 453 | QUnit.test('clears the update timeout when switching quality', function() { |
449 | let loader = new PlaylistLoader('live-master.m3u8'); | 454 | let loader = new PlaylistLoader('live-master.m3u8', this.fakeHls); |
450 | let refreshes = 0; | 455 | let refreshes = 0; |
451 | 456 | ||
452 | // track the number of playlist refreshes triggered | 457 | // track the number of playlist refreshes triggered |
... | @@ -481,7 +486,7 @@ QUnit.test('clears the update timeout when switching quality', function() { | ... | @@ -481,7 +486,7 @@ QUnit.test('clears the update timeout when switching quality', function() { |
481 | 486 | ||
482 | QUnit.test('media-sequence updates are considered a playlist change', function() { | 487 | QUnit.test('media-sequence updates are considered a playlist change', function() { |
483 | /* eslint-disable no-unused-vars */ | 488 | /* eslint-disable no-unused-vars */ |
484 | let loader = new PlaylistLoader('live.m3u8'); | 489 | let loader = new PlaylistLoader('live.m3u8', this.fakeHls); |
485 | /* eslint-enable no-unused-vars */ | 490 | /* eslint-enable no-unused-vars */ |
486 | 491 | ||
487 | this.requests.pop().respond(200, null, | 492 | this.requests.pop().respond(200, null, |
... | @@ -505,7 +510,7 @@ QUnit.test('media-sequence updates are considered a playlist change', function() | ... | @@ -505,7 +510,7 @@ QUnit.test('media-sequence updates are considered a playlist change', function() |
505 | QUnit.test('emits an error if a media refresh fails', function() { | 510 | QUnit.test('emits an error if a media refresh fails', function() { |
506 | let errors = 0; | 511 | let errors = 0; |
507 | let errorResponseText = 'custom error message'; | 512 | let errorResponseText = 'custom error message'; |
508 | let loader = new PlaylistLoader('live.m3u8'); | 513 | let loader = new PlaylistLoader('live.m3u8', this.fakeHls); |
509 | 514 | ||
510 | loader.on('error', function() { | 515 | loader.on('error', function() { |
511 | errors++; | 516 | errors++; |
... | @@ -527,7 +532,7 @@ QUnit.test('emits an error if a media refresh fails', function() { | ... | @@ -527,7 +532,7 @@ QUnit.test('emits an error if a media refresh fails', function() { |
527 | }); | 532 | }); |
528 | 533 | ||
529 | QUnit.test('switches media playlists when requested', function() { | 534 | QUnit.test('switches media playlists when requested', function() { |
530 | let loader = new PlaylistLoader('master.m3u8'); | 535 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
531 | 536 | ||
532 | this.requests.pop().respond(200, null, | 537 | this.requests.pop().respond(200, null, |
533 | '#EXTM3U\n' + | 538 | '#EXTM3U\n' + |
... | @@ -556,7 +561,7 @@ QUnit.test('switches media playlists when requested', function() { | ... | @@ -556,7 +561,7 @@ QUnit.test('switches media playlists when requested', function() { |
556 | }); | 561 | }); |
557 | 562 | ||
558 | QUnit.test('can switch playlists immediately after the master is downloaded', function() { | 563 | QUnit.test('can switch playlists immediately after the master is downloaded', function() { |
559 | let loader = new PlaylistLoader('master.m3u8'); | 564 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
560 | 565 | ||
561 | loader.on('loadedplaylist', function() { | 566 | loader.on('loadedplaylist', function() { |
562 | loader.media('high.m3u8'); | 567 | loader.media('high.m3u8'); |
... | @@ -571,7 +576,7 @@ QUnit.test('can switch playlists immediately after the master is downloaded', fu | ... | @@ -571,7 +576,7 @@ QUnit.test('can switch playlists immediately after the master is downloaded', fu |
571 | }); | 576 | }); |
572 | 577 | ||
573 | QUnit.test('can switch media playlists based on URI', function() { | 578 | QUnit.test('can switch media playlists based on URI', function() { |
574 | let loader = new PlaylistLoader('master.m3u8'); | 579 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
575 | 580 | ||
576 | this.requests.pop().respond(200, null, | 581 | this.requests.pop().respond(200, null, |
577 | '#EXTM3U\n' + | 582 | '#EXTM3U\n' + |
... | @@ -600,7 +605,7 @@ QUnit.test('can switch media playlists based on URI', function() { | ... | @@ -600,7 +605,7 @@ QUnit.test('can switch media playlists based on URI', function() { |
600 | }); | 605 | }); |
601 | 606 | ||
602 | QUnit.test('aborts in-flight playlist refreshes when switching', function() { | 607 | QUnit.test('aborts in-flight playlist refreshes when switching', function() { |
603 | let loader = new PlaylistLoader('master.m3u8'); | 608 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
604 | 609 | ||
605 | this.requests.pop().respond(200, null, | 610 | this.requests.pop().respond(200, null, |
606 | '#EXTM3U\n' + | 611 | '#EXTM3U\n' + |
... | @@ -622,7 +627,7 @@ QUnit.test('aborts in-flight playlist refreshes when switching', function() { | ... | @@ -622,7 +627,7 @@ QUnit.test('aborts in-flight playlist refreshes when switching', function() { |
622 | }); | 627 | }); |
623 | 628 | ||
624 | QUnit.test('switching to the active playlist is a no-op', function() { | 629 | QUnit.test('switching to the active playlist is a no-op', function() { |
625 | let loader = new PlaylistLoader('master.m3u8'); | 630 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
626 | 631 | ||
627 | this.requests.pop().respond(200, null, | 632 | this.requests.pop().respond(200, null, |
628 | '#EXTM3U\n' + | 633 | '#EXTM3U\n' + |
... | @@ -642,7 +647,7 @@ QUnit.test('switching to the active playlist is a no-op', function() { | ... | @@ -642,7 +647,7 @@ QUnit.test('switching to the active playlist is a no-op', function() { |
642 | }); | 647 | }); |
643 | 648 | ||
644 | QUnit.test('switching to the active live playlist is a no-op', function() { | 649 | QUnit.test('switching to the active live playlist is a no-op', function() { |
645 | let loader = new PlaylistLoader('master.m3u8'); | 650 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
646 | 651 | ||
647 | this.requests.pop().respond(200, null, | 652 | this.requests.pop().respond(200, null, |
648 | '#EXTM3U\n' + | 653 | '#EXTM3U\n' + |
... | @@ -661,7 +666,7 @@ QUnit.test('switching to the active live playlist is a no-op', function() { | ... | @@ -661,7 +666,7 @@ QUnit.test('switching to the active live playlist is a no-op', function() { |
661 | }); | 666 | }); |
662 | 667 | ||
663 | QUnit.test('switches back to loaded playlists without re-requesting them', function() { | 668 | QUnit.test('switches back to loaded playlists without re-requesting them', function() { |
664 | let loader = new PlaylistLoader('master.m3u8'); | 669 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
665 | 670 | ||
666 | this.requests.pop().respond(200, null, | 671 | this.requests.pop().respond(200, null, |
667 | '#EXTM3U\n' + | 672 | '#EXTM3U\n' + |
... | @@ -690,7 +695,7 @@ QUnit.test('switches back to loaded playlists without re-requesting them', funct | ... | @@ -690,7 +695,7 @@ QUnit.test('switches back to loaded playlists without re-requesting them', funct |
690 | 695 | ||
691 | QUnit.test('aborts outstanding requests if switching back to an already loaded playlist', | 696 | QUnit.test('aborts outstanding requests if switching back to an already loaded playlist', |
692 | function() { | 697 | function() { |
693 | let loader = new PlaylistLoader('master.m3u8'); | 698 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
694 | 699 | ||
695 | this.requests.pop().respond(200, null, | 700 | this.requests.pop().respond(200, null, |
696 | '#EXTM3U\n' + | 701 | '#EXTM3U\n' + |
... | @@ -724,7 +729,7 @@ function() { | ... | @@ -724,7 +729,7 @@ function() { |
724 | 729 | ||
725 | QUnit.test('does not abort requests when the same playlist is re-requested', | 730 | QUnit.test('does not abort requests when the same playlist is re-requested', |
726 | function() { | 731 | function() { |
727 | let loader = new PlaylistLoader('master.m3u8'); | 732 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
728 | 733 | ||
729 | this.requests.pop().respond(200, null, | 734 | this.requests.pop().respond(200, null, |
730 | '#EXTM3U\n' + | 735 | '#EXTM3U\n' + |
... | @@ -746,7 +751,7 @@ function() { | ... | @@ -746,7 +751,7 @@ function() { |
746 | }); | 751 | }); |
747 | 752 | ||
748 | QUnit.test('throws an error if a media switch is initiated too early', function() { | 753 | QUnit.test('throws an error if a media switch is initiated too early', function() { |
749 | let loader = new PlaylistLoader('master.m3u8'); | 754 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
750 | 755 | ||
751 | QUnit.throws(function() { | 756 | QUnit.throws(function() { |
752 | loader.media('high.m3u8'); | 757 | loader.media('high.m3u8'); |
... | @@ -762,7 +767,7 @@ QUnit.test('throws an error if a media switch is initiated too early', function( | ... | @@ -762,7 +767,7 @@ QUnit.test('throws an error if a media switch is initiated too early', function( |
762 | 767 | ||
763 | QUnit.test('throws an error if a switch to an unrecognized playlist is requested', | 768 | QUnit.test('throws an error if a switch to an unrecognized playlist is requested', |
764 | function() { | 769 | function() { |
765 | let loader = new PlaylistLoader('master.m3u8'); | 770 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
766 | 771 | ||
767 | this.requests.pop().respond(200, null, | 772 | this.requests.pop().respond(200, null, |
768 | '#EXTM3U\n' + | 773 | '#EXTM3U\n' + |
... | @@ -775,7 +780,7 @@ function() { | ... | @@ -775,7 +780,7 @@ function() { |
775 | }); | 780 | }); |
776 | 781 | ||
777 | QUnit.test('dispose cancels the refresh timeout', function() { | 782 | QUnit.test('dispose cancels the refresh timeout', function() { |
778 | let loader = new PlaylistLoader('live.m3u8'); | 783 | let loader = new PlaylistLoader('live.m3u8', this.fakeHls); |
779 | 784 | ||
780 | this.requests.pop().respond(200, null, | 785 | this.requests.pop().respond(200, null, |
781 | '#EXTM3U\n' + | 786 | '#EXTM3U\n' + |
... | @@ -790,7 +795,7 @@ QUnit.test('dispose cancels the refresh timeout', function() { | ... | @@ -790,7 +795,7 @@ QUnit.test('dispose cancels the refresh timeout', function() { |
790 | }); | 795 | }); |
791 | 796 | ||
792 | QUnit.test('dispose aborts pending refresh requests', function() { | 797 | QUnit.test('dispose aborts pending refresh requests', function() { |
793 | let loader = new PlaylistLoader('live.m3u8'); | 798 | let loader = new PlaylistLoader('live.m3u8', this.fakeHls); |
794 | 799 | ||
795 | this.requests.pop().respond(200, null, | 800 | this.requests.pop().respond(200, null, |
796 | '#EXTM3U\n' + | 801 | '#EXTM3U\n' + |
... | @@ -807,7 +812,7 @@ QUnit.test('dispose aborts pending refresh requests', function() { | ... | @@ -807,7 +812,7 @@ QUnit.test('dispose aborts pending refresh requests', function() { |
807 | }); | 812 | }); |
808 | 813 | ||
809 | QUnit.test('errors if requests take longer than 45s', function() { | 814 | QUnit.test('errors if requests take longer than 45s', function() { |
810 | let loader = new PlaylistLoader('media.m3u8'); | 815 | let loader = new PlaylistLoader('media.m3u8', this.fakeHls); |
811 | let errors = 0; | 816 | let errors = 0; |
812 | 817 | ||
813 | loader.on('error', function() { | 818 | loader.on('error', function() { |
... | @@ -820,7 +825,7 @@ QUnit.test('errors if requests take longer than 45s', function() { | ... | @@ -820,7 +825,7 @@ QUnit.test('errors if requests take longer than 45s', function() { |
820 | }); | 825 | }); |
821 | 826 | ||
822 | QUnit.test('triggers an event when the active media changes', function() { | 827 | QUnit.test('triggers an event when the active media changes', function() { |
823 | let loader = new PlaylistLoader('master.m3u8'); | 828 | let loader = new PlaylistLoader('master.m3u8', this.fakeHls); |
824 | let mediaChanges = 0; | 829 | let mediaChanges = 0; |
825 | 830 | ||
826 | loader.on('mediachange', function() { | 831 | loader.on('mediachange', function() { |
... | @@ -861,7 +866,7 @@ QUnit.test('triggers an event when the active media changes', function() { | ... | @@ -861,7 +866,7 @@ QUnit.test('triggers an event when the active media changes', function() { |
861 | }); | 866 | }); |
862 | 867 | ||
863 | QUnit.test('can get media index by playback position for non-live videos', function() { | 868 | QUnit.test('can get media index by playback position for non-live videos', function() { |
864 | let loader = new PlaylistLoader('media.m3u8'); | 869 | let loader = new PlaylistLoader('media.m3u8', this.fakeHls); |
865 | 870 | ||
866 | this.requests.shift().respond(200, null, | 871 | this.requests.shift().respond(200, null, |
867 | '#EXTM3U\n' + | 872 | '#EXTM3U\n' + |
... | @@ -886,7 +891,7 @@ QUnit.test('can get media index by playback position for non-live videos', funct | ... | @@ -886,7 +891,7 @@ QUnit.test('can get media index by playback position for non-live videos', funct |
886 | }); | 891 | }); |
887 | 892 | ||
888 | QUnit.test('returns the lower index when calculating for a segment boundary', function() { | 893 | QUnit.test('returns the lower index when calculating for a segment boundary', function() { |
889 | let loader = new PlaylistLoader('media.m3u8'); | 894 | let loader = new PlaylistLoader('media.m3u8', this.fakeHls); |
890 | 895 | ||
891 | this.requests.shift().respond(200, null, | 896 | this.requests.shift().respond(200, null, |
892 | '#EXTM3U\n' + | 897 | '#EXTM3U\n' + |
... | @@ -903,7 +908,7 @@ QUnit.test('returns the lower index when calculating for a segment boundary', fu | ... | @@ -903,7 +908,7 @@ QUnit.test('returns the lower index when calculating for a segment boundary', fu |
903 | 908 | ||
904 | QUnit.test('accounts for non-zero starting segment time when calculating media index', | 909 | QUnit.test('accounts for non-zero starting segment time when calculating media index', |
905 | function() { | 910 | function() { |
906 | let loader = new PlaylistLoader('media.m3u8'); | 911 | let loader = new PlaylistLoader('media.m3u8', this.fakeHls); |
907 | 912 | ||
908 | this.requests.shift().respond(200, null, | 913 | this.requests.shift().respond(200, null, |
909 | '#EXTM3U\n' + | 914 | '#EXTM3U\n' + |
... | @@ -941,7 +946,7 @@ function() { | ... | @@ -941,7 +946,7 @@ function() { |
941 | }); | 946 | }); |
942 | 947 | ||
943 | QUnit.test('prefers precise segment timing when tracking expired time', function() { | 948 | QUnit.test('prefers precise segment timing when tracking expired time', function() { |
944 | let loader = new PlaylistLoader('media.m3u8'); | 949 | let loader = new PlaylistLoader('media.m3u8', this.fakeHls); |
945 | 950 | ||
946 | loader.trigger('firstplay'); | 951 | loader.trigger('firstplay'); |
947 | this.requests.shift().respond(200, null, | 952 | this.requests.shift().respond(200, null, |
... | @@ -975,7 +980,7 @@ QUnit.test('prefers precise segment timing when tracking expired time', function | ... | @@ -975,7 +980,7 @@ QUnit.test('prefers precise segment timing when tracking expired time', function |
975 | }); | 980 | }); |
976 | 981 | ||
977 | QUnit.test('accounts for expired time when calculating media index', function() { | 982 | QUnit.test('accounts for expired time when calculating media index', function() { |
978 | let loader = new PlaylistLoader('media.m3u8'); | 983 | let loader = new PlaylistLoader('media.m3u8', this.fakeHls); |
979 | 984 | ||
980 | this.requests.shift().respond(200, null, | 985 | this.requests.shift().respond(200, null, |
981 | '#EXTM3U\n' + | 986 | '#EXTM3U\n' + |
... | @@ -1007,7 +1012,7 @@ QUnit.test('accounts for expired time when calculating media index', function() | ... | @@ -1007,7 +1012,7 @@ QUnit.test('accounts for expired time when calculating media index', function() |
1007 | }); | 1012 | }); |
1008 | 1013 | ||
1009 | QUnit.test('does not misintrepret playlists missing newlines at the end', function() { | 1014 | QUnit.test('does not misintrepret playlists missing newlines at the end', function() { |
1010 | let loader = new PlaylistLoader('media.m3u8'); | 1015 | let loader = new PlaylistLoader('media.m3u8', this.fakeHls); |
1011 | 1016 | ||
1012 | // no newline | 1017 | // no newline |
1013 | this.requests.shift().respond(200, null, | 1018 | this.requests.shift().respond(200, null, | ... | ... |
... | @@ -3363,6 +3363,79 @@ QUnit.test('selectPlaylist does not fail if getComputedStyle returns null', func | ... | @@ -3363,6 +3363,79 @@ QUnit.test('selectPlaylist does not fail if getComputedStyle returns null', func |
3363 | window.getComputedStyle = oldGetComputedStyle; | 3363 | window.getComputedStyle = oldGetComputedStyle; |
3364 | }); | 3364 | }); |
3365 | 3365 | ||
3366 | QUnit.test('Allows specifying the beforeRequest functionon the player', function() { | ||
3367 | let beforeRequestCalled = false; | ||
3368 | |||
3369 | this.player.ready(function() { | ||
3370 | this.hls.xhr.beforeRequest = function() { | ||
3371 | beforeRequestCalled = true; | ||
3372 | }; | ||
3373 | }); | ||
3374 | this.player.src({ | ||
3375 | src: 'master.m3u8', | ||
3376 | type: 'application/vnd.apple.mpegurl' | ||
3377 | }); | ||
3378 | |||
3379 | openMediaSource(this.player, this.clock); | ||
3380 | // master | ||
3381 | standardXHRResponse(this.requests.shift()); | ||
3382 | // media | ||
3383 | standardXHRResponse(this.requests.shift()); | ||
3384 | |||
3385 | QUnit.ok(beforeRequestCalled, 'beforeRequest was called'); | ||
3386 | }); | ||
3387 | |||
3388 | QUnit.test('Allows specifying the beforeRequest function globally', function() { | ||
3389 | let beforeRequestCalled = false; | ||
3390 | |||
3391 | videojs.Hls.xhr.beforeRequest = function() { | ||
3392 | beforeRequestCalled = true; | ||
3393 | }; | ||
3394 | |||
3395 | this.player.src({ | ||
3396 | src: 'master.m3u8', | ||
3397 | type: 'application/vnd.apple.mpegurl' | ||
3398 | }); | ||
3399 | |||
3400 | QUnit.ok(beforeRequestCalled, 'beforeRequest was called'); | ||
3401 | |||
3402 | delete videojs.Hls.xhr.beforeRequest; | ||
3403 | }); | ||
3404 | |||
3405 | QUnit.test('Allows overriding the global beforeRequest function', function() { | ||
3406 | let beforeGlobalRequestCalled = 0; | ||
3407 | let beforeLocalRequestCalled = 0; | ||
3408 | |||
3409 | videojs.Hls.xhr.beforeRequest = function() { | ||
3410 | beforeGlobalRequestCalled++; | ||
3411 | }; | ||
3412 | |||
3413 | this.player.src({ | ||
3414 | src: 'master.m3u8', | ||
3415 | type: 'application/vnd.apple.mpegurl' | ||
3416 | }); | ||
3417 | this.player.ready(function() { | ||
3418 | this.hls.xhr.beforeRequest = function() { | ||
3419 | beforeLocalRequestCalled++; | ||
3420 | }; | ||
3421 | }); | ||
3422 | |||
3423 | openMediaSource(this.player, this.clock); | ||
3424 | // master | ||
3425 | standardXHRResponse(this.requests.shift()); | ||
3426 | // media | ||
3427 | standardXHRResponse(this.requests.shift()); | ||
3428 | // ts | ||
3429 | standardXHRResponse(this.requests.shift()); | ||
3430 | |||
3431 | QUnit.equal(beforeLocalRequestCalled, 2, 'local beforeRequest was called twice ' + | ||
3432 | 'for the media playlist and media'); | ||
3433 | QUnit.equal(beforeGlobalRequestCalled, 1, 'global beforeRequest was called once ' + | ||
3434 | 'for the master playlist'); | ||
3435 | |||
3436 | delete videojs.Hls.xhr.beforeRequest; | ||
3437 | }); | ||
3438 | |||
3366 | QUnit.module('Buffer Inspection'); | 3439 | QUnit.module('Buffer Inspection'); |
3367 | QUnit.test('detects time range end-point changed by updates', function() { | 3440 | QUnit.test('detects time range end-point changed by updates', function() { |
3368 | let edge; | 3441 | let edge; | ... | ... |
-
Please register or sign in to post a comment