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 @@
<script src="/node_modules/video.js/dist/video.js"></script>
<script src="/node_modules/videojs-contrib-media-sources/dist/videojs-media-sources.js"></script>
<script src="/src/videojs-contrib-hls.js"></script>
<script src="/src/xhr.js"></script>
<script src="/dist/videojs-contrib-hls.js"></script>
<script src="/src/playlist.js"></script>
<script src="/src/playlist-loader.js"></script>
<script src="/src/bin-utils.js"></script>
<script>
(function(window, videojs) {
......
......@@ -2,7 +2,7 @@ var browserify = require('browserify');
var fs = require('fs');
var glob = require('glob');
glob('test/{decryper,m3u8,stub}.test.js', function(err, files) {
glob('test/{playlist*,decryper,m3u8,stub}.test.js', function(err, files) {
browserify(files)
.transform('babelify')
.bundle()
......
......@@ -3,7 +3,7 @@ var fs = require('fs');
var glob = require('glob');
var watchify = require('watchify');
glob('test/{decrypter,m3u8,stub}.test.js', function(err, files) {
glob('test/{playlist*,decrypter,m3u8,stub}.test.js', function(err, files) {
var b = browserify(files, {
cache: {},
packageCache: {},
......
/**
* Playlist related utilities.
*/
(function(window, videojs) {
'use strict';
import {createTimeRange} from 'video.js';
var duration, intervalDuration, backwardDuration, forwardDuration, seekable;
backwardDuration = function(playlist, endSequence) {
var result = 0, segment, i;
i = endSequence - playlist.mediaSequence;
const backwardDuration = function(playlist, endSequence) {
let result = 0;
let i = endSequence - playlist.mediaSequence;
// if a start time is available for segment immediately following
// the interval, use it
segment = playlist.segments[i];
let segment = playlist.segments[i];
// Walk backward until we find the latest segment with timeline
// information that is earlier than endSequence
if (segment) {
if (segment.start !== undefined) {
if (typeof segment.start !== 'undefined') {
return { result: segment.start, precise: true };
}
if (segment.end !== undefined) {
if (typeof segment.end !== 'undefined') {
return {
result: segment.end - segment.duration,
precise: true
......@@ -28,28 +25,29 @@
}
while (i--) {
segment = playlist.segments[i];
if (segment.end !== undefined) {
if (typeof segment.end !== 'undefined') {
return { result: result + segment.end, precise: true };
}
result += segment.duration;
if (segment.start !== undefined) {
if (typeof segment.start !== 'undefined') {
return { result: result + segment.start, precise: true };
}
}
return { result: result, precise: false };
};
forwardDuration = function(playlist, endSequence) {
var result = 0, segment, i;
return { result, precise: false };
};
i = endSequence - playlist.mediaSequence;
const forwardDuration = function(playlist, endSequence) {
let result = 0;
let segment;
let i = endSequence - playlist.mediaSequence;
// Walk forward until we find the earliest segment with timeline
// information
for (; i < playlist.segments.length; i++) {
segment = playlist.segments[i];
if (segment.start !== undefined) {
if (typeof segment.start !== 'undefined') {
return {
result: segment.start - result,
precise: true
......@@ -58,7 +56,7 @@
result += segment.duration;
if (segment.end !== undefined) {
if (typeof segment.end !== 'undefined') {
return {
result: segment.end - result,
precise: true
......@@ -68,9 +66,9 @@
}
// indicate we didn't find a useful duration estimate
return { result: -1, precise: false };
};
};
/**
/**
* Calculate the media duration from the segments associated with a
* playlist. The duration of a subinterval of the available segments
* may be calculated by specifying an end index.
......@@ -81,10 +79,11 @@
* @return {number} the duration between the first available segment
* and end index.
*/
intervalDuration = function(playlist, endSequence) {
var backward, forward;
const intervalDuration = function(playlist, endSequence) {
let backward;
let forward;
if (endSequence === undefined) {
if (typeof endSequence === 'undefined') {
endSequence = playlist.mediaSequence + playlist.segments.length;
}
......@@ -112,9 +111,9 @@
// return the less-precise, playlist-based duration estimate
return backward.result;
};
};
/**
/**
* Calculates the duration of a playlist. If a start and end index
* are specified, the duration will be for the subset of the media
* timeline between those two indices. The total duration for live
......@@ -129,18 +128,18 @@
* @return {number} the duration between the start index and end
* index.
*/
duration = function(playlist, endSequence, includeTrailingTime) {
export const duration = function(playlist, endSequence, includeTrailingTime) {
if (!playlist) {
return 0;
}
if (includeTrailingTime === undefined) {
if (typeof includeTrailingTime === 'undefined') {
includeTrailingTime = true;
}
// if a slice of the total duration is not requested, use
// playlist-level duration indicators when they're present
if (endSequence === undefined) {
if (typeof endSequence === 'undefined') {
// if present, use the duration specified in the playlist
if (playlist.totalDuration) {
return playlist.totalDuration;
......@@ -153,12 +152,14 @@
}
// calculate the total duration based on the segment durations
return intervalDuration(playlist,
return intervalDuration(
playlist,
endSequence,
includeTrailingTime);
};
includeTrailingTime
);
};
/**
/**
* Calculates the interval of time that is currently seekable in a
* playlist. The returned time ranges are relative to the earliest
* moment in the specified playlist that is still available. A full
......@@ -169,30 +170,32 @@
* @return {TimeRanges} the periods of time that are valid targets
* for seeking
*/
seekable = function(playlist) {
var start, end;
export const seekable = function(playlist) {
let start;
let end;
// without segments, there are no seekable ranges
if (!playlist.segments) {
return videojs.createTimeRange();
return createTimeRange();
}
// when the playlist is complete, the entire duration is seekable
if (playlist.endList) {
return videojs.createTimeRange(0, duration(playlist));
return createTimeRange(0, duration(playlist));
}
// live playlists should not expose three segment durations worth
// of content from the end of the playlist
// https://tools.ietf.org/html/draft-pantos-http-live-streaming-16#section-6.3.3
start = intervalDuration(playlist, playlist.mediaSequence);
end = intervalDuration(playlist,
playlist.mediaSequence + Math.max(0, playlist.segments.length - 3));
return videojs.createTimeRange(start, end);
};
// exports
videojs.Hls.Playlist = {
duration: duration,
seekable: seekable
};
})(window, window.videojs);
end = intervalDuration(
playlist,
playlist.mediaSequence + Math.max(0, playlist.segments.length - 3)
);
return createTimeRange(start, end);
};
// exports
export default {
duration,
seekable
};
......
import document from 'global/document';
/* eslint-disable max-len */
/**
* Constructs a new URI by interpreting a path relative to another
* URI.
* @param basePath {string} a relative or absolute URI
* @param path {string} a path part to combine with the base
* @return {string} a URI that is equivalent to composing `base`
* with `path`
* @see http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
*/
/* eslint-enable max-len */
const resolveUrl = function(basePath, path) {
// use the base element to get the browser to handle URI resolution
let oldBase = document.querySelector('base');
let docHead = document.querySelector('head');
let a = document.createElement('a');
let base = oldBase;
let oldHref;
let result;
// prep the document
if (oldBase) {
oldHref = oldBase.href;
} else {
base = docHead.appendChild(document.createElement('base'));
}
base.href = basePath;
a.href = path;
result = a.href;
// clean up
if (oldBase) {
oldBase.href = oldHref;
} else {
docHead.removeChild(base);
}
return result;
};
export default resolveUrl;
......@@ -2,6 +2,10 @@ import m3u8 from './m3u8';
import Stream from './stream';
import videojs from 'video.js';
import {Decrypter, decrypt, AsyncStream} from './decrypter';
import Playlist from './playlist';
import PlaylistLoader from './playlist-loader';
import xhr from './xhr';
if(typeof window.videojs.Hls === 'undefined') {
videojs.Hls = {};
......@@ -11,3 +15,6 @@ videojs.m3u8 = m3u8;
videojs.Hls.decrypt = decrypt;
videojs.Hls.Decrypter = Decrypter;
videojs.Hls.AsyncStream = AsyncStream;
videojs.Hls.xhr = xhr;
videojs.Hls.Playlist = Playlist;
videojs.Hls.PlaylistLoader = PlaylistLoader;
......
(function(videojs) {
'use strict';
/**
/**
* A wrapper for videojs.xhr that tracks bandwidth.
*/
videojs.Hls.xhr = function(options, callback) {
import {xhr as videojsXHR, mergeOptions} from 'video.js';
const xhr = function(options, callback) {
// Add a default timeout for all hls requests
options = videojs.mergeOptions({
options = mergeOptions({
timeout: 45e3
}, options);
var request = videojs.xhr(options, function(error, response) {
let request = videojsXHR(options, function(error, response) {
if (!error && request.response) {
request.responseTime = (new Date()).getTime();
request.roundTripTime = request.responseTime - request.requestTime;
request.bytesReceived = request.response.byteLength || request.response.length;
if (!request.bandwidth) {
request.bandwidth = Math.floor((request.bytesReceived / request.roundTripTime) * 8 * 1000);
request.bandwidth =
Math.floor((request.bytesReceived / request.roundTripTime) * 8 * 1000);
}
}
// videojs.xhr now uses a specific code on the error object to signal that a request has
// videojs.xhr now uses a specific code
// on the error object to signal that a request has
// timed out errors of setting a boolean on the request object
if (error || request.timedout) {
request.timedout = request.timedout || (error.code === 'ETIMEDOUT');
......@@ -35,8 +35,10 @@
response.statusCode !== 200 &&
response.statusCode !== 206 &&
response.statusCode !== 0) {
error = new Error('XHR Failed with a response of: ' +
(request && (request.response || request.responseText)));
error = new Error(
'XHR Failed with a response of: ' +
(request && (request.response || request.responseText))
);
}
callback(error, request);
......@@ -44,5 +46,6 @@
request.requestTime = (new Date()).getTime();
return request;
};
})(window.videojs);
};
export default xhr;
......
......@@ -16,16 +16,11 @@
<script src="/node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js"></script>
<script src="/src/videojs-contrib-hls.js"></script>
<script src="/src/xhr.js"></script>
<script src="/dist/videojs-contrib-hls.js"></script>
<script src="/src/playlist.js"></script>
<script src="/src/playlist-loader.js"></script>
<script src="/src/bin-utils.js"></script>
<script src="/test/videojs-contrib-hls.test.js"></script>
<script src="/dist-test/videojs-contrib-hls.js"></script>
<script src="/test/playlist.test.js"></script>
<script src="/test/playlist-loader.test.js"></script>
</body>
</html>
......
......@@ -16,11 +16,8 @@ var DEFAULTS = {
// these two stub old functionality
'src/videojs-contrib-hls.js',
'src/xhr.js',
'dist/videojs-contrib-hls.js',
'src/playlist.js',
'src/playlist-loader.js',
'src/bin-utils.js',
'test/stub.test.js',
......@@ -45,7 +42,7 @@ var DEFAULTS = {
],
preprocessors: {
'test/{decrypter,stub,m3u8}.test.js': ['browserify']
'test/{playlist*,decrypter,stub,m3u8}.test.js': ['browserify']
},
reporters: ['dots'],
......