timelineTimeParser.js
3.97 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
126
127
128
129
/**
* Calculates the R (repetition) value for a live stream (for the final segment
* in a manifest where the r value is negative 1)
*
* @param {Object} attributes
* Object containing all inherited attributes from parent elements with attribute
* names as keys
* @param {number} time
* current time (typically the total time up until the final segment)
* @param {number} duration
* duration property for the given <S />
*
* @return {number}
* R value to reach the end of the given period
*/
const getLiveRValue = (attributes, time, duration) => {
const {
NOW,
clientOffset,
availabilityStartTime,
timescale = 1,
start = 0,
minimumUpdatePeriod = 0
} = attributes;
const now = (NOW + clientOffset) / 1000;
const periodStartWC = availabilityStartTime + start;
const periodEndWC = now + minimumUpdatePeriod;
const periodDuration = periodEndWC - periodStartWC;
return Math.ceil(((periodDuration * timescale) - time) / duration);
};
/**
* Uses information provided by SegmentTemplate.SegmentTimeline to determine segment
* timing and duration
*
* @param {Object} attributes
* Object containing all inherited attributes from parent elements with attribute
* names as keys
* @param {Object[]} segmentTimeline
* List of objects representing the attributes of each S element contained within
*
* @return {{number: number, duration: number, time: number, timeline: number}[]}
* List of Objects with segment timing and duration info
*/
export const parseByTimeline = (attributes, segmentTimeline) => {
const {
type,
minimumUpdatePeriod = 0,
media = '',
sourceDuration,
timescale = 1,
startNumber = 1,
periodIndex: timeline
} = attributes;
const segments = [];
let time = -1;
for (let sIndex = 0; sIndex < segmentTimeline.length; sIndex++) {
const S = segmentTimeline[sIndex];
const duration = S.d;
const repeat = S.r || 0;
const segmentTime = S.t || 0;
if (time < 0) {
// first segment
time = segmentTime;
}
if (segmentTime && segmentTime > time) {
// discontinuity
// TODO: How to handle this type of discontinuity
// timeline++ here would treat it like HLS discontuity and content would
// get appended without gap
// E.G.
// <S t="0" d="1" />
// <S d="1" />
// <S d="1" />
// <S t="5" d="1" />
// would have $Time$ values of [0, 1, 2, 5]
// should this be appened at time positions [0, 1, 2, 3],(#EXT-X-DISCONTINUITY)
// or [0, 1, 2, gap, gap, 5]? (#EXT-X-GAP)
// does the value of sourceDuration consider this when calculating arbitrary
// negative @r repeat value?
// E.G. Same elements as above with this added at the end
// <S d="1" r="-1" />
// with a sourceDuration of 10
// Would the 2 gaps be included in the time duration calculations resulting in
// 8 segments with $Time$ values of [0, 1, 2, 5, 6, 7, 8, 9] or 10 segments
// with $Time$ values of [0, 1, 2, 5, 6, 7, 8, 9, 10, 11] ?
time = segmentTime;
}
let count;
if (repeat < 0) {
const nextS = sIndex + 1;
if (nextS === segmentTimeline.length) {
// last segment
if (type === 'dynamic' &&
minimumUpdatePeriod > 0 &&
media.indexOf('$Number$') > 0) {
count = getLiveRValue(attributes, time, duration);
} else {
// TODO: This may be incorrect depending on conclusion of TODO above
count = ((sourceDuration * timescale) - time) / duration;
}
} else {
count = (segmentTimeline[nextS].t - time) / duration;
}
} else {
count = repeat + 1;
}
const end = startNumber + segments.length + count;
let number = startNumber + segments.length;
while (number < end) {
segments.push({ number, duration: duration / timescale, time, timeline });
time += duration;
number++;
}
}
return segments;
};