forked from video-dev/hls.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
level-helper.js
139 lines (126 loc) · 4.93 KB
/
level-helper.js
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
/**
* Level Helper class, providing methods dealing with playlist sliding and drift
*/
import {logger} from '../utils/logger';
class LevelHelper {
static mergeDetails(oldDetails,newDetails) {
var start = Math.max(oldDetails.startSN,newDetails.startSN)-newDetails.startSN,
end = Math.min(oldDetails.endSN,newDetails.endSN)-newDetails.startSN,
delta = newDetails.startSN - oldDetails.startSN,
oldfragments = oldDetails.fragments,
newfragments = newDetails.fragments,
ccOffset =0,
PTSFrag;
// check if old/new playlists have fragments in common
if ( end < start) {
newDetails.PTSKnown = false;
return;
}
// loop through overlapping SN and update startPTS , cc, and duration if any found
for(var i = start ; i <= end ; i++) {
var oldFrag = oldfragments[delta+i],
newFrag = newfragments[i];
if (newFrag && oldFrag) {
ccOffset = oldFrag.cc - newFrag.cc;
if (!isNaN(oldFrag.startPTS)) {
newFrag.start = newFrag.startPTS = oldFrag.startPTS;
newFrag.endPTS = oldFrag.endPTS;
newFrag.duration = oldFrag.duration;
PTSFrag = newFrag;
}
}
}
if(ccOffset) {
logger.log(`discontinuity sliding from playlist, take drift into account`);
for(i = 0 ; i < newfragments.length ; i++) {
newfragments[i].cc += ccOffset;
}
}
// if at least one fragment contains PTS info, recompute PTS information for all fragments
if(PTSFrag) {
LevelHelper.updateFragPTSDTS(newDetails,PTSFrag.sn,PTSFrag.startPTS,PTSFrag.endPTS,PTSFrag.startDTS,PTSFrag.endDTS);
} else {
// ensure that delta is within oldfragments range
// also adjust sliding in case delta is 0 (we could have old=[50-60] and new=old=[50-61])
// in that case we also need to adjust start offset of all fragments
if (delta >= 0 && delta < oldfragments.length) {
// adjust start by sliding offset
var sliding = oldfragments[delta].start;
for(i = 0 ; i < newfragments.length ; i++) {
newfragments[i].start += sliding;
}
}
}
// if we are here, it means we have fragments overlapping between
// old and new level. reliable PTS info is thus relying on old level
newDetails.PTSKnown = oldDetails.PTSKnown;
return;
}
static updateFragPTSDTS(details,sn,startPTS,endPTS,startDTS,endDTS) {
var fragIdx, fragments, frag, i;
// exit if sn out of range
if (sn < details.startSN || sn > details.endSN) {
return 0;
}
fragIdx = sn - details.startSN;
fragments = details.fragments;
frag = fragments[fragIdx];
if(!isNaN(frag.startPTS)) {
// delta PTS between audio and video
let deltaPTS = Math.abs(frag.startPTS-startPTS);
if (isNaN(frag.deltaPTS)) {
frag.deltaPTS = deltaPTS;
} else {
frag.deltaPTS = Math.max(deltaPTS,frag.deltaPTS);
}
startPTS = Math.min(startPTS,frag.startPTS);
endPTS = Math.max(endPTS, frag.endPTS);
startDTS = Math.min(startDTS,frag.startDTS);
endDTS = Math.max(endDTS, frag.endDTS);
}
var drift = startPTS - frag.start;
frag.start = frag.startPTS = startPTS;
frag.endPTS = endPTS;
frag.startDTS = startDTS;
frag.endDTS = endDTS;
frag.duration = endPTS - startPTS;
// adjust fragment PTS/duration from seqnum-1 to frag 0
for(i = fragIdx ; i > 0 ; i--) {
LevelHelper.updatePTS(fragments,i,i-1);
}
// adjust fragment PTS/duration from seqnum to last frag
for(i = fragIdx ; i < fragments.length - 1 ; i++) {
LevelHelper.updatePTS(fragments,i,i+1);
}
details.PTSKnown = true;
//logger.log(` frag start/end:${startPTS.toFixed(3)}/${endPTS.toFixed(3)}`);
return drift;
}
static updatePTS(fragments,fromIdx, toIdx) {
var fragFrom = fragments[fromIdx],fragTo = fragments[toIdx], fragToPTS = fragTo.startPTS;
// if we know startPTS[toIdx]
if(!isNaN(fragToPTS)) {
// update fragment duration.
// it helps to fix drifts between playlist reported duration and fragment real duration
if (toIdx > fromIdx) {
fragFrom.duration = fragToPTS-fragFrom.start;
if(fragFrom.duration < 0) {
logger.error(`negative duration computed for frag ${fragFrom.sn},level ${fragFrom.level}, there should be some duration drift between playlist and fragment!`);
}
} else {
fragTo.duration = fragFrom.start - fragToPTS;
if(fragTo.duration < 0) {
logger.error(`negative duration computed for frag ${fragTo.sn},level ${fragTo.level}, there should be some duration drift between playlist and fragment!`);
}
}
} else {
// we dont know startPTS[toIdx]
if (toIdx > fromIdx) {
fragTo.start = fragFrom.start + fragFrom.duration;
} else {
fragTo.start = fragFrom.start - fragTo.duration;
}
}
}
}
export default LevelHelper;