hls.js 1.5.14-0.canary.10515 → 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.
Files changed (107) hide show
  1. package/README.md +3 -4
  2. package/dist/hls-demo.js +38 -41
  3. package/dist/hls-demo.js.map +1 -1
  4. package/dist/hls.js +2903 -4542
  5. package/dist/hls.js.d.ts +112 -186
  6. package/dist/hls.js.map +1 -1
  7. package/dist/hls.light.js +2284 -3295
  8. package/dist/hls.light.js.map +1 -1
  9. package/dist/hls.light.min.js +1 -1
  10. package/dist/hls.light.min.js.map +1 -1
  11. package/dist/hls.light.mjs +1804 -2817
  12. package/dist/hls.light.mjs.map +1 -1
  13. package/dist/hls.min.js +1 -1
  14. package/dist/hls.min.js.map +1 -1
  15. package/dist/hls.mjs +4652 -6293
  16. package/dist/hls.mjs.map +1 -1
  17. package/dist/hls.worker.js +1 -1
  18. package/dist/hls.worker.js.map +1 -1
  19. package/package.json +38 -38
  20. package/src/config.ts +2 -5
  21. package/src/controller/abr-controller.ts +25 -39
  22. package/src/controller/audio-stream-controller.ts +136 -156
  23. package/src/controller/audio-track-controller.ts +1 -1
  24. package/src/controller/base-playlist-controller.ts +10 -27
  25. package/src/controller/base-stream-controller.ts +107 -263
  26. package/src/controller/buffer-controller.ts +98 -252
  27. package/src/controller/buffer-operation-queue.ts +19 -16
  28. package/src/controller/cap-level-controller.ts +2 -3
  29. package/src/controller/cmcd-controller.ts +14 -51
  30. package/src/controller/content-steering-controller.ts +15 -29
  31. package/src/controller/eme-controller.ts +23 -10
  32. package/src/controller/error-controller.ts +22 -28
  33. package/src/controller/fps-controller.ts +3 -8
  34. package/src/controller/fragment-finders.ts +16 -44
  35. package/src/controller/fragment-tracker.ts +25 -58
  36. package/src/controller/gap-controller.ts +16 -43
  37. package/src/controller/id3-track-controller.ts +35 -45
  38. package/src/controller/latency-controller.ts +13 -18
  39. package/src/controller/level-controller.ts +19 -37
  40. package/src/controller/stream-controller.ts +83 -100
  41. package/src/controller/subtitle-stream-controller.ts +47 -35
  42. package/src/controller/subtitle-track-controller.ts +3 -5
  43. package/src/controller/timeline-controller.ts +22 -20
  44. package/src/crypt/aes-crypto.ts +2 -21
  45. package/src/crypt/decrypter.ts +16 -32
  46. package/src/crypt/fast-aes-key.ts +5 -28
  47. package/src/demux/audio/aacdemuxer.ts +5 -5
  48. package/src/demux/audio/ac3-demuxer.ts +4 -5
  49. package/src/demux/audio/adts.ts +4 -9
  50. package/src/demux/audio/base-audio-demuxer.ts +14 -16
  51. package/src/demux/audio/mp3demuxer.ts +3 -4
  52. package/src/demux/audio/mpegaudio.ts +1 -1
  53. package/src/demux/id3.ts +411 -0
  54. package/src/demux/inject-worker.ts +4 -38
  55. package/src/demux/mp4demuxer.ts +7 -7
  56. package/src/demux/sample-aes.ts +0 -2
  57. package/src/demux/transmuxer-interface.ts +83 -106
  58. package/src/demux/transmuxer-worker.ts +77 -111
  59. package/src/demux/transmuxer.ts +22 -46
  60. package/src/demux/tsdemuxer.ts +62 -122
  61. package/src/demux/video/avc-video-parser.ts +121 -210
  62. package/src/demux/video/base-video-parser.ts +2 -135
  63. package/src/demux/video/exp-golomb.ts +208 -0
  64. package/src/errors.ts +0 -2
  65. package/src/events.ts +1 -8
  66. package/src/exports-named.ts +1 -1
  67. package/src/hls.ts +48 -97
  68. package/src/loader/date-range.ts +5 -71
  69. package/src/loader/fragment-loader.ts +21 -23
  70. package/src/loader/fragment.ts +4 -8
  71. package/src/loader/key-loader.ts +1 -3
  72. package/src/loader/level-details.ts +6 -6
  73. package/src/loader/level-key.ts +9 -10
  74. package/src/loader/m3u8-parser.ts +144 -138
  75. package/src/loader/playlist-loader.ts +7 -5
  76. package/src/remux/mp4-generator.ts +1 -196
  77. package/src/remux/mp4-remuxer.ts +84 -55
  78. package/src/remux/passthrough-remuxer.ts +8 -23
  79. package/src/task-loop.ts +2 -5
  80. package/src/types/component-api.ts +1 -3
  81. package/src/types/demuxer.ts +0 -3
  82. package/src/types/events.ts +6 -19
  83. package/src/types/fragment-tracker.ts +2 -2
  84. package/src/types/general.ts +6 -0
  85. package/src/types/media-playlist.ts +1 -9
  86. package/src/types/remuxer.ts +1 -1
  87. package/src/utils/attr-list.ts +9 -96
  88. package/src/utils/buffer-helper.ts +31 -12
  89. package/src/utils/cea-608-parser.ts +3 -1
  90. package/src/utils/codecs.ts +5 -34
  91. package/src/utils/discontinuities.ts +47 -21
  92. package/src/utils/fetch-loader.ts +1 -1
  93. package/src/utils/hdr.ts +7 -4
  94. package/src/utils/imsc1-ttml-parser.ts +1 -1
  95. package/src/utils/keysystem-util.ts +6 -1
  96. package/src/utils/level-helper.ts +44 -71
  97. package/src/utils/logger.ts +23 -58
  98. package/src/utils/mp4-tools.ts +3 -5
  99. package/src/utils/rendition-helper.ts +74 -100
  100. package/src/utils/variable-substitution.ts +19 -0
  101. package/src/utils/webvtt-parser.ts +12 -2
  102. package/src/crypt/decrypter-aes-mode.ts +0 -4
  103. package/src/demux/video/hevc-video-parser.ts +0 -749
  104. package/src/utils/encryption-methods-util.ts +0 -21
  105. package/src/utils/hash.ts +0 -10
  106. package/src/utils/utf8-utils.ts +0 -18
  107. package/src/version.ts +0 -1
@@ -1,4 +1,7 @@
1
- import type { Fragment, MediaFragment, Part } from '../loader/fragment';
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: PlaylistLevelType;
333
- frag: MediaFragment;
319
+ id: string;
320
+ frag: Fragment;
334
321
  initPTS: number;
335
322
  timescale: number;
336
323
  }
@@ -1,9 +1,9 @@
1
- import type { MediaFragment } from '../loader/fragment';
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: MediaFragment;
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;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Make specific properties in T required
3
+ */
4
+ export type RequiredProperties<T, K extends keyof T> = T & {
5
+ [P in K]-?: T[P];
6
+ };
@@ -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 { Level, VideoRange } from './level';
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 = {
@@ -1,5 +1,5 @@
1
1
  import type { TrackSet } from './track';
2
- import type {
2
+ import {
3
3
  DemuxedAudioTrack,
4
4
  DemuxedMetadataTrack,
5
5
  DemuxedUserdataTrack,
@@ -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, parsed);
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
- input: string,
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
- const quotedString =
94
+
95
+ if (
127
96
  value.indexOf(quote) === 0 &&
128
- value.lastIndexOf(quote) === value.length - 1;
129
- let hexadecimalSequence = false;
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
- if (media) {
39
- const buffered = BufferHelper.getBuffered(media);
40
- for (let i = buffered.length; i--; ) {
41
- if (position >= buffered.start(i) && position <= buffered.end(i)) {
42
- return true;
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
- if (media) {
55
- const vbuffered = BufferHelper.getBuffered(media);
56
- if (vbuffered.length) {
60
+ try {
61
+ if (media) {
62
+ const vbuffered = BufferHelper.getBuffered(media);
57
63
  const buffered: BufferTimeRange[] = [];
58
- for (let i = 0; i < vbuffered.length; i++) {
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
- return BufferHelper.bufferedInfo(buffered, pos, maxHoleDuration);
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) => a.start - b.start || b.end - a.end);
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 || noopBuffered;
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
- () => 'Char codes = ' + numArrayToHexArray(charCodes).join(','),
1334
+ () =>
1335
+ 'Char codes = ' +
1336
+ numArrayToHexArray(charCodes as number[]).join(','),
1335
1337
  );
1336
1338
  }
1337
1339
  return charCodes;
@@ -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|mp4a\.40\.34/i;
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 | undefined,
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
- refDetails: LevelDetails | undefined,
20
+ lastFrag: Fragment | null,
21
+ switchDetails: LevelDetails | undefined,
21
22
  details: LevelDetails,
22
- ): refDetails is LevelDetails & boolean {
23
- if (refDetails) {
23
+ ): switchDetails is LevelDetails & boolean {
24
+ if (switchDetails) {
24
25
  if (
25
- details.startCC < refDetails.endCC &&
26
- details.endCC > refDetails.startCC
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
- * Ajust the start of fragments in `details` by the difference in time between fragments of the latest
90
- * shared discontinuity sequence change.
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
- export function alignDiscontinuities(
119
+ function alignDiscontinuities(
120
+ lastFrag: Fragment | null,
95
121
  details: LevelDetails,
96
- refDetails: LevelDetails | undefined,
122
+ switchDetails: LevelDetails | undefined,
97
123
  ) {
98
- if (!shouldAlignOnDiscontinuities(refDetails, details)) {
99
- return;
100
- }
101
- const targetCC = Math.min(refDetails.endCC, details.endCC);
102
- const refFrag = findFirstFragWithCC(refDetails.fragments, targetCC);
103
- const frag = findFirstFragWithCC(details.fragments, targetCC);
104
- if (!refFrag || !frag) {
105
- return;
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
  /**
@@ -1,4 +1,4 @@
1
- import type {
1
+ import {
2
2
  LoaderCallbacks,
3
3
  LoaderContext,
4
4
  Loader,
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
- : allowAutoPreferHDR && isHdrSupported();
58
- if (!preferHDR) {
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 '@svta/common-media-library/utils/utf8ArrayToStr';
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
+ }