segmentBase.js
3.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import errors from '../errors';
import urlTypeConverter from './urlType';
import { parseByDuration } from './durationTimeParser';
/**
* 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 = '',
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 = 0;
}
// This is used for mediaSequence
segment.number = 0;
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';
// firstOffset is the offset from the end of the sidx box
let 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
const endIndex = startIndex + size - 1;
const indexRange = `${startIndex}-${endIndex}`;
const attributes = {
baseUrl,
timescale,
timeline,
// this is used in parseByDuration
periodIndex: timeline,
duration,
sourceDuration,
indexRange,
type
};
const segment = segmentsFromBase(attributes)[0];
if (initSegment) {
segment.map = initSegment;
}
segments.push(segment);
startIndex += size;
}
playlist.segments = segments;
return playlist;
};