651d468e by David LaPalomento

Merge pull request #229 from videojs/live-start-time

Live start time tweaks
2 parents 7744312d 8028f6b0
......@@ -39,7 +39,7 @@
"karma-sauce-launcher": "~0.1.8",
"qunitjs": "^1.15.0",
"sinon": "1.10.2",
"video.js": "^4.9.0"
"video.js": "^4.12.0"
},
"dependencies": {
"pkcs7": "^0.2.2",
......
......@@ -164,6 +164,16 @@ videojs.Hls.prototype.src = function(src) {
});
}
// Start live playlists 30 seconds before the current time
// This is done using the old playlist because of a race condition
// where the playlist selected below may not be loaded quickly
// enough to have its segments available for review. When we receive
// a loadedplaylist event, we will call translateMediaIndex and
// maintain our position at the live point.
if (this.duration() === Infinity && this.mediaIndex === 0) {
this.mediaIndex = videojs.Hls.getMediaIndexForLive_(oldMediaPlaylist);
}
selectedPlaylist = this.selectPlaylist();
oldBitrate = oldMediaPlaylist.attributes &&
oldMediaPlaylist.attributes.BANDWIDTH || 0;
......@@ -179,11 +189,6 @@ videojs.Hls.prototype.src = function(src) {
segmentDlTime = Infinity;
}
// start live playlists 30 seconds before the current time
if (this.duration() === Infinity && this.mediaIndex === 0 && selectedPlaylist.segments) {
this.mediaIndex = videojs.Hls.setMediaIndexForLive(selectedPlaylist);
}
// this threshold is to account for having a high latency on the manifest
// request which is a somewhat small file.
threshold = 10;
......@@ -239,7 +244,14 @@ videojs.Hls.prototype.src = function(src) {
});
};
videojs.Hls.setMediaIndexForLive = function(selectedPlaylist) {
/* Returns the media index for the live point in the current playlist, and updates
the current time to go along with it.
*/
videojs.Hls.getMediaIndexForLive_ = function(selectedPlaylist) {
if (!selectedPlaylist.segments) {
return 0;
}
var tailIterator = selectedPlaylist.segments.length,
tailDuration = 0,
targetTail = (selectedPlaylist.targetDuration || 10) * 3;
......@@ -279,6 +291,11 @@ videojs.Hls.prototype.play = function() {
this.mediaIndex = 0;
}
if (this.duration() === Infinity && this.playlists.media() && !this.player().hasClass('vjs-has-started')) {
this.mediaIndex = videojs.Hls.getMediaIndexForLive_(this.playlists.media());
this.setCurrentTime(this.getCurrentTimeByMediaIndex_(this.playlists.media(), this.mediaIndex));
}
// delegate back to the Flash implementation
return videojs.Flash.prototype.play.apply(this, arguments);
};
......@@ -936,7 +953,7 @@ videojs.Hls.translateMediaIndex = function(mediaIndex, original, update) {
if (translatedMediaIndex >= update.segments.length || translatedMediaIndex < 0) {
// recalculate the live point if the streams are too far out of sync
return videojs.Hls.setMediaIndexForLive(update) + 1;
return videojs.Hls.getMediaIndexForLive_(update) + 1;
}
// sync on media sequence
......@@ -974,6 +991,29 @@ videojs.Hls.getMediaIndexByTime = function(playlist, time) {
};
/**
* Determine the current time in seconds in one playlist by a media index. This
* function iterates through the segments of a playlist up to the specified index
* and then returns the time up to that point.
*
* @param playlist {object} The playlist of the segments being searched.
* @param mediaIndex {number} The index of the target segment in the playlist.
* @returns {number} The current time to that point, or 0 if none appropriate.
*/
videojs.Hls.prototype.getCurrentTimeByMediaIndex_ = function(playlist, mediaIndex) {
var index, time = 0;
if (!playlist.segments || mediaIndex === 0) {
return 0;
}
for (index = 0; index < mediaIndex; index++) {
time += playlist.segments[index].duration;
}
return time;
};
/**
* A comparator function to sort two playlist object by bandwidth.
* @param left {object} a media playlist object
* @param right {object} a media playlist object
......
......@@ -1249,6 +1249,22 @@ test('live playlist starts 30s before live', function() {
strictEqual(player.hls.mediaIndex, 6, 'mediaIndex is updated after the reload');
});
test('live playlist starts with correct currentTime value', function() {
player.src({
src: 'http://example.com/manifest/liveStart30sBefore.m3u8',
type: 'application/vnd.apple.mpegurl'
});
openMediaSource(player);
standardXHRResponse(requests[0]);
player.hls.playlists.trigger('loadedmetadata');
player.hls.play();
strictEqual(player.currentTime(), 70, 'currentTime is updated at playback');
});
test('mediaIndex is zero before the first segment loads', function() {
window.manifests['first-seg-load'] =
'#EXTM3U\n' +
......@@ -1720,6 +1736,7 @@ test('calling play() at the end of a video resets the media index', function() {
player.hls.ended = function() {
return true;
};
player.play();
strictEqual(player.hls.mediaIndex, 0, 'index is 1 after the first segment');
});
......