Skip to content

Commit

Permalink
fix: use partIndex and segmentIndex for syncPoints/getMediaInfoForTime
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonocasey committed May 6, 2021
1 parent a842b84 commit 4f842e6
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 70 deletions.
83 changes: 57 additions & 26 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 {Object} durationList list to iterate over for durations.
* @param {number} defaultDuration duration to use for elements before or after the durationList
* @param {number} startIndex partsAndSegments index to start
* @param {number} 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}) {
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 @@ -374,30 +375,54 @@ export const seekable = function(playlist, expired, liveEdgePadding) {
* @param {number} startTime
* @return {Object}
*/
export const getMediaInfoForTime = function(
export const getMediaInfoForTime = function({
playlist,
currentTime,
startIndex,
segmentIndex,
partIndex,
startTime
) {
}) {

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

let startIndex;

for (let i = 0; i < partsAndSegments.length; i++) {
const partAndSegment = partsAndSegments[i];

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

// skip this if part index does not match.
if (typeof partIndex === 'number' && typeof partAndSegment.partIndex === 'number' && partIndex !== 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_FUDGE_FACTOR);
time += (partAndSegment.duration + TIME_FUDGE_FACTOR);

if (time > 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 @@ -406,8 +431,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 @@ -420,7 +445,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 @@ -431,22 +457,27 @@ 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_FUDGE_FACTOR;
time -= (partAndSegment.duration + TIME_FUDGE_FACTOR);

if (time < 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,
partIndex: partsAndSegments[partsAndSegments.length - 1].partIndex,
startTime: currentTime
};
Expand Down
19 changes: 10 additions & 9 deletions src/segment-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -1396,17 +1396,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_(),
partIndex: this.syncPoint_.partIndex,
segmentIndex: 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
92 changes: 57 additions & 35 deletions src/sync-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,10 @@
* @file sync-controller.js
*/

import {sumDurations} from './playlist';
import {sumDurations, getPartsAndSegments} 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 All @@ -37,15 +31,15 @@ export const syncPointStrategies = [
return null;
}

const segments = playlist.segments || [];
let syncPoint = null;
let lastDistance = null;
const partsAndSegments = getPartsAndSegments(playlist);

currentTime = currentTime || 0;

for (let i = 0; i < segments.length; i++) {
const segmentIndex = getSegmentIndex(i, playlist, currentTime);
const segment = segments[segmentIndex];
for (let i = 0; i < partsAndSegments.length; i++) {
const index = (playlist.endList || currentTime === 0) ? i : partsAndSegments.length - (i + 1);
const partAndSegment = partsAndSegments[index];
const segment = partAndSegment.segment;
const datetimeMapping =
syncController.timelineToDatetimeMappings[segment.timeline];

Expand All @@ -67,7 +61,8 @@ export const syncPointStrategies = [
lastDistance = distance;
syncPoint = {
time: segmentStart,
segmentIndex
segmentIndex: partAndSegment.segmentIndex,
partIndex: partAndSegment.partIndex
};
}
}
Expand All @@ -79,15 +74,16 @@ export const syncPointStrategies = [
{
name: 'Segment',
run: (syncController, playlist, duration, currentTimeline, currentTime) => {
const segments = playlist.segments || [];
let syncPoint = null;
let lastDistance = null;

currentTime = currentTime || 0;
const partsAndSegments = getPartsAndSegments(playlist);

for (let i = 0; i < segments.length; i++) {
const segmentIndex = getSegmentIndex(i, playlist, currentTime);
const segment = segments[segmentIndex];
for (let i = 0; i < partsAndSegments.length; i++) {
const index = (playlist.endList || currentTime === 0) ? i : partsAndSegments.length - (i + 1);
const partAndSegment = partsAndSegments[index];
const segment = partAndSegment.segment;

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

Expand Down Expand Up @@ -142,7 +139,8 @@ export const syncPointStrategies = [
lastDistance = distance;
syncPoint = {
time: discontinuitySync.time,
segmentIndex
segmentIndex,
partIndex: null
};
}
}
Expand Down Expand Up @@ -255,7 +253,12 @@ export default class SyncController extends videojs.EventTarget {
syncPoint.time *= -1;
}

return Math.abs(syncPoint.time + sumDurations(playlist, syncPoint.segmentIndex, 0));
return Math.abs(syncPoint.time + sumDurations({
defaultDuration: playlist.targetDuration,
durationList: playlist.segments,
startIndex: syncPoint.segmentIndex,
endIndex: 0
}));
}

/**
Expand Down Expand Up @@ -330,7 +333,9 @@ export default class SyncController extends videojs.EventTarget {

this.logger_(`syncPoint for [${target.key}: ${target.value}] chosen with strategy` +
` [${bestStrategy}]: [time:${bestSyncPoint.time},` +
` segmentIndex:${bestSyncPoint.segmentIndex}]`);
` segmentIndex:${bestSyncPoint.segmentIndex}` +
(typeof partIndex === 'number' ? `,partIndex:${bestSyncPoint.partIndex}` : '') +
']');

return bestSyncPoint;
}
Expand Down Expand Up @@ -457,8 +462,12 @@ export default class SyncController extends videojs.EventTarget {
* Returns false if segment time mapping could not be calculated
*/
calculateSegmentTimeMapping_(segmentInfo, timingInfo, shouldSaveTimelineMapping) {
// TODO: a calculate function, shouldn't have side effects...
const segment = segmentInfo.segment;
const part = segmentInfo.part;
let mappingObj = this.timelines[segmentInfo.timeline];
let start;
let end;

if (typeof segmentInfo.timestampOffset === 'number') {
mappingObj = {
Expand All @@ -473,15 +482,26 @@ export default class SyncController extends videojs.EventTarget {
`[time: ${mappingObj.time}] [mapping: ${mappingObj.mapping}]`);
}

segment.start = segmentInfo.startOfSegment;
segment.end = timingInfo.end + mappingObj.mapping;
start = segmentInfo.startOfSegment;
end = timingInfo.end + mappingObj.mapping;

} else if (mappingObj) {
segment.start = timingInfo.start + mappingObj.mapping;
segment.end = timingInfo.end + mappingObj.mapping;
start = timingInfo.start + mappingObj.mapping;
end = timingInfo.end + mappingObj.mapping;
} else {
return false;
}

if (part) {
part.start = start;
part.end = end;
}

if (!segment.start) {
segment.start = start;
}
segment.end = end;

return true;
}

Expand Down Expand Up @@ -519,17 +539,19 @@ export default class SyncController extends videojs.EventTarget {
let time;

if (mediaIndexDiff < 0) {
time = segment.start - sumDurations(
playlist,
segmentInfo.mediaIndex,
segmentIndex
);
time = segment.start - sumDurations({
defaultDuration: playlist.targetDuration,
durationList: playlist.segments,
startIndex: segmentInfo.mediaIndex,
endIndex: segmentIndex
});
} else {
time = segment.end + sumDurations(
playlist,
segmentInfo.mediaIndex + 1,
segmentIndex
);
time = segment.end + sumDurations({
defaultDuration: playlist.targetDuration,
durationList: playlist.segments,
startIndex: segmentInfo.mediaIndex + 1,
endIndex: segmentIndex
});
}

this.discontinuities[discontinuity] = {
Expand Down

0 comments on commit 4f842e6

Please sign in to comment.