hls.js 1.6.0-beta.1.0.canary.10745 → 1.6.0-beta.1.0.canary.10746
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hls.d.mts +36 -10
- package/dist/hls.d.ts +36 -10
- package/dist/hls.js +172 -87
- package/dist/hls.js.d.ts +36 -10
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +171 -86
- package/dist/hls.light.js.map +1 -1
- package/dist/hls.light.min.js +1 -1
- package/dist/hls.light.min.js.map +1 -1
- package/dist/hls.light.mjs +160 -89
- package/dist/hls.light.mjs.map +1 -1
- package/dist/hls.min.js +1 -1
- package/dist/hls.min.js.map +1 -1
- package/dist/hls.mjs +161 -90
- package/dist/hls.mjs.map +1 -1
- package/dist/hls.worker.js +1 -1
- package/dist/hls.worker.js.map +1 -1
- package/package.json +1 -1
- package/src/controller/latency-controller.ts +1 -1
- package/src/controller/stream-controller.ts +1 -1
- package/src/loader/date-range.ts +2 -2
- package/src/loader/fragment.ts +116 -23
- package/src/loader/interstitial-event.ts +8 -5
- package/src/loader/m3u8-parser.ts +49 -56
- package/src/utils/level-helper.ts +22 -15
package/package.json
CHANGED
@@ -33,7 +33,7 @@ export default class LatencyController implements ComponentAPI {
|
|
33
33
|
if (config.liveMaxLatencyDuration !== undefined) {
|
34
34
|
return config.liveMaxLatencyDuration;
|
35
35
|
}
|
36
|
-
const levelDetails = this.hls
|
36
|
+
const levelDetails = this.hls?.latestLevelDetails;
|
37
37
|
return levelDetails
|
38
38
|
? config.liveMaxLatencyDurationCount * levelDetails.targetduration
|
39
39
|
: 0;
|
@@ -11,8 +11,8 @@ import { PlaylistContextType, PlaylistLevelType } from '../types/loader';
|
|
11
11
|
import { ChunkMetadata } from '../types/transmuxer';
|
12
12
|
import { BufferHelper } from '../utils/buffer-helper';
|
13
13
|
import { pickMostCompleteCodecName } from '../utils/codecs';
|
14
|
-
import type Hls from '../hls';
|
15
14
|
import type { FragmentTracker } from './fragment-tracker';
|
15
|
+
import type Hls from '../hls';
|
16
16
|
import type { Fragment, MediaFragment } from '../loader/fragment';
|
17
17
|
import type KeyLoader from '../loader/key-loader';
|
18
18
|
import type { LevelDetails } from '../loader/level-details';
|
package/src/loader/date-range.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { AttrList } from '../utils/attr-list';
|
2
2
|
import { logger } from '../utils/logger';
|
3
|
-
import type {
|
3
|
+
import type { MediaFragmentRef } from './fragment';
|
4
4
|
|
5
5
|
// Avoid exporting const enum so that these values can be inlined
|
6
6
|
const enum DateRangeAttribute {
|
@@ -47,7 +47,7 @@ export function isSCTE35Attribute(attrName: string): boolean {
|
|
47
47
|
|
48
48
|
export class DateRange {
|
49
49
|
public attr: AttrList;
|
50
|
-
public tagAnchor:
|
50
|
+
public tagAnchor: MediaFragmentRef | null;
|
51
51
|
public tagOrder: number;
|
52
52
|
private _startDate: Date;
|
53
53
|
private _endDate?: Date;
|
package/src/loader/fragment.ts
CHANGED
@@ -29,23 +29,27 @@ export type ElementaryStreams = Record<
|
|
29
29
|
ElementaryStreamInfo | null
|
30
30
|
>;
|
31
31
|
|
32
|
+
export type Base = {
|
33
|
+
url: string;
|
34
|
+
};
|
35
|
+
|
32
36
|
export class BaseSegment {
|
33
37
|
private _byteRange: [number, number] | null = null;
|
34
38
|
private _url: string | null = null;
|
39
|
+
private _stats: LoadStats | null = null;
|
40
|
+
private _streams: ElementaryStreams | null = null;
|
35
41
|
|
36
42
|
// baseurl is the URL to the playlist
|
37
|
-
public readonly
|
43
|
+
public readonly base: Base;
|
44
|
+
|
38
45
|
// relurl is the portion of the URL that comes from inside the playlist.
|
39
46
|
public relurl?: string;
|
40
|
-
// Holds the types of data this fragment supports
|
41
|
-
public elementaryStreams: ElementaryStreams = {
|
42
|
-
[ElementaryStreamTypes.AUDIO]: null,
|
43
|
-
[ElementaryStreamTypes.VIDEO]: null,
|
44
|
-
[ElementaryStreamTypes.AUDIOVIDEO]: null,
|
45
|
-
};
|
46
47
|
|
47
|
-
constructor(
|
48
|
-
|
48
|
+
constructor(base: Base | string) {
|
49
|
+
if (typeof base === 'string') {
|
50
|
+
base = { url: base };
|
51
|
+
}
|
52
|
+
this.base = base;
|
49
53
|
}
|
50
54
|
|
51
55
|
// setByteRange converts a EXT-X-BYTERANGE attribute into a two element array
|
@@ -60,8 +64,12 @@ export class BaseSegment {
|
|
60
64
|
this._byteRange = [start, parseInt(params[0]) + start];
|
61
65
|
}
|
62
66
|
|
67
|
+
get baseurl(): string {
|
68
|
+
return this.base.url;
|
69
|
+
}
|
70
|
+
|
63
71
|
get byteRange(): [number, number] | [] {
|
64
|
-
if (
|
72
|
+
if (this._byteRange === null) {
|
65
73
|
return [];
|
66
74
|
}
|
67
75
|
|
@@ -76,6 +84,40 @@ export class BaseSegment {
|
|
76
84
|
return this.byteRange[1];
|
77
85
|
}
|
78
86
|
|
87
|
+
get elementaryStreams(): ElementaryStreams {
|
88
|
+
if (this._streams === null) {
|
89
|
+
this._streams = {
|
90
|
+
[ElementaryStreamTypes.AUDIO]: null,
|
91
|
+
[ElementaryStreamTypes.VIDEO]: null,
|
92
|
+
[ElementaryStreamTypes.AUDIOVIDEO]: null,
|
93
|
+
};
|
94
|
+
}
|
95
|
+
return this._streams;
|
96
|
+
}
|
97
|
+
|
98
|
+
set elementaryStreams(value: ElementaryStreams) {
|
99
|
+
this._streams = value;
|
100
|
+
}
|
101
|
+
|
102
|
+
get hasStats(): boolean {
|
103
|
+
return this._stats !== null;
|
104
|
+
}
|
105
|
+
|
106
|
+
get hasStreams(): boolean {
|
107
|
+
return this._streams !== null;
|
108
|
+
}
|
109
|
+
|
110
|
+
get stats(): LoadStats {
|
111
|
+
if (this._stats === null) {
|
112
|
+
this._stats = new LoadStats();
|
113
|
+
}
|
114
|
+
return this._stats;
|
115
|
+
}
|
116
|
+
|
117
|
+
set stats(value: LoadStats) {
|
118
|
+
this._stats = value;
|
119
|
+
}
|
120
|
+
|
79
121
|
get url(): string {
|
80
122
|
if (!this._url && this.baseurl && this.relurl) {
|
81
123
|
this._url = buildAbsoluteURL(this.baseurl, this.relurl, {
|
@@ -99,16 +141,26 @@ export class BaseSegment {
|
|
99
141
|
|
100
142
|
export interface MediaFragment extends Fragment {
|
101
143
|
sn: number;
|
144
|
+
ref: MediaFragmentRef;
|
102
145
|
}
|
103
146
|
|
147
|
+
export type MediaFragmentRef = {
|
148
|
+
base: Base;
|
149
|
+
start: number;
|
150
|
+
duration: number;
|
151
|
+
sn: number;
|
152
|
+
programDateTime: number | null;
|
153
|
+
};
|
154
|
+
|
104
155
|
/**
|
105
156
|
* Object representing parsed data from an HLS Segment. Found in {@link hls.js#LevelDetails.fragments}.
|
106
157
|
*/
|
107
158
|
export class Fragment extends BaseSegment {
|
108
159
|
private _decryptdata: LevelKey | null = null;
|
160
|
+
private _programDateTime: number | null = null;
|
161
|
+
private _ref: MediaFragmentRef | null = null;
|
109
162
|
|
110
163
|
public rawProgramDateTime: string | null = null;
|
111
|
-
public programDateTime: number | null = null;
|
112
164
|
public tagList: Array<string[]> = [];
|
113
165
|
|
114
166
|
// EXTINF has to be present for a m3u8 to be considered valid
|
@@ -147,8 +199,6 @@ export class Fragment extends BaseSegment {
|
|
147
199
|
public maxStartPTS?: number;
|
148
200
|
// The minimum ending Presentation Time Stamp (audio/video PTS) of the fragment. Set after transmux complete.
|
149
201
|
public minEndPTS?: number;
|
150
|
-
// Load/parse timing information
|
151
|
-
public stats: LoadStats = new LoadStats();
|
152
202
|
// Init Segment bytes (unset for media segments)
|
153
203
|
public data?: Uint8Array;
|
154
204
|
// A flag indicating whether the segment was downloaded in order to test bitrate, and was not buffered
|
@@ -164,8 +214,8 @@ export class Fragment extends BaseSegment {
|
|
164
214
|
// Deprecated
|
165
215
|
public urlId: number = 0;
|
166
216
|
|
167
|
-
constructor(type: PlaylistLevelType,
|
168
|
-
super(
|
217
|
+
constructor(type: PlaylistLevelType, base: Base | string) {
|
218
|
+
super(base);
|
169
219
|
this.type = type;
|
170
220
|
}
|
171
221
|
|
@@ -203,10 +253,6 @@ export class Fragment extends BaseSegment {
|
|
203
253
|
return null;
|
204
254
|
}
|
205
255
|
|
206
|
-
if (!Number.isFinite(this.programDateTime)) {
|
207
|
-
return null;
|
208
|
-
}
|
209
|
-
|
210
256
|
const duration = !Number.isFinite(this.duration) ? 0 : this.duration;
|
211
257
|
|
212
258
|
return this.programDateTime + duration * 1000;
|
@@ -225,10 +271,58 @@ export class Fragment extends BaseSegment {
|
|
225
271
|
return true;
|
226
272
|
}
|
227
273
|
}
|
228
|
-
|
229
274
|
return false;
|
230
275
|
}
|
231
276
|
|
277
|
+
get programDateTime(): number | null {
|
278
|
+
if (this._programDateTime === null && this.rawProgramDateTime) {
|
279
|
+
this.programDateTime = Date.parse(this.rawProgramDateTime);
|
280
|
+
}
|
281
|
+
return this._programDateTime;
|
282
|
+
}
|
283
|
+
|
284
|
+
set programDateTime(value: number | null) {
|
285
|
+
if (!Number.isFinite(value)) {
|
286
|
+
this._programDateTime = this.rawProgramDateTime = null;
|
287
|
+
return;
|
288
|
+
}
|
289
|
+
this._programDateTime = value;
|
290
|
+
}
|
291
|
+
|
292
|
+
get ref(): MediaFragmentRef | null {
|
293
|
+
if (this.sn === 'initSegment') {
|
294
|
+
return null;
|
295
|
+
}
|
296
|
+
if (!this._ref) {
|
297
|
+
this._ref = {
|
298
|
+
base: this.base,
|
299
|
+
start: this.start,
|
300
|
+
duration: this.duration,
|
301
|
+
sn: this.sn,
|
302
|
+
programDateTime: this.programDateTime,
|
303
|
+
};
|
304
|
+
}
|
305
|
+
return this._ref;
|
306
|
+
}
|
307
|
+
|
308
|
+
addStart(value: number) {
|
309
|
+
this.setStart(this.start + value);
|
310
|
+
}
|
311
|
+
|
312
|
+
setStart(value: number) {
|
313
|
+
this.start = value;
|
314
|
+
if (this._ref) {
|
315
|
+
this._ref.start = value;
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
319
|
+
setDuration(value: number) {
|
320
|
+
this.duration = value;
|
321
|
+
if (this._ref) {
|
322
|
+
this._ref.duration = value;
|
323
|
+
}
|
324
|
+
}
|
325
|
+
|
232
326
|
setKeyFormat(keyFormat: KeySystemFormats) {
|
233
327
|
if (this.levelkeys) {
|
234
328
|
const key = this.levelkeys[keyFormat];
|
@@ -282,16 +376,15 @@ export class Part extends BaseSegment {
|
|
282
376
|
public readonly relurl: string;
|
283
377
|
public readonly fragment: MediaFragment;
|
284
378
|
public readonly index: number;
|
285
|
-
public stats: LoadStats = new LoadStats();
|
286
379
|
|
287
380
|
constructor(
|
288
381
|
partAttrs: AttrList,
|
289
382
|
frag: MediaFragment,
|
290
|
-
|
383
|
+
base: Base | string,
|
291
384
|
index: number,
|
292
385
|
previous?: Part,
|
293
386
|
) {
|
294
|
-
super(
|
387
|
+
super(base);
|
295
388
|
this.duration = partAttrs.decimalFloatingPoint('DURATION');
|
296
389
|
this.gap = partAttrs.bool('GAP');
|
297
390
|
this.independent = partAttrs.bool('INDEPENDENT');
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { hash } from '../utils/hash';
|
2
2
|
import type { DateRange, DateRangeCue } from './date-range';
|
3
|
-
import type {
|
3
|
+
import type { MediaFragmentRef } from './fragment';
|
4
4
|
import type { Loader, LoaderContext } from '../types/loader';
|
5
5
|
|
6
6
|
export const ALIGNED_END_THRESHOLD_SECONDS = 0.02; // 0.1 // 0.2
|
@@ -74,7 +74,7 @@ export class InterstitialEvent {
|
|
74
74
|
public assetList: InterstitialAssetItem[] = [];
|
75
75
|
public assetListLoader?: Loader<LoaderContext>;
|
76
76
|
public assetListResponse: AssetListJSON | null = null;
|
77
|
-
public resumeAnchor?:
|
77
|
+
public resumeAnchor?: MediaFragmentRef;
|
78
78
|
public error?: Error;
|
79
79
|
|
80
80
|
constructor(dateRange: DateRange, base: BaseData) {
|
@@ -261,11 +261,14 @@ export class InterstitialEvent {
|
|
261
261
|
}
|
262
262
|
}
|
263
263
|
|
264
|
-
function getSnapToFragmentTime(time: number, frag:
|
264
|
+
function getSnapToFragmentTime(time: number, frag: MediaFragmentRef) {
|
265
265
|
return time - frag.start < frag.duration / 2 &&
|
266
|
-
!(
|
266
|
+
!(
|
267
|
+
Math.abs(time - (frag.start + frag.duration)) <
|
268
|
+
ALIGNED_END_THRESHOLD_SECONDS
|
269
|
+
)
|
267
270
|
? frag.start
|
268
|
-
: frag.
|
271
|
+
: frag.start + frag.duration;
|
269
272
|
}
|
270
273
|
|
271
274
|
export function getInterstitialUrl(
|
@@ -48,8 +48,6 @@ const LEVEL_PLAYLIST_REGEX_FAST = new RegExp(
|
|
48
48
|
[
|
49
49
|
/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source, // duration (#EXTINF:<duration>,<title>), group 1 => duration, group 2 => title
|
50
50
|
/(?!#) *(\S[^\r\n]*)/.source, // segment URI, group 3 => the URI (note newline is not eaten)
|
51
|
-
/#EXT-X-BYTERANGE:*(.+)/.source, // next segment's byterange, group 4 => range spec (x@y)
|
52
|
-
/#EXT-X-PROGRAM-DATE-TIME:(.+)/.source, // next segment's program date/time group 5 => the datetime spec
|
53
51
|
/#.*/.source, // All other non-segment oriented tags will match with all groups empty
|
54
52
|
].join('|'),
|
55
53
|
'g',
|
@@ -58,7 +56,7 @@ const LEVEL_PLAYLIST_REGEX_FAST = new RegExp(
|
|
58
56
|
const LEVEL_PLAYLIST_REGEX_SLOW = new RegExp(
|
59
57
|
[
|
60
58
|
/#(EXTM3U)/.source,
|
61
|
-
/#EXT-X-(DATERANGE|DEFINE|KEY|MAP|PART|PART-INF|PLAYLIST-TYPE|PRELOAD-HINT|RENDITION-REPORT|SERVER-CONTROL|SKIP|START):(.+)/
|
59
|
+
/#EXT-X-(PROGRAM-DATE-TIME|BYTERANGE|DATERANGE|DEFINE|KEY|MAP|PART|PART-INF|PLAYLIST-TYPE|PRELOAD-HINT|RENDITION-REPORT|SERVER-CONTROL|SKIP|START):(.+)/
|
62
60
|
.source,
|
63
61
|
/#EXT-X-(BITRATE|DISCONTINUITY-SEQUENCE|MEDIA-SEQUENCE|TARGETDURATION|VERSION): *(\d+)/
|
64
62
|
.source,
|
@@ -306,6 +304,7 @@ export default class M3U8Parser {
|
|
306
304
|
levelUrlId: number,
|
307
305
|
multivariantVariableList: VariableMap | null,
|
308
306
|
): LevelDetails {
|
307
|
+
const base = { url: baseurl };
|
309
308
|
const level = new LevelDetails(baseurl);
|
310
309
|
const fragments: M3U8ParserFragments = level.fragments;
|
311
310
|
const programDateTimes: MediaFragment[] = [];
|
@@ -316,7 +315,7 @@ export default class M3U8Parser {
|
|
316
315
|
let totalduration = 0;
|
317
316
|
let discontinuityCounter = 0;
|
318
317
|
let prevFrag: Fragment | null = null;
|
319
|
-
let frag: Fragment = new Fragment(type,
|
318
|
+
let frag: Fragment = new Fragment(type, base);
|
320
319
|
let result: RegExpExecArray | RegExpMatchArray | null;
|
321
320
|
let i: number;
|
322
321
|
let levelkeys: { [key: string]: LevelKey } | undefined;
|
@@ -333,7 +332,7 @@ export default class M3U8Parser {
|
|
333
332
|
while ((result = LEVEL_PLAYLIST_REGEX_FAST.exec(string)) !== null) {
|
334
333
|
if (createNextFrag) {
|
335
334
|
createNextFrag = false;
|
336
|
-
frag = new Fragment(type,
|
335
|
+
frag = new Fragment(type, base);
|
337
336
|
// setup the next fragment for part loading
|
338
337
|
frag.playlistOffset = totalduration;
|
339
338
|
frag.start = totalduration;
|
@@ -342,8 +341,10 @@ export default class M3U8Parser {
|
|
342
341
|
frag.level = id;
|
343
342
|
if (currentInitSegment) {
|
344
343
|
frag.initSegment = currentInitSegment;
|
345
|
-
|
346
|
-
|
344
|
+
if (currentInitSegment.rawProgramDateTime) {
|
345
|
+
frag.rawProgramDateTime = currentInitSegment.rawProgramDateTime;
|
346
|
+
currentInitSegment.rawProgramDateTime = null;
|
347
|
+
}
|
347
348
|
if (nextByteRange) {
|
348
349
|
frag.setByteRange(nextByteRange);
|
349
350
|
nextByteRange = null;
|
@@ -387,22 +388,6 @@ export default class M3U8Parser {
|
|
387
388
|
currentPart = 0;
|
388
389
|
createNextFrag = true;
|
389
390
|
}
|
390
|
-
} else if (result[4]) {
|
391
|
-
// X-BYTERANGE
|
392
|
-
const data = (' ' + result[4]).slice(1);
|
393
|
-
if (prevFrag) {
|
394
|
-
frag.setByteRange(data, prevFrag);
|
395
|
-
} else {
|
396
|
-
frag.setByteRange(data);
|
397
|
-
}
|
398
|
-
} else if (result[5]) {
|
399
|
-
// PROGRAM-DATE-TIME
|
400
|
-
// avoid sliced strings https://github.com/video-dev/hls.js/issues/939
|
401
|
-
frag.rawProgramDateTime = (' ' + result[5]).slice(1);
|
402
|
-
frag.tagList.push(['PROGRAM-DATE-TIME', frag.rawProgramDateTime]);
|
403
|
-
if (firstPdtIndex === -1) {
|
404
|
-
firstPdtIndex = fragments.length;
|
405
|
-
}
|
406
391
|
} else {
|
407
392
|
result = result[0].match(LEVEL_PLAYLIST_REGEX_SLOW);
|
408
393
|
if (!result) {
|
@@ -410,7 +395,7 @@ export default class M3U8Parser {
|
|
410
395
|
continue;
|
411
396
|
}
|
412
397
|
for (i = 1; i < result.length; i++) {
|
413
|
-
if (
|
398
|
+
if (result[i] !== undefined) {
|
414
399
|
break;
|
415
400
|
}
|
416
401
|
}
|
@@ -418,9 +403,24 @@ export default class M3U8Parser {
|
|
418
403
|
// avoid sliced strings https://github.com/video-dev/hls.js/issues/939
|
419
404
|
const tag = (' ' + result[i]).slice(1);
|
420
405
|
const value1 = (' ' + result[i + 1]).slice(1);
|
421
|
-
const value2 = result[i + 2] ? (' ' + result[i + 2]).slice(1) :
|
406
|
+
const value2 = result[i + 2] ? (' ' + result[i + 2]).slice(1) : null;
|
422
407
|
|
423
408
|
switch (tag) {
|
409
|
+
case 'BYTERANGE':
|
410
|
+
if (prevFrag) {
|
411
|
+
frag.setByteRange(value1, prevFrag);
|
412
|
+
} else {
|
413
|
+
frag.setByteRange(value1);
|
414
|
+
}
|
415
|
+
break;
|
416
|
+
case 'PROGRAM-DATE-TIME':
|
417
|
+
// avoid sliced strings https://github.com/video-dev/hls.js/issues/939
|
418
|
+
frag.rawProgramDateTime = value1;
|
419
|
+
frag.tagList.push(['PROGRAM-DATE-TIME', value1]);
|
420
|
+
if (firstPdtIndex === -1) {
|
421
|
+
firstPdtIndex = fragments.length;
|
422
|
+
}
|
423
|
+
break;
|
424
424
|
case 'PLAYLIST-TYPE':
|
425
425
|
level.type = value1.toUpperCase();
|
426
426
|
break;
|
@@ -546,7 +546,7 @@ export default class M3U8Parser {
|
|
546
546
|
// Initial segment tag is after segment duration tag.
|
547
547
|
// #EXTINF: 6.0
|
548
548
|
// #EXT-X-MAP:URI="init.mp4
|
549
|
-
const init = new Fragment(type,
|
549
|
+
const init = new Fragment(type, base);
|
550
550
|
setInitSegment(init, mapAttrs, id, levelkeys);
|
551
551
|
currentInitSegment = init;
|
552
552
|
frag.initSegment = currentInitSegment;
|
@@ -607,7 +607,7 @@ export default class M3U8Parser {
|
|
607
607
|
const part = new Part(
|
608
608
|
partAttrs,
|
609
609
|
frag as MediaFragment,
|
610
|
-
|
610
|
+
base,
|
611
611
|
index,
|
612
612
|
previousFragmentPart,
|
613
613
|
);
|
@@ -709,7 +709,7 @@ export function mapDateRanges(
|
|
709
709
|
for (let i = dateRangeIds.length; i--; ) {
|
710
710
|
const dateRange = details.dateRanges[dateRangeIds[i]];
|
711
711
|
const startDateTime = dateRange.startDate.getTime();
|
712
|
-
dateRange.tagAnchor = lastProgramDateTime;
|
712
|
+
dateRange.tagAnchor = lastProgramDateTime.ref;
|
713
713
|
for (let j = programDateTimeCount; j--; ) {
|
714
714
|
const fragIndex = findFragmentWithStartDate(
|
715
715
|
details,
|
@@ -719,7 +719,7 @@ export function mapDateRanges(
|
|
719
719
|
playlistEnd,
|
720
720
|
);
|
721
721
|
if (fragIndex !== -1) {
|
722
|
-
dateRange.tagAnchor = details.fragments[fragIndex];
|
722
|
+
dateRange.tagAnchor = details.fragments[fragIndex].ref;
|
723
723
|
break;
|
724
724
|
}
|
725
725
|
}
|
@@ -736,31 +736,30 @@ function findFragmentWithStartDate(
|
|
736
736
|
const pdtFragment = programDateTimes[index];
|
737
737
|
if (pdtFragment) {
|
738
738
|
// find matching range between PDT tags
|
739
|
-
const durationBetweenPdt =
|
740
|
-
(programDateTimes[index + 1]?.start || endTime) - pdtFragment.start;
|
741
739
|
const pdtStart = pdtFragment.programDateTime as number;
|
742
|
-
if (
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
740
|
+
if (startDateTime >= pdtStart || index === 0) {
|
741
|
+
const durationBetweenPdt =
|
742
|
+
(programDateTimes[index + 1]?.start || endTime) - pdtFragment.start;
|
743
|
+
if (startDateTime <= pdtStart + durationBetweenPdt * 1000) {
|
744
|
+
// map to fragment with date-time range
|
745
|
+
const startIndex = programDateTimes[index].sn - details.startSN;
|
746
|
+
const fragments = details.fragments;
|
747
|
+
if (fragments.length > programDateTimes.length) {
|
748
|
+
const endSegment =
|
749
|
+
programDateTimes[index + 1] || fragments[fragments.length - 1];
|
750
|
+
const endIndex = endSegment.sn - details.startSN;
|
751
|
+
for (let i = endIndex; i > startIndex; i--) {
|
752
|
+
const fragStartDateTime = fragments[i].programDateTime as number;
|
753
|
+
if (
|
754
|
+
startDateTime >= fragStartDateTime &&
|
755
|
+
startDateTime < fragStartDateTime + fragments[i].duration * 1000
|
756
|
+
) {
|
757
|
+
return i;
|
758
|
+
}
|
760
759
|
}
|
761
760
|
}
|
761
|
+
return startIndex;
|
762
762
|
}
|
763
|
-
return startIndex;
|
764
763
|
}
|
765
764
|
}
|
766
765
|
return -1;
|
@@ -862,12 +861,6 @@ export function assignProgramDateTime(
|
|
862
861
|
programDateTimes: MediaFragment[],
|
863
862
|
) {
|
864
863
|
if (frag.rawProgramDateTime) {
|
865
|
-
frag.programDateTime = Date.parse(frag.rawProgramDateTime);
|
866
|
-
if (!Number.isFinite(frag.programDateTime)) {
|
867
|
-
frag.programDateTime = null;
|
868
|
-
frag.rawProgramDateTime = null;
|
869
|
-
return;
|
870
|
-
}
|
871
864
|
programDateTimes.push(frag);
|
872
865
|
} else if (prevFrag?.programDateTime) {
|
873
866
|
frag.programDateTime = prevFrag.endProgramDateTime;
|
@@ -38,19 +38,19 @@ function updateFromToPTS(fragFrom: MediaFragment, fragTo: MediaFragment) {
|
|
38
38
|
frag = fragTo;
|
39
39
|
}
|
40
40
|
if (frag.duration !== duration) {
|
41
|
-
frag.duration
|
41
|
+
frag.setDuration(duration);
|
42
42
|
}
|
43
43
|
// we dont know startPTS[toIdx]
|
44
44
|
} else if (fragTo.sn > fragFrom.sn) {
|
45
45
|
const contiguous = fragFrom.cc === fragTo.cc;
|
46
46
|
// TODO: With part-loading end/durations we need to confirm the whole fragment is loaded before using (or setting) minEndPTS
|
47
47
|
if (contiguous && fragFrom.minEndPTS) {
|
48
|
-
fragTo.
|
48
|
+
fragTo.setStart(fragFrom.start + (fragFrom.minEndPTS - fragFrom.start));
|
49
49
|
} else {
|
50
|
-
fragTo.
|
50
|
+
fragTo.setStart(fragFrom.start + fragFrom.duration);
|
51
51
|
}
|
52
52
|
} else {
|
53
|
-
fragTo.
|
53
|
+
fragTo.setStart(Math.max(fragFrom.start - fragTo.duration, 0));
|
54
54
|
}
|
55
55
|
}
|
56
56
|
|
@@ -92,9 +92,9 @@ export function updateFragPTSDTS(
|
|
92
92
|
|
93
93
|
const drift = startPTS - frag.start;
|
94
94
|
if (frag.start !== 0) {
|
95
|
-
frag.
|
95
|
+
frag.setStart(startPTS);
|
96
96
|
}
|
97
|
-
frag.
|
97
|
+
frag.setDuration(endPTS - frag.start);
|
98
98
|
frag.startPTS = startPTS;
|
99
99
|
frag.maxStartPTS = maxStartPTS;
|
100
100
|
frag.startDTS = startDTS;
|
@@ -170,15 +170,14 @@ export function mergeDetails(
|
|
170
170
|
Number.isFinite(oldFrag.startPTS) &&
|
171
171
|
Number.isFinite(oldFrag.endPTS)
|
172
172
|
) {
|
173
|
-
newFrag.
|
173
|
+
newFrag.setStart((newFrag.startPTS = oldFrag.startPTS!));
|
174
174
|
newFrag.startDTS = oldFrag.startDTS;
|
175
175
|
newFrag.maxStartPTS = oldFrag.maxStartPTS;
|
176
176
|
|
177
177
|
newFrag.endPTS = oldFrag.endPTS;
|
178
178
|
newFrag.endDTS = oldFrag.endDTS;
|
179
179
|
newFrag.minEndPTS = oldFrag.minEndPTS;
|
180
|
-
newFrag.
|
181
|
-
(oldFrag.endPTS as number) - (oldFrag.startPTS as number);
|
180
|
+
newFrag.setDuration(oldFrag.endPTS! - oldFrag.startPTS!);
|
182
181
|
|
183
182
|
if (newFrag.duration) {
|
184
183
|
PTSFrag = newFrag;
|
@@ -187,9 +186,17 @@ export function mergeDetails(
|
|
187
186
|
// PTS is known when any segment has startPTS and endPTS
|
188
187
|
newDetails.PTSKnown = newDetails.alignedSliding = true;
|
189
188
|
}
|
190
|
-
|
189
|
+
|
190
|
+
if (oldFrag.hasStreams) {
|
191
|
+
newFrag.elementaryStreams = oldFrag.elementaryStreams;
|
192
|
+
}
|
193
|
+
|
191
194
|
newFrag.loader = oldFrag.loader;
|
192
|
-
|
195
|
+
|
196
|
+
if (oldFrag.hasStats) {
|
197
|
+
newFrag.stats = oldFrag.stats;
|
198
|
+
}
|
199
|
+
|
193
200
|
if (oldFrag.initSegment) {
|
194
201
|
newFrag.initSegment = oldFrag.initSegment;
|
195
202
|
currentInitSegment = oldFrag.initSegment;
|
@@ -432,14 +439,14 @@ export function adjustSliding(
|
|
432
439
|
addSliding(newDetails, sliding);
|
433
440
|
}
|
434
441
|
|
435
|
-
export function addSliding(details: LevelDetails,
|
436
|
-
if (
|
442
|
+
export function addSliding(details: LevelDetails, sliding: number) {
|
443
|
+
if (sliding) {
|
437
444
|
const fragments = details.fragments;
|
438
445
|
for (let i = details.skippedSegments; i < fragments.length; i++) {
|
439
|
-
fragments[i].
|
446
|
+
fragments[i].addStart(sliding);
|
440
447
|
}
|
441
448
|
if (details.fragmentHint) {
|
442
|
-
details.fragmentHint.
|
449
|
+
details.fragmentHint.addStart(sliding);
|
443
450
|
}
|
444
451
|
}
|
445
452
|
}
|