abfea271 by Gary Katsevman

Merge branch 'master' into saucelabs-take2

Conflicts:
	.travis.yml
2 parents 2279f842 58952618
1 .DS_Store
2 dist/*
1 /node_modules/ 3 /node_modules/
2 *~ 4 *~
3 *.iml 5 *.iml
......
1 dist/*
2 *~
3 *.iml
4 *.swp
5 tmp/**
6 test/**
...\ No newline at end of file ...\ No newline at end of file
...@@ -9,7 +9,10 @@ notifications: ...@@ -9,7 +9,10 @@ notifications:
9 hipchat: 9 hipchat:
10 rooms: 10 rooms:
11 secure: l5TTd5JuPAW883PtcyaIBcJI9Chr9JpsZPQAEUBKAgIEwzuS6y7t5arlkS1PwH6gi1FADzYDf+OXSIou4GkTSrIetnBcT/SAgF0gBKgIhj+eRkuCfZ4VaC7BPhfZ0hgYRE+5Ejf5BM2MJafRm0pj7OlqG4xKrQZwtuV1te5r3JY= 11 secure: l5TTd5JuPAW883PtcyaIBcJI9Chr9JpsZPQAEUBKAgIEwzuS6y7t5arlkS1PwH6gi1FADzYDf+OXSIou4GkTSrIetnBcT/SAgF0gBKgIhj+eRkuCfZ4VaC7BPhfZ0hgYRE+5Ejf5BM2MJafRm0pj7OlqG4xKrQZwtuV1te5r3JY=
12 irc: chat.freenode.net#videojs 12 irc:
13 channels:
14 - "chat.freenode.net#videojs"
15 use_notice: true
13 env: 16 env:
14 global: 17 global:
15 - secure: dM7svnHPPu5IiUMeFWW5zg+iuWNpwt6SSDi3MmVvhSclNMRLesQoRB+7Qq5J/LiKhmjpv1/GlNVV0CTsHMRhZNwQ3fo38eEuTXv99aAflEITXwSEh/VntKViHbGFubn06EnVkJoH6MX3zJ6kbiwc2QdSQbywKzS6l6quUEpWpd0= 18 - secure: dM7svnHPPu5IiUMeFWW5zg+iuWNpwt6SSDi3MmVvhSclNMRLesQoRB+7Qq5J/LiKhmjpv1/GlNVV0CTsHMRhZNwQ3fo38eEuTXv99aAflEITXwSEh/VntKViHbGFubn06EnVkJoH6MX3zJ6kbiwc2QdSQbywKzS6l6quUEpWpd0=
......
...@@ -24,7 +24,6 @@ module.exports = function(grunt) { ...@@ -24,7 +24,6 @@ module.exports = function(grunt) {
24 dist: { 24 dist: {
25 nonull: true, 25 nonull: true,
26 src: ['src/videojs-hls.js', 26 src: ['src/videojs-hls.js',
27 'src/async-queue.js',
28 'src/flv-tag.js', 27 'src/flv-tag.js',
29 'src/exp-golomb.js', 28 'src/exp-golomb.js',
30 'src/h264-stream.js', 29 'src/h264-stream.js',
......
1 [![Build Status](https://travis-ci.org/videojs/videojs-contrib-hls.png)](https://travis-ci.org/videojs/videojs-contrib-hls)
2
3 # video.js HLS Plugin 1 # video.js HLS Plugin
4 2
5 A video.js plugin that plays HLS video on platforms that don't support it but have Flash. 3 A video.js plugin that plays HLS video on platforms that don't support it but have Flash.
6 4
5 [![Build Status](https://travis-ci.org/videojs/videojs-contrib-hls.svg?branch=master)](https://travis-ci.org/videojs/videojs-contrib-hls)
6
7 ## Getting Started 7 ## Getting Started
8 Download the [plugin](https://github.com/videojs/videojs-contrib-hls/releases). On your web page: 8 Download the [plugin](https://github.com/videojs/videojs-contrib-hls/releases). On your web page:
9 9
...@@ -47,9 +47,26 @@ support for: ...@@ -47,9 +47,26 @@ support for:
47 - Alternate audio and video tracks 47 - Alternate audio and video tracks
48 - Subtitles 48 - Subtitles
49 - Segment codecs _other than_ H.264 with AAC audio 49 - Segment codecs _other than_ H.264 with AAC audio
50 - Live streams
51 - Internet Explorer < 10 50 - Internet Explorer < 10
52 51
52 ### Plugin Options
53
54 You may pass in an options object to the hls plugin upon initialization. This
55 object may contain one of the following properties:
56
57 #### withCredentials
58 Type: `boolean`
59
60 When the `withCredentials` property is set to `true`, all XHR requests for
61 manifests and segments would have `withCredentials` set to `true` as well. This
62 enables storing and passing cookies from the server that the manifests and
63 segments live on. This has some implications on CORS because when set, the
64 `Access-Control-Allow-Origin` header cannot be set to `*`, also, the response
65 headers require the addition of `Access-Control-Allow-Credentials` header which
66 is set to `true`.
67 See html5rocks's [article](http://www.html5rocks.com/en/tutorials/cors/)
68 for more info.
69
53 ### Runtime Properties 70 ### Runtime Properties
54 #### player.hls.master 71 #### player.hls.master
55 Type: `object` 72 Type: `object`
...@@ -119,6 +136,8 @@ bandwidth and viewport dimensions. ...@@ -119,6 +136,8 @@ bandwidth and viewport dimensions.
119 - [Best RESOLUTION variant] OR [Best BANDWIDTH variant] OR [inital playlist in manifest] 136 - [Best RESOLUTION variant] OR [Best BANDWIDTH variant] OR [inital playlist in manifest]
120 137
121 ## Release History 138 ## Release History
139 - 0.5.0: cookie-based content protection support (see `withCredentials`)
140 - 0.4.0: Live stream support
122 - 0.3.0: Performance fixes for high-bitrate streams 141 - 0.3.0: Performance fixes for high-bitrate streams
123 - 0.2.0: Basic playback and adaptive bitrate selection 142 - 0.2.0: Basic playback and adaptive bitrate selection
124 - 0.1.0: Initial release 143 - 0.1.0: Initial release
......
...@@ -10,11 +10,10 @@ ...@@ -10,11 +10,10 @@
10 <script src="node_modules/video.js/dist/video-js/video.js"></script> 10 <script src="node_modules/video.js/dist/video-js/video.js"></script>
11 11
12 <!-- Media Sources plugin --> 12 <!-- Media Sources plugin -->
13 <script src="node_modules/videojs-contrib-media-sources/videojs-media-sources.js"></script> 13 <script src="node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js"></script>
14 14
15 <!-- HLS plugin --> 15 <!-- HLS plugin -->
16 <script src="src/videojs-hls.js"></script> 16 <script src="src/videojs-hls.js"></script>
17 <script src="src/async-queue.js"></script>
18 17
19 <!-- segment handling --> 18 <!-- segment handling -->
20 <script src="src/flv-tag.js"></script> 19 <script src="src/flv-tag.js"></script>
......
1 { 1 {
2 "name": "videojs-contrib-hls", 2 "name": "videojs-contrib-hls",
3 "version": "0.3.2", 3 "version": "0.5.0",
4 "engines": { 4 "engines": {
5 "node": ">= 0.10.12" 5 "node": ">= 0.10.12"
6 }, 6 },
...@@ -13,33 +13,34 @@ ...@@ -13,33 +13,34 @@
13 "test": "grunt test" 13 "test": "grunt test"
14 }, 14 },
15 "devDependencies": { 15 "devDependencies": {
16 "grunt": "~0.4.1",
17 "grunt-concurrent": "0.4.3",
18 "grunt-contrib-clean": "~0.4.0",
19 "grunt-contrib-concat": "~0.3.0",
20 "grunt-contrib-connect": "~0.6.0",
16 "grunt-contrib-jshint": "~0.6.0", 21 "grunt-contrib-jshint": "~0.6.0",
17 "grunt-contrib-qunit": "~0.2.0", 22 "grunt-contrib-qunit": "~0.2.0",
18 "grunt-contrib-concat": "~0.3.0",
19 "grunt-contrib-uglify": "~0.2.0", 23 "grunt-contrib-uglify": "~0.2.0",
20 "grunt-contrib-watch": "~0.4.0", 24 "grunt-contrib-watch": "~0.4.0",
21 "grunt-contrib-clean": "~0.4.0", 25 "grunt-karma": "~0.6.2",
22 "grunt-contrib-connect": "~0.6.0",
23 "grunt-concurrent": "0.4.3",
24 "grunt-open": "0.2.3", 26 "grunt-open": "0.2.3",
25 "grunt-shell": "0.6.1", 27 "grunt-shell": "0.6.1",
26 "grunt": "~0.4.1",
27 "grunt-karma": "~0.6.2",
28 "karma": "~0.10.0", 28 "karma": "~0.10.0",
29 "karma-sauce-launcher": "~0.1.8",
30 "karma-chrome-launcher": "~0.1.2", 29 "karma-chrome-launcher": "~0.1.2",
31 "karma-firefox-launcher": "~0.1.3", 30 "karma-firefox-launcher": "~0.1.3",
32 "karma-ie-launcher": "~0.1.1", 31 "karma-ie-launcher": "~0.1.1",
33 "karma-opera-launcher": "~0.1.0", 32 "karma-opera-launcher": "~0.1.0",
34 "karma-phantomjs-launcher": "~0.1.1", 33 "karma-phantomjs-launcher": "~0.1.1",
35 "karma-safari-launcher": "~0.1.1",
36 "karma-qunit": "~0.1.1", 34 "karma-qunit": "~0.1.1",
35 "karma-safari-launcher": "~0.1.1",
36 "karma-sauce-launcher": "~0.1.8",
37 "sinon": "^1.9.1",
37 "video.js": "^4.5" 38 "video.js": "^4.5"
38 }, 39 },
39 "peerDependencies": { 40 "peerDependencies": {
40 "video.js": "^4.5" 41 "video.js": "^4.5"
41 }, 42 },
42 "dependencies": { 43 "dependencies": {
43 "videojs-contrib-media-sources": "git+https://github.com/videojs/videojs-contrib-media-sources.git" 44 "videojs-contrib-media-sources": "^0.2"
44 } 45 }
45 } 46 }
......
1 (function(window, videojs, undefined) {
2 'use strict';
3 /**
4 * A queue object that manages tasks that should be processed
5 * serially but asynchronously. Loosely adapted from
6 * https://github.com/caolan/async#queue.
7 * @param worker {function} the callback to invoke with each value
8 * pushed onto the queue
9 * @return {object} an object with an array of `tasks` that remain to
10 * be processed and function `push` to add new tasks
11 */
12 videojs.hls.queue = function(worker) {
13 var
14 q = {
15 tasks: [],
16 running: false,
17 push: function(task) {
18 q.tasks.push(task);
19 if (!q.running) {
20 window.setTimeout(process, 0);
21 q.running = true;
22 }
23 }
24 },
25 process = function() {
26 var task;
27 if (q.tasks.length) {
28 task = q.tasks.shift();
29 worker.call(this, task);
30 window.setTimeout(process, 0);
31 } else {
32 q.running = false;
33 }
34 };
35 return q;
36 };
37 })(window, window.videojs);
...@@ -31,6 +31,9 @@ videojs.hls = { ...@@ -31,6 +31,9 @@ videojs.hls = {
31 }; 31 };
32 32
33 var 33 var
34
35 settings,
36
34 // the desired length of video to maintain in the buffer, in seconds 37 // the desired length of video to maintain in the buffer, in seconds
35 goalBufferLength = 5, 38 goalBufferLength = 5,
36 39
...@@ -109,12 +112,26 @@ var ...@@ -109,12 +112,26 @@ var
109 method: 'GET' 112 method: 'GET'
110 }, 113 },
111 request; 114 request;
115
116 if (typeof callback !== 'function') {
117 callback = function() {};
118 }
119
112 if (typeof url === 'object') { 120 if (typeof url === 'object') {
113 options = videojs.util.mergeOptions(options, url); 121 options = videojs.util.mergeOptions(options, url);
114 url = options.url; 122 url = options.url;
115 } 123 }
124
116 request = new window.XMLHttpRequest(); 125 request = new window.XMLHttpRequest();
117 request.open(options.method, url); 126 request.open(options.method, url);
127
128 if (options.responseType) {
129 request.responseType = options.responseType;
130 }
131 if (settings.withCredentials) {
132 request.withCredentials = true;
133 }
134
118 request.onreadystatechange = function() { 135 request.onreadystatechange = function() {
119 // wait until the request completes 136 // wait until the request completes
120 if (this.readyState !== 4) { 137 if (this.readyState !== 4) {
...@@ -204,13 +221,8 @@ var ...@@ -204,13 +221,8 @@ var
204 totalDuration = function(playlist) { 221 totalDuration = function(playlist) {
205 var 222 var
206 duration = 0, 223 duration = 0,
207 i, 224 segment,
208 segment; 225 i = (playlist.segments || []).length;
209
210 if (!playlist.segments) {
211 return 0;
212 }
213 i = playlist.segments.length;
214 226
215 // if present, use the duration specified in the playlist 227 // if present, use the duration specified in the playlist
216 if (playlist.totalDuration) { 228 if (playlist.totalDuration) {
...@@ -277,28 +289,22 @@ var ...@@ -277,28 +289,22 @@ var
277 mediaSource = new videojs.MediaSource(), 289 mediaSource = new videojs.MediaSource(),
278 segmentParser = new videojs.hls.SegmentParser(), 290 segmentParser = new videojs.hls.SegmentParser(),
279 player = this, 291 player = this,
280
281 // async queue of Uint8Arrays to be appended to the SourceBuffer
282 tags = videojs.hls.queue(function(tag) {
283 player.hls.sourceBuffer.appendBuffer(tag, player);
284
285 if (player.hls.mediaIndex === player.hls.media.segments.length) {
286 mediaSource.endOfStream();
287 }
288 }),
289 srcUrl, 292 srcUrl,
290 293
291 playlistXhr, 294 playlistXhr,
292 segmentXhr, 295 segmentXhr,
293 loadedPlaylist, 296 loadedPlaylist,
294 fillBuffer, 297 fillBuffer,
295 updateCurrentPlaylist; 298 updateCurrentPlaylist,
299 updateDuration;
296 300
297 // if the video element supports HLS natively, do nothing 301 // if the video element supports HLS natively, do nothing
298 if (videojs.hls.supportsNativeHls) { 302 if (videojs.hls.supportsNativeHls) {
299 return; 303 return;
300 } 304 }
301 305
306 settings = videojs.util.mergeOptions({}, options);
307
302 srcUrl = (function() { 308 srcUrl = (function() {
303 var 309 var
304 extname, 310 extname,
...@@ -312,7 +318,7 @@ var ...@@ -312,7 +318,7 @@ var
312 // use the URL specified in options if one was provided 318 // use the URL specified in options if one was provided
313 if (typeof options === 'string') { 319 if (typeof options === 'string') {
314 return options; 320 return options;
315 } else if (options) { 321 } else if (options && options.url) {
316 return options.url; 322 return options.url;
317 } 323 }
318 324
...@@ -370,17 +376,35 @@ var ...@@ -370,17 +376,35 @@ var
370 var currentTime = player.currentTime(); 376 var currentTime = player.currentTime();
371 player.hls.mediaIndex = getMediaIndexByTime(player.hls.media, currentTime); 377 player.hls.mediaIndex = getMediaIndexByTime(player.hls.media, currentTime);
372 378
379 // abort any segments still being decoded
380 player.hls.sourceBuffer.abort();
381
373 // cancel outstanding requests and buffer appends 382 // cancel outstanding requests and buffer appends
374 if (segmentXhr) { 383 if (segmentXhr) {
375 segmentXhr.abort(); 384 segmentXhr.abort();
376 } 385 }
377 tags.tasks = [];
378 386
379 // begin filling the buffer at the new position 387 // begin filling the buffer at the new position
380 fillBuffer(currentTime * 1000); 388 fillBuffer(currentTime * 1000);
381 }); 389 });
382 390
383 /** 391 /**
392 * Update the player duration
393 */
394 updateDuration = function(playlist) {
395 var tech;
396 // update the duration
397 player.duration(totalDuration(playlist));
398 // tell the flash tech of the new duration
399 tech = player.el().querySelector('.vjs-tech');
400 if(tech.vjs_setProperty) {
401 tech.vjs_setProperty('duration', player.duration());
402 }
403 // manually fire the duration change
404 player.trigger('durationchange');
405 };
406
407 /**
384 * Determine whether the current media playlist should be changed 408 * Determine whether the current media playlist should be changed
385 * and trigger a switch if necessary. If a sufficiently fresh 409 * and trigger a switch if necessary. If a sufficiently fresh
386 * version of the target playlist is available, the switch will take 410 * version of the target playlist is available, the switch will take
...@@ -406,8 +430,7 @@ var ...@@ -406,8 +430,7 @@ var
406 playlist); 430 playlist);
407 player.hls.media = playlist; 431 player.hls.media = playlist;
408 432
409 // update the duration 433 updateDuration(player.hls.media);
410 player.duration(totalDuration(player.hls.media));
411 } 434 }
412 }; 435 };
413 436
...@@ -558,7 +581,7 @@ var ...@@ -558,7 +581,7 @@ var
558 player.hls.media = player.hls.master.playlists[0]; 581 player.hls.media = player.hls.master.playlists[0];
559 582
560 // update the duration 583 // update the duration
561 player.duration(totalDuration(parser.manifest)); 584 updateDuration(parser.manifest);
562 585
563 // periodicaly check if the buffer needs to be refilled 586 // periodicaly check if the buffer needs to be refilled
564 player.on('timeupdate', fillBuffer); 587 player.on('timeupdate', fillBuffer);
...@@ -585,7 +608,7 @@ var ...@@ -585,7 +608,7 @@ var
585 var 608 var
586 buffered = player.buffered(), 609 buffered = player.buffered(),
587 bufferedTime = 0, 610 bufferedTime = 0,
588 segment = player.hls.media.segments[player.hls.mediaIndex], 611 segment,
589 segmentUri, 612 segmentUri,
590 startTime; 613 startTime;
591 614
...@@ -594,7 +617,13 @@ var ...@@ -594,7 +617,13 @@ var
594 return; 617 return;
595 } 618 }
596 619
620 // if no segments are available, do nothing
621 if (!player.hls.media.segments) {
622 return;
623 }
624
597 // if the video has finished downloading, stop trying to buffer 625 // if the video has finished downloading, stop trying to buffer
626 segment = player.hls.media.segments[player.hls.mediaIndex];
598 if (!segment) { 627 if (!segment) {
599 return; 628 return;
600 } 629 }
...@@ -617,24 +646,20 @@ var ...@@ -617,24 +646,20 @@ var
617 segment.uri); 646 segment.uri);
618 } 647 }
619 648
620 // request the next segment 649 startTime = +new Date();
621 segmentXhr = new window.XMLHttpRequest();
622 segmentXhr.open('GET', segmentUri);
623 segmentXhr.responseType = 'arraybuffer';
624 segmentXhr.onreadystatechange = function() {
625 // wait until the request completes
626 if (this.readyState !== 4) {
627 return;
628 }
629 650
651 // request the next segment
652 segmentXhr = xhr({
653 url: segmentUri,
654 responseType: 'arraybuffer'
655 }, function(error, url) {
630 // the segment request is no longer outstanding 656 // the segment request is no longer outstanding
631 segmentXhr = null; 657 segmentXhr = null;
632 658
633 // trigger an error if the request was not successful 659 if (error) {
634 if (this.status >= 400) {
635 player.hls.error = { 660 player.hls.error = {
636 status: this.status, 661 status: this.status,
637 message: 'HLS segment request error at URL: ' + segmentUri, 662 message: 'HLS segment request error at URL: ' + url,
638 code: (this.status >= 500) ? 4 : 2 663 code: (this.status >= 500) ? 4 : 2
639 }; 664 };
640 665
...@@ -669,17 +694,22 @@ var ...@@ -669,17 +694,22 @@ var
669 // queue up the bytes to be appended to the SourceBuffer 694 // queue up the bytes to be appended to the SourceBuffer
670 // the queue gives control back to the browser between tags 695 // the queue gives control back to the browser between tags
671 // so that large segments don't cause a "hiccup" in playback 696 // so that large segments don't cause a "hiccup" in playback
672 tags.push(segmentParser.getNextTag().bytes); 697
698 player.hls.sourceBuffer.appendBuffer(segmentParser.getNextTag().bytes,
699 player);
700
673 } 701 }
674 702
675 player.hls.mediaIndex++; 703 player.hls.mediaIndex++;
676 704
705 if (player.hls.mediaIndex === player.hls.media.segments.length) {
706 mediaSource.endOfStream();
707 }
708
677 // figure out what stream the next segment should be downloaded from 709 // figure out what stream the next segment should be downloaded from
678 // with the updated bandwidth information 710 // with the updated bandwidth information
679 updateCurrentPlaylist(); 711 updateCurrentPlaylist();
680 }; 712 });
681 startTime = +new Date();
682 segmentXhr.send(null);
683 }; 713 };
684 714
685 // load the MediaSource into the player 715 // load the MediaSource into the player
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
28 "strictEqual", 28 "strictEqual",
29 "notStrictEqual", 29 "notStrictEqual",
30 "throws", 30 "throws",
31 "sinon",
31 "process" 32 "process"
32 ] 33 ]
33 } 34 }
......
1 (function(window, queue, undefined) {
2 var
3 oldSetTimeout,
4 callbacks;
5 module('async queue', {
6 setup: function() {
7 oldSetTimeout = window.setTimeout;
8 callbacks = [];
9 window.setTimeout = function(callback) {
10 callbacks.push(callback);
11 };
12 },
13 teardown: function() {
14 window.setTimeout = oldSetTimeout;
15 }
16 });
17
18 test('runs tasks asynchronously', function() {
19 var
20 run = false,
21 q = queue(function() {
22 run = true;
23 });
24 q.push(1);
25
26 ok(!run, 'tasks are not run immediately');
27
28 callbacks[0]();
29 ok(run, 'tasks are run asynchronously');
30 });
31
32 test('runs one task at a time', function() {
33 var q = queue(function() {});
34 q.push(1);
35 q.push(2);
36 q.push(3);
37 q.push(4);
38 q.push(5);
39
40 strictEqual(q.tasks.length, 5, 'all tasks are queued');
41 strictEqual(1, callbacks.length, 'one callback is registered');
42 });
43
44 test('tasks are scheduled until the queue is empty', function() {
45 var q = queue(function() {});
46 q.push(1);
47 q.push(2);
48
49 callbacks.shift()();
50 strictEqual(1, callbacks.length, 'the next task is scheduled');
51
52 callbacks.shift()();
53 strictEqual(1, callbacks.length, 'nothing is scheduled on an empty queue');
54 });
55
56 test('can be emptied at any time', function() {
57 var
58 runs = 0,
59 q = queue(function() {
60 runs++;
61 });
62 q.push(1);
63 q.push(2);
64
65 callbacks.shift()();
66 strictEqual(1, runs, 'task one is run');
67
68 q.tasks = [];
69 callbacks.shift()();
70 strictEqual(1, runs, 'the remaining tasks are cancelled');
71 });
72 })(window, window.videojs.hls.queue);
...@@ -40,7 +40,7 @@ module.exports = function(config) { ...@@ -40,7 +40,7 @@ module.exports = function(config) {
40 40
41 files: [ 41 files: [
42 '../node_modules/video.js/dist/video-js/video.js', 42 '../node_modules/video.js/dist/video-js/video.js',
43 '../node_modules/videojs-contrib-media-sources/videojs-media-sources.js', 43 '../node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js',
44 '../test/karma-qunit-shim.js', 44 '../test/karma-qunit-shim.js',
45 "../src/videojs-hls.js", 45 "../src/videojs-hls.js",
46 "../src/flv-tag.js", 46 "../src/flv-tag.js",
......
...@@ -35,7 +35,7 @@ module.exports = function(config) { ...@@ -35,7 +35,7 @@ module.exports = function(config) {
35 35
36 files: [ 36 files: [
37 '../node_modules/video.js/dist/video-js/video.js', 37 '../node_modules/video.js/dist/video-js/video.js',
38 '../node_modules/videojs-contrib-media-sources/videojs-media-sources.js', 38 '../node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js',
39 '../test/karma-qunit-shim.js', 39 '../test/karma-qunit-shim.js',
40 "../src/videojs-hls.js", 40 "../src/videojs-hls.js",
41 "../src/flv-tag.js", 41 "../src/flv-tag.js",
......
...@@ -3,13 +3,19 @@ ...@@ -3,13 +3,19 @@
3 <head> 3 <head>
4 <meta charset="utf-8"> 4 <meta charset="utf-8">
5 <title>video.js HLS Plugin Test Suite</title> 5 <title>video.js HLS Plugin Test Suite</title>
6 <!-- Load sinon server for fakeXHR -->
7 <script src="../node_modules/sinon/lib/sinon.js"></script>
8 <script src="../node_modules/sinon/lib/sinon/util/event.js"></script>
9 <script src="../node_modules/sinon/lib/sinon/util/xhr_ie.js"></script>
10 <script src="../node_modules/sinon/lib/sinon/util/fake_xml_http_request.js"></script>
11
6 <!-- Load local QUnit. --> 12 <!-- Load local QUnit. -->
7 <link rel="stylesheet" href="../libs/qunit/qunit.css" media="screen"> 13 <link rel="stylesheet" href="../libs/qunit/qunit.css" media="screen">
8 <script src="../libs/qunit/qunit.js"></script> 14 <script src="../libs/qunit/qunit.js"></script>
9 15
10 <!-- video.js --> 16 <!-- video.js -->
11 <script src="../node_modules/video.js/dist/video-js/video.js"></script> 17 <script src="../node_modules/video.js/dist/video-js/video.js"></script>
12 <script src="../node_modules/videojs-contrib-media-sources/videojs-media-sources.js"></script> 18 <script src="../node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js"></script>
13 19
14 <!-- HLS plugin --> 20 <!-- HLS plugin -->
15 <script src="../src/videojs-hls.js"></script> 21 <script src="../src/videojs-hls.js"></script>
...@@ -31,9 +37,6 @@ ...@@ -31,9 +37,6 @@
31 <script src="tsSegment-bc.js"></script> 37 <script src="tsSegment-bc.js"></script>
32 <script src="../src/bin-utils.js"></script> 38 <script src="../src/bin-utils.js"></script>
33 39
34 <!-- async queue -->
35 <script src="../src/async-queue.js"></script>
36
37 <!-- Test cases --> 40 <!-- Test cases -->
38 <script> 41 <script>
39 module('environment'); 42 module('environment');
...@@ -48,7 +51,6 @@ ...@@ -48,7 +51,6 @@
48 <script src="exp-golomb_test.js"></script> 51 <script src="exp-golomb_test.js"></script>
49 <script src="flv-tag_test.js"></script> 52 <script src="flv-tag_test.js"></script>
50 <script src="m3u8_test.js"></script> 53 <script src="m3u8_test.js"></script>
51 <script src="async-queue_test.js"></script>
52 </head> 54 </head>
53 <body> 55 <body>
54 <div id="qunit"></div> 56 <div id="qunit"></div>
......