Skip to content

Commit

Permalink
fix: better time to first frame for live playlists (#1105)
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonocasey authored Apr 5, 2021
1 parent 1b990f1 commit 1e94680
Show file tree
Hide file tree
Showing 3 changed files with 1,040 additions and 1,276 deletions.
70 changes: 18 additions & 52 deletions src/playlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,53 +213,6 @@ export const sumDurations = function(playlist, startIndex, endIndex) {
return durations;
};

/**
* Determines the media index of the segment corresponding to the safe edge of the live
* 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, liveEdgePadding) {
if (!playlist.segments.length) {
return 0;
}

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;

if (distanceFromEnd >= safeDistance) {
break;
}
}

return Math.max(0, i);
};

/**
* Calculates the playlist end time
*
Expand Down Expand Up @@ -293,13 +246,27 @@ export const playlistEnd = function(playlist, expired, useSafeLiveEnd, liveEdgeP

expired = expired || 0;

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

return intervalDuration(
let lastSegmentTime = intervalDuration(
playlist,
playlist.mediaSequence + endSequence,
playlist.mediaSequence + playlist.segments.length,
expired
);

// "The client SHALL choose which Media Segment to play first from the
// Media Playlist when playback starts. If the EXT-X-ENDLIST tag is not
// present and the client intends to play the media normally, the client
// SHOULD NOT choose a segment which starts less than three target
// durations from the end of the Playlist file. Doing so can trigger
// playback stalls."
// https://tools.ietf.org/html/draft-pantos-http-live-streaming-23#section-6.3.3
if (useSafeLiveEnd) {
liveEdgePadding = typeof liveEdgePadding === 'number' ? liveEdgePadding : playlist.targetDuration * 3;
lastSegmentTime -= liveEdgePadding;

}

// don't return a time less than zero
return Math.max(0, lastSegmentTime);
};

/**
Expand Down Expand Up @@ -545,7 +512,6 @@ export const isLowestEnabledRendition = (master, media) => {
export default {
duration,
seekable,
safeLiveIndex,
getMediaInfoForTime,
isEnabled,
isDisabled,
Expand Down
16 changes: 12 additions & 4 deletions src/sync-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import {sumDurations} from './playlist';
import videojs from 'video.js';
import logger from './util/logger';

const getSegmentIndex = (i, playlist, currentTime = 0) => {
const segments = playlist.segments;

return (playlist.endList || currentTime === 0) ? i : segments.length - (i + 1);
};

export const syncPointStrategies = [
// Stategy "VOD": Handle the VOD-case where the sync-point is *always*
// the equivalence display-time 0 === segment-index 0
Expand Down Expand Up @@ -38,7 +44,8 @@ export const syncPointStrategies = [
currentTime = currentTime || 0;

for (let i = 0; i < segments.length; i++) {
const segment = segments[i];
const segmentIndex = getSegmentIndex(i, playlist, currentTime);
const segment = segments[segmentIndex];
const datetimeMapping =
syncController.timelineToDatetimeMappings[segment.timeline];

Expand All @@ -60,7 +67,7 @@ export const syncPointStrategies = [
lastDistance = distance;
syncPoint = {
time: segmentStart,
segmentIndex: i
segmentIndex
};
}
}
Expand All @@ -79,7 +86,8 @@ export const syncPointStrategies = [
currentTime = currentTime || 0;

for (let i = 0; i < segments.length; i++) {
const segment = segments[i];
const segmentIndex = getSegmentIndex(i, playlist, currentTime);
const segment = segments[segmentIndex];

if (segment.timeline === currentTimeline &&
typeof segment.start !== 'undefined') {
Expand All @@ -95,7 +103,7 @@ export const syncPointStrategies = [
lastDistance = distance;
syncPoint = {
time: segment.start,
segmentIndex: i
segmentIndex
};
}

Expand Down
Loading

0 comments on commit 1e94680

Please sign in to comment.