b0caca88 by David LaPalomento

Merge pull request #530 from 'BrandonOCasey-plugin-generator-squash'

2 parents 25033e31 8582ba38
Showing 161 changed files with 686 additions and 949 deletions
1 # http://editorconfig.org
2 root = true
3
4 [*]
5 charset = utf-8
6 end_of_line = lf
7 indent_style = space
8 indent_size = 2
9 insert_final_newline = true
10 trim_trailing_whitespace = true
11
12 [*.md]
13 trim_trailing_whitespace = false
1 # OS
2 Thumbs.db
3 ehthumbs.db
4 Desktop.ini
1 .DS_Store 5 .DS_Store
2 dist/* 6 ._*
3 /node_modules/ 7
8 # Editors
4 *~ 9 *~
5 *.iml
6 *.ipr
7 *.iws
8 *.swp 10 *.swp
9 tmp/**.*.swo 11 *.tmproj
12 *.tmproject
13 *.sublime-*
14 .idea/
15 .project/
16 .settings/
17 .vscode/
18
19 # Logs
20 logs
21 *.log
22 npm-debug.log*
23
24 # Dependency directories
25 bower_components/
26 node_modules/
27
28 # Yeoman meta-data
29 .yo-rc.json
30
31 # Build-related directories
32 dist/
33 dist-test/
34 docs/api/
35 es5/
36 tmp
37 test/data/manifests.js
38 test/data/expected.js
......
1 {
2 "curly": true,
3 "eqeqeq": true,
4 "immed": true,
5 "latedef": true,
6 "newcap": true,
7 "noarg": true,
8 "sub": true,
9 "undef": true,
10 "unused": true,
11 "boss": true,
12 "eqnull": true,
13 "node": true,
14
15 "camelcase": true,
16 "nonew": true,
17 "quotmark": "single",
18 "trailing": true,
19 "maxlen": 80
20 }
1 *~ 1 # Intentionally left blank, so that npm does not ignore anything by default,
2 *.iml 2 # but relies on the package.json "files" array to explicitly define what ends
3 *.swp 3 # up in the package.
4 tmp/**
5 test/**
......
1 language: node_js
2 sudo: false 1 sudo: false
2 language: node_js
3 node_js: 3 node_js:
4 - "stable" 4 - 'node'
5 install: 5 - '4.2'
6 - npm install -g grunt-cli && npm install 6 - '0.12'
7 - '0.10'
7 notifications: 8 notifications:
8 hipchat: 9 hipchat:
9 rooms: 10 rooms:
...@@ -12,12 +13,7 @@ notifications: ...@@ -12,12 +13,7 @@ notifications:
12 channels: 13 channels:
13 - "chat.freenode.net#videojs" 14 - "chat.freenode.net#videojs"
14 use_notice: true 15 use_notice: true
16 # Set up a virtual screen for Firefox.
15 before_script: 17 before_script:
16 - export DISPLAY=:99.0 18 - export DISPLAY=:99.0
17 - sh -e /etc/init.d/xvfb start 19 - sh -e /etc/init.d/xvfb start
18 env:
19 global:
20 - secure: dM7svnHPPu5IiUMeFWW5zg+iuWNpwt6SSDi3MmVvhSclNMRLesQoRB+7Qq5J/LiKhmjpv1/GlNVV0CTsHMRhZNwQ3fo38eEuTXv99aAflEITXwSEh/VntKViHbGFubn06EnVkJoH6MX3zJ6kbiwc2QdSQbywKzS6l6quUEpWpd0=
21 - secure: AnduYGXka5ft1x7V3SuVYqvlKLvJGhUaRNFdy4UDJr3ZVuwpQjE4TMDG8REmJIJvXfHbh4qY4N1cFSGnXkZ4bH21Xk0v9DLhsxbarKz+X2BvPgXs+Af9EQ6vLEy/5S1vMLxfT5+y+Ec5bVNGOsdUZby8Y21CRzSg6ADN9kwPGlE=
22 addons:
23 sauce_connect: true
......
1 'use strict';
2
3 var
4 basename = require('path').basename,
5 mediaSourcesPath = 'node_modules/videojs-contrib-media-sources/dist/',
6 mediaSourcesDebug = mediaSourcesPath + 'videojs-media-sources.js';
7
8 module.exports = function(grunt) {
9 var pkg = grunt.file.readJSON('package.json');
10
11 // Project configuration.
12 grunt.initConfig({
13 // Metadata.
14 pkg: pkg,
15 banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
16 '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
17 '* Copyright (c) <%= grunt.template.today("yyyy") %> Brightcove;' +
18 ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n',
19 // Task configuration.
20 clean: {
21 files: ['build', 'dist', 'tmp']
22 },
23 concat: {
24 options: {
25 banner: '<%= banner %>',
26 stripBanners: true
27 },
28 dist: {
29 nonull: true,
30 src: [
31 mediaSourcesDebug,
32 'src/videojs-hls.js',
33 'src/xhr.js',
34 'src/stream.js',
35 'src/m3u8/m3u8-parser.js',
36 'src/playlist.js',
37 'src/playlist-loader.js',
38 'node_modules/pkcs7/dist/pkcs7.unpad.js',
39 'src/decrypter.js'
40 ],
41 dest: 'dist/videojs.hls.js'
42 }
43 },
44 uglify: {
45 options: {
46 banner: '<%= banner %>'
47 },
48 dist: {
49 src: '<%= concat.dist.dest %>',
50 dest: 'dist/videojs.hls.min.js'
51 }
52 },
53 jshint: {
54 gruntfile: {
55 options: {
56 jshintrc: '.jshintrc'
57 },
58 src: 'Gruntfile.js'
59 },
60 src: {
61 options: {
62 jshintrc: 'src/.jshintrc'
63 },
64 src: ['src/**/*.js']
65 },
66 test: {
67 options: {
68 jshintrc: 'test/.jshintrc'
69 },
70 src: ['test/**/*.js',
71 '!test/tsSegment.js',
72 '!test/fixtures/*.js',
73 '!test/manifest/**',
74 '!test/muxer/**',
75 '!test/switcher/**']
76 }
77 },
78 connect: {
79 dev: {
80 options: {
81 hostname: '*',
82 port: 9999,
83 keepalive: true
84 }
85 },
86 test: {
87 options: {
88 hostname: '*',
89 port: 9999
90 }
91 }
92 },
93 open : {
94 dev : {
95 path: 'http://127.0.0.1:<%= connect.dev.options.port %>/example.html',
96 app: 'Google Chrome'
97 }
98 },
99 watch: {
100 build: {
101 files: '<%= concat.dist.src %>',
102 tasks: ['clean', 'concat', 'uglify']
103 },
104 gruntfile: {
105 files: '<%= jshint.gruntfile.src %>',
106 tasks: ['jshint:gruntfile']
107 },
108 src: {
109 files: '<%= jshint.src.src %>',
110 tasks: ['jshint:src', 'test']
111 },
112 test: {
113 files: '<%= jshint.test.src %>',
114 tasks: ['jshint:test', 'test']
115 }
116 },
117 concurrent: {
118 dev: {
119 tasks: ['connect', 'open', 'watch'],
120 options: {
121 logConcurrentOutput: true
122 }
123 }
124 },
125 version: {
126 project: {
127 src: ['package.json']
128 }
129 },
130 'github-release': {
131 options: {
132 repository: 'videojs/videojs-contrib-hls',
133 auth: {
134 user: process.env.VJS_GITHUB_USER,
135 password: process.env.VJS_GITHUB_TOKEN
136 },
137 release: {
138 'tag_name': 'v' + pkg.version,
139 name: pkg.version,
140 body: require('chg').find(pkg.version).changesRaw
141 }
142 },
143 files: {
144 'dist': ['videojs.hls.min.js']
145 }
146 },
147 karma: {
148 options: {
149 frameworks: ['qunit']
150 },
151
152 saucelabs: {
153 configFile: 'test/karma.conf.js',
154 autoWatch: true
155 },
156
157 dev: {
158 browsers: ['Chrome', 'Safari', 'Firefox',
159 'Opera', 'IE', 'PhantomJS', 'ChromeCanary'],
160 configFile: 'test/localkarma.conf.js',
161 autoWatch: true
162 },
163
164 chromecanary: {
165 options: {
166 browsers: ['ChromeCanary'],
167 configFile: 'test/localkarma.conf.js',
168 autoWatch: true
169 }
170 },
171
172 phantomjs: {
173 options: {
174 browsers: ['PhantomJS'],
175 configFile: 'test/localkarma.conf.js',
176 autoWatch: true
177 }
178 },
179
180 opera: {
181 options: {
182 browsers: ['Opera'],
183 configFile: 'test/localkarma.conf.js',
184 autoWatch: true
185 }
186 },
187
188 chrome: {
189 options: {
190 browsers: ['Chrome'],
191 configFile: 'test/localkarma.conf.js',
192 autoWatch: true
193 }
194 },
195
196 safari: {
197 options: {
198 browsers: ['Safari'],
199 configFile: 'test/localkarma.conf.js',
200 autoWatch: true
201 }
202 },
203
204 firefox: {
205 options: {
206 browsers: ['Firefox'],
207 configFile: 'test/localkarma.conf.js',
208 autoWatch: true
209 }
210 },
211
212 ie: {
213 options: {
214 browsers: ['IE'],
215 configFile: 'test/localkarma.conf.js',
216 autoWatch: true
217 }
218 },
219
220 ci: {
221 configFile: 'test/karma.conf.js',
222 autoWatch: false
223 }
224 },
225 protractor: {
226 options: {
227 configFile: 'test/functional/protractor.config.js',
228 webdriverManagerUpdate: process.env.TRAVIS ? false : true
229 },
230
231 chrome: {
232 options: {
233 args: {
234 capabilities: {
235 browserName: 'chrome'
236 }
237 }
238 }
239 },
240
241 firefox: {
242 options: {
243 args: {
244 capabilities: {
245 browserName: 'firefox'
246 }
247 }
248 }
249 },
250
251 safari: {
252 options: {
253 args: {
254 capabilities: {
255 browserName: 'safari'
256 }
257 }
258 }
259 },
260
261 ie: {
262 options: {
263 args: {
264 capabilities: {
265 browserName: 'internet explorer'
266 }
267 }
268 }
269 },
270
271 saucelabs:{}
272 }
273 });
274
275 // These plugins provide necessary tasks.
276 grunt.loadNpmTasks('grunt-karma');
277 grunt.loadNpmTasks('grunt-contrib-clean');
278 grunt.loadNpmTasks('grunt-contrib-concat');
279 grunt.loadNpmTasks('grunt-contrib-uglify');
280 grunt.loadNpmTasks('grunt-contrib-jshint');
281 grunt.loadNpmTasks('grunt-contrib-watch');
282 grunt.loadNpmTasks('grunt-contrib-connect');
283 grunt.loadNpmTasks('grunt-open');
284 grunt.loadNpmTasks('grunt-concurrent');
285 grunt.loadNpmTasks('grunt-contrib-watch');
286 grunt.loadNpmTasks('grunt-github-releaser');
287 grunt.loadNpmTasks('grunt-version');
288 grunt.loadNpmTasks('grunt-protractor-runner');
289 grunt.loadNpmTasks('chg');
290
291
292 grunt.registerTask('manifests-to-js', 'Wrap the test fixtures and output' +
293 ' so they can be loaded in a browser',
294 function() {
295 var
296 jsManifests = 'window.manifests = {\n',
297 jsExpected = 'window.expected = {\n';
298 grunt.file.recurse('test/manifest/',
299 function(abspath, root, sub, filename) {
300 if ((/\.m3u8$/).test(abspath)) {
301
302 // translate this manifest
303 jsManifests += ' \'' + basename(filename, '.m3u8') + '\': ' +
304 grunt.file.read(abspath)
305 .split(/\r\n|\n/)
306
307 // quote and concatenate
308 .map(function(line) {
309 return ' \'' + line + '\\n\' +\n';
310 }).join('')
311
312 // strip leading spaces and the trailing '+'
313 .slice(4, -3);
314 jsManifests += ',\n';
315 }
316
317 if ((/\.js$/).test(abspath)) {
318
319 // append the expected parse
320 jsExpected += ' "' + basename(filename, '.js') + '": ' +
321 grunt.file.read(abspath) + ',\n';
322 }
323 });
324
325 // clean up and close the objects
326 jsManifests = jsManifests.slice(0, -2);
327 jsManifests += '\n};\n';
328 jsExpected = jsExpected.slice(0, -2);
329 jsExpected += '\n};\n';
330
331 // write out the manifests
332 grunt.file.write('tmp/manifests.js', jsManifests);
333 grunt.file.write('tmp/expected.js', jsExpected);
334 });
335
336 // Launch a Development Environment
337 grunt.registerTask('dev', 'Launching Dev Environment', 'concurrent:dev');
338
339 grunt.registerTask('build',
340 ['clean',
341 'concat',
342 'uglify']);
343
344 // Default task.
345 grunt.registerTask('default',
346 ['test',
347 'build']);
348
349 // The test task will run `karma:saucelabs` when running in travis,
350 // otherwise, it'll default to running karma in chrome.
351 // You can specify which browsers to build with by using grunt-style arguments
352 // or separating them with a comma:
353 // grunt test:chrome:firefox # grunt-style
354 // grunt test:chrome,firefox # comma-separated
355 grunt.registerTask('test', function() {
356 var tasks = this.args;
357
358 grunt.task.run(['jshint', 'manifests-to-js']);
359
360 if (process.env.TRAVIS) {
361 if (process.env.TRAVIS_PULL_REQUEST === 'false') {
362 grunt.task.run(['karma:saucelabs']);
363 grunt.task.run(['connect:test', 'protractor:saucelabs']);
364 } else {
365 grunt.task.run(['karma:firefox']);
366 }
367 } else {
368 if (tasks.length === 0) {
369 tasks.push('chrome');
370 }
371 if (tasks.length === 1) {
372 tasks = tasks[0].split(',');
373 }
374 tasks = tasks.reduce(function(acc, el) {
375 acc.push('karma:' + el);
376 if (/chrome|firefox|safari|ie/.test(el)) {
377 acc.push('protractor:' + el);
378 }
379 return acc;
380 }, ['connect:test']);
381
382 grunt.task.run(tasks);
383 }
384 });
385 };
1 <!-- START doctoc generated TOC please keep comment here to allow auto update -->
2 <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
3 **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
4
5 - [video.js HLS Source Handler](#videojs-hls-source-handler)
6 - [Getting Started](#getting-started)
7 - [Documentation](#documentation)
8 - [Options](#options)
9 - [withCredentials](#withcredentials)
10 - [Runtime Properties](#runtime-properties)
11 - [hls.playlists.master](#hlsplaylistsmaster)
12 - [hls.playlists.media](#hlsplaylistsmedia)
13 - [hls.segmentXhrTime](#hlssegmentxhrtime)
14 - [hls.bandwidth](#hlsbandwidth)
15 - [hls.bytesReceived](#hlsbytesreceived)
16 - [hls.selectPlaylist](#hlsselectplaylist)
17 - [Events](#events)
18 - [loadedmetadata](#loadedmetadata)
19 - [loadedplaylist](#loadedplaylist)
20 - [mediachange](#mediachange)
21 - [In-Band Metadata](#in-band-metadata)
22 - [Hosting Considerations](#hosting-considerations)
23 - [Testing](#testing)
24 - [Release History](#release-history)
25
26 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
27
1 # video.js HLS Source Handler 28 # video.js HLS Source Handler
2 29
3 Play back HLS with video.js, even where it's not natively supported. 30 Play back HLS with video.js, even where it's not natively supported.
......
...@@ -2,31 +2,8 @@ ...@@ -2,31 +2,8 @@
2 <html> 2 <html>
3 <head> 3 <head>
4 <meta charset="utf-8"> 4 <meta charset="utf-8">
5 <title>video.js HLS Plugin Example</title> 5 <title>videojs-contrib-hls Demo</title>
6 6 <link href="/node_modules/video.js/dist/video-js.css" rel="stylesheet">
7 <link href="node_modules/video.js/dist/video-js.css" rel="stylesheet">
8
9 <!-- video.js -->
10 <script src="node_modules/video.js/dist/video.js"></script>
11
12 <!-- Media Sources plugin -->
13 <script src="node_modules/videojs-contrib-media-sources/dist/videojs-media-sources.js"></script>
14
15 <!-- HLS plugin -->
16 <script src="src/videojs-hls.js"></script>
17
18 <!-- m3u8 handling -->
19 <script src="src/xhr.js"></script>
20 <script src="src/stream.js"></script>
21 <script src="src/m3u8/m3u8-parser.js"></script>
22 <script src="src/playlist.js"></script>
23 <script src="src/playlist-loader.js"></script>
24
25 <script src="node_modules/pkcs7/dist/pkcs7.unpad.js"></script>
26 <script src="src/decrypter.js"></script>
27
28 <script src="src/bin-utils.js"></script>
29
30 <style> 7 <style>
31 body { 8 body {
32 font-family: Arial, sans-serif; 9 font-family: Arial, sans-serif;
...@@ -52,14 +29,8 @@ ...@@ -52,14 +29,8 @@
52 <p>The video below is an <a href="https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008332-CH1-SW1">HTTP Live Stream</a>. On desktop browsers other than Safari, the HLS plugin will polyfill support for the format on top of the video.js Flash tech.</p> 29 <p>The video below is an <a href="https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008332-CH1-SW1">HTTP Live Stream</a>. On desktop browsers other than Safari, the HLS plugin will polyfill support for the format on top of the video.js Flash tech.</p>
53 <p>Due to security restrictions in Flash, you will have to load this page over HTTP(S) to see the example in action.</p> 30 <p>Due to security restrictions in Flash, you will have to load this page over HTTP(S) to see the example in action.</p>
54 </div> 31 </div>
55 <video id="video" 32 <video id="videojs-contrib-hls-player" class="video-js vjs-default-skin" controls>
56 class="video-js vjs-default-skin" 33 <source src="http://solutions.brightcove.com/jwhisenant/hls/apple/bipbop/bipbopall.m3u8" type="application/x-mpegURL">
57 height="300"
58 width="600"
59 controls>
60 <source
61 src="http://solutions.brightcove.com/jwhisenant/hls/apple/bipbop/bipbopall.m3u8"
62 type="application/x-mpegURL">
63 </video> 34 </video>
64 35
65 <form id=load-url> 36 <form id=load-url>
...@@ -70,11 +41,20 @@ ...@@ -70,11 +41,20 @@
70 <button type=submit>Load</button> 41 <button type=submit>Load</button>
71 </form> 42 </form>
72 43
44 <script src="/node_modules/video.js/dist/video.js"></script>
45 <script src="/node_modules/videojs-contrib-media-sources/dist/videojs-media-sources.js"></script>
46 <script src="/node_modules/pkcs7/dist/pkcs7.unpad.js"></script>
47 <script src="/src/videojs-hls.js"></script>
48 <script src="/src/xhr.js"></script>
49 <script src="/src/stream.js"></script>
50 <script src="/src/m3u8/m3u8-parser.js"></script>
51 <script src="/src/playlist.js"></script>
52 <script src="/src/playlist-loader.js"></script>
53 <script src="/src/decrypter.js"></script>
54 <script src="/src/bin-utils.js"></script>
73 <script> 55 <script>
74 videojs.options.flash.swf = 'node_modules/videojs-swf/dist/video-js.swf'; 56 (function(window, videojs) {
75 // initialize the player 57 var player = window.player = videojs('videojs-contrib-hls-player');
76 var player = videojs('video');
77
78 // hook up the video switcher 58 // hook up the video switcher
79 var loadUrl = document.getElementById('load-url'); 59 var loadUrl = document.getElementById('load-url');
80 var url = document.getElementById('url'); 60 var url = document.getElementById('url');
...@@ -86,6 +66,7 @@ ...@@ -86,6 +66,7 @@
86 }); 66 });
87 return false; 67 return false;
88 }); 68 });
69 }(window, window.videojs));
89 </script> 70 </script>
90 </body> 71 </body>
91 </html> 72 </html>
......
1 { 1 {
2 "name": "videojs-contrib-hls", 2 "name": "videojs-contrib-hls",
3 "version": "1.3.4", 3 "version": "1.3.5",
4 "description": "Play back HLS with video.js, even where it's not natively supported",
5 "main": "es5/videojs-hls.js",
4 "engines": { 6 "engines": {
5 "node": ">= 0.10.12" 7 "node": ">= 0.10.12"
6 }, 8 },
...@@ -8,47 +10,116 @@ ...@@ -8,47 +10,116 @@
8 "type": "git", 10 "type": "git",
9 "url": "git@github.com:videojs/videojs-contrib-hls.git" 11 "url": "git@github.com:videojs/videojs-contrib-hls.git"
10 }, 12 },
11 "license": "Apache-2.0",
12 "scripts": { 13 "scripts": {
13 "test": "grunt test", 14 "prebuild": "npm run clean",
14 "prepublish": "if [ -z \"$TRAVIS\" ]; then grunt; fi" 15 "build": "npm-run-all -p build:*",
16 "build:manifest": "node -e \"var b=require('./scripts/manifest-data.js'); b.build();\"",
17 "build:js": "npm-run-all build:js:babel build:js:browserify build:js:bannerize build:js:uglify",
18 "build:js:babel": "babel src -d es5",
19 "build:js:bannerize": "bannerize dist/videojs-contrib-hls.js --banner=scripts/banner.ejs",
20 "build:js:browserify": "browserify . -s src/videojs-hls.js -o dist/videojs-contrib-hls.js",
21 "build:js:uglify": "uglifyjs dist/videojs-contrib-hls.js --comments --mangle --compress -o dist/videojs-contrib-hls.min.js",
22 "build:test": "node scripts/build-test.js",
23 "clean": "npm-run-all clean:*",
24 "clean:build": "node -e \"var s=require('shelljs'),d=['dist','dist-test','es5'];s.rm('-rf',d);s.mkdir('-p',d);\"",
25 "clean:manifest": "node -e \"var b=require('./scripts/manifest-data.js'); b.clean();\"",
26 "docs": "npm-run-all docs:*",
27 "docs:api": "jsdoc src -r -d docs/api",
28 "docs:toc": "doctoc README.md",
29 "lint": "vjsstandard :",
30 "prestart": "npm-run-all docs build",
31 "start": "npm-run-all -p start:* watch:*",
32 "start:serve": "babel-node scripts/server.js",
33 "pretest": "npm-run-all lint build",
34 "test": "karma start test/karma/detected.js",
35 "test:chrome": "npm run pretest && karma start test/karma/chrome.js",
36 "test:firefox": "npm run pretest && karma start test/karma/firefox.js",
37 "test:ie": "npm run pretest && karma start test/karma/ie.js",
38 "test:safari": "npm run pretest && karma start test/karma/safari.js",
39 "preversion": "npm test",
40 "version": "npm run build",
41 "watch": "npm-run-all -p watch:*",
42 "watch:manifest": "node -e \"var b=require('./scripts/manifest-data.js'); b.watch();\"",
43 "watch:js": "watchify src/videojs-hls.js -t babelify -v -o dist/videojs-contrib-hls.js",
44 "watch:test": "node scripts/watch-test.js",
45 "prepublish": "npm run build"
15 }, 46 },
16 "keywords": [ 47 "keywords": [
17 "videojs", 48 "videojs",
18 "videojs-plugin" 49 "videojs-plugin"
19 ], 50 ],
20 "devDependencies": { 51 "author": "Brightcove, Inc",
21 "chg": "^0.2.0", 52 "license": "Apache-2.0",
22 "grunt": "^0.4.5", 53 "browserify": {
23 "grunt-concurrent": "0.4.3", 54 "transform": [
24 "grunt-contrib-clean": "~0.4.0", 55 "browserify-shim"
25 "grunt-contrib-concat": "~0.3.0", 56 ]
26 "grunt-contrib-connect": "~0.6.0", 57 },
27 "grunt-contrib-jshint": "~0.6.0", 58 "browserify-shim": {
28 "grunt-contrib-uglify": "~0.2.0", 59 "qunit": "global:QUnit",
29 "grunt-contrib-watch": "~0.4.0", 60 "sinon": "global:sinon",
30 "grunt-github-releaser": "^0.1.17", 61 "video.js": "global:videojs"
31 "grunt-karma": "~0.6.2",
32 "grunt-open": "0.2.3",
33 "grunt-protractor-runner": "forbesjo/grunt-protractor-runner.git#webdriverManagerUpdate",
34 "grunt-shell": "0.6.1",
35 "grunt-version": "^1.0.0",
36 "karma": "~0.10.0",
37 "karma-chrome-launcher": "~0.1.2",
38 "karma-firefox-launcher": "~0.1.3",
39 "karma-ie-launcher": "~0.1.1",
40 "karma-opera-launcher": "~0.1.0",
41 "karma-phantomjs-launcher": "^0.1.4",
42 "karma-qunit": "~0.1.1",
43 "karma-safari-launcher": "~0.1.1",
44 "karma-sauce-launcher": "~0.1.8",
45 "qunitjs": "^1.18.0",
46 "sinon": "1.10.2",
47 "video.js": "^5.2.1"
48 }, 62 },
63 "vjsstandard": {
64 "ignore": [
65 "dist",
66 "dist-test",
67 "docs",
68 "es5",
69 "test/karma",
70 "scripts",
71 "utils",
72 "test/data"
73 ]
74 },
75 "files": [
76 "CONTRIBUTING.md",
77 "dist-test/",
78 "dist/",
79 "docs/",
80 "es5/",
81 "index.html",
82 "scripts/",
83 "src/",
84 "test/",
85 "utils/"
86 ],
49 "dependencies": { 87 "dependencies": {
50 "pkcs7": "^0.2.2", 88 "pkcs7": "^0.2.3",
51 "videojs-contrib-media-sources": "^2.4.0", 89 "video.js": "^5.0.0",
52 "videojs-swf": "^5.0.0" 90 "videojs-contrib-media-sources": "^2.4.4",
91 "videojs-swf": "^5.0.1"
92 },
93 "devDependencies": {
94 "babel": "^5.8.0",
95 "babelify": "^6.0.0",
96 "bannerize": "^1.0.0",
97 "browserify": "^11.0.0",
98 "browserify-shim": "^3.0.0",
99 "connect": "^3.4.0",
100 "cowsay": "^1.1.0",
101 "doctoc": "^0.15.0",
102 "glob": "^6.0.3",
103 "global": "^4.3.0",
104 "jsdoc": "^3.4.0",
105 "karma": "^0.13.0",
106 "karma-browserify": "^4.4.0",
107 "karma-chrome-launcher": "^0.2.0",
108 "karma-detect-browsers": "^2.0.0",
109 "karma-firefox-launcher": "^0.1.0",
110 "karma-ie-launcher": "^0.2.0",
111 "karma-qunit": "^0.1.9",
112 "karma-safari-launcher": "^0.1.0",
113 "lodash-compat": "^3.10.0",
114 "minimist": "^1.2.0",
115 "npm-run-all": "^1.2.0",
116 "portscanner": "^1.0.0",
117 "qunitjs": "^1.0.0",
118 "serve-static": "^1.10.0",
119 "shelljs": "^0.5.3",
120 "sinon": "1.10.2",
121 "uglify-js": "^2.5.0",
122 "videojs-standard": "^4.0.0",
123 "watchify": "^3.6.0"
53 } 124 }
54 } 125 }
......
1 /**
2 * <%- pkg.name %>
3 * @version <%- pkg.version %>
4 * @copyright <%- date.getFullYear() %> <%- pkg.author %>
5 * @license <%- pkg.license %>
6 */
1 var browserify = require('browserify');
2 var fs = require('fs');
3 var glob = require('glob');
4
5 glob('test/**/*.test.js', function(err, files) {
6 browserify(files)
7 .transform('babelify')
8 .bundle()
9 .pipe(fs.createWriteStream('dist-test/videojs-contrib-hls.js'));
10 });
1 var fs = require('fs');
2 var path = require('path');
3
4 var basePath = path.resolve(__dirname + '/..');
5 var testDataDir = basePath + '/test/data';
6 var manifestDir = basePath + '/utils/manifest';
7 var manifestFilepath = testDataDir + '/manifests.js';
8 var expectedFilepath = testDataDir + '/expected.js';
9
10
11 var build = function() {
12 var manifests = 'window.manifests = {\n';
13 var expected = 'window.expected = {\n';
14
15 var files = fs.readdirSync(manifestDir);
16 while (files.length > 0) {
17 var file = path.resolve(manifestDir, files.shift());
18 var extname = path.extname(file);
19
20 if (extname === '.m3u8') {
21 // translate this manifest
22 manifests += ' \'' + path.basename(file, '.m3u8') + '\': ';
23 manifests += fs.readFileSync(file, 'utf8')
24 .split(/\r\n|\n/)
25 // quote and concatenate
26 .map(function(line) {
27 return ' \'' + line + '\\n\' +\n';
28 }).join('')
29 // strip leading spaces and the trailing '+'
30 .slice(4, -3);
31 manifests += ',\n';
32 } else if (extname === '.js') {
33 // append the expected parse
34 expected += ' "' + path.basename(file, '.js') + '": ';
35 expected += fs.readFileSync(file, 'utf8');
36 expected += ',\n';
37 } else {
38 console.log('Unknown file ' + file + ' found in manifest dir ' + manifestDir);
39 }
40
41 }
42
43 // clean up and close the objects
44 manifests = manifests.slice(0, -2);
45 manifests += '\n};\n';
46 expected = expected.slice(0, -2);
47 expected += '\n};\n';
48
49 fs.writeFileSync(manifestFilepath, manifests);
50 fs.writeFileSync(expectedFilepath, expected);
51 console.log('Wrote test data file ' + manifestFilepath);
52 console.log('Wrote test data file ' + expectedFilepath);
53 };
54
55 var watch = function() {
56 build();
57 fs.watch(manifestDir, function(event, filename) {
58 console.log('files in manifest dir were changed rebuilding manifest data');
59 build();
60 });
61 };
62
63 var clean = function() {
64 try {
65 fs.unlinkSync(manifestFilepath);
66 } catch(e) {
67 console.log(e);
68 }
69 try {
70 fs.unlinkSync(expectedFilepath);
71 } catch(e) {
72 console.log(e);
73 }
74 }
75
76 module.exports = {
77 build: build,
78 watch: watch,
79 clean: clean
80 };
1 import connect from 'connect';
2 import cowsay from 'cowsay';
3 import path from 'path';
4 import portscanner from 'portscanner';
5 import serveStatic from 'serve-static';
6
7 // Configuration for the server.
8 const PORT = 9999;
9 const MAX_PORT = PORT + 100;
10 const HOST = '127.0.0.1';
11
12 const app = connect();
13
14 const verbs = [
15 'Chewing the cud',
16 'Grazing',
17 'Mooing',
18 'Lowing',
19 'Churning the cream'
20 ];
21
22 app.use(serveStatic(path.join(__dirname, '..')));
23
24 portscanner.findAPortNotInUse(PORT, MAX_PORT, HOST, (error, port) => {
25 if (error) {
26 throw error;
27 }
28
29 process.stdout.write(cowsay.say({
30 text: `${verbs[Math.floor(Math.random() * 5)]} on ${HOST}:${port}`
31 }) + '\n\n');
32
33 app.listen(port);
34 });
1 var browserify = require('browserify');
2 var fs = require('fs');
3 var glob = require('glob');
4 var watchify = require('watchify');
5
6 glob('test/**/*.test.js', function(err, files) {
7 var b = browserify(files, {
8 cache: {},
9 packageCache: {},
10 plugin: [watchify]
11 }).transform('babelify');
12
13 var bundle = function() {
14 b.bundle().pipe(fs.createWriteStream('dist-test/videojs-contrib-hls.js'));
15 };
16
17 b.on('log', function(msg) {
18 process.stdout.write(msg + '\n');
19 });
20
21 b.on('update', bundle);
22 bundle();
23 });
...@@ -90,6 +90,7 @@ ...@@ -90,6 +90,7 @@
90 dispose, 90 dispose,
91 mediaUpdateTimeout, 91 mediaUpdateTimeout,
92 request, 92 request,
93 playlistRequestError,
93 haveMetadata; 94 haveMetadata;
94 95
95 PlaylistLoader.prototype.init.call(this); 96 PlaylistLoader.prototype.init.call(this);
...@@ -102,17 +103,16 @@ ...@@ -102,17 +103,16 @@
102 throw new Error('A non-empty playlist URL is required'); 103 throw new Error('A non-empty playlist URL is required');
103 } 104 }
104 105
105 // update the playlist loader's state in response to a new or 106 playlistRequestError = function(xhr, url, startingState) {
106 // updated playlist.
107 haveMetadata = function(error, xhr, url) {
108 var parser, refreshDelay, update;
109
110 loader.setBandwidth(request || xhr); 107 loader.setBandwidth(request || xhr);
111 108
112 // any in-flight request is now finished 109 // any in-flight request is now finished
113 request = null; 110 request = null;
114 111
115 if (error) { 112 if (startingState) {
113 loader.state = startingState;
114 }
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,
...@@ -120,9 +120,19 @@ ...@@ -120,9 +120,19 @@
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 return loader.trigger('error'); 123 loader.trigger('error');
124 } 124 };
125
126 // update the playlist loader's state in response to a new or
127 // updated playlist.
128
129 haveMetadata = function(xhr, url) {
130 var parser, refreshDelay, update;
125 131
132 loader.setBandwidth(request || xhr);
133
134 // any in-flight request is now finished
135 request = null;
126 loader.state = 'HAVE_METADATA'; 136 loader.state = 'HAVE_METADATA';
127 137
128 parser = new videojs.m3u8.Parser(); 138 parser = new videojs.m3u8.Parser();
...@@ -252,12 +262,12 @@ ...@@ -252,12 +262,12 @@
252 uri: resolveUrl(loader.master.uri, playlist.uri), 262 uri: resolveUrl(loader.master.uri, playlist.uri),
253 withCredentials: withCredentials 263 withCredentials: withCredentials
254 }, function(error, request) { 264 }, function(error, request) {
255 haveMetadata(error, request, playlist.uri);
256
257 if (error) { 265 if (error) {
258 return; 266 return playlistRequestError(request, playlist.uri, startingState);
259 } 267 }
260 268
269 haveMetadata(request, playlist.uri);
270
261 // fire loadedmetadata the first time a media playlist is loaded 271 // fire loadedmetadata the first time a media playlist is loaded
262 if (startingState === 'HAVE_MASTER') { 272 if (startingState === 'HAVE_MASTER') {
263 loader.trigger('loadedmetadata'); 273 loader.trigger('loadedmetadata');
...@@ -289,7 +299,10 @@ ...@@ -289,7 +299,10 @@
289 uri: resolveUrl(loader.master.uri, loader.media().uri), 299 uri: resolveUrl(loader.master.uri, loader.media().uri),
290 withCredentials: withCredentials 300 withCredentials: withCredentials
291 }, function(error, request) { 301 }, function(error, request) {
292 haveMetadata(error, request, loader.media().uri); 302 if (error) {
303 return playlistRequestError(request, loader.media().uri);
304 }
305 haveMetadata(request, loader.media().uri);
293 }); 306 });
294 }); 307 });
295 308
...@@ -349,7 +362,7 @@ ...@@ -349,7 +362,7 @@
349 }] 362 }]
350 }; 363 };
351 loader.master.playlists[srcUrl] = loader.master.playlists[0]; 364 loader.master.playlists[srcUrl] = loader.master.playlists[0];
352 haveMetadata(null, req, srcUrl); 365 haveMetadata(req, srcUrl);
353 return loader.trigger('loadedmetadata'); 366 return loader.trigger('loadedmetadata');
354 }); 367 });
355 }; 368 };
......
1 {
2 "curly": true,
3 "eqeqeq": true,
4 "immed": true,
5 "latedef": true,
6 "newcap": true,
7 "noarg": true,
8 "sub": true,
9 "undef": true,
10 "unused": true,
11 "boss": true,
12 "eqnull": true,
13 "browser": true,
14 "node": true,
15 "predef": [
16 "QUnit",
17 "module",
18 "test",
19 "asyncTest",
20 "expect",
21 "start",
22 "stop",
23 "ok",
24 "equal",
25 "notEqual",
26 "deepEqual",
27 "notDeepEqual",
28 "strictEqual",
29 "notStrictEqual",
30 "throws",
31 "sinon",
32 "process"
33 ]
34 }
...@@ -32,7 +32,7 @@ var stringFromBytes = function(bytes) { ...@@ -32,7 +32,7 @@ var stringFromBytes = function(bytes) {
32 return result; 32 return result;
33 }; 33 };
34 34
35 module('Decryption'); 35 QUnit.module('Decryption');
36 36
37 test('decrypts a single AES-128 with PKCS7 block', function() { 37 test('decrypts a single AES-128 with PKCS7 block', function() {
38 var 38 var
...@@ -74,7 +74,7 @@ test('decrypts multiple AES-128 blocks with CBC', function() { ...@@ -74,7 +74,7 @@ test('decrypts multiple AES-128 blocks with CBC', function() {
74 74
75 var clock; 75 var clock;
76 76
77 module('Incremental Processing', { 77 QUnit.module('Incremental Processing', {
78 setup: function() { 78 setup: function() {
79 clock = sinon.useFakeTimers(); 79 clock = sinon.useFakeTimers();
80 }, 80 },
...@@ -114,7 +114,7 @@ test('executes callback in series', function() { ...@@ -114,7 +114,7 @@ test('executes callback in series', function() {
114 114
115 var decrypter; 115 var decrypter;
116 116
117 module('Incremental Decryption', { 117 QUnit.module('Incremental Decryption', {
118 setup: function() { 118 setup: function() {
119 clock = sinon.useFakeTimers(); 119 clock = sinon.useFakeTimers();
120 }, 120 },
......
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <title>video.js HLS Plugin Test Suite</title>
6 <link rel="stylesheet" href="/node_modules/qunitjs/qunit/qunit.css" media="screen">
7 <link rel="stylesheet" href="/node_modules/video.js/dist/video-js.css" media="screen">
8 </head>
9 <body>
10 <div id="qunit"></div>
11 <div id="qunit-fixture"></div>
12 <!-- NOTE in order for test to pass we require sinon 1.10.2 exactly -->
13 <script src="/node_modules/sinon/pkg/sinon.js"></script>
14 <script src="/node_modules/qunitjs/qunit/qunit.js"></script>
15 <script src="/node_modules/pkcs7/dist/pkcs7.unpad.js"></script>
16 <script src="/node_modules/video.js/dist/video.js"></script>
17 <script src="/node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js"></script>
18
19 <script src="/src/videojs-hls.js"></script>
20 <script src="/src/xhr.js"></script>
21 <script src="/src/stream.js"></script>
22 <script src="/src/m3u8/m3u8-parser.js"></script>
23 <script src="/src/playlist.js"></script>
24 <script src="/src/playlist-loader.js"></script>
25 <script src="/src/decrypter.js"></script>
26 <script src="/src/bin-utils.js"></script>
27
28 <script src="/test/data/manifests.js"></script>
29 <script src="/test/data/expected.js"></script>
30 <script src="/test/data/ts-segment-bc.js"></script>
31
32
33 <script src="/test/videojs-hls.test.js"></script>
34 <script src="/test/m3u8.test.js"></script>
35 <script src="/test/playlist.test.js"></script>
36 <script src="/test/playlist-loader.test.js"></script>
37 <script src="/test/decrypter.test.js"></script>
38
39 </body>
40 </html>
1
2
3 var fixture = document.createElement('div');
4 fixture.id = 'qunit-fixture';
5 document.body.appendChild(fixture);
1 // Karma example configuration file
2 // NOTE: To configure Karma tests, do the following:
3 // 1. Copy this file and rename the copy with a .conf.js extension, for example: karma.conf.js
4 // 2. Configure the properties below in your conf.js copy
5 // 3. Run your tests
6
7 module.exports = function(config) {
8 var customLaunchers = {
9 chrome_sl: {
10 singleRun: true,
11 base: 'SauceLabs',
12 browserName: 'chrome',
13 platform: 'Windows 7'
14 },
15
16 firefox_sl: {
17 singleRun: true,
18 base: 'SauceLabs',
19 browserName: 'firefox',
20 platform: 'Windows 8'
21 },
22
23 safari_sl: {
24 singleRun: true,
25 base: 'SauceLabs',
26 browserName: 'safari',
27 platform: 'OS X 10.8'
28 },
29
30 ipad_sl: {
31 singleRun: true,
32 base: 'SauceLabs',
33 browserName: 'ipad',
34 platform:'OS X 10.9',
35 version: '7.1'
36 },
37
38 android_sl: {
39 singleRun: true,
40 base: 'SauceLabs',
41 browserName: 'android',
42 platform:'Linux'
43 }
44 };
45
46 config.set({
47 // base path, that will be used to resolve files and exclude
48 basePath: '',
49
50 frameworks: ['qunit'],
51
52 // Set autoWatch to true if you plan to run `grunt karma` continuously, to automatically test changes as you make them.
53 autoWatch: false,
54
55 // Setting singleRun to true here will start up your specified browsers, run tests, and then shut down the browsers. Helpful to have in a CI environment, where you don't want to leave browsers running continuously.
56 singleRun: true,
57
58 // custom launchers for sauce labs
59 //define SL browsers
60 customLaunchers: customLaunchers,
61
62 // Start these browsers
63 browsers: ['chrome_sl'], //Object.keys(customLaunchers),
64
65 // List of files / patterns to load in the browser
66 // Add any new src files to this list.
67 // If you add new unit tests, they will be picked up automatically by Karma,
68 // unless you've added them to a nested directory, in which case you should
69 // add their paths to this list.
70
71 files: [
72 '../node_modules/sinon/pkg/sinon.js',
73 '../node_modules/video.js/dist/video-js.css',
74 '../node_modules/video.js/dist/video.js',
75 '../node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js',
76 '../node_modules/pkcs7/dist/pkcs7.unpad.js',
77 '../test/karma-qunit-shim.js',
78 '../src/videojs-hls.js',
79 '../src/stream.js',
80 '../src/m3u8/m3u8-parser.js',
81 '../src/xhr.js',
82 '../src/playlist.js',
83 '../src/playlist-loader.js',
84 '../src/decrypter.js',
85 '../tmp/manifests.js',
86 '../tmp/expected.js',
87 'tsSegment-bc.js',
88 '../src/bin-utils.js',
89 '../test/*.js',
90 ],
91
92 plugins: [
93 'karma-qunit',
94 'karma-chrome-launcher',
95 'karma-firefox-launcher',
96 'karma-ie-launcher',
97 'karma-opera-launcher',
98 'karma-phantomjs-launcher',
99 'karma-safari-launcher',
100 'karma-sauce-launcher'
101 ],
102
103 // test results reporter to use
104 // possible values: 'dots', 'progress', 'junit'
105 reporters: ['dots', 'progress'],
106
107 // web server port
108 port: 9876,
109
110 // cli runner port
111 runnerPort: 9100,
112
113 // enable / disable colors in the output (reporters and logs)
114 colors: true,
115
116 // level of logging
117 // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
118 //logLevel: config.LOG_INFO,
119
120 // If browser does not capture in given timeout [ms], kill it
121 captureTimeout: 60000,
122
123 // global config for SauceLabs
124 sauceLabs: {
125 startConnect: false,
126 tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER,
127 build: process.env.TRAVIS_BUILD_NUMBER,
128 testName: process.env.TRAVIS_BUILD_NUMBER + process.env.TRAVIS_BRANCH,
129 recordScreenshots: false
130 }
131 });
132 };
1 var common = require('./common');
2
3 module.exports = function(config) {
4 config.set(common({
5 plugins: ['karma-chrome-launcher'],
6 browsers: ['Chrome']
7 }));
8 };
1 var merge = require('lodash-compat/object/merge');
2
3 var DEFAULTS = {
4 basePath: '../..',
5 //frameworks: ['browserify', 'qunit'],
6 frameworks: ['qunit'],
7
8
9 files: [
10 'node_modules/sinon/pkg/sinon.js',
11 'node_modules/sinon/pkg/sinon-ie.js',
12 'node_modules/video.js/dist/video.js',
13 'node_modules/video.js/dist/video-js.css',
14
15 // REMOVE ME WHEN BROWSERIFIED
16 'node_modules/pkcs7/dist/pkcs7.unpad.js',
17 'node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js',
18
19 'src/videojs-hls.js',
20 'src/xhr.js',
21 'src/stream.js',
22 'src/m3u8/m3u8-parser.js',
23 'src/playlist.js',
24 'src/playlist-loader.js',
25 'src/decrypter.js',
26 'src/bin-utils.js',
27
28 'test/data/manifests.js',
29 'test/data/expected.js',
30 'test/data/ts-segment-bc.js',
31
32 'test/videojs-hls.test.js',
33 'test/m3u8.test.js',
34 'test/playlist.test.js',
35 'test/playlist-loader.test.js',
36 'test/decrypter.test.js',
37 // END REMOVE ME
38 // 'test/**/*.js'
39 ],
40
41 exclude: [
42 'test/bundle.js',
43 // 'test/data/**'
44 ],
45
46 plugins: [
47 // 'karma-browserify',
48 'karma-qunit'
49 ],
50
51 preprocessors: {
52 // 'test/**/*.js': ['browserify']
53 },
54
55 reporters: ['dots'],
56 port: 9876,
57 colors: true,
58 autoWatch: false,
59 singleRun: true,
60 concurrency: Infinity,
61
62 /*
63 browserify: {
64 debug: true,
65 transform: [
66 'babelify',
67 'browserify-shim'
68 ],
69 noparse: [
70 'test/data/**',
71 ]
72 }
73 */
74 };
75
76 /**
77 * Customizes target/source merging with lodash merge.
78 *
79 * @param {Mixed} target
80 * @param {Mixed} source
81 * @return {Mixed}
82 */
83 var customizer = function(target, source) {
84 if (Array.isArray(target)) {
85 return target.concat(source);
86 }
87 };
88
89 /**
90 * Generates a new Karma config with a common set of base configuration.
91 *
92 * @param {Object} custom
93 * Configuration that will be deep-merged. Arrays will be
94 * concatenated.
95 * @return {Object}
96 */
97 module.exports = function(custom) {
98 return merge({}, custom, DEFAULTS, customizer);
99 };
1 var common = require('./common');
2
3 // Runs default testing configuration in multiple environments.
4
5 module.exports = function(config) {
6
7 // Travis CI should run in its available Firefox headless browser.
8 if (process.env.TRAVIS) {
9
10 config.set(common({
11 browsers: ['Firefox'],
12 plugins: ['karma-firefox-launcher']
13 }))
14 } else {
15 config.set(common({
16
17 frameworks: ['detectBrowsers'],
18
19 plugins: [
20 'karma-chrome-launcher',
21 'karma-detect-browsers',
22 'karma-firefox-launcher',
23 'karma-ie-launcher',
24 'karma-safari-launcher'
25 ],
26
27 detectBrowsers: {
28 // disable safari as it was not previously supported and causes test failures
29 postDetection: function(availableBrowsers) {
30 var safariIndex = availableBrowsers.indexOf('Safari');
31 if(safariIndex !== -1) {
32 availableBrowsers.splice(safariIndex, 1);
33 }
34 return availableBrowsers;
35 },
36 usePhantomJS: false
37 }
38 }));
39 }
40 };
1 var common = require('./common');
2
3 module.exports = function(config) {
4 config.set(common({
5 plugins: ['karma-firefox-launcher'],
6 browsers: ['Firefox']
7 }));
8 };
1 var common = require('./common');
2
3 module.exports = function(config) {
4 config.set(common({
5 plugins: ['karma-ie-launcher'],
6 browsers: ['IE']
7 }));
8 };
1 var common = require('./common');
2
3 module.exports = function(config) {
4 config.set(common({
5 plugins: ['karma-safari-launcher'],
6 browsers: ['Safari']
7 }));
8 };
1 // Karma example configuration file
2 // NOTE: To configure Karma tests, do the following:
3 // 1. Copy this file and rename the copy with a .conf.js extension, for example: karma.conf.js
4 // 2. Configure the properties below in your conf.js copy
5 // 3. Run your tests
6
7 module.exports = function(config) {
8 config.set({
9 // base path, that will be used to resolve files and exclude
10 basePath: '',
11
12 frameworks: ['qunit'],
13
14 // Set autoWatch to true if you plan to run `grunt karma` continuously, to automatically test changes as you make them.
15 autoWatch: false,
16
17 // Setting singleRun to true here will start up your specified browsers, run tests, and then shut down the browsers. Helpful to have in a CI environment, where you don't want to leave browsers running continuously.
18 singleRun: true,
19
20 // Start these browsers, currently available:
21 // - Chrome
22 // - ChromeCanary
23 // - Firefox
24 // - Opera
25 // - Safari (only Mac)
26 // - PhantomJS
27 // - IE (only Windows)
28 // Example usage:
29 // browsers: [],
30 // List of files / patterns to load in the browser
31 // Add any new src files to this list.
32 // If you add new unit tests, they will be picked up automatically by Karma,
33 // unless you've added them to a nested directory, in which case you should
34 // add their paths to this list.
35
36 files: [
37 '../node_modules/sinon/pkg/sinon.js',
38 '../node_modules/video.js/dist/video-js.css',
39 '../node_modules/video.js/dist/video.js',
40 '../node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js',
41 '../node_modules/pkcs7/dist/pkcs7.unpad.js',
42 '../test/karma-qunit-shim.js',
43 '../src/videojs-hls.js',
44 '../src/stream.js',
45 '../src/m3u8/m3u8-parser.js',
46 '../src/xhr.js',
47 '../src/playlist.js',
48 '../src/playlist-loader.js',
49 '../src/decrypter.js',
50 '../tmp/manifests.js',
51 '../tmp/expected.js',
52 'tsSegment-bc.js',
53 '../src/bin-utils.js',
54 '../test/*.js',
55 ],
56
57 plugins: [
58 'karma-qunit',
59 'karma-chrome-launcher',
60 'karma-firefox-launcher',
61 'karma-ie-launcher',
62 'karma-opera-launcher',
63 'karma-phantomjs-launcher',
64 'karma-safari-launcher'
65 ],
66
67 // list of files to exclude
68 exclude: [
69
70 ],
71
72
73 // test results reporter to use
74 // possible values: 'dots', 'progress', 'junit'
75 reporters: ['progress'],
76
77
78 // web server port
79 port: 9876,
80
81
82 // cli runner port
83 runnerPort: 9100,
84
85
86 // enable / disable colors in the output (reporters and logs)
87 colors: true,
88
89
90 // level of logging
91 // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
92 logLevel: config.LOG_DISABLE,
93
94 // If browser does not capture in given timeout [ms], kill it
95 captureTimeout: 60000
96 });
97 };
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
13 M3U8 Test Suite 13 M3U8 Test Suite
14 */ 14 */
15 15
16 module('LineStream', { 16 QUnit.module('LineStream', {
17 setup: function() { 17 setup: function() {
18 lineStream = new LineStream(); 18 lineStream = new LineStream();
19 } 19 }
...@@ -83,7 +83,7 @@ ...@@ -83,7 +83,7 @@
83 strictEqual(2, permanentLines.length, 'new events are still received'); 83 strictEqual(2, permanentLines.length, 'new events are still received');
84 }); 84 });
85 85
86 module('ParseStream', { 86 QUnit.module('ParseStream', {
87 setup: function() { 87 setup: function() {
88 lineStream = new LineStream(); 88 lineStream = new LineStream();
89 parseStream = new ParseStream(); 89 parseStream = new ParseStream();
...@@ -622,13 +622,13 @@ ...@@ -622,13 +622,13 @@
622 ok(!event, 'no event is triggered'); 622 ok(!event, 'no event is triggered');
623 }); 623 });
624 624
625 module('m3u8 parser'); 625 QUnit.module('m3u8 parser');
626 626
627 test('can be constructed', function() { 627 test('can be constructed', function() {
628 notStrictEqual(new Parser(), undefined, 'parser is defined'); 628 notStrictEqual(new Parser(), undefined, 'parser is defined');
629 }); 629 });
630 630
631 module('m3u8s'); 631 QUnit.module('m3u8s');
632 632
633 test('parses static manifests as expected', function() { 633 test('parses static manifests as expected', function() {
634 var key; 634 var key;
......
1 <!doctype html>
2 <html>
3 <head>
4 <title>MPEG-TS Parser Performance Workbench</title>
5 <!-- video.js -->
6 <script src="../node_modules/video.js/video.dev.js"></script>
7
8 <!-- HLS plugin -->
9 <script src="../src/video-js-hls.js"></script>
10 <script src="../src/flv-tag.js"></script>
11 <script src="../src/exp-golomb.js"></script>
12 <script src="../src/h264-stream.js"></script>
13 <script src="../src/aac-stream.js"></script>
14 <script src="../src/segment-parser.js"></script>
15
16 <!-- MPEG-TS segment -->
17 <script src="tsSegment-bc.js"></script>
18 <style>
19 .desc {
20 background-color: #ddd;
21 border: thin solid #333;
22 padding: 8px;
23 }
24 </style>
25 </head>
26 <body>
27 <p class="desc">Select your number of iterations and then press "Run" to begin parsing MPEG-TS packets into FLV tags. This page can be handy for identifying segment parser performance bottlenecks.</p>
28 <form>
29 <input name="iterations" min="1" type="number" value="1">
30 <button type="sumbit">Run</button>
31 </form>
32 <table>
33 <thead>
34 <th>Iterations</th><th>Time</th><th>MB/second</th>
35 </thead>
36 <tbody class="results"></tbody>
37 </table>
38 <script>
39 var
40 button = document.querySelector('button'),
41 input = document.querySelector('input'),
42 results = document.querySelector('.results'),
43
44 reportResults = function(count, elapsed) {
45 var
46 row = document.createElement('tr'),
47 countCell = document.createElement('td'),
48 elapsedCell = document.createElement('td'),
49 throughputCell = document.createElement('td');
50
51 countCell.innerText = count;
52 elapsedCell.innerText = elapsed;
53 throughputCell.innerText = (((bcSegment.byteLength * count * 1000) / elapsed) / (Math.pow(2, 20))).toFixed(3);
54 row.appendChild(countCell);
55 row.appendChild(elapsedCell);
56 row.appendChild(throughputCell);
57
58 results.insertBefore(row, results.firstChild);
59 };
60
61 button.addEventListener('click', function(event) {
62 var
63 iterations = input.value,
64 parser = new window.videojs.hls.SegmentParser(),
65 start;
66
67 // setup
68 start = +new Date();
69
70 while (iterations--) {
71
72 // parse the segment
73 parser.parseSegmentBinaryData(window.bcSegment);
74
75 // finalize all the FLV tags
76 while (parser.tagsAvailable()) {
77 parser.getNextTag();
78 }
79 }
80
81 // report
82 reportResults(input.value, (+new Date()) - start);
83
84 // don't actually submit the form
85 event.preventDefault();
86 }, false);
87 </script>
88 </body>
89 </html>
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
16 .join('/'); 16 .join('/');
17 }; 17 };
18 18
19 module('Playlist Loader', { 19 QUnit.module('Playlist Loader', {
20 setup: function() { 20 setup: function() {
21 // fake XHRs 21 // fake XHRs
22 sinonXhr = sinon.useFakeXMLHttpRequest(); 22 sinonXhr = sinon.useFakeXMLHttpRequest();
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 'use strict'; 3 'use strict';
4 var Playlist = videojs.Hls.Playlist; 4 var Playlist = videojs.Hls.Playlist;
5 5
6 module('Playlist Duration'); 6 QUnit.module('Playlist Duration');
7 7
8 test('total duration for live playlists is Infinity', function() { 8 test('total duration for live playlists is Infinity', function() {
9 var duration = Playlist.duration({ 9 var duration = Playlist.duration({
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
16 equal(duration, Infinity, 'duration is infinity'); 16 equal(duration, Infinity, 'duration is infinity');
17 }); 17 });
18 18
19 module('Playlist Interval Duration'); 19 QUnit.module('Playlist Interval Duration');
20 20
21 test('accounts for non-zero starting VOD media sequences', function() { 21 test('accounts for non-zero starting VOD media sequences', function() {
22 var duration = Playlist.duration({ 22 var duration = Playlist.duration({
...@@ -266,7 +266,7 @@ ...@@ -266,7 +266,7 @@
266 equal(Playlist.duration(playlist, -1), 0, 'negative length duration is zero'); 266 equal(Playlist.duration(playlist, -1), 0, 'negative length duration is zero');
267 }); 267 });
268 268
269 module('Playlist Seekable'); 269 QUnit.module('Playlist Seekable');
270 270
271 test('calculates seekable time ranges from the available segments', function() { 271 test('calculates seekable time ranges from the available segments', function() {
272 var playlist = { 272 var playlist = {
......
1 import document from 'global/document';
2
3 import QUnit from 'qunit';
4 import sinon from 'sinon';
5 import videojs from 'video.js';
6
7 QUnit.module('videojs-contrib-hls - sanity', {
8 beforeEach() {
9 this.fixture = document.getElementById('qunit-fixture');
10 this.video = document.createElement('video');
11 this.fixture.appendChild(this.video);
12 this.player = videojs(this.video);
13
14 // Mock the environment's timers because certain things - particularly
15 // player readiness - are asynchronous in video.js 5.
16 this.clock = sinon.useFakeTimers();
17 },
18
19 afterEach() {
20
21 // The clock _must_ be restored before disposing the player; otherwise,
22 // certain timeout listeners that happen inside video.js may throw errors.
23 this.clock.restore();
24 this.player.dispose();
25 }
26 });
27
28 QUnit.test('the environment is sane', function(assert) {
29 assert.strictEqual(typeof Array.isArray, 'function', 'es5 exists');
30 assert.strictEqual(typeof sinon, 'object', 'sinon exists');
31 assert.strictEqual(typeof videojs, 'function', 'videojs exists');
32 assert.strictEqual(typeof videojs.MediaSource, 'object', 'MediaSource is an object');
33 assert.strictEqual(typeof videojs.URL, 'object', 'URL is an object');
34 assert.strictEqual(typeof videojs.Hls, 'object', 'Hls is an object');
35 assert.strictEqual(typeof videojs.HlsSourceHandler,'function', 'HlsSourceHandler is a function');
36 assert.strictEqual(typeof videojs.HlsHandler, 'function', 'HlsHandler is a function');
37 });
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <title>video.js HLS Plugin Test Suite</title>
6 <!-- Load sinon server for fakeXHR -->
7 <script src="../node_modules/sinon/pkg/sinon.js"></script>
8
9 <!-- Load local QUnit. -->
10 <link rel="stylesheet" href="../node_modules/qunitjs/qunit/qunit.css" media="screen">
11 <script src="../node_modules/qunitjs/qunit/qunit.js"></script>
12
13 <!-- video.js -->
14 <script src="../node_modules/video.js/dist/video.js"></script>
15 <link rel="stylesheet" href="../node_modules/video.js/dist/video-js.css" media="screen">
16 <script src="../node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js"></script>
17
18 <!-- HLS plugin -->
19 <script src="../src/videojs-hls.js"></script>
20 <script src="../src/xhr.js"></script>
21 <script src="../src/stream.js"></script>
22
23 <!-- M3U8 -->
24 <script src="../src/m3u8/m3u8-parser.js"></script>
25 <script src="../src/playlist.js"></script>
26 <script src="../src/playlist-loader.js"></script>
27 <script src="../node_modules/pkcs7/dist/pkcs7.unpad.js"></script>
28 <script src="../src/decrypter.js"></script>
29 <!-- M3U8 TEST DATA -->
30 <script src="../tmp/manifests.js"></script>
31 <script src="../tmp/expected.js"></script>
32
33 <!-- M3U8 -->
34 <!-- SEGMENT -->
35 <script src="tsSegment-bc.js"></script>
36 <script src="../src/bin-utils.js"></script>
37
38 <!-- Test cases -->
39 <script>
40 module('environment');
41 test('is sane', function() {
42 expect(1);
43 ok(true);
44 });
45 </script>
46 <script src="videojs-hls_test.js"></script>
47 <script src="m3u8_test.js"></script>
48 <script src="playlist_test.js"></script>
49 <script src="playlist-loader_test.js"></script>
50 <script src="decrypter_test.js"></script>
51 </head>
52 <body>
53 <div id="qunit"></div>
54 <div id="qunit-fixture">
55 <span>test markup</span>
56 </div>
57 </body>
58 </html>
...@@ -206,7 +206,7 @@ var ...@@ -206,7 +206,7 @@ var
206 206
207 MockMediaSource.open = function() {}; 207 MockMediaSource.open = function() {};
208 208
209 module('HLS', { 209 QUnit.module('HLS', {
210 beforeEach: function() { 210 beforeEach: function() {
211 oldMediaSource = videojs.MediaSource; 211 oldMediaSource = videojs.MediaSource;
212 videojs.MediaSource = MockMediaSource; 212 videojs.MediaSource = MockMediaSource;
...@@ -1296,6 +1296,28 @@ test('blacklists switching from video-only playlists to video+audio', function() ...@@ -1296,6 +1296,28 @@ test('blacklists switching from video-only playlists to video+audio', function()
1296 equal(videoAudioPlaylist.excludeUntil, Infinity, 'excluded incompatible playlist'); 1296 equal(videoAudioPlaylist.excludeUntil, Infinity, 'excluded incompatible playlist');
1297 }); 1297 });
1298 1298
1299 test('After an initial media playlist 404s, we fire loadedmetadata once we successfully load a playlist', function() {
1300 var count = 0;
1301 player.src({
1302 src: 'manifest/master.m3u8',
1303 type: 'application/vnd.apple.mpegurl'
1304 });
1305 openMediaSource(player);
1306 player.tech_.hls.bandwidth = 20000;
1307 player.on('loadedmetadata', function() {
1308 count += 1;
1309 });
1310 standardXHRResponse(requests.shift()); //master
1311 equal(count, 0,
1312 'loadedMedia not triggered before requesting playlist');
1313 requests.shift().respond(404); //media
1314 equal(count, 0,
1315 'loadedMedia not triggered after playlist 404');
1316 standardXHRResponse(requests.shift()); //media
1317 equal(count, 1,
1318 'loadedMedia triggered after successful recovery from 404');
1319 });
1320
1299 test('does not blacklist compatible H.264 codec strings', function() { 1321 test('does not blacklist compatible H.264 codec strings', function() {
1300 var master; 1322 var master;
1301 player.src({ 1323 player.src({
...@@ -2884,7 +2906,7 @@ test('does not download segments if preload option set to none', function() { ...@@ -2884,7 +2906,7 @@ test('does not download segments if preload option set to none', function() {
2884 equal(requests.length, 0, 'did not download any segments'); 2906 equal(requests.length, 0, 'did not download any segments');
2885 }); 2907 });
2886 2908
2887 module('Buffer Inspection'); 2909 QUnit.module('Buffer Inspection');
2888 2910
2889 test('detects time range end-point changed by updates', function() { 2911 test('detects time range end-point changed by updates', function() {
2890 var edge; 2912 var edge;
......
...@@ -20,7 +20,7 @@ if (process.env.SAUCE_USERNAME) { ...@@ -20,7 +20,7 @@ if (process.env.SAUCE_USERNAME) {
20 config.maxDuration = 300; 20 config.maxDuration = 300;
21 } 21 }
22 22
23 config.baseUrl = 'http://127.0.0.1:9999/example.html'; 23 config.baseUrl = 'http://127.0.0.1:9999/';
24 config.specs = ['spec.js']; 24 config.specs = ['spec.js'];
25 25
26 config.framework = 'jasmine2'; 26 config.framework = 'jasmine2';
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
12 12
13 <link rel="stylesheet" href="css/normalize.min.css"> 13 <link rel="stylesheet" href="css/normalize.min.css">
14 <link rel="stylesheet" href="css/main.css"> 14 <link rel="stylesheet" href="css/main.css">
15 <link rel="stylesheet" href="../../node_modules/video.js/dist/video-js/video-js.css"> 15 <link rel="stylesheet" href="../../node_modules/video.js/dist/video-js.css">
16 16
17 <script src="js/vendor/modernizr-2.6.2.min.js"></script> 17 <script src="js/vendor/modernizr-2.6.2.min.js"></script>
18 </head> 18 </head>
...@@ -120,13 +120,17 @@ ...@@ -120,13 +120,17 @@
120 <script src="../../node_modules/sinon/lib/sinon/util/fake_timers.js"></script> 120 <script src="../../node_modules/sinon/lib/sinon/util/fake_timers.js"></script>
121 <script src="js/vendor/d3.min.js"></script> 121 <script src="js/vendor/d3.min.js"></script>
122 122
123 <script src="../../node_modules/video.js/dist/video-js/video.js"></script> 123 <script src="/node_modules/video.js/dist/video.js"></script>
124 <script src="../../node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js"></script> 124 <script src="/node_modules/videojs-contrib-media-sources/dist/videojs-media-sources.js"></script>
125 <script src="../../src/videojs-hls.js"></script> 125 <script src="/node_modules/pkcs7/dist/pkcs7.unpad.js"></script>
126 <script src="../../src/xhr.js"></script> 126 <script src="/src/videojs-hls.js"></script>
127 <script src="../../src/stream.js"></script> 127 <script src="/src/xhr.js"></script>
128 <script src="../../src/m3u8/m3u8-parser.js"></script> 128 <script src="/src/stream.js"></script>
129 <script src="../../src/playlist-loader.js"></script> 129 <script src="/src/m3u8/m3u8-parser.js"></script>
130 <script src="/src/playlist.js"></script>
131 <script src="/src/playlist-loader.js"></script>
132 <script src="/src/decrypter.js"></script>
133 <script src="/src/bin-utils.js"></script>
130 134
131 <script src="js/switcher.js"></script> 135 <script src="js/switcher.js"></script>
132 </body> 136 </body>
......
...@@ -142,6 +142,7 @@ ...@@ -142,6 +142,7 @@
142 // mock out the environment 142 // mock out the environment
143 clock = sinon.useFakeTimers(); 143 clock = sinon.useFakeTimers();
144 fakeXhr = sinon.useFakeXMLHttpRequest(); 144 fakeXhr = sinon.useFakeXMLHttpRequest();
145 videojs.xhr.XMLHttpRequest = fakeXhr;
145 requests = []; 146 requests = [];
146 fakeXhr.onCreate = function(xhr) { 147 fakeXhr.onCreate = function(xhr) {
147 xhr.startTime = +new Date(); 148 xhr.startTime = +new Date();
...@@ -156,7 +157,6 @@ ...@@ -156,7 +157,6 @@
156 video.controls = true; 157 video.controls = true;
157 fixture.appendChild(video); 158 fixture.appendChild(video);
158 player = videojs(video, { 159 player = videojs(video, {
159 techOrder: ['hls'],
160 sources: [{ 160 sources: [{
161 src: 'http://example.com/master.m3u8', 161 src: 'http://example.com/master.m3u8',
162 type: 'application/x-mpegurl' 162 type: 'application/x-mpegurl'
...@@ -295,6 +295,8 @@ ...@@ -295,6 +295,8 @@
295 done(null, results); 295 done(null, results);
296 }, 0); 296 }, 0);
297 }); 297 });
298 /// trigger the ready function through set timeout
299 clock.tick(1);
298 }; 300 };
299 runButton = document.getElementById('run-simulation'); 301 runButton = document.getElementById('run-simulation');
300 runButton.addEventListener('click', function() { 302 runButton.addEventListener('click', function() {
......