5a33fc57 by brandonocasey

browserify-p4: playlist*, xhr, and resolve-url

updated stub.js to keep unit tests working
updated build/watch scripts
ripped resolve-url out of videojs-contrib-hls for now
1 parent e3c93f60
...@@ -48,10 +48,7 @@ ...@@ -48,10 +48,7 @@
48 <script src="/node_modules/video.js/dist/video.js"></script> 48 <script src="/node_modules/video.js/dist/video.js"></script>
49 <script src="/node_modules/videojs-contrib-media-sources/dist/videojs-media-sources.js"></script> 49 <script src="/node_modules/videojs-contrib-media-sources/dist/videojs-media-sources.js"></script>
50 <script src="/src/videojs-contrib-hls.js"></script> 50 <script src="/src/videojs-contrib-hls.js"></script>
51 <script src="/src/xhr.js"></script>
52 <script src="/dist/videojs-contrib-hls.js"></script> 51 <script src="/dist/videojs-contrib-hls.js"></script>
53 <script src="/src/playlist.js"></script>
54 <script src="/src/playlist-loader.js"></script>
55 <script src="/src/bin-utils.js"></script> 52 <script src="/src/bin-utils.js"></script>
56 <script> 53 <script>
57 (function(window, videojs) { 54 (function(window, videojs) {
......
...@@ -2,7 +2,7 @@ var browserify = require('browserify'); ...@@ -2,7 +2,7 @@ var browserify = require('browserify');
2 var fs = require('fs'); 2 var fs = require('fs');
3 var glob = require('glob'); 3 var glob = require('glob');
4 4
5 glob('test/{decryper,m3u8,stub}.test.js', function(err, files) { 5 glob('test/{playlist*,decryper,m3u8,stub}.test.js', function(err, files) {
6 browserify(files) 6 browserify(files)
7 .transform('babelify') 7 .transform('babelify')
8 .bundle() 8 .bundle()
......
...@@ -3,7 +3,7 @@ var fs = require('fs'); ...@@ -3,7 +3,7 @@ var fs = require('fs');
3 var glob = require('glob'); 3 var glob = require('glob');
4 var watchify = require('watchify'); 4 var watchify = require('watchify');
5 5
6 glob('test/{decrypter,m3u8,stub}.test.js', function(err, files) { 6 glob('test/{playlist*,decrypter,m3u8,stub}.test.js', function(err, files) {
7 var b = browserify(files, { 7 var b = browserify(files, {
8 cache: {}, 8 cache: {},
9 packageCache: {}, 9 packageCache: {},
......
1 /** 1 /**
2 * Playlist related utilities. 2 * Playlist related utilities.
3 */ 3 */
4 (function(window, videojs) { 4 import {createTimeRange} from 'video.js';
5 'use strict';
6 5
7 var duration, intervalDuration, backwardDuration, forwardDuration, seekable; 6 const backwardDuration = function(playlist, endSequence) {
8 7 let result = 0;
9 backwardDuration = function(playlist, endSequence) { 8 let i = endSequence - playlist.mediaSequence;
10 var result = 0, segment, i;
11
12 i = endSequence - playlist.mediaSequence;
13 // if a start time is available for segment immediately following 9 // if a start time is available for segment immediately following
14 // the interval, use it 10 // the interval, use it
15 segment = playlist.segments[i]; 11 let segment = playlist.segments[i];
12
16 // Walk backward until we find the latest segment with timeline 13 // Walk backward until we find the latest segment with timeline
17 // information that is earlier than endSequence 14 // information that is earlier than endSequence
18 if (segment) { 15 if (segment) {
19 if (segment.start !== undefined) { 16 if (typeof segment.start !== 'undefined') {
20 return { result: segment.start, precise: true }; 17 return { result: segment.start, precise: true };
21 } 18 }
22 if (segment.end !== undefined) { 19 if (typeof segment.end !== 'undefined') {
23 return { 20 return {
24 result: segment.end - segment.duration, 21 result: segment.end - segment.duration,
25 precise: true 22 precise: true
...@@ -28,28 +25,29 @@ ...@@ -28,28 +25,29 @@
28 } 25 }
29 while (i--) { 26 while (i--) {
30 segment = playlist.segments[i]; 27 segment = playlist.segments[i];
31 if (segment.end !== undefined) { 28 if (typeof segment.end !== 'undefined') {
32 return { result: result + segment.end, precise: true }; 29 return { result: result + segment.end, precise: true };
33 } 30 }
34 31
35 result += segment.duration; 32 result += segment.duration;
36 33
37 if (segment.start !== undefined) { 34 if (typeof segment.start !== 'undefined') {
38 return { result: result + segment.start, precise: true }; 35 return { result: result + segment.start, precise: true };
39 } 36 }
40 } 37 }
41 return { result: result, precise: false }; 38 return { result, precise: false };
42 }; 39 };
43
44 forwardDuration = function(playlist, endSequence) {
45 var result = 0, segment, i;
46 40
47 i = endSequence - playlist.mediaSequence; 41 const forwardDuration = function(playlist, endSequence) {
42 let result = 0;
43 let segment;
44 let i = endSequence - playlist.mediaSequence;
48 // Walk forward until we find the earliest segment with timeline 45 // Walk forward until we find the earliest segment with timeline
49 // information 46 // information
47
50 for (; i < playlist.segments.length; i++) { 48 for (; i < playlist.segments.length; i++) {
51 segment = playlist.segments[i]; 49 segment = playlist.segments[i];
52 if (segment.start !== undefined) { 50 if (typeof segment.start !== 'undefined') {
53 return { 51 return {
54 result: segment.start - result, 52 result: segment.start - result,
55 precise: true 53 precise: true
...@@ -58,7 +56,7 @@ ...@@ -58,7 +56,7 @@
58 56
59 result += segment.duration; 57 result += segment.duration;
60 58
61 if (segment.end !== undefined) { 59 if (typeof segment.end !== 'undefined') {
62 return { 60 return {
63 result: segment.end - result, 61 result: segment.end - result,
64 precise: true 62 precise: true
...@@ -68,9 +66,9 @@ ...@@ -68,9 +66,9 @@
68 } 66 }
69 // indicate we didn't find a useful duration estimate 67 // indicate we didn't find a useful duration estimate
70 return { result: -1, precise: false }; 68 return { result: -1, precise: false };
71 }; 69 };
72 70
73 /** 71 /**
74 * Calculate the media duration from the segments associated with a 72 * Calculate the media duration from the segments associated with a
75 * playlist. The duration of a subinterval of the available segments 73 * playlist. The duration of a subinterval of the available segments
76 * may be calculated by specifying an end index. 74 * may be calculated by specifying an end index.
...@@ -81,10 +79,11 @@ ...@@ -81,10 +79,11 @@
81 * @return {number} the duration between the first available segment 79 * @return {number} the duration between the first available segment
82 * and end index. 80 * and end index.
83 */ 81 */
84 intervalDuration = function(playlist, endSequence) { 82 const intervalDuration = function(playlist, endSequence) {
85 var backward, forward; 83 let backward;
84 let forward;
86 85
87 if (endSequence === undefined) { 86 if (typeof endSequence === 'undefined') {
88 endSequence = playlist.mediaSequence + playlist.segments.length; 87 endSequence = playlist.mediaSequence + playlist.segments.length;
89 } 88 }
90 89
...@@ -112,9 +111,9 @@ ...@@ -112,9 +111,9 @@
112 111
113 // return the less-precise, playlist-based duration estimate 112 // return the less-precise, playlist-based duration estimate
114 return backward.result; 113 return backward.result;
115 }; 114 };
116 115
117 /** 116 /**
118 * Calculates the duration of a playlist. If a start and end index 117 * Calculates the duration of a playlist. If a start and end index
119 * are specified, the duration will be for the subset of the media 118 * are specified, the duration will be for the subset of the media
120 * timeline between those two indices. The total duration for live 119 * timeline between those two indices. The total duration for live
...@@ -129,18 +128,18 @@ ...@@ -129,18 +128,18 @@
129 * @return {number} the duration between the start index and end 128 * @return {number} the duration between the start index and end
130 * index. 129 * index.
131 */ 130 */
132 duration = function(playlist, endSequence, includeTrailingTime) { 131 export const duration = function(playlist, endSequence, includeTrailingTime) {
133 if (!playlist) { 132 if (!playlist) {
134 return 0; 133 return 0;
135 } 134 }
136 135
137 if (includeTrailingTime === undefined) { 136 if (typeof includeTrailingTime === 'undefined') {
138 includeTrailingTime = true; 137 includeTrailingTime = true;
139 } 138 }
140 139
141 // if a slice of the total duration is not requested, use 140 // if a slice of the total duration is not requested, use
142 // playlist-level duration indicators when they're present 141 // playlist-level duration indicators when they're present
143 if (endSequence === undefined) { 142 if (typeof endSequence === 'undefined') {
144 // if present, use the duration specified in the playlist 143 // if present, use the duration specified in the playlist
145 if (playlist.totalDuration) { 144 if (playlist.totalDuration) {
146 return playlist.totalDuration; 145 return playlist.totalDuration;
...@@ -153,12 +152,14 @@ ...@@ -153,12 +152,14 @@
153 } 152 }
154 153
155 // calculate the total duration based on the segment durations 154 // calculate the total duration based on the segment durations
156 return intervalDuration(playlist, 155 return intervalDuration(
156 playlist,
157 endSequence, 157 endSequence,
158 includeTrailingTime); 158 includeTrailingTime
159 }; 159 );
160 };
160 161
161 /** 162 /**
162 * Calculates the interval of time that is currently seekable in a 163 * Calculates the interval of time that is currently seekable in a
163 * playlist. The returned time ranges are relative to the earliest 164 * playlist. The returned time ranges are relative to the earliest
164 * moment in the specified playlist that is still available. A full 165 * moment in the specified playlist that is still available. A full
...@@ -169,30 +170,32 @@ ...@@ -169,30 +170,32 @@
169 * @return {TimeRanges} the periods of time that are valid targets 170 * @return {TimeRanges} the periods of time that are valid targets
170 * for seeking 171 * for seeking
171 */ 172 */
172 seekable = function(playlist) { 173 export const seekable = function(playlist) {
173 var start, end; 174 let start;
175 let end;
174 176
175 // without segments, there are no seekable ranges 177 // without segments, there are no seekable ranges
176 if (!playlist.segments) { 178 if (!playlist.segments) {
177 return videojs.createTimeRange(); 179 return createTimeRange();
178 } 180 }
179 // when the playlist is complete, the entire duration is seekable 181 // when the playlist is complete, the entire duration is seekable
180 if (playlist.endList) { 182 if (playlist.endList) {
181 return videojs.createTimeRange(0, duration(playlist)); 183 return createTimeRange(0, duration(playlist));
182 } 184 }
183 185
184 // live playlists should not expose three segment durations worth 186 // live playlists should not expose three segment durations worth
185 // of content from the end of the playlist 187 // of content from the end of the playlist
186 // https://tools.ietf.org/html/draft-pantos-http-live-streaming-16#section-6.3.3 188 // https://tools.ietf.org/html/draft-pantos-http-live-streaming-16#section-6.3.3
187 start = intervalDuration(playlist, playlist.mediaSequence); 189 start = intervalDuration(playlist, playlist.mediaSequence);
188 end = intervalDuration(playlist, 190 end = intervalDuration(
189 playlist.mediaSequence + Math.max(0, playlist.segments.length - 3)); 191 playlist,
190 return videojs.createTimeRange(start, end); 192 playlist.mediaSequence + Math.max(0, playlist.segments.length - 3)
191 }; 193 );
192 194 return createTimeRange(start, end);
193 // exports 195 };
194 videojs.Hls.Playlist = { 196
195 duration: duration, 197 // exports
196 seekable: seekable 198 export default {
197 }; 199 duration,
198 })(window, window.videojs); 200 seekable
201 };
......
1 import document from 'global/document';
2 /* eslint-disable max-len */
3 /**
4 * Constructs a new URI by interpreting a path relative to another
5 * URI.
6 * @param basePath {string} a relative or absolute URI
7 * @param path {string} a path part to combine with the base
8 * @return {string} a URI that is equivalent to composing `base`
9 * with `path`
10 * @see http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
11 */
12 /* eslint-enable max-len */
13 const resolveUrl = function(basePath, path) {
14 // use the base element to get the browser to handle URI resolution
15 let oldBase = document.querySelector('base');
16 let docHead = document.querySelector('head');
17 let a = document.createElement('a');
18 let base = oldBase;
19 let oldHref;
20 let result;
21
22 // prep the document
23 if (oldBase) {
24 oldHref = oldBase.href;
25 } else {
26 base = docHead.appendChild(document.createElement('base'));
27 }
28
29 base.href = basePath;
30 a.href = path;
31 result = a.href;
32
33 // clean up
34 if (oldBase) {
35 oldBase.href = oldHref;
36 } else {
37 docHead.removeChild(base);
38 }
39 return result;
40 };
41
42 export default resolveUrl;
...@@ -2,6 +2,10 @@ import m3u8 from './m3u8'; ...@@ -2,6 +2,10 @@ import m3u8 from './m3u8';
2 import Stream from './stream'; 2 import Stream from './stream';
3 import videojs from 'video.js'; 3 import videojs from 'video.js';
4 import {Decrypter, decrypt, AsyncStream} from './decrypter'; 4 import {Decrypter, decrypt, AsyncStream} from './decrypter';
5 import Playlist from './playlist';
6 import PlaylistLoader from './playlist-loader';
7 import xhr from './xhr';
8
5 9
6 if(typeof window.videojs.Hls === 'undefined') { 10 if(typeof window.videojs.Hls === 'undefined') {
7 videojs.Hls = {}; 11 videojs.Hls = {};
...@@ -11,3 +15,6 @@ videojs.m3u8 = m3u8; ...@@ -11,3 +15,6 @@ videojs.m3u8 = m3u8;
11 videojs.Hls.decrypt = decrypt; 15 videojs.Hls.decrypt = decrypt;
12 videojs.Hls.Decrypter = Decrypter; 16 videojs.Hls.Decrypter = Decrypter;
13 videojs.Hls.AsyncStream = AsyncStream; 17 videojs.Hls.AsyncStream = AsyncStream;
18 videojs.Hls.xhr = xhr;
19 videojs.Hls.Playlist = Playlist;
20 videojs.Hls.PlaylistLoader = PlaylistLoader;
......
1 (function(videojs) { 1 /**
2 'use strict';
3
4 /**
5 * A wrapper for videojs.xhr that tracks bandwidth. 2 * A wrapper for videojs.xhr that tracks bandwidth.
6 */ 3 */
7 videojs.Hls.xhr = function(options, callback) { 4 import {xhr as videojsXHR, mergeOptions} from 'video.js';
5 const xhr = function(options, callback) {
8 // Add a default timeout for all hls requests 6 // Add a default timeout for all hls requests
9 options = videojs.mergeOptions({ 7 options = mergeOptions({
10 timeout: 45e3 8 timeout: 45e3
11 }, options); 9 }, options);
12 10
13 var request = videojs.xhr(options, function(error, response) { 11 let request = videojsXHR(options, function(error, response) {
14 if (!error && request.response) { 12 if (!error && request.response) {
15 request.responseTime = (new Date()).getTime(); 13 request.responseTime = (new Date()).getTime();
16 request.roundTripTime = request.responseTime - request.requestTime; 14 request.roundTripTime = request.responseTime - request.requestTime;
17 request.bytesReceived = request.response.byteLength || request.response.length; 15 request.bytesReceived = request.response.byteLength || request.response.length;
18 if (!request.bandwidth) { 16 if (!request.bandwidth) {
19 request.bandwidth = Math.floor((request.bytesReceived / request.roundTripTime) * 8 * 1000); 17 request.bandwidth =
18 Math.floor((request.bytesReceived / request.roundTripTime) * 8 * 1000);
20 } 19 }
21 } 20 }
22 21
23 // videojs.xhr now uses a specific code on the error object to signal that a request has 22 // videojs.xhr now uses a specific code
23 // on the error object to signal that a request has
24 // timed out errors of setting a boolean on the request object 24 // timed out errors of setting a boolean on the request object
25 if (error || request.timedout) { 25 if (error || request.timedout) {
26 request.timedout = request.timedout || (error.code === 'ETIMEDOUT'); 26 request.timedout = request.timedout || (error.code === 'ETIMEDOUT');
...@@ -35,8 +35,10 @@ ...@@ -35,8 +35,10 @@
35 response.statusCode !== 200 && 35 response.statusCode !== 200 &&
36 response.statusCode !== 206 && 36 response.statusCode !== 206 &&
37 response.statusCode !== 0) { 37 response.statusCode !== 0) {
38 error = new Error('XHR Failed with a response of: ' + 38 error = new Error(
39 (request && (request.response || request.responseText))); 39 'XHR Failed with a response of: ' +
40 (request && (request.response || request.responseText))
41 );
40 } 42 }
41 43
42 callback(error, request); 44 callback(error, request);
...@@ -44,5 +46,6 @@ ...@@ -44,5 +46,6 @@
44 46
45 request.requestTime = (new Date()).getTime(); 47 request.requestTime = (new Date()).getTime();
46 return request; 48 return request;
47 }; 49 };
48 })(window.videojs); 50
51 export default xhr;
......
...@@ -16,16 +16,11 @@ ...@@ -16,16 +16,11 @@
16 <script src="/node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js"></script> 16 <script src="/node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js"></script>
17 17
18 <script src="/src/videojs-contrib-hls.js"></script> 18 <script src="/src/videojs-contrib-hls.js"></script>
19 <script src="/src/xhr.js"></script>
20 <script src="/dist/videojs-contrib-hls.js"></script> 19 <script src="/dist/videojs-contrib-hls.js"></script>
21 <script src="/src/playlist.js"></script>
22 <script src="/src/playlist-loader.js"></script>
23 <script src="/src/bin-utils.js"></script> 20 <script src="/src/bin-utils.js"></script>
24 21
25 <script src="/test/videojs-contrib-hls.test.js"></script> 22 <script src="/test/videojs-contrib-hls.test.js"></script>
26 <script src="/dist-test/videojs-contrib-hls.js"></script> 23 <script src="/dist-test/videojs-contrib-hls.js"></script>
27 <script src="/test/playlist.test.js"></script>
28 <script src="/test/playlist-loader.test.js"></script>
29 24
30 </body> 25 </body>
31 </html> 26 </html>
......
...@@ -16,11 +16,8 @@ var DEFAULTS = { ...@@ -16,11 +16,8 @@ var DEFAULTS = {
16 16
17 // these two stub old functionality 17 // these two stub old functionality
18 'src/videojs-contrib-hls.js', 18 'src/videojs-contrib-hls.js',
19 'src/xhr.js',
20 'dist/videojs-contrib-hls.js', 19 'dist/videojs-contrib-hls.js',
21 20
22 'src/playlist.js',
23 'src/playlist-loader.js',
24 'src/bin-utils.js', 21 'src/bin-utils.js',
25 22
26 'test/stub.test.js', 23 'test/stub.test.js',
...@@ -45,7 +42,7 @@ var DEFAULTS = { ...@@ -45,7 +42,7 @@ var DEFAULTS = {
45 ], 42 ],
46 43
47 preprocessors: { 44 preprocessors: {
48 'test/{decrypter,stub,m3u8}.test.js': ['browserify'] 45 'test/{playlist*,decrypter,stub,m3u8}.test.js': ['browserify']
49 }, 46 },
50 47
51 reporters: ['dots'], 48 reporters: ['dots'],
......