158 lines
4.8 KiB
JavaScript
158 lines
4.8 KiB
JavaScript
import errors from '../errors';
|
|
import urlTypeConverter from './urlType';
|
|
import { parseByDuration } from './durationTimeParser';
|
|
import window from 'global/window';
|
|
|
|
/**
|
|
* Translates SegmentBase into a set of segments.
|
|
* (DASH SPEC Section 5.3.9.3.2) contains a set of <SegmentURL> nodes. Each
|
|
* node should be translated into segment.
|
|
*
|
|
* @param {Object} attributes
|
|
* Object containing all inherited attributes from parent elements with attribute
|
|
* names as keys
|
|
* @return {Object.<Array>} list of segments
|
|
*/
|
|
export const segmentsFromBase = (attributes) => {
|
|
const {
|
|
baseUrl,
|
|
initialization = {},
|
|
sourceDuration,
|
|
indexRange = '',
|
|
periodStart,
|
|
presentationTime,
|
|
number = 0,
|
|
duration
|
|
} = attributes;
|
|
|
|
// base url is required for SegmentBase to work, per spec (Section 5.3.9.2.1)
|
|
if (!baseUrl) {
|
|
throw new Error(errors.NO_BASE_URL);
|
|
}
|
|
|
|
const initSegment = urlTypeConverter({
|
|
baseUrl,
|
|
source: initialization.sourceURL,
|
|
range: initialization.range
|
|
});
|
|
|
|
const segment = urlTypeConverter({ baseUrl, source: baseUrl, indexRange });
|
|
|
|
segment.map = initSegment;
|
|
|
|
// If there is a duration, use it, otherwise use the given duration of the source
|
|
// (since SegmentBase is only for one total segment)
|
|
if (duration) {
|
|
const segmentTimeInfo = parseByDuration(attributes);
|
|
|
|
if (segmentTimeInfo.length) {
|
|
segment.duration = segmentTimeInfo[0].duration;
|
|
segment.timeline = segmentTimeInfo[0].timeline;
|
|
}
|
|
} else if (sourceDuration) {
|
|
segment.duration = sourceDuration;
|
|
segment.timeline = periodStart;
|
|
}
|
|
|
|
// If presentation time is provided, these segments are being generated by SIDX
|
|
// references, and should use the time provided. For the general case of SegmentBase,
|
|
// there should only be one segment in the period, so its presentation time is the same
|
|
// as its period start.
|
|
segment.presentationTime = presentationTime || periodStart;
|
|
segment.number = number;
|
|
|
|
return [segment];
|
|
};
|
|
|
|
/**
|
|
* Given a playlist, a sidx box, and a baseUrl, update the segment list of the playlist
|
|
* according to the sidx information given.
|
|
*
|
|
* playlist.sidx has metadadata about the sidx where-as the sidx param
|
|
* is the parsed sidx box itself.
|
|
*
|
|
* @param {Object} playlist the playlist to update the sidx information for
|
|
* @param {Object} sidx the parsed sidx box
|
|
* @return {Object} the playlist object with the updated sidx information
|
|
*/
|
|
export const addSidxSegmentsToPlaylist = (playlist, sidx, baseUrl) => {
|
|
// Retain init segment information
|
|
const initSegment = playlist.sidx.map ? playlist.sidx.map : null;
|
|
// Retain source duration from initial main manifest parsing
|
|
const sourceDuration = playlist.sidx.duration;
|
|
// Retain source timeline
|
|
const timeline = playlist.timeline || 0;
|
|
const sidxByteRange = playlist.sidx.byterange;
|
|
const sidxEnd = sidxByteRange.offset + sidxByteRange.length;
|
|
// Retain timescale of the parsed sidx
|
|
const timescale = sidx.timescale;
|
|
// referenceType 1 refers to other sidx boxes
|
|
const mediaReferences = sidx.references.filter(r => r.referenceType !== 1);
|
|
const segments = [];
|
|
const type = playlist.endList ? 'static' : 'dynamic';
|
|
const periodStart = playlist.sidx.timeline;
|
|
let presentationTime = periodStart;
|
|
let number = playlist.mediaSequence || 0;
|
|
|
|
// firstOffset is the offset from the end of the sidx box
|
|
let startIndex;
|
|
|
|
// eslint-disable-next-line
|
|
if (typeof sidx.firstOffset === 'bigint') {
|
|
startIndex = window.BigInt(sidxEnd) + sidx.firstOffset;
|
|
} else {
|
|
startIndex = sidxEnd + sidx.firstOffset;
|
|
}
|
|
|
|
for (let i = 0; i < mediaReferences.length; i++) {
|
|
const reference = sidx.references[i];
|
|
// size of the referenced (sub)segment
|
|
const size = reference.referencedSize;
|
|
// duration of the referenced (sub)segment, in the timescale
|
|
// this will be converted to seconds when generating segments
|
|
const duration = reference.subsegmentDuration;
|
|
// should be an inclusive range
|
|
let endIndex;
|
|
|
|
// eslint-disable-next-line
|
|
if (typeof startIndex === 'bigint') {
|
|
endIndex = startIndex + window.BigInt(size) - window.BigInt(1);
|
|
} else {
|
|
endIndex = startIndex + size - 1;
|
|
}
|
|
const indexRange = `${startIndex}-${endIndex}`;
|
|
|
|
const attributes = {
|
|
baseUrl,
|
|
timescale,
|
|
timeline,
|
|
periodStart,
|
|
presentationTime,
|
|
number,
|
|
duration,
|
|
sourceDuration,
|
|
indexRange,
|
|
type
|
|
};
|
|
|
|
const segment = segmentsFromBase(attributes)[0];
|
|
|
|
if (initSegment) {
|
|
segment.map = initSegment;
|
|
}
|
|
|
|
segments.push(segment);
|
|
if (typeof startIndex === 'bigint') {
|
|
startIndex += window.BigInt(size);
|
|
} else {
|
|
startIndex += size;
|
|
}
|
|
presentationTime += duration / timescale;
|
|
number++;
|
|
}
|
|
|
|
playlist.segments = segments;
|
|
|
|
return playlist;
|
|
};
|