Skip to content

Commit

Permalink
feat: support suggestedPresentationDelay in DASH manifests
Browse files Browse the repository at this point in the history
This is a port of #698 against master.
  • Loading branch information
gkatsev committed Feb 4, 2020
1 parent 5a483dc commit 96f0f3f
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 10 deletions.
5 changes: 3 additions & 2 deletions src/master-playlist-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -1004,7 +1004,8 @@ export class MasterPlaylistController extends videojs.EventTarget {
return;
}

const mainSeekable = Hls.Playlist.seekable(media, expired);
const suggestedPresentationDelay = this.masterPlaylistLoader_.master.suggestedPresentationDelay;
const mainSeekable = Hls.Playlist.seekable(media, expired, suggestedPresentationDelay);

if (mainSeekable.length === 0) {
return;
Expand All @@ -1018,7 +1019,7 @@ export class MasterPlaylistController extends videojs.EventTarget {
return;
}

audioSeekable = Hls.Playlist.seekable(media, expired);
audioSeekable = Hls.Playlist.seekable(media, expired, suggestedPresentationDelay);

if (audioSeekable.length === 0) {
return;
Expand Down
39 changes: 31 additions & 8 deletions src/playlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,21 +217,36 @@ export const sumDurations = function(playlist, startIndex, endIndex) {
* window which is the duration of the last segment plus 2 target durations from the end
* of the playlist.
*
* A liveEdgePadding can be provided which will be used instead of calculating the safe live edge.
* This corresponds to suggestedPresentationDelay in DASH manifests.
*
* @param {Object} playlist
* a media playlist object
* @param {number} [liveEdgePadding]
* A number in seconds indicating how far from the end we want to be.
* If provided, this value is used instead of calculating the safe live index from the target durations.
* Corresponds to suggestedPresentationDelay in DASH manifests.
* @return {number}
* The media index of the segment at the safe live point. 0 if there is no "safe"
* point.
* @function safeLiveIndex
*/
export const safeLiveIndex = function(playlist) {
export const safeLiveIndex = function(playlist, liveEdgePadding) {
if (!playlist.segments.length) {
return 0;
}

let i = playlist.segments.length - 1;
let distanceFromEnd = playlist.segments[i].duration || playlist.targetDuration;
const safeDistance = distanceFromEnd + playlist.targetDuration * 2;
let i = playlist.segments.length;
const lastSegmentDuration = playlist.segments[i - 1].duration || playlist.targetDuration;
const safeDistance = typeof liveEdgePadding === 'number' ?
liveEdgePadding :
lastSegmentDuration + playlist.targetDuration * 2;

if (safeDistance === 0) {
return i;
}

let distanceFromEnd = 0;

while (i--) {
distanceFromEnd += playlist.segments[i].duration;
Expand All @@ -254,10 +269,16 @@ export const safeLiveIndex = function(playlist) {
* playlist end calculation should consider the safe live end
* (truncate the playlist end by three segments). This is normally
* used for calculating the end of the playlist's seekable range.
* This takes into account the value of liveEdgePadding.
* Setting liveEdgePadding to 0 is equivalent to setting this to false.
* @param {number} liveEdgePadding a number indicating how far from the end of the playlist we should be in seconds.
* If this is provided, it is used in the safe live end calculation.
* Setting useSafeLiveEnd=false or liveEdgePadding=0 are equivalent.
* Corresponds to suggestedPresentationDelay in DASH manifests.
* @return {number} the end time of playlist
* @function playlistEnd
*/
export const playlistEnd = function(playlist, expired, useSafeLiveEnd) {
export const playlistEnd = function(playlist, expired, useSafeLiveEnd, liveEdgePadding) {
if (!playlist || !playlist.segments) {
return null;
}
Expand All @@ -271,7 +292,7 @@ export const playlistEnd = function(playlist, expired, useSafeLiveEnd) {

expired = expired || 0;

const endSequence = useSafeLiveEnd ? safeLiveIndex(playlist) : playlist.segments.length;
const endSequence = useSafeLiveEnd ? safeLiveIndex(playlist, liveEdgePadding) : playlist.segments.length;

return intervalDuration(
playlist,
Expand All @@ -292,13 +313,15 @@ export const playlistEnd = function(playlist, expired, useSafeLiveEnd) {
* dropped off the front of the playlist in a live scenario
* @param {number=} expired the amount of time that has
* dropped off the front of the playlist in a live scenario
* @param {number} liveEdgePadding how far from the end of the playlist we should be in seconds.
* Corresponds to suggestedPresentationDelay in DASH manifests.
* @return {TimeRanges} the periods of time that are valid targets
* for seeking
*/
export const seekable = function(playlist, expired) {
export const seekable = function(playlist, expired, liveEdgePadding) {
const useSafeLiveEnd = true;
const seekableStart = expired || 0;
const seekableEnd = playlistEnd(playlist, expired, useSafeLiveEnd);
const seekableEnd = playlistEnd(playlist, expired, useSafeLiveEnd, liveEdgePadding);

if (seekableEnd === null) {
return createTimeRange();
Expand Down
2 changes: 2 additions & 0 deletions test/master-playlist-controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2111,6 +2111,7 @@ QUnit.test(
let mainTimeRanges = [];
let audioTimeRanges = [];

this.masterPlaylistController.masterPlaylistLoader_.master = {};
this.masterPlaylistController.masterPlaylistLoader_.media = () => mainMedia;
this.masterPlaylistController.syncController_.getExpiredTime = () => 0;

Expand Down Expand Up @@ -2243,6 +2244,7 @@ QUnit.test(
Playlist.seekable = () => {
return videojs.createTimeRanges(mainTimeRanges);
};
this.masterPlaylistController.masterPlaylistLoader_.master = {};
this.masterPlaylistController.masterPlaylistLoader_.media = () => media;
this.masterPlaylistController.syncController_.getExpiredTime = () => 0;

Expand Down
142 changes: 142 additions & 0 deletions test/playlist.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,148 @@ QUnit.test('safeLiveIndex is 0 when no safe live point', function(assert) {
);
});

QUnit.test('safeLiveIndex accounts for liveEdgePadding in simple case', function(assert) {
const playlist = {
targetDuration: 6,
mediaSequence: 10,
syncInfo: {
time: 0,
mediaSequence: 10
},
segments: [
{
duration: 6
},
{
duration: 6
},
{
duration: 6
},
{
duration: 6
},
{
duration: 6
},
{
duration: 6
}
]
};

assert.equal(
Playlist.safeLiveIndex(playlist, 36), 0,
'returns 0 when liveEdgePadding is 30 and duration is 6'
);

assert.equal(
Playlist.safeLiveIndex(playlist, 30), 1,
'returns 1 when liveEdgePadding is 30 and duration is 6'
);

assert.equal(
Playlist.safeLiveIndex(playlist, 24), 2,
'returns 2 when liveEdgePadding is 24 and duration is 6'
);

assert.equal(
Playlist.safeLiveIndex(playlist, 18), 3,
'returns 3 when liveEdgePadding is 18 and duration is 6'
);

assert.equal(
Playlist.safeLiveIndex(playlist, 12), 4,
'returns 4 when liveEdgePadding is 12 and duration is 6'
);

assert.equal(
Playlist.safeLiveIndex(playlist, 6), 5,
'returns 5 when liveEdgePadding is 6 and duration is 6'
);

assert.equal(
Playlist.safeLiveIndex(playlist, 0), 6,
'returns 6 when liveEdgePadding is 0 and duration is 6'
);
});

QUnit.test('safeLiveIndex accounts for liveEdgePadding in non-simple case', function(assert) {
const playlist = {
targetDuration: 6,
mediaSequence: 10,
syncInfo: {
time: 0,
mediaSequence: 10
},
segments: [
{
duration: 3
},
{
duration: 6
},
{
duration: 6
},
{
duration: 3
},
{
duration: 3
},
{
duration: 0.5
}
]
};

assert.equal(
Playlist.safeLiveIndex(playlist, 24), 0,
'returns 0 when liveEdgePadding is 24'
);

assert.equal(
Playlist.safeLiveIndex(playlist, 18), 1,
'returns 1 when liveEdgePadding is 18'
);

assert.equal(
Playlist.safeLiveIndex(playlist, 12), 2,
'returns 2 when liveEdgePadding is 12'
);

assert.equal(
Playlist.safeLiveIndex(playlist, 6), 3,
'returns 3 when liveEdgePadding is 6'
);

assert.equal(
Playlist.safeLiveIndex(playlist, 4), 3,
'returns 3 when liveEdgePadding is 4'
);

assert.equal(
Playlist.safeLiveIndex(playlist, 1), 4,
'returns 4 when liveEdgePadding is 1'
);

assert.equal(
Playlist.safeLiveIndex(playlist, 0.5), 5,
'returns 5 when liveEdgePadding is 0.5'
);

assert.equal(
Playlist.safeLiveIndex(playlist, 0.25), 5,
'returns 5 when liveEdgePadding is 0.25'
);

assert.equal(
Playlist.safeLiveIndex(playlist, 0), 6,
'returns 6 when liveEdgePadding is 0'
);
});

QUnit.test(
'seekable end and playlist end account for non-zero starting VOD media sequence',
function(assert) {
Expand Down

0 comments on commit 96f0f3f

Please sign in to comment.