Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: llhls syncing fixes #1125

Merged
merged 36 commits into from
May 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3d39612
feat: Use ll-hls query directives and support skipping segments
brandonocasey Feb 11, 2021
5e46a1e
Merge branch 'main' into feat/llhls-3
brandonocasey Apr 7, 2021
df957d4
Merge branch 'main' into feat/llhls-3
brandonocasey Apr 7, 2021
ff40332
unified manifest parse function
brandonocasey Apr 8, 2021
53ec980
tests for llhls and query directives in playlist-loader
brandonocasey Apr 8, 2021
addf841
Merge branch 'main' into feat/llhls-3
brandonocasey Apr 8, 2021
285a4ae
Merge branch 'main' into feat/llhls-3
brandonocasey Apr 26, 2021
a46f0a7
remove unused option
brandonocasey Apr 26, 2021
9d1fc06
add duration to preload segment
brandonocasey Apr 27, 2021
00a8e25
always add a part target duration when we have parts
brandonocasey Apr 28, 2021
1ff48b0
query directive fixes
brandonocasey Apr 28, 2021
3ff694a
small logging fix, bring map to skipped segments
brandonocasey Apr 28, 2021
d3fb477
pare down changes
brandonocasey Apr 28, 2021
2854967
cover all scenarios
brandonocasey Apr 29, 2021
a842b84
fix logging and merging issues
brandonocasey Apr 29, 2021
4f842e6
fix: use partIndex and segmentIndex for syncPoints/getMediaInfoForTime
brandonocasey Apr 30, 2021
0205771
fix tests
brandonocasey May 7, 2021
402de98
tests
brandonocasey May 7, 2021
2c5080c
tests
brandonocasey May 7, 2021
6899546
Update src/playlist.js
brandonocasey May 25, 2021
00fd02c
code review
brandonocasey May 25, 2021
842650c
add question mark
brandonocasey May 25, 2021
4c19976
Update src/playlist.js
brandonocasey May 26, 2021
e079124
Update src/playlist.js
brandonocasey May 26, 2021
112d029
Update src/sync-controller.js
brandonocasey May 26, 2021
d0679f6
Update src/sync-controller.js
brandonocasey May 26, 2021
b3141c1
Update test/playlist.test.js
brandonocasey May 26, 2021
99dd646
Update test/playlist.test.js
brandonocasey May 26, 2021
ac9de31
code review
brandonocasey May 26, 2021
6c32f9b
Merge branch 'main' into fix/llhls-fixes
brandonocasey May 26, 2021
941bec9
Merge remote-tracking branch 'origin/main' into fix/llhls-fixes
brandonocasey May 26, 2021
99ee6e6
revert main merge changes
brandonocasey May 26, 2021
680d740
add comment for segment.start timing info
brandonocasey May 26, 2021
65da7f2
Merge remote-tracking branch 'origin/main' into fix/llhls-fixes
brandonocasey May 26, 2021
944df45
Update src/sync-controller.js
brandonocasey May 26, 2021
18d06c5
Update src/sync-controller.js
brandonocasey May 26, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 64 additions & 31 deletions src/playlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ const {createTimeRange} = videojs;
*
* @return {Array} The part/segment list.
*/
const getPartsAndSegments = (playlist) => (playlist.segments || []).reduce((acc, segment, si) => {
export const getPartsAndSegments = (playlist) => (playlist.segments || []).reduce((acc, segment, si) => {
if (segment.parts) {
segment.parts.forEach(function(part, pi) {
acc.push({duration: part.duration, segmentIndex: si, partIndex: pi});
acc.push({duration: part.duration, segmentIndex: si, partIndex: pi, part, segment});
});
} else {
acc.push({duration: segment.duration, segmentIndex: si, partIndex: null});
acc.push({duration: segment.duration, segmentIndex: si, partIndex: null, segment, part: null});
}
return acc;
}, []);
Expand Down Expand Up @@ -261,12 +261,13 @@ export const duration = function(playlist, endSequence, expired) {
* playlist in which case, the targetDuration of the playlist is used
* to approximate the durations of the segments
*
* @param {Object} playlist a media playlist object
* @param {number} startIndex
* @param {number} endIndex
* @param {Array} options.durationList list to iterate over for durations.
* @param {number} options.defaultDuration duration to use for elements before or after the durationList
* @param {number} options.startIndex partsAndSegments index to start
* @param {number} options.endIndex partsAndSegments index to end.
* @return {number} the number of seconds between startIndex and endIndex
*/
export const sumDurations = function(playlist, startIndex, endIndex) {
export const sumDurations = function({defaultDuration, durationList, startIndex, endIndex}) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changes to allow passing in a list and a default duration. This allows us to use partsAndSegments, just parts, or just segments

let durations = 0;

if (startIndex > endIndex) {
Expand All @@ -275,13 +276,13 @@ export const sumDurations = function(playlist, startIndex, endIndex) {

if (startIndex < 0) {
for (let i = startIndex; i < Math.min(0, endIndex); i++) {
durations += playlist.targetDuration;
durations += defaultDuration;
}
startIndex = 0;
}

for (let i = startIndex; i < endIndex; i++) {
durations += playlist.segments[i].duration;
durations += durationList[i].duration;
}

return durations;
Expand Down Expand Up @@ -367,38 +368,64 @@ export const seekable = function(playlist, expired, liveEdgePadding) {
* Determine the index and estimated starting time of the segment that
* contains a specified playback position in a media playlist.
*
* @param {Object} playlist the media playlist to query
* @param {number} currentTime The number of seconds since the earliest
* @param {Object} options.playlist the media playlist to query
* @param {number} options.currentTime The number of seconds since the earliest
* possible position to determine the containing segment for
* @param {number} startIndex
* @param {number} startTime
* @return {Object}
* @param {number} options.startTime the time when the segment/part starts
* @param {number} options.startingSegmentIndex the segment index to start looking at.
* @param {number?} [options.startingPartIndex] the part index to look at within the segment.
*
* @return {Object} an object with partIndex, segmentIndex, and startTime.
*/
export const getMediaInfoForTime = function(
export const getMediaInfoForTime = function({
brandonocasey marked this conversation as resolved.
Show resolved Hide resolved
playlist,
currentTime,
startIndex,
startingSegmentIndex,
startingPartIndex,
startTime
) {
}) {

const partsAndSegments = getPartsAndSegments(playlist);
let time = currentTime - startTime;
const partsAndSegments = getPartsAndSegments(playlist);

let startIndex = 0;

for (let i = 0; i < partsAndSegments.length; i++) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to find the partAndSegment index for the specified segment/part

const partAndSegment = partsAndSegments[i];

if (startingSegmentIndex !== partAndSegment.segmentIndex) {
continue;
}

// skip this if part index does not match.
if (typeof startingPartIndex === 'number' && typeof partAndSegment.partIndex === 'number' && startingPartIndex !== partAndSegment.partIndex) {
continue;
}

startIndex = i;
break;
}

if (time < 0) {
// Walk backward from startIndex in the playlist, adding durations
// until we find a segment that contains `time` and return it
if (startIndex > 0) {
for (let i = startIndex - 1; i >= 0; i--) {
const segment = partsAndSegments[i];
const partAndSegment = partsAndSegments[i];

time += segment.duration;
time += partAndSegment.duration;

// TODO: consider not using TIME_FUDGE_FACTOR at all here
if ((time + TIME_FUDGE_FACTOR) > 0) {
return {
mediaIndex: segment.segmentIndex,
startTime: startTime - sumDurations(playlist, startIndex, segment.segmentIndex),
partIndex: segment.partIndex
partIndex: partAndSegment.partIndex,
segmentIndex: partAndSegment.segmentIndex,
startTime: startTime - sumDurations({
defaultDuration: playlist.targetDuration,
durationList: partsAndSegments,
startIndex,
endIndex: i
})
};
}
}
Expand All @@ -407,8 +434,8 @@ export const getMediaInfoForTime = function(
// We were unable to find a good segment within the playlist
// so select the first segment
return {
mediaIndex: partsAndSegments[0] && partsAndSegments[0].segmentIndex || 0,
partIndex: partsAndSegments[0] && partsAndSegments[0].partIndex || null,
segmentIndex: partsAndSegments[0] && partsAndSegments[0].segmentIndex || 0,
startTime: currentTime
};
}
Expand All @@ -421,7 +448,8 @@ export const getMediaInfoForTime = function(
time -= playlist.targetDuration;
if (time < 0) {
return {
mediaIndex: partsAndSegments[0].segmentIndex,
partIndex: partsAndSegments[0] && partsAndSegments[0].partIndex || null,
segmentIndex: partsAndSegments[0] && partsAndSegments[0].segmentIndex || 0,
startTime: currentTime
};
}
Expand All @@ -432,23 +460,28 @@ export const getMediaInfoForTime = function(
// Walk forward from startIndex in the playlist, subtracting durations
// until we find a segment that contains `time` and return it
for (let i = startIndex; i < partsAndSegments.length; i++) {
const partSegment = partsAndSegments[i];
const partAndSegment = partsAndSegments[i];

time -= partSegment.duration;
time -= partAndSegment.duration;

// TODO: consider not using TIME_FUDGE_FACTOR at all here
if ((time - TIME_FUDGE_FACTOR) < 0) {
return {
mediaIndex: partSegment.segmentIndex,
startTime: startTime + sumDurations(playlist, startIndex, partSegment.segmentIndex),
partIndex: partSegment.partIndex
partIndex: partAndSegment.partIndex,
segmentIndex: partAndSegment.segmentIndex,
startTime: startTime + sumDurations({
defaultDuration: playlist.targetDuration,
durationList: partsAndSegments,
startIndex,
endIndex: i
})
};
}
}

// We are out of possible candidates so load the last one...
return {
mediaIndex: partsAndSegments[partsAndSegments.length - 1].segmentIndex,
segmentIndex: partsAndSegments[partsAndSegments.length - 1].segmentIndex,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed to segmentIndex as that is actually what this is.

partIndex: partsAndSegments[partsAndSegments.length - 1].partIndex,
startTime: currentTime
};
Expand Down
21 changes: 11 additions & 10 deletions src/segment-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -1400,17 +1400,18 @@ export default class SegmentLoader extends videojs.EventTarget {
}
} else {
// Find the segment containing the end of the buffer or current time.
const mediaSourceInfo = Playlist.getMediaInfoForTime(
this.playlist_,
this.fetchAtBuffer_ ? bufferedEnd : this.currentTime_(),
this.syncPoint_.segmentIndex,
this.syncPoint_.time
);
const {segmentIndex, startTime, partIndex} = Playlist.getMediaInfoForTime({
playlist: this.playlist_,
currentTime: this.fetchAtBuffer_ ? bufferedEnd : this.currentTime_(),
startingPartIndex: this.syncPoint_.partIndex,
startingSegmentIndex: this.syncPoint_.segmentIndex,
startTime: this.syncPoint_.time
});

next.getMediaInfoForTime = this.fetchAtBuffer_ ? 'bufferedEnd' : 'currentTime';
next.mediaIndex = mediaSourceInfo.mediaIndex;
next.startOfSegment = mediaSourceInfo.startTime;
next.partIndex = mediaSourceInfo.partIndex;
next.mediaIndex = segmentIndex;
next.startOfSegment = startTime;
next.partIndex = partIndex;
}

const nextSegment = segments[next.mediaIndex];
Expand Down Expand Up @@ -1479,7 +1480,7 @@ export default class SegmentLoader extends videojs.EventTarget {
// The timeline that the segment is in
timeline: segment.timeline,
// The expected duration of the segment in seconds
duration: segment.duration,
duration: part && part.duration || segment.duration,
// retain the segment in case the playlist updates while doing an async process
segment,
part,
Expand Down
Loading