hls.js 1.5.8-0.canary.10153 → 1.5.8-0.canary.10154

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/package.json CHANGED
@@ -130,5 +130,5 @@
130
130
  "url-toolkit": "2.2.5",
131
131
  "wrangler": "3.45.0"
132
132
  },
133
- "version": "1.5.8-0.canary.10153"
133
+ "version": "1.5.8-0.canary.10154"
134
134
  }
@@ -19,7 +19,7 @@ import {
19
19
  KeySystems,
20
20
  requestMediaKeySystemAccess,
21
21
  } from '../utils/mediakeys-helper';
22
- import { strToUtf8array } from '../utils/keysystem-util';
22
+ import { strToUtf8array } from '../utils/utf8-utils';
23
23
  import { base64Decode } from '../utils/numeric-encoding-utils';
24
24
  import { DecryptData, LevelKey } from '../loader/level-key';
25
25
  import Hex from '../utils/hex';
@@ -4,7 +4,6 @@ import {
4
4
  clearCurrentCues,
5
5
  removeCuesInRange,
6
6
  } from '../utils/texttrack-utils';
7
- import * as ID3 from '../demux/id3';
8
7
  import {
9
8
  DateRange,
10
9
  isDateRangeCueAttribute,
@@ -19,6 +18,8 @@ import type {
19
18
  } from '../types/events';
20
19
  import type { ComponentAPI } from '../types/component-api';
21
20
  import type Hls from '../hls';
21
+ import { getId3Frames } from '@svta/common-media-library/id3/getId3Frames';
22
+ import { isId3TimestampFrame } from '@svta/common-media-library/id3/isId3TimestampFrame';
22
23
 
23
24
  declare global {
24
25
  interface Window {
@@ -210,7 +211,7 @@ class ID3TrackController implements ComponentAPI {
210
211
  continue;
211
212
  }
212
213
 
213
- const frames = ID3.getID3Frames(samples[i].data);
214
+ const frames = getId3Frames(samples[i].data);
214
215
  if (frames) {
215
216
  const startTime = samples[i].pts;
216
217
  let endTime: number = startTime + samples[i].duration;
@@ -227,7 +228,7 @@ class ID3TrackController implements ComponentAPI {
227
228
  for (let j = 0; j < frames.length; j++) {
228
229
  const frame = frames[j];
229
230
  // Safari doesn't put the timestamp frame in the TextTrack
230
- if (!ID3.isTimeStampFrame(frame)) {
231
+ if (!isId3TimestampFrame(frame)) {
231
232
  // add a bounds to any unbounded cues
232
233
  this.updateId3CueEnds(startTime, type);
233
234
  const cue = createCueWithDataFields(
@@ -5,7 +5,7 @@ import BaseAudioDemuxer from './base-audio-demuxer';
5
5
  import * as ADTS from './adts';
6
6
  import * as MpegAudio from './mpegaudio';
7
7
  import { logger } from '../../utils/logger';
8
- import * as ID3 from '../id3';
8
+ import { getId3Data } from '@svta/common-media-library/id3/getId3Data';
9
9
  import type { HlsEventEmitter } from '../../events';
10
10
  import type { HlsConfig } from '../../config';
11
11
 
@@ -51,7 +51,7 @@ class AACDemuxer extends BaseAudioDemuxer {
51
51
  // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1
52
52
  // Layer bits (position 14 and 15) in header should be always 0 for ADTS
53
53
  // More info https://wiki.multimedia.cx/index.php?title=ADTS
54
- const id3Data = ID3.getID3Data(data, 0);
54
+ const id3Data = getId3Data(data, 0);
55
55
  let offset = id3Data?.length || 0;
56
56
 
57
57
  if (MpegAudio.probe(data, offset)) {
@@ -1,5 +1,6 @@
1
1
  import BaseAudioDemuxer from './base-audio-demuxer';
2
- import { getID3Data, getTimeStamp } from '../id3';
2
+ import { getId3Data } from '@svta/common-media-library/id3/getId3Data';
3
+ import { getId3Timestamp } from '@svta/common-media-library/id3/getId3Timestamp';
3
4
  import { getAudioBSID } from './dolby';
4
5
  import type { HlsEventEmitter } from '../../events';
5
6
  import type { AudioFrame, DemuxedAudioTrack } from '../../types/demuxer';
@@ -61,7 +62,7 @@ export class AC3Demuxer extends BaseAudioDemuxer {
61
62
  return false;
62
63
  }
63
64
 
64
- const id3Data = getID3Data(data, 0);
65
+ const id3Data = getId3Data(data, 0);
65
66
  if (!id3Data) {
66
67
  return false;
67
68
  }
@@ -71,7 +72,7 @@ export class AC3Demuxer extends BaseAudioDemuxer {
71
72
  if (
72
73
  data[offset] === 0x0b &&
73
74
  data[offset + 1] === 0x77 &&
74
- getTimeStamp(id3Data) !== undefined &&
75
+ getId3Timestamp(id3Data) !== undefined &&
75
76
  // check the bsid to confirm ac-3
76
77
  getAudioBSID(data, offset) < 16
77
78
  ) {
@@ -1,4 +1,3 @@
1
- import * as ID3 from '../id3';
2
1
  import {
3
2
  DemuxerResult,
4
3
  Demuxer,
@@ -14,6 +13,9 @@ import { dummyTrack } from '../dummy-demuxed-track';
14
13
  import { appendUint8Array } from '../../utils/mp4-tools';
15
14
  import { sliceUint8 } from '../../utils/typed-array';
16
15
  import { RationalTimestamp } from '../../utils/timescale-conversion';
16
+ import { getId3Data } from '@svta/common-media-library/id3/getId3Data';
17
+ import { getId3Timestamp } from '@svta/common-media-library/id3/getId3Timestamp';
18
+ import { canParseId3 } from '@svta/common-media-library/id3/canParseId3';
17
19
 
18
20
  class BaseAudioDemuxer implements Demuxer {
19
21
  protected _audioTrack!: DemuxedAudioTrack;
@@ -69,12 +71,12 @@ class BaseAudioDemuxer implements Demuxer {
69
71
  this.cachedData = null;
70
72
  }
71
73
 
72
- let id3Data: Uint8Array | undefined = ID3.getID3Data(data, 0);
74
+ let id3Data: Uint8Array | undefined = getId3Data(data, 0);
73
75
  let offset = id3Data ? id3Data.length : 0;
74
76
  let lastDataIndex;
75
77
  const track = this._audioTrack;
76
78
  const id3Track = this._id3Track;
77
- const timestamp = id3Data ? ID3.getTimeStamp(id3Data) : undefined;
79
+ const timestamp = id3Data ? getId3Timestamp(id3Data) : undefined;
78
80
  const length = data.length;
79
81
 
80
82
  if (
@@ -111,9 +113,9 @@ class BaseAudioDemuxer implements Demuxer {
111
113
  } else {
112
114
  offset = length;
113
115
  }
114
- } else if (ID3.canParse(data, offset)) {
115
- // after a ID3.canParse, a call to ID3.getID3Data *should* always returns some data
116
- id3Data = ID3.getID3Data(data, offset)!;
116
+ } else if (canParseId3(data, offset)) {
117
+ // after a canParse, a call to getId3Data *should* always returns some data
118
+ id3Data = getId3Data(data, offset)!;
117
119
  id3Track.samples.push({
118
120
  pts: this.lastPTS,
119
121
  dts: this.lastPTS,
@@ -2,10 +2,11 @@
2
2
  * MP3 demuxer
3
3
  */
4
4
  import BaseAudioDemuxer from './base-audio-demuxer';
5
- import { getID3Data, getTimeStamp } from '../id3';
6
5
  import { getAudioBSID } from './dolby';
7
6
  import { logger } from '../../utils/logger';
8
7
  import * as MpegAudio from './mpegaudio';
8
+ import { getId3Data } from '@svta/common-media-library/id3/getId3Data';
9
+ import { getId3Timestamp } from '@svta/common-media-library/id3/getId3Timestamp';
9
10
 
10
11
  class MP3Demuxer extends BaseAudioDemuxer {
11
12
  resetInitSegment(
@@ -39,7 +40,7 @@ class MP3Demuxer extends BaseAudioDemuxer {
39
40
  // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1
40
41
  // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III)
41
42
  // More info http://www.mp3-tech.org/programmer/frame_header.html
42
- const id3Data = getID3Data(data, 0);
43
+ const id3Data = getId3Data(data, 0);
43
44
  let offset = id3Data?.length || 0;
44
45
 
45
46
  // Check for ac-3|ec-3 sync bytes and return false if present
@@ -47,7 +48,7 @@ class MP3Demuxer extends BaseAudioDemuxer {
47
48
  id3Data &&
48
49
  data[offset] === 0x0b &&
49
50
  data[offset + 1] === 0x77 &&
50
- getTimeStamp(id3Data) !== undefined &&
51
+ getId3Timestamp(id3Data) !== undefined &&
51
52
  // check the bsid to confirm ac-3 or ec-3 (not mp3)
52
53
  getAudioBSID(data, offset) <= 16
53
54
  ) {
@@ -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 '../demux/id3';
4
+ import { utf8ArrayToStr } from '@svta/common-media-library/utils/utf8ArrayToStr';
5
5
  import {
6
6
  RationalTimestamp,
7
7
  toTimescaleFromScale,
@@ -1,4 +1,5 @@
1
1
  import { base64Decode } from './numeric-encoding-utils';
2
+ import { strToUtf8array } from './utf8-utils';
2
3
 
3
4
  function getKeyIdBytes(str: string): Uint8Array {
4
5
  const keyIdbytes = strToUtf8array(str).subarray(0, 16);
@@ -40,9 +41,3 @@ export function convertDataUriToArrayBytes(uri: string): Uint8Array | null {
40
41
  }
41
42
  return keydata;
42
43
  }
43
-
44
- export function strToUtf8array(str: string): Uint8Array {
45
- return Uint8Array.from(unescape(encodeURIComponent(str)), (c) =>
46
- c.charCodeAt(0),
47
- );
48
- }
@@ -1,6 +1,6 @@
1
1
  import { ElementaryStreamTypes } from '../loader/fragment';
2
2
  import { sliceUint8 } from './typed-array';
3
- import { utf8ArrayToStr } from '../demux/id3';
3
+ import { utf8ArrayToStr } from '@svta/common-media-library/utils/utf8ArrayToStr';
4
4
  import { logger } from '../utils/logger';
5
5
  import Hex from './hex';
6
6
  import type { PassthroughTrack, UserdataSample } from '../types/demuxer';
@@ -0,0 +1,18 @@
1
+ // breaking up those two types in order to clarify what is happening in the decoding path.
2
+ type DecodedFrame<T> = { key: string; data: T; info?: any };
3
+ export type Frame = DecodedFrame<ArrayBuffer | string>;
4
+ // http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197
5
+ // http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
6
+ /* utf.js - UTF-8 <=> UTF-16 convertion
7
+ *
8
+ * Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
9
+ * Version: 1.0
10
+ * LastModified: Dec 25 1999
11
+ * This library is free. You can redistribute it and/or modify it.
12
+ */
13
+
14
+ export function strToUtf8array(str: string): Uint8Array {
15
+ return Uint8Array.from(unescape(encodeURIComponent(str)), (c) =>
16
+ c.charCodeAt(0),
17
+ );
18
+ }
@@ -1,5 +1,5 @@
1
1
  import { VTTParser } from './vttparser';
2
- import { utf8ArrayToStr } from '../demux/id3';
2
+ import { utf8ArrayToStr } from '@svta/common-media-library/utils/utf8ArrayToStr';
3
3
  import {
4
4
  RationalTimestamp,
5
5
  toMpegTsClockFromTimescale,
package/src/demux/id3.ts DELETED
@@ -1,411 +0,0 @@
1
- type RawFrame = { type: string; size: number; data: Uint8Array };
2
-
3
- // breaking up those two types in order to clarify what is happening in the decoding path.
4
- type DecodedFrame<T> = { key: string; data: T; info?: any };
5
- export type Frame = DecodedFrame<ArrayBuffer | string>;
6
-
7
- /**
8
- * Returns true if an ID3 header can be found at offset in data
9
- * @param data - The data to search
10
- * @param offset - The offset at which to start searching
11
- */
12
- export const isHeader = (data: Uint8Array, offset: number): boolean => {
13
- /*
14
- * http://id3.org/id3v2.3.0
15
- * [0] = 'I'
16
- * [1] = 'D'
17
- * [2] = '3'
18
- * [3,4] = {Version}
19
- * [5] = {Flags}
20
- * [6-9] = {ID3 Size}
21
- *
22
- * An ID3v2 tag can be detected with the following pattern:
23
- * $49 44 33 yy yy xx zz zz zz zz
24
- * Where yy is less than $FF, xx is the 'flags' byte and zz is less than $80
25
- */
26
- if (offset + 10 <= data.length) {
27
- // look for 'ID3' identifier
28
- if (
29
- data[offset] === 0x49 &&
30
- data[offset + 1] === 0x44 &&
31
- data[offset + 2] === 0x33
32
- ) {
33
- // check version is within range
34
- if (data[offset + 3] < 0xff && data[offset + 4] < 0xff) {
35
- // check size is within range
36
- if (
37
- data[offset + 6] < 0x80 &&
38
- data[offset + 7] < 0x80 &&
39
- data[offset + 8] < 0x80 &&
40
- data[offset + 9] < 0x80
41
- ) {
42
- return true;
43
- }
44
- }
45
- }
46
- }
47
-
48
- return false;
49
- };
50
-
51
- /**
52
- * Returns true if an ID3 footer can be found at offset in data
53
- * @param data - The data to search
54
- * @param offset - The offset at which to start searching
55
- */
56
- export const isFooter = (data: Uint8Array, offset: number): boolean => {
57
- /*
58
- * The footer is a copy of the header, but with a different identifier
59
- */
60
- if (offset + 10 <= data.length) {
61
- // look for '3DI' identifier
62
- if (
63
- data[offset] === 0x33 &&
64
- data[offset + 1] === 0x44 &&
65
- data[offset + 2] === 0x49
66
- ) {
67
- // check version is within range
68
- if (data[offset + 3] < 0xff && data[offset + 4] < 0xff) {
69
- // check size is within range
70
- if (
71
- data[offset + 6] < 0x80 &&
72
- data[offset + 7] < 0x80 &&
73
- data[offset + 8] < 0x80 &&
74
- data[offset + 9] < 0x80
75
- ) {
76
- return true;
77
- }
78
- }
79
- }
80
- }
81
-
82
- return false;
83
- };
84
-
85
- /**
86
- * Returns any adjacent ID3 tags found in data starting at offset, as one block of data
87
- * @param data - The data to search in
88
- * @param offset - The offset at which to start searching
89
- * @returns the block of data containing any ID3 tags found
90
- * or *undefined* if no header is found at the starting offset
91
- */
92
- export const getID3Data = (
93
- data: Uint8Array,
94
- offset: number,
95
- ): Uint8Array | undefined => {
96
- const front = offset;
97
- let length = 0;
98
-
99
- while (isHeader(data, offset)) {
100
- // ID3 header is 10 bytes
101
- length += 10;
102
-
103
- const size = readSize(data, offset + 6);
104
- length += size;
105
-
106
- if (isFooter(data, offset + 10)) {
107
- // ID3 footer is 10 bytes
108
- length += 10;
109
- }
110
-
111
- offset += length;
112
- }
113
-
114
- if (length > 0) {
115
- return data.subarray(front, front + length);
116
- }
117
-
118
- return undefined;
119
- };
120
-
121
- const readSize = (data: Uint8Array, offset: number): number => {
122
- let size = 0;
123
- size = (data[offset] & 0x7f) << 21;
124
- size |= (data[offset + 1] & 0x7f) << 14;
125
- size |= (data[offset + 2] & 0x7f) << 7;
126
- size |= data[offset + 3] & 0x7f;
127
- return size;
128
- };
129
-
130
- export const canParse = (data: Uint8Array, offset: number): boolean => {
131
- return (
132
- isHeader(data, offset) &&
133
- readSize(data, offset + 6) + 10 <= data.length - offset
134
- );
135
- };
136
-
137
- /**
138
- * Searches for the Elementary Stream timestamp found in the ID3 data chunk
139
- * @param data - Block of data containing one or more ID3 tags
140
- */
141
- export const getTimeStamp = (data: Uint8Array): number | undefined => {
142
- const frames: Frame[] = getID3Frames(data);
143
-
144
- for (let i = 0; i < frames.length; i++) {
145
- const frame = frames[i];
146
-
147
- if (isTimeStampFrame(frame)) {
148
- return readTimeStamp(frame as DecodedFrame<ArrayBuffer>);
149
- }
150
- }
151
-
152
- return undefined;
153
- };
154
-
155
- /**
156
- * Returns true if the ID3 frame is an Elementary Stream timestamp frame
157
- */
158
- export const isTimeStampFrame = (frame: Frame): boolean => {
159
- return (
160
- frame &&
161
- frame.key === 'PRIV' &&
162
- frame.info === 'com.apple.streaming.transportStreamTimestamp'
163
- );
164
- };
165
-
166
- const getFrameData = (data: Uint8Array): RawFrame => {
167
- /*
168
- Frame ID $xx xx xx xx (four characters)
169
- Size $xx xx xx xx
170
- Flags $xx xx
171
- */
172
- const type: string = String.fromCharCode(data[0], data[1], data[2], data[3]);
173
- const size: number = readSize(data, 4);
174
-
175
- // skip frame id, size, and flags
176
- const offset = 10;
177
-
178
- return { type, size, data: data.subarray(offset, offset + size) };
179
- };
180
-
181
- /**
182
- * Returns an array of ID3 frames found in all the ID3 tags in the id3Data
183
- * @param id3Data - The ID3 data containing one or more ID3 tags
184
- */
185
- export const getID3Frames = (id3Data: Uint8Array): Frame[] => {
186
- let offset = 0;
187
- const frames: Frame[] = [];
188
-
189
- while (isHeader(id3Data, offset)) {
190
- const size = readSize(id3Data, offset + 6);
191
- // skip past ID3 header
192
- offset += 10;
193
- const end = offset + size;
194
- // loop through frames in the ID3 tag
195
- while (offset + 8 < end) {
196
- const frameData: RawFrame = getFrameData(id3Data.subarray(offset));
197
- const frame: Frame | undefined = decodeFrame(frameData);
198
- if (frame) {
199
- frames.push(frame);
200
- }
201
-
202
- // skip frame header and frame data
203
- offset += frameData.size + 10;
204
- }
205
-
206
- if (isFooter(id3Data, offset)) {
207
- offset += 10;
208
- }
209
- }
210
-
211
- return frames;
212
- };
213
-
214
- export const decodeFrame = (frame: RawFrame): Frame | undefined => {
215
- if (frame.type === 'PRIV') {
216
- return decodePrivFrame(frame);
217
- } else if (frame.type[0] === 'W') {
218
- return decodeURLFrame(frame);
219
- }
220
-
221
- return decodeTextFrame(frame);
222
- };
223
-
224
- const decodePrivFrame = (
225
- frame: RawFrame,
226
- ): DecodedFrame<ArrayBuffer> | undefined => {
227
- /*
228
- Format: <text string>\0<binary data>
229
- */
230
- if (frame.size < 2) {
231
- return undefined;
232
- }
233
-
234
- const owner = utf8ArrayToStr(frame.data, true);
235
- const privateData = new Uint8Array(frame.data.subarray(owner.length + 1));
236
-
237
- return { key: frame.type, info: owner, data: privateData.buffer };
238
- };
239
-
240
- const decodeTextFrame = (frame: RawFrame): DecodedFrame<string> | undefined => {
241
- if (frame.size < 2) {
242
- return undefined;
243
- }
244
-
245
- if (frame.type === 'TXXX') {
246
- /*
247
- Format:
248
- [0] = {Text Encoding}
249
- [1-?] = {Description}\0{Value}
250
- */
251
- let index = 1;
252
- const description = utf8ArrayToStr(frame.data.subarray(index), true);
253
-
254
- index += description.length + 1;
255
- const value = utf8ArrayToStr(frame.data.subarray(index));
256
-
257
- return { key: frame.type, info: description, data: value };
258
- }
259
- /*
260
- Format:
261
- [0] = {Text Encoding}
262
- [1-?] = {Value}
263
- */
264
- const text = utf8ArrayToStr(frame.data.subarray(1));
265
- return { key: frame.type, data: text };
266
- };
267
-
268
- const decodeURLFrame = (frame: RawFrame): DecodedFrame<string> | undefined => {
269
- if (frame.type === 'WXXX') {
270
- /*
271
- Format:
272
- [0] = {Text Encoding}
273
- [1-?] = {Description}\0{URL}
274
- */
275
- if (frame.size < 2) {
276
- return undefined;
277
- }
278
-
279
- let index = 1;
280
- const description: string = utf8ArrayToStr(
281
- frame.data.subarray(index),
282
- true,
283
- );
284
-
285
- index += description.length + 1;
286
- const value: string = utf8ArrayToStr(frame.data.subarray(index));
287
-
288
- return { key: frame.type, info: description, data: value };
289
- }
290
- /*
291
- Format:
292
- [0-?] = {URL}
293
- */
294
- const url: string = utf8ArrayToStr(frame.data);
295
- return { key: frame.type, data: url };
296
- };
297
-
298
- const readTimeStamp = (
299
- timeStampFrame: DecodedFrame<ArrayBuffer>,
300
- ): number | undefined => {
301
- if (timeStampFrame.data.byteLength === 8) {
302
- const data = new Uint8Array(timeStampFrame.data);
303
- // timestamp is 33 bit expressed as a big-endian eight-octet number,
304
- // with the upper 31 bits set to zero.
305
- const pts33Bit = data[3] & 0x1;
306
- let timestamp =
307
- (data[4] << 23) + (data[5] << 15) + (data[6] << 7) + data[7];
308
- timestamp /= 45;
309
-
310
- if (pts33Bit) {
311
- timestamp += 47721858.84;
312
- } // 2^32 / 90
313
-
314
- return Math.round(timestamp);
315
- }
316
-
317
- return undefined;
318
- };
319
-
320
- // http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197
321
- // http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
322
- /* utf.js - UTF-8 <=> UTF-16 convertion
323
- *
324
- * Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
325
- * Version: 1.0
326
- * LastModified: Dec 25 1999
327
- * This library is free. You can redistribute it and/or modify it.
328
- */
329
- export const utf8ArrayToStr = (
330
- array: Uint8Array,
331
- exitOnNull: boolean = false,
332
- ): string => {
333
- const decoder = getTextDecoder();
334
- if (decoder) {
335
- const decoded = decoder.decode(array);
336
-
337
- if (exitOnNull) {
338
- // grab up to the first null
339
- const idx = decoded.indexOf('\0');
340
- return idx !== -1 ? decoded.substring(0, idx) : decoded;
341
- }
342
-
343
- // remove any null characters
344
- return decoded.replace(/\0/g, '');
345
- }
346
-
347
- const len = array.length;
348
- let c;
349
- let char2;
350
- let char3;
351
- let out = '';
352
- let i = 0;
353
- while (i < len) {
354
- c = array[i++];
355
- if (c === 0x00 && exitOnNull) {
356
- return out;
357
- } else if (c === 0x00 || c === 0x03) {
358
- // If the character is 3 (END_OF_TEXT) or 0 (NULL) then skip it
359
- continue;
360
- }
361
- switch (c >> 4) {
362
- case 0:
363
- case 1:
364
- case 2:
365
- case 3:
366
- case 4:
367
- case 5:
368
- case 6:
369
- case 7:
370
- // 0xxxxxxx
371
- out += String.fromCharCode(c);
372
- break;
373
- case 12:
374
- case 13:
375
- // 110x xxxx 10xx xxxx
376
- char2 = array[i++];
377
- out += String.fromCharCode(((c & 0x1f) << 6) | (char2 & 0x3f));
378
- break;
379
- case 14:
380
- // 1110 xxxx 10xx xxxx 10xx xxxx
381
- char2 = array[i++];
382
- char3 = array[i++];
383
- out += String.fromCharCode(
384
- ((c & 0x0f) << 12) | ((char2 & 0x3f) << 6) | ((char3 & 0x3f) << 0),
385
- );
386
- break;
387
- default:
388
- }
389
- }
390
- return out;
391
- };
392
-
393
- export const testables = {
394
- decodeTextFrame: decodeTextFrame,
395
- };
396
-
397
- let decoder: TextDecoder;
398
-
399
- function getTextDecoder() {
400
- // On Play Station 4, TextDecoder is defined but partially implemented.
401
- // Manual decoding option is preferable
402
- if (navigator.userAgent.includes('PlayStation 4')) {
403
- return;
404
- }
405
-
406
- if (!decoder && typeof self.TextDecoder !== 'undefined') {
407
- decoder = new self.TextDecoder('utf-8');
408
- }
409
-
410
- return decoder;
411
- }