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
Showing
12 changed files
with
121 additions
and
77 deletions
... | @@ -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: {}, | ... | ... |
This diff is collapsed.
Click to expand it.
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 | }; | ... | ... |
src/resolve-url.js
0 → 100644
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'], | ... | ... |
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
-
Please register or sign in to post a comment