playlist.js
6.61 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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/**
* Playlist related utilities.
*/
(function(window, videojs) {
'use strict';
var DEFAULT_TARGET_DURATION = 10;
var duration, seekable, segmentsDuration;
/**
* Calculate the media duration from the segments associated with a
* playlist. The duration of a subinterval of the available segments
* may be calculated by specifying a start and end index.
*
* @param playlist {object} a media playlist object
* @param startSequence {number} (optional) an inclusive lower
* boundary for the playlist. Defaults to 0.
* @param endSequence {number} (optional) an exclusive upper boundary
* for the playlist. Defaults to playlist length.
* @param strict {boolean} (optional) if true, the interval between
* the final segment and the subsequent segment will not be included
* in the result
* @return {number} the duration between the start index and end
* index.
*/
segmentsDuration = function(playlist, startSequence, endSequence, strict) {
var targetDuration, i, j, segment, endSegment, expiredSegmentCount, result = 0;
startSequence = startSequence || 0;
i = startSequence;
endSequence = endSequence !== undefined ? endSequence : (playlist.segments || []).length;
targetDuration = playlist.targetDuration || DEFAULT_TARGET_DURATION;
// estimate expired segment duration using the target duration
expiredSegmentCount = Math.max(playlist.mediaSequence - startSequence, 0);
result += expiredSegmentCount * targetDuration;
i += expiredSegmentCount;
// accumulate the segment durations into the result
for (; i < endSequence; i++) {
segment = playlist.segments[i - playlist.mediaSequence];
// when PTS values aren't available, use information from the playlist
if (segment.minVideoPts === undefined) {
result += segment.duration ||
targetDuration;
continue;
}
// find the last segment with PTS info and use that to calculate
// the interval duration
for(j = i; j < endSequence - 1; j++) {
endSegment = playlist.segments[j - playlist.mediaSequence + 1];
if (endSegment.maxVideoPts === undefined ||
endSegment.discontinuity) {
break;
}
}
endSegment = playlist.segments[j - playlist.mediaSequence];
result += (Math.max(endSegment.maxVideoPts, endSegment.maxAudioPts) -
Math.min(segment.minVideoPts, segment.minAudioPts)) * 0.001;
i = j;
}
// attribute the gap between the latest PTS value in end segment
// and the earlier PTS in the next one to the result
segment = playlist.segments[endSequence - 1];
endSegment = playlist.segments[endSequence];
if (!strict &&
endSegment &&
!endSegment.discontinuity &&
endSegment.minVideoPts &&
segment &&
segment.maxVideoPts) {
result += (Math.min(endSegment.minVideoPts, endSegment.minAudioPts) -
Math.max(segment.maxVideoPts, segment.maxAudioPts)) * 0.001;
}
return result;
};
/**
* Calculates the duration of a playlist. If a start and end index
* are specified, the duration will be for the subset of the media
* timeline between those two indices. The total duration for live
* playlists is always Infinity.
* @param playlist {object} a media playlist object
* @param startSequence {number} (optional) an inclusive lower
* boundary for the playlist. Defaults to 0.
* @param endSequence {number} (optional) an exclusive upper boundary
* for the playlist. Defaults to playlist length.
* @param strict {boolean} (optional) if true, the interval between
* the final segment and the subsequent segment will not be included
* in the result
* @return {number} the duration between the start index and end
* index.
*/
duration = function(playlist, startSequence, endSequence, strict) {
if (!playlist) {
return 0;
}
// if a slice of the total duration is not requested, use
// playlist-level duration indicators when they're present
if (startSequence === undefined && endSequence === undefined) {
// if present, use the duration specified in the playlist
if (playlist.totalDuration) {
return playlist.totalDuration;
}
// duration should be Infinity for live playlists
if (!playlist.endList) {
return window.Infinity;
}
}
// calculate the total duration based on the segment durations
return segmentsDuration(playlist,
startSequence,
endSequence,
strict);
};
/**
* Calculates the interval of time that is currently seekable in a
* playlist.
* @param playlist {object} a media playlist object
* @return {TimeRanges} the periods of time that are valid targets
* for seeking
*/
seekable = function(playlist) {
var start, end, liveBuffer, targetDuration, segment, pending, i;
// without segments, there are no seekable ranges
if (!playlist.segments) {
return videojs.createTimeRange();
}
// when the playlist is complete, the entire duration is seekable
if (playlist.endList) {
return videojs.createTimeRange(0, duration(playlist));
}
start = segmentsDuration(playlist, 0, playlist.mediaSequence);
end = start + segmentsDuration(playlist,
playlist.mediaSequence,
playlist.mediaSequence + playlist.segments.length);
targetDuration = playlist.targetDuration || DEFAULT_TARGET_DURATION;
// live playlists should not expose three segment durations worth
// of content from the end of the playlist
// https://tools.ietf.org/html/draft-pantos-http-live-streaming-16#section-6.3.3
if (!playlist.endList) {
liveBuffer = targetDuration * 3;
// walk backward from the last available segment and track how
// much media time has elapsed until three target durations have
// been traversed. if a segment is part of the interval being
// reported, subtract the overlapping portion of its duration
// from the result.
for (i = playlist.segments.length - 1; i >= 0 && liveBuffer > 0; i--) {
segment = playlist.segments[i];
pending = Math.min(segment.preciseDuration ||
segment.duration ||
targetDuration,
liveBuffer);
liveBuffer -= pending;
end -= pending;
}
}
return videojs.createTimeRange(start, end);
};
// exports
videojs.Hls.Playlist = {
duration: duration,
seekable: seekable
};
})(window, window.videojs);