hls.js 1.5.14-0.canary.10517 → 1.5.14
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/README.md +3 -4
- package/dist/hls-demo.js +38 -41
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +2903 -4542
- package/dist/hls.js.d.ts +112 -186
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +2284 -3295
- 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 +1804 -2817
- 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 +4652 -6293
- 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 +38 -38
- package/src/config.ts +2 -5
- package/src/controller/abr-controller.ts +25 -39
- package/src/controller/audio-stream-controller.ts +136 -156
- package/src/controller/audio-track-controller.ts +1 -1
- package/src/controller/base-playlist-controller.ts +10 -27
- package/src/controller/base-stream-controller.ts +107 -263
- package/src/controller/buffer-controller.ts +98 -252
- package/src/controller/buffer-operation-queue.ts +19 -16
- package/src/controller/cap-level-controller.ts +2 -3
- package/src/controller/cmcd-controller.ts +14 -51
- package/src/controller/content-steering-controller.ts +15 -29
- package/src/controller/eme-controller.ts +23 -10
- package/src/controller/error-controller.ts +22 -28
- package/src/controller/fps-controller.ts +3 -8
- package/src/controller/fragment-finders.ts +16 -44
- package/src/controller/fragment-tracker.ts +25 -58
- package/src/controller/gap-controller.ts +16 -43
- package/src/controller/id3-track-controller.ts +35 -45
- package/src/controller/latency-controller.ts +13 -18
- package/src/controller/level-controller.ts +19 -37
- package/src/controller/stream-controller.ts +83 -100
- package/src/controller/subtitle-stream-controller.ts +47 -35
- package/src/controller/subtitle-track-controller.ts +3 -5
- package/src/controller/timeline-controller.ts +22 -20
- package/src/crypt/aes-crypto.ts +2 -21
- package/src/crypt/decrypter.ts +16 -32
- package/src/crypt/fast-aes-key.ts +5 -28
- package/src/demux/audio/aacdemuxer.ts +5 -5
- package/src/demux/audio/ac3-demuxer.ts +4 -5
- package/src/demux/audio/adts.ts +4 -9
- package/src/demux/audio/base-audio-demuxer.ts +14 -16
- package/src/demux/audio/mp3demuxer.ts +3 -4
- package/src/demux/audio/mpegaudio.ts +1 -1
- package/src/demux/id3.ts +411 -0
- package/src/demux/inject-worker.ts +4 -38
- package/src/demux/mp4demuxer.ts +7 -7
- package/src/demux/sample-aes.ts +0 -2
- package/src/demux/transmuxer-interface.ts +83 -106
- package/src/demux/transmuxer-worker.ts +77 -111
- package/src/demux/transmuxer.ts +22 -46
- package/src/demux/tsdemuxer.ts +62 -122
- package/src/demux/video/avc-video-parser.ts +121 -210
- package/src/demux/video/base-video-parser.ts +2 -135
- package/src/demux/video/exp-golomb.ts +208 -0
- package/src/errors.ts +0 -2
- package/src/events.ts +1 -8
- package/src/exports-named.ts +1 -1
- package/src/hls.ts +48 -97
- package/src/loader/date-range.ts +5 -71
- package/src/loader/fragment-loader.ts +21 -23
- package/src/loader/fragment.ts +4 -8
- package/src/loader/key-loader.ts +1 -3
- package/src/loader/level-details.ts +6 -6
- package/src/loader/level-key.ts +9 -10
- package/src/loader/m3u8-parser.ts +144 -138
- package/src/loader/playlist-loader.ts +7 -5
- package/src/remux/mp4-generator.ts +1 -196
- package/src/remux/mp4-remuxer.ts +84 -55
- package/src/remux/passthrough-remuxer.ts +8 -23
- package/src/task-loop.ts +2 -5
- package/src/types/component-api.ts +1 -3
- package/src/types/demuxer.ts +0 -3
- package/src/types/events.ts +6 -19
- package/src/types/fragment-tracker.ts +2 -2
- package/src/types/general.ts +6 -0
- package/src/types/media-playlist.ts +1 -9
- package/src/types/remuxer.ts +1 -1
- package/src/utils/attr-list.ts +9 -96
- package/src/utils/buffer-helper.ts +31 -12
- package/src/utils/cea-608-parser.ts +3 -1
- package/src/utils/codecs.ts +5 -34
- package/src/utils/discontinuities.ts +47 -21
- package/src/utils/fetch-loader.ts +1 -1
- package/src/utils/hdr.ts +7 -4
- package/src/utils/imsc1-ttml-parser.ts +1 -1
- package/src/utils/keysystem-util.ts +6 -1
- package/src/utils/level-helper.ts +44 -71
- package/src/utils/logger.ts +23 -58
- package/src/utils/mp4-tools.ts +3 -5
- package/src/utils/rendition-helper.ts +74 -100
- package/src/utils/variable-substitution.ts +19 -0
- package/src/utils/webvtt-parser.ts +12 -2
- package/src/crypt/decrypter-aes-mode.ts +0 -4
- package/src/demux/video/hevc-video-parser.ts +0 -749
- package/src/utils/encryption-methods-util.ts +0 -21
- package/src/utils/hash.ts +0 -10
- package/src/utils/utf8-utils.ts +0 -18
- package/src/version.ts +0 -1
package/src/types/events.ts
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
// eslint-disable-next-line import/no-duplicates
|
2
|
+
import type { Fragment } from '../loader/fragment';
|
3
|
+
// eslint-disable-next-line import/no-duplicates
|
4
|
+
import type { Part } from '../loader/fragment';
|
2
5
|
import type { LevelDetails } from '../loader/level-details';
|
3
6
|
import type {
|
4
7
|
HdcpLevel,
|
@@ -39,10 +42,6 @@ export interface MediaAttachedData {
|
|
39
42
|
mediaSource?: MediaSource;
|
40
43
|
}
|
41
44
|
|
42
|
-
export interface MediaEndedData {
|
43
|
-
stalled: boolean;
|
44
|
-
}
|
45
|
-
|
46
45
|
export interface BufferCodecsData {
|
47
46
|
video?: Track;
|
48
47
|
audio?: Track;
|
@@ -192,18 +191,6 @@ export interface LevelUpdatedData {
|
|
192
191
|
level: number;
|
193
192
|
}
|
194
193
|
|
195
|
-
export interface AudioTrackUpdatedData {
|
196
|
-
details: LevelDetails;
|
197
|
-
id: number;
|
198
|
-
groupId: string;
|
199
|
-
}
|
200
|
-
|
201
|
-
export interface SubtitleTrackUpdatedData {
|
202
|
-
details: LevelDetails;
|
203
|
-
id: number;
|
204
|
-
groupId: string;
|
205
|
-
}
|
206
|
-
|
207
194
|
export interface LevelPTSUpdatedData {
|
208
195
|
details: LevelDetails;
|
209
196
|
level: Level;
|
@@ -329,8 +316,8 @@ export interface NonNativeTextTracksData {
|
|
329
316
|
}
|
330
317
|
|
331
318
|
export interface InitPTSFoundData {
|
332
|
-
id:
|
333
|
-
frag:
|
319
|
+
id: string;
|
320
|
+
frag: Fragment;
|
334
321
|
initPTS: number;
|
335
322
|
timescale: number;
|
336
323
|
}
|
@@ -1,9 +1,9 @@
|
|
1
|
-
import type {
|
1
|
+
import type { Fragment } from '../loader/fragment';
|
2
2
|
import type { SourceBufferName } from './buffer';
|
3
3
|
import type { FragLoadedData } from './events';
|
4
4
|
|
5
5
|
export interface FragmentEntity {
|
6
|
-
body:
|
6
|
+
body: Fragment;
|
7
7
|
// appendedPTS is the latest buffered presentation time within the fragment's time range.
|
8
8
|
// It is used to determine: which fragment is appended at any given position, and hls.currentLevel.
|
9
9
|
appendedPTS: number | null;
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import type { AttrList } from '../utils/attr-list';
|
2
2
|
import type { LevelDetails } from '../loader/level-details';
|
3
|
-
import type {
|
4
|
-
import type { PlaylistLevelType } from './loader';
|
3
|
+
import type { VideoRange } from './level';
|
5
4
|
|
6
5
|
export type AudioPlaylistType = 'AUDIO';
|
7
6
|
|
@@ -11,16 +10,9 @@ export type SubtitlePlaylistType = 'SUBTITLES' | 'CLOSED-CAPTIONS';
|
|
11
10
|
|
12
11
|
export type MediaPlaylistType = MainPlaylistType | SubtitlePlaylistType;
|
13
12
|
|
14
|
-
export type MediaSelection = {
|
15
|
-
[PlaylistLevelType.MAIN]: Level;
|
16
|
-
[PlaylistLevelType.AUDIO]?: MediaPlaylist;
|
17
|
-
[PlaylistLevelType.SUBTITLE]?: MediaPlaylist;
|
18
|
-
};
|
19
|
-
|
20
13
|
export type VideoSelectionOption = {
|
21
14
|
preferHDR?: boolean;
|
22
15
|
allowedVideoRanges?: Array<VideoRange>;
|
23
|
-
videoCodec?: string;
|
24
16
|
};
|
25
17
|
|
26
18
|
export type AudioSelectionOption = {
|
package/src/types/remuxer.ts
CHANGED
package/src/utils/attr-list.ts
CHANGED
@@ -1,8 +1,3 @@
|
|
1
|
-
import type { LevelDetails } from '../loader/level-details';
|
2
|
-
import type { ParsedMultivariantPlaylist } from '../loader/m3u8-parser';
|
3
|
-
import { logger } from './logger';
|
4
|
-
import { substituteVariables } from './variable-substitution';
|
5
|
-
|
6
1
|
const DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/;
|
7
2
|
const ATTR_LIST_REGEX = /(.+?)=(".*?"|.*?)(?:,|$)/g;
|
8
3
|
|
@@ -10,15 +5,9 @@ const ATTR_LIST_REGEX = /(.+?)=(".*?"|.*?)(?:,|$)/g;
|
|
10
5
|
export class AttrList {
|
11
6
|
[key: string]: any;
|
12
7
|
|
13
|
-
constructor(
|
14
|
-
attrs: string | Record<string, any>,
|
15
|
-
parsed?: Pick<
|
16
|
-
ParsedMultivariantPlaylist | LevelDetails,
|
17
|
-
'variableList' | 'hasVariableRefs' | 'playlistParsingError'
|
18
|
-
>,
|
19
|
-
) {
|
8
|
+
constructor(attrs: string | Record<string, any>) {
|
20
9
|
if (typeof attrs === 'string') {
|
21
|
-
attrs = AttrList.parseAttrList(attrs
|
10
|
+
attrs = AttrList.parseAttrList(attrs);
|
22
11
|
}
|
23
12
|
Object.assign(this, attrs);
|
24
13
|
}
|
@@ -74,20 +63,6 @@ export class AttrList {
|
|
74
63
|
return this[attrName];
|
75
64
|
}
|
76
65
|
|
77
|
-
enumeratedStringList<T extends { [key: string]: boolean }>(
|
78
|
-
attrName: string,
|
79
|
-
dict: T,
|
80
|
-
): { [key in keyof T]: boolean } {
|
81
|
-
const attrValue = this[attrName];
|
82
|
-
return (attrValue ? attrValue.split(/[ ,]+/) : []).reduce(
|
83
|
-
(result: { [key in keyof T]: boolean }, identifier: string) => {
|
84
|
-
result[identifier.toLowerCase() as keyof T] = true;
|
85
|
-
return result;
|
86
|
-
},
|
87
|
-
dict,
|
88
|
-
);
|
89
|
-
}
|
90
|
-
|
91
66
|
bool(attrName: string): boolean {
|
92
67
|
return this[attrName] === 'YES';
|
93
68
|
}
|
@@ -109,83 +84,21 @@ export class AttrList {
|
|
109
84
|
};
|
110
85
|
}
|
111
86
|
|
112
|
-
static parseAttrList(
|
113
|
-
|
114
|
-
parsed?: Pick<
|
115
|
-
ParsedMultivariantPlaylist | LevelDetails,
|
116
|
-
'variableList' | 'hasVariableRefs' | 'playlistParsingError'
|
117
|
-
>,
|
118
|
-
): Record<string, string> {
|
119
|
-
let match: RegExpExecArray | null;
|
87
|
+
static parseAttrList(input: string): Record<string, any> {
|
88
|
+
let match;
|
120
89
|
const attrs = {};
|
121
90
|
const quote = '"';
|
122
91
|
ATTR_LIST_REGEX.lastIndex = 0;
|
123
92
|
while ((match = ATTR_LIST_REGEX.exec(input)) !== null) {
|
124
|
-
const name = match[1].trim();
|
125
93
|
let value = match[2];
|
126
|
-
|
94
|
+
|
95
|
+
if (
|
127
96
|
value.indexOf(quote) === 0 &&
|
128
|
-
value.lastIndexOf(quote) === value.length - 1
|
129
|
-
|
130
|
-
if (quotedString) {
|
97
|
+
value.lastIndexOf(quote) === value.length - 1
|
98
|
+
) {
|
131
99
|
value = value.slice(1, -1);
|
132
|
-
} else {
|
133
|
-
switch (name) {
|
134
|
-
case 'IV':
|
135
|
-
case 'SCTE35-CMD':
|
136
|
-
case 'SCTE35-IN':
|
137
|
-
case 'SCTE35-OUT':
|
138
|
-
hexadecimalSequence = true;
|
139
|
-
}
|
140
|
-
}
|
141
|
-
if (parsed && (quotedString || hexadecimalSequence)) {
|
142
|
-
if (__USE_VARIABLE_SUBSTITUTION__) {
|
143
|
-
value = substituteVariables(parsed, value);
|
144
|
-
}
|
145
|
-
} else if (!hexadecimalSequence && !quotedString) {
|
146
|
-
switch (name) {
|
147
|
-
case 'CLOSED-CAPTIONS':
|
148
|
-
if (value === 'NONE') {
|
149
|
-
break;
|
150
|
-
}
|
151
|
-
// falls through
|
152
|
-
case 'ALLOWED-CPC':
|
153
|
-
case 'CLASS':
|
154
|
-
case 'ASSOC-LANGUAGE':
|
155
|
-
case 'AUDIO':
|
156
|
-
case 'BYTERANGE':
|
157
|
-
case 'CHANNELS':
|
158
|
-
case 'CHARACTERISTICS':
|
159
|
-
case 'CODECS':
|
160
|
-
case 'DATA-ID':
|
161
|
-
case 'END-DATE':
|
162
|
-
case 'GROUP-ID':
|
163
|
-
case 'ID':
|
164
|
-
case 'IMPORT':
|
165
|
-
case 'INSTREAM-ID':
|
166
|
-
case 'KEYFORMAT':
|
167
|
-
case 'KEYFORMATVERSIONS':
|
168
|
-
case 'LANGUAGE':
|
169
|
-
case 'NAME':
|
170
|
-
case 'PATHWAY-ID':
|
171
|
-
case 'QUERYPARAM':
|
172
|
-
case 'RECENTLY-REMOVED-DATERANGES':
|
173
|
-
case 'SERVER-URI':
|
174
|
-
case 'STABLE-RENDITION-ID':
|
175
|
-
case 'STABLE-VARIANT-ID':
|
176
|
-
case 'START-DATE':
|
177
|
-
case 'SUBTITLES':
|
178
|
-
case 'SUPPLEMENTAL-CODECS':
|
179
|
-
case 'URI':
|
180
|
-
case 'VALUE':
|
181
|
-
case 'VIDEO':
|
182
|
-
case 'X-ASSET-LIST':
|
183
|
-
case 'X-ASSET-URI':
|
184
|
-
// Since we are not checking tag:attribute combination, just warn rather than ignoring attribute
|
185
|
-
logger.warn(`${input}: attribute ${name} is missing quotes`);
|
186
|
-
// continue;
|
187
|
-
}
|
188
100
|
}
|
101
|
+
const name = match[1].trim();
|
189
102
|
attrs[name] = value;
|
190
103
|
}
|
191
104
|
return attrs;
|
@@ -35,13 +35,19 @@ export class BufferHelper {
|
|
35
35
|
* Return true if `media`'s buffered include `position`
|
36
36
|
*/
|
37
37
|
static isBuffered(media: Bufferable, position: number): boolean {
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
try {
|
39
|
+
if (media) {
|
40
|
+
const buffered = BufferHelper.getBuffered(media);
|
41
|
+
for (let i = 0; i < buffered.length; i++) {
|
42
|
+
if (position >= buffered.start(i) && position <= buffered.end(i)) {
|
43
|
+
return true;
|
44
|
+
}
|
43
45
|
}
|
44
46
|
}
|
47
|
+
} catch (error) {
|
48
|
+
// this is to catch
|
49
|
+
// InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer':
|
50
|
+
// This SourceBuffer has been removed from the parent media source
|
45
51
|
}
|
46
52
|
return false;
|
47
53
|
}
|
@@ -51,15 +57,21 @@ export class BufferHelper {
|
|
51
57
|
pos: number,
|
52
58
|
maxHoleDuration: number,
|
53
59
|
): BufferInfo {
|
54
|
-
|
55
|
-
|
56
|
-
|
60
|
+
try {
|
61
|
+
if (media) {
|
62
|
+
const vbuffered = BufferHelper.getBuffered(media);
|
57
63
|
const buffered: BufferTimeRange[] = [];
|
58
|
-
|
64
|
+
let i: number;
|
65
|
+
for (i = 0; i < vbuffered.length; i++) {
|
59
66
|
buffered.push({ start: vbuffered.start(i), end: vbuffered.end(i) });
|
60
67
|
}
|
61
|
-
|
68
|
+
|
69
|
+
return this.bufferedInfo(buffered, pos, maxHoleDuration);
|
62
70
|
}
|
71
|
+
} catch (error) {
|
72
|
+
// this is to catch
|
73
|
+
// InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer':
|
74
|
+
// This SourceBuffer has been removed from the parent media source
|
63
75
|
}
|
64
76
|
return { len: 0, start: pos, end: pos, nextStart: undefined };
|
65
77
|
}
|
@@ -76,7 +88,14 @@ export class BufferHelper {
|
|
76
88
|
} {
|
77
89
|
pos = Math.max(0, pos);
|
78
90
|
// sort on buffer.start/smaller end (IE does not always return sorted buffered range)
|
79
|
-
buffered.sort((a, b)
|
91
|
+
buffered.sort(function (a, b) {
|
92
|
+
const diff = a.start - b.start;
|
93
|
+
if (diff) {
|
94
|
+
return diff;
|
95
|
+
} else {
|
96
|
+
return b.end - a.end;
|
97
|
+
}
|
98
|
+
});
|
80
99
|
|
81
100
|
let buffered2: BufferTimeRange[] = [];
|
82
101
|
if (maxHoleDuration) {
|
@@ -145,7 +164,7 @@ export class BufferHelper {
|
|
145
164
|
*/
|
146
165
|
static getBuffered(media: Bufferable): TimeRanges {
|
147
166
|
try {
|
148
|
-
return media.buffered
|
167
|
+
return media.buffered;
|
149
168
|
} catch (e) {
|
150
169
|
logger.log('failed to get media.buffered', e);
|
151
170
|
return noopBuffered;
|
@@ -1331,7 +1331,9 @@ class Cea608Parser {
|
|
1331
1331
|
if (charCodes) {
|
1332
1332
|
this.logger.log(
|
1333
1333
|
VerboseLevel.DEBUG,
|
1334
|
-
() =>
|
1334
|
+
() =>
|
1335
|
+
'Char codes = ' +
|
1336
|
+
numArrayToHexArray(charCodes as number[]).join(','),
|
1335
1337
|
);
|
1336
1338
|
}
|
1337
1339
|
return charCodes;
|
package/src/utils/codecs.ts
CHANGED
@@ -147,15 +147,12 @@ function getCodecCompatibleNameLower(
|
|
147
147
|
return CODEC_COMPATIBLE_NAMES[lowerCaseCodec]!;
|
148
148
|
}
|
149
149
|
|
150
|
+
// Idealy fLaC and Opus would be first (spec-compliant) but
|
151
|
+
// some browsers will report that fLaC is supported then fail.
|
152
|
+
// see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
|
150
153
|
const codecsToCheck = {
|
151
|
-
// Idealy fLaC and Opus would be first (spec-compliant) but
|
152
|
-
// some browsers will report that fLaC is supported then fail.
|
153
|
-
// see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
|
154
154
|
flac: ['flac', 'fLaC', 'FLAC'],
|
155
155
|
opus: ['opus', 'Opus'],
|
156
|
-
// Replace audio codec info if browser does not support mp4a.40.34,
|
157
|
-
// and demuxer can fallback to 'audio/mpeg' or 'audio/mp4;codecs="mp3"'
|
158
|
-
'mp4a.40.34': ['mp3'],
|
159
156
|
}[lowerCaseCodec];
|
160
157
|
|
161
158
|
for (let i = 0; i < codecsToCheck.length; i++) {
|
@@ -168,18 +165,13 @@ function getCodecCompatibleNameLower(
|
|
168
165
|
) {
|
169
166
|
CODEC_COMPATIBLE_NAMES[lowerCaseCodec] = codecsToCheck[i];
|
170
167
|
return codecsToCheck[i];
|
171
|
-
} else if (
|
172
|
-
codecsToCheck[i] === 'mp3' &&
|
173
|
-
getMediaSource(preferManagedMediaSource)?.isTypeSupported('audio/mpeg')
|
174
|
-
) {
|
175
|
-
return '';
|
176
168
|
}
|
177
169
|
}
|
178
170
|
|
179
171
|
return lowerCaseCodec;
|
180
172
|
}
|
181
173
|
|
182
|
-
const AUDIO_CODEC_REGEXP = /flac|opus
|
174
|
+
const AUDIO_CODEC_REGEXP = /flac|opus/i;
|
183
175
|
export function getCodecCompatibleName(
|
184
176
|
codec: string,
|
185
177
|
preferManagedMediaSource = true,
|
@@ -193,7 +185,7 @@ export function getCodecCompatibleName(
|
|
193
185
|
}
|
194
186
|
|
195
187
|
export function pickMostCompleteCodecName(
|
196
|
-
parsedCodec: string
|
188
|
+
parsedCodec: string,
|
197
189
|
levelCodec: string | undefined,
|
198
190
|
): string | undefined {
|
199
191
|
// Parsing of mp4a codecs strings in mp4-tools from media is incomplete as of d8c6c7a
|
@@ -221,24 +213,3 @@ export function convertAVC1ToAVCOTI(codec: string) {
|
|
221
213
|
}
|
222
214
|
return codecs.join(',');
|
223
215
|
}
|
224
|
-
|
225
|
-
export interface TypeSupported {
|
226
|
-
mpeg: boolean;
|
227
|
-
mp3: boolean;
|
228
|
-
ac3: boolean;
|
229
|
-
}
|
230
|
-
|
231
|
-
export function getM2TSSupportedAudioTypes(
|
232
|
-
preferManagedMediaSource: boolean,
|
233
|
-
): TypeSupported {
|
234
|
-
const MediaSource = getMediaSource(preferManagedMediaSource) || {
|
235
|
-
isTypeSupported: () => false,
|
236
|
-
};
|
237
|
-
return {
|
238
|
-
mpeg: MediaSource.isTypeSupported('audio/mpeg'),
|
239
|
-
mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
|
240
|
-
ac3: __USE_M2TS_ADVANCED_CODECS__
|
241
|
-
? MediaSource.isTypeSupported('audio/mp4; codecs="ac-3"')
|
242
|
-
: false,
|
243
|
-
};
|
244
|
-
}
|
@@ -17,13 +17,14 @@ export function findFirstFragWithCC(
|
|
17
17
|
}
|
18
18
|
|
19
19
|
export function shouldAlignOnDiscontinuities(
|
20
|
-
|
20
|
+
lastFrag: Fragment | null,
|
21
|
+
switchDetails: LevelDetails | undefined,
|
21
22
|
details: LevelDetails,
|
22
|
-
):
|
23
|
-
if (
|
23
|
+
): switchDetails is LevelDetails & boolean {
|
24
|
+
if (switchDetails) {
|
24
25
|
if (
|
25
|
-
details.
|
26
|
-
|
26
|
+
details.endCC > details.startCC ||
|
27
|
+
(lastFrag && lastFrag.cc < details.startCC)
|
27
28
|
) {
|
28
29
|
return true;
|
29
30
|
}
|
@@ -31,6 +32,29 @@ export function shouldAlignOnDiscontinuities(
|
|
31
32
|
return false;
|
32
33
|
}
|
33
34
|
|
35
|
+
// Find the first frag in the previous level which matches the CC of the first frag of the new level
|
36
|
+
export function findDiscontinuousReferenceFrag(
|
37
|
+
prevDetails: LevelDetails,
|
38
|
+
curDetails: LevelDetails,
|
39
|
+
) {
|
40
|
+
const prevFrags = prevDetails.fragments;
|
41
|
+
const curFrags = curDetails.fragments;
|
42
|
+
|
43
|
+
if (!curFrags.length || !prevFrags.length) {
|
44
|
+
logger.log('No fragments to align');
|
45
|
+
return;
|
46
|
+
}
|
47
|
+
|
48
|
+
const prevStartFrag = findFirstFragWithCC(prevFrags, curFrags[0].cc);
|
49
|
+
|
50
|
+
if (!prevStartFrag || (prevStartFrag && !prevStartFrag.startPTS)) {
|
51
|
+
logger.log('No frag in previous level to align on');
|
52
|
+
return;
|
53
|
+
}
|
54
|
+
|
55
|
+
return prevStartFrag;
|
56
|
+
}
|
57
|
+
|
34
58
|
function adjustFragmentStart(frag: Fragment, sliding: number) {
|
35
59
|
if (frag) {
|
36
60
|
const start = frag.start + sliding;
|
@@ -70,7 +94,7 @@ export function alignStream(
|
|
70
94
|
if (!switchDetails) {
|
71
95
|
return;
|
72
96
|
}
|
73
|
-
alignDiscontinuities(details, switchDetails);
|
97
|
+
alignDiscontinuities(lastFrag, details, switchDetails);
|
74
98
|
if (!details.alignedSliding && switchDetails) {
|
75
99
|
// If the PTS wasn't figured out via discontinuity sequence that means there was no CC increase within the level.
|
76
100
|
// Aligning via Program Date Time should therefore be reliable, since PDT should be the same within the same
|
@@ -86,27 +110,29 @@ export function alignStream(
|
|
86
110
|
}
|
87
111
|
|
88
112
|
/**
|
89
|
-
*
|
90
|
-
*
|
113
|
+
* Computes the PTS if a new level's fragments using the PTS of a fragment in the last level which shares the same
|
114
|
+
* discontinuity sequence.
|
115
|
+
* @param lastFrag - The last Fragment which shares the same discontinuity sequence
|
91
116
|
* @param lastLevel - The details of the last loaded level
|
92
117
|
* @param details - The details of the new level
|
93
118
|
*/
|
94
|
-
|
119
|
+
function alignDiscontinuities(
|
120
|
+
lastFrag: Fragment | null,
|
95
121
|
details: LevelDetails,
|
96
|
-
|
122
|
+
switchDetails: LevelDetails | undefined,
|
97
123
|
) {
|
98
|
-
if (
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
124
|
+
if (shouldAlignOnDiscontinuities(lastFrag, switchDetails, details)) {
|
125
|
+
const referenceFrag = findDiscontinuousReferenceFrag(
|
126
|
+
switchDetails,
|
127
|
+
details,
|
128
|
+
);
|
129
|
+
if (referenceFrag && Number.isFinite(referenceFrag.start)) {
|
130
|
+
logger.log(
|
131
|
+
`Adjusting PTS using last level due to CC increase within current level ${details.url}`,
|
132
|
+
);
|
133
|
+
adjustSlidingStart(referenceFrag.start, details);
|
134
|
+
}
|
106
135
|
}
|
107
|
-
logger.log(`Aligning playlist at start of dicontinuity sequence ${targetCC}`);
|
108
|
-
const delta = refFrag.start - frag.start;
|
109
|
-
adjustSlidingStart(delta, details);
|
110
136
|
}
|
111
137
|
|
112
138
|
/**
|
package/src/utils/hdr.ts
CHANGED
@@ -49,13 +49,16 @@ export function getVideoSelectionOptions(
|
|
49
49
|
if (videoPreference) {
|
50
50
|
allowedVideoRanges =
|
51
51
|
videoPreference.allowedVideoRanges || VideoRangeValues.slice(0);
|
52
|
-
const allowAutoPreferHDR =
|
53
|
-
allowedVideoRanges.join('') !== 'SDR' && !videoPreference.videoCodec;
|
54
52
|
preferHDR =
|
55
53
|
videoPreference.preferHDR !== undefined
|
56
54
|
? videoPreference.preferHDR
|
57
|
-
:
|
58
|
-
|
55
|
+
: isHdrSupported();
|
56
|
+
|
57
|
+
if (preferHDR) {
|
58
|
+
allowedVideoRanges = allowedVideoRanges.filter(
|
59
|
+
(range: VideoRange) => range !== 'SDR',
|
60
|
+
);
|
61
|
+
} else {
|
59
62
|
allowedVideoRanges = ['SDR'];
|
60
63
|
}
|
61
64
|
}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { findBox } from './mp4-tools';
|
2
2
|
import { parseTimeStamp } from './vttparser';
|
3
3
|
import VTTCue from './vttcue';
|
4
|
-
import { utf8ArrayToStr } from '
|
4
|
+
import { utf8ArrayToStr } from '../demux/id3';
|
5
5
|
import {
|
6
6
|
RationalTimestamp,
|
7
7
|
toTimescaleFromScale,
|
@@ -1,5 +1,4 @@
|
|
1
1
|
import { base64Decode } from './numeric-encoding-utils';
|
2
|
-
import { strToUtf8array } from './utf8-utils';
|
3
2
|
|
4
3
|
function getKeyIdBytes(str: string): Uint8Array {
|
5
4
|
const keyIdbytes = strToUtf8array(str).subarray(0, 16);
|
@@ -41,3 +40,9 @@ export function convertDataUriToArrayBytes(uri: string): Uint8Array | null {
|
|
41
40
|
}
|
42
41
|
return keydata;
|
43
42
|
}
|
43
|
+
|
44
|
+
export function strToUtf8array(str: string): Uint8Array {
|
45
|
+
return Uint8Array.from(unescape(encodeURIComponent(str)), (c) =>
|
46
|
+
c.charCodeAt(0),
|
47
|
+
);
|
48
|
+
}
|