hls.js 1.5.8-0.canary.10170 → 1.5.8
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 +3 -12
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +2366 -3626
- package/dist/hls.js.d.ts +84 -98
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +1643 -2278
- 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 +1258 -1903
- 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 +1531 -2794
- 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 +30 -30
- package/src/config.ts +2 -3
- package/src/controller/abr-controller.ts +20 -24
- package/src/controller/audio-stream-controller.ts +74 -68
- 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 +38 -160
- package/src/controller/buffer-controller.ts +92 -230
- package/src/controller/buffer-operation-queue.ts +19 -16
- package/src/controller/cap-level-controller.ts +2 -3
- package/src/controller/cmcd-controller.ts +9 -30
- package/src/controller/content-steering-controller.ts +6 -8
- package/src/controller/eme-controller.ts +23 -10
- package/src/controller/error-controller.ts +8 -6
- package/src/controller/fps-controller.ts +3 -8
- package/src/controller/fragment-tracker.ts +11 -15
- package/src/controller/gap-controller.ts +16 -43
- package/src/controller/id3-track-controller.ts +7 -7
- package/src/controller/latency-controller.ts +11 -9
- package/src/controller/level-controller.ts +19 -13
- package/src/controller/stream-controller.ts +32 -37
- package/src/controller/subtitle-stream-controller.ts +40 -28
- package/src/controller/subtitle-track-controller.ts +3 -5
- package/src/controller/timeline-controller.ts +31 -25
- package/src/crypt/aes-crypto.ts +2 -21
- package/src/crypt/decrypter.ts +18 -32
- package/src/crypt/fast-aes-key.ts +5 -24
- package/src/demux/audio/aacdemuxer.ts +2 -2
- package/src/demux/audio/ac3-demuxer.ts +3 -4
- 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/mp4demuxer.ts +7 -7
- package/src/demux/sample-aes.ts +0 -2
- package/src/demux/transmuxer-interface.ts +12 -4
- package/src/demux/transmuxer-worker.ts +4 -4
- package/src/demux/transmuxer.ts +3 -16
- package/src/demux/tsdemuxer.ts +37 -71
- package/src/demux/video/avc-video-parser.ts +119 -208
- package/src/demux/video/base-video-parser.ts +2 -134
- package/src/demux/video/exp-golomb.ts +208 -0
- package/src/events.ts +1 -8
- package/src/exports-named.ts +1 -1
- package/src/hls.ts +37 -49
- package/src/loader/fragment-loader.ts +3 -10
- package/src/loader/key-loader.ts +1 -3
- package/src/loader/level-key.ts +9 -10
- package/src/loader/playlist-loader.ts +5 -4
- package/src/remux/mp4-generator.ts +1 -196
- package/src/remux/mp4-remuxer.ts +8 -24
- 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 +0 -4
- package/src/types/remuxer.ts +1 -1
- package/src/utils/buffer-helper.ts +31 -12
- package/src/utils/codecs.ts +5 -34
- package/src/utils/fetch-loader.ts +1 -1
- package/src/utils/imsc1-ttml-parser.ts +1 -1
- package/src/utils/keysystem-util.ts +6 -1
- package/src/utils/logger.ts +23 -58
- package/src/utils/mp4-tools.ts +3 -5
- package/src/utils/webvtt-parser.ts +1 -1
- 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/utf8-utils.ts +0 -18
@@ -1,6 +1,6 @@
|
|
1
1
|
import { ErrorTypes, ErrorDetails } from '../errors';
|
2
2
|
import { Fragment } from './fragment';
|
3
|
-
import
|
3
|
+
import {
|
4
4
|
Loader,
|
5
5
|
LoaderConfiguration,
|
6
6
|
FragmentLoaderContext,
|
@@ -336,11 +336,8 @@ function createLoaderContext(
|
|
336
336
|
if (Number.isFinite(start) && Number.isFinite(end)) {
|
337
337
|
let byteRangeStart = start;
|
338
338
|
let byteRangeEnd = end;
|
339
|
-
if (
|
340
|
-
|
341
|
-
isMethodFullSegmentAesCbc(frag.decryptdata?.method)
|
342
|
-
) {
|
343
|
-
// MAP segment encrypted with method 'AES-128' or 'AES-256' (cbc), when served with HTTP Range,
|
339
|
+
if (frag.sn === 'initSegment' && frag.decryptdata?.method === 'AES-128') {
|
340
|
+
// MAP segment encrypted with method 'AES-128', when served with HTTP Range,
|
344
341
|
// has the unencrypted size specified in the range.
|
345
342
|
// Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6
|
346
343
|
const fragmentLen = end - start;
|
@@ -375,10 +372,6 @@ function createGapLoadError(frag: Fragment, part?: Part): LoadError {
|
|
375
372
|
return new LoadError(errorData);
|
376
373
|
}
|
377
374
|
|
378
|
-
function isMethodFullSegmentAesCbc(method) {
|
379
|
-
return method === 'AES-128' || method === 'AES-256';
|
380
|
-
}
|
381
|
-
|
382
375
|
export class LoadError extends Error {
|
383
376
|
public readonly data: FragLoadFailResult;
|
384
377
|
constructor(data: FragLoadFailResult) {
|
package/src/loader/key-loader.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { ErrorTypes, ErrorDetails } from '../errors';
|
2
|
-
import
|
2
|
+
import {
|
3
3
|
LoaderStats,
|
4
4
|
LoaderResponse,
|
5
5
|
LoaderConfiguration,
|
@@ -194,8 +194,6 @@ export default class KeyLoader implements ComponentAPI {
|
|
194
194
|
}
|
195
195
|
return this.loadKeyEME(keyInfo, frag);
|
196
196
|
case 'AES-128':
|
197
|
-
case 'AES-256':
|
198
|
-
case 'AES-256-CTR':
|
199
197
|
return this.loadKeyHTTP(keyInfo, frag);
|
200
198
|
default:
|
201
199
|
return Promise.reject(
|
package/src/loader/level-key.ts
CHANGED
@@ -2,7 +2,6 @@ import {
|
|
2
2
|
changeEndianness,
|
3
3
|
convertDataUriToArrayBytes,
|
4
4
|
} from '../utils/keysystem-util';
|
5
|
-
import { isFullSegmentEncryption } from '../utils/encryption-methods-util';
|
6
5
|
import { KeySystemFormats } from '../utils/mediakeys-helper';
|
7
6
|
import { mp4pssh } from '../utils/mp4-tools';
|
8
7
|
import { logger } from '../utils/logger';
|
@@ -52,14 +51,13 @@ export class LevelKey implements DecryptData {
|
|
52
51
|
this.keyFormatVersions = formatversions;
|
53
52
|
this.iv = iv;
|
54
53
|
this.encrypted = method ? method !== 'NONE' : false;
|
55
|
-
this.isCommonEncryption =
|
56
|
-
this.encrypted && !isFullSegmentEncryption(method);
|
54
|
+
this.isCommonEncryption = this.encrypted && method !== 'AES-128';
|
57
55
|
}
|
58
56
|
|
59
57
|
public isSupported(): boolean {
|
60
58
|
// If it's Segment encryption or No encryption, just select that key system
|
61
59
|
if (this.method) {
|
62
|
-
if (
|
60
|
+
if (this.method === 'AES-128' || this.method === 'NONE') {
|
63
61
|
return true;
|
64
62
|
}
|
65
63
|
if (this.keyFormat === 'identity') {
|
@@ -90,15 +88,16 @@ export class LevelKey implements DecryptData {
|
|
90
88
|
return null;
|
91
89
|
}
|
92
90
|
|
93
|
-
if (
|
91
|
+
if (this.method === 'AES-128' && this.uri && !this.iv) {
|
94
92
|
if (typeof sn !== 'number') {
|
95
93
|
// We are fetching decryption data for a initialization segment
|
96
|
-
// If the segment was encrypted with AES-128
|
94
|
+
// If the segment was encrypted with AES-128
|
97
95
|
// It must have an IV defined. We cannot substitute the Segment Number in.
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
96
|
+
if (this.method === 'AES-128' && !this.iv) {
|
97
|
+
logger.warn(
|
98
|
+
`missing IV for initialization segment with method="${this.method}" - compliance issue`,
|
99
|
+
);
|
100
|
+
}
|
102
101
|
// Explicitly set sn to resulting value from implicit conversions 'initSegment' values for IV generation.
|
103
102
|
sn = 0;
|
104
103
|
}
|
@@ -8,6 +8,7 @@
|
|
8
8
|
|
9
9
|
import { Events } from '../events';
|
10
10
|
import { ErrorDetails, ErrorTypes } from '../errors';
|
11
|
+
import { logger } from '../utils/logger';
|
11
12
|
import M3U8Parser from './m3u8-parser';
|
12
13
|
import type { LevelParsed, VariableMap } from '../types/level';
|
13
14
|
import type {
|
@@ -220,10 +221,10 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
220
221
|
loaderContext.level === context.level
|
221
222
|
) {
|
222
223
|
// same URL can't overlap
|
223
|
-
|
224
|
+
logger.trace('[playlist-loader]: playlist request ongoing');
|
224
225
|
return;
|
225
226
|
}
|
226
|
-
|
227
|
+
logger.log(
|
227
228
|
`[playlist-loader]: aborting previous loader for type: ${context.type}`,
|
228
229
|
);
|
229
230
|
loader.abort();
|
@@ -407,7 +408,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
407
408
|
levels[0].audioCodec &&
|
408
409
|
!levels[0].attrs.AUDIO
|
409
410
|
) {
|
410
|
-
|
411
|
+
logger.log(
|
411
412
|
'[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one',
|
412
413
|
);
|
413
414
|
audioTracks.unshift({
|
@@ -554,7 +555,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
554
555
|
message += ` id: ${context.id} group-id: "${context.groupId}"`;
|
555
556
|
}
|
556
557
|
const error = new Error(message);
|
557
|
-
|
558
|
+
logger.warn(`[playlist-loader]: ${message}`);
|
558
559
|
let details = ErrorDetails.UNKNOWN;
|
559
560
|
let fatal = false;
|
560
561
|
|
@@ -28,8 +28,6 @@ class MP4 {
|
|
28
28
|
MP4.types = {
|
29
29
|
avc1: [], // codingname
|
30
30
|
avcC: [],
|
31
|
-
hvc1: [],
|
32
|
-
hvcC: [],
|
33
31
|
btrt: [],
|
34
32
|
dinf: [],
|
35
33
|
dref: [],
|
@@ -841,10 +839,8 @@ class MP4 {
|
|
841
839
|
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ac3(track));
|
842
840
|
}
|
843
841
|
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
|
844
|
-
} else if (track.segmentCodec === 'avc') {
|
845
|
-
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
|
846
842
|
} else {
|
847
|
-
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.
|
843
|
+
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
|
848
844
|
}
|
849
845
|
}
|
850
846
|
|
@@ -1127,197 +1123,6 @@ class MP4 {
|
|
1127
1123
|
const result = appendUint8Array(MP4.FTYP, movie);
|
1128
1124
|
return result;
|
1129
1125
|
}
|
1130
|
-
|
1131
|
-
static hvc1(track) {
|
1132
|
-
const ps = track.params;
|
1133
|
-
const units = [track.vps, track.sps, track.pps];
|
1134
|
-
const NALuLengthSize = 4;
|
1135
|
-
const config = new Uint8Array([
|
1136
|
-
0x01,
|
1137
|
-
(ps.general_profile_space << 6) |
|
1138
|
-
(ps.general_tier_flag ? 32 : 0) |
|
1139
|
-
ps.general_profile_idc,
|
1140
|
-
ps.general_profile_compatibility_flags[0],
|
1141
|
-
ps.general_profile_compatibility_flags[1],
|
1142
|
-
ps.general_profile_compatibility_flags[2],
|
1143
|
-
ps.general_profile_compatibility_flags[3],
|
1144
|
-
ps.general_constraint_indicator_flags[0],
|
1145
|
-
ps.general_constraint_indicator_flags[1],
|
1146
|
-
ps.general_constraint_indicator_flags[2],
|
1147
|
-
ps.general_constraint_indicator_flags[3],
|
1148
|
-
ps.general_constraint_indicator_flags[4],
|
1149
|
-
ps.general_constraint_indicator_flags[5],
|
1150
|
-
ps.general_level_idc,
|
1151
|
-
240 | (ps.min_spatial_segmentation_idc >> 8),
|
1152
|
-
255 & ps.min_spatial_segmentation_idc,
|
1153
|
-
252 | ps.parallelismType,
|
1154
|
-
252 | ps.chroma_format_idc,
|
1155
|
-
248 | ps.bit_depth_luma_minus8,
|
1156
|
-
248 | ps.bit_depth_chroma_minus8,
|
1157
|
-
0x00,
|
1158
|
-
parseInt(ps.frame_rate.fps),
|
1159
|
-
(NALuLengthSize - 1) |
|
1160
|
-
(ps.temporal_id_nested << 2) |
|
1161
|
-
(ps.num_temporal_layers << 3) |
|
1162
|
-
(ps.frame_rate.fixed ? 64 : 0),
|
1163
|
-
units.length,
|
1164
|
-
]);
|
1165
|
-
|
1166
|
-
// compute hvcC size in bytes
|
1167
|
-
let length = config.length;
|
1168
|
-
for (let i = 0; i < units.length; i += 1) {
|
1169
|
-
length += 3;
|
1170
|
-
for (let j = 0; j < units[i].length; j += 1) {
|
1171
|
-
length += 2 + units[i][j].length;
|
1172
|
-
}
|
1173
|
-
}
|
1174
|
-
|
1175
|
-
const hvcC = new Uint8Array(length);
|
1176
|
-
hvcC.set(config, 0);
|
1177
|
-
length = config.length;
|
1178
|
-
// append parameter set units: one vps, one or more sps and pps
|
1179
|
-
const iMax = units.length - 1;
|
1180
|
-
for (let i = 0; i < units.length; i += 1) {
|
1181
|
-
hvcC.set(
|
1182
|
-
new Uint8Array([
|
1183
|
-
(32 + i) | (i === iMax ? 128 : 0),
|
1184
|
-
0x00,
|
1185
|
-
units[i].length,
|
1186
|
-
]),
|
1187
|
-
length,
|
1188
|
-
);
|
1189
|
-
length += 3;
|
1190
|
-
for (let j = 0; j < units[i].length; j += 1) {
|
1191
|
-
hvcC.set(
|
1192
|
-
new Uint8Array([units[i][j].length >> 8, units[i][j].length & 255]),
|
1193
|
-
length,
|
1194
|
-
);
|
1195
|
-
length += 2;
|
1196
|
-
hvcC.set(units[i][j], length);
|
1197
|
-
length += units[i][j].length;
|
1198
|
-
}
|
1199
|
-
}
|
1200
|
-
const hvcc = MP4.box(MP4.types.hvcC, hvcC);
|
1201
|
-
const width = track.width;
|
1202
|
-
const height = track.height;
|
1203
|
-
const hSpacing = track.pixelRatio[0];
|
1204
|
-
const vSpacing = track.pixelRatio[1];
|
1205
|
-
|
1206
|
-
return MP4.box(
|
1207
|
-
MP4.types.hvc1,
|
1208
|
-
new Uint8Array([
|
1209
|
-
0x00,
|
1210
|
-
0x00,
|
1211
|
-
0x00, // reserved
|
1212
|
-
0x00,
|
1213
|
-
0x00,
|
1214
|
-
0x00, // reserved
|
1215
|
-
0x00,
|
1216
|
-
0x01, // data_reference_index
|
1217
|
-
0x00,
|
1218
|
-
0x00, // pre_defined
|
1219
|
-
0x00,
|
1220
|
-
0x00, // reserved
|
1221
|
-
0x00,
|
1222
|
-
0x00,
|
1223
|
-
0x00,
|
1224
|
-
0x00,
|
1225
|
-
0x00,
|
1226
|
-
0x00,
|
1227
|
-
0x00,
|
1228
|
-
0x00,
|
1229
|
-
0x00,
|
1230
|
-
0x00,
|
1231
|
-
0x00,
|
1232
|
-
0x00, // pre_defined
|
1233
|
-
(width >> 8) & 0xff,
|
1234
|
-
width & 0xff, // width
|
1235
|
-
(height >> 8) & 0xff,
|
1236
|
-
height & 0xff, // height
|
1237
|
-
0x00,
|
1238
|
-
0x48,
|
1239
|
-
0x00,
|
1240
|
-
0x00, // horizresolution
|
1241
|
-
0x00,
|
1242
|
-
0x48,
|
1243
|
-
0x00,
|
1244
|
-
0x00, // vertresolution
|
1245
|
-
0x00,
|
1246
|
-
0x00,
|
1247
|
-
0x00,
|
1248
|
-
0x00, // reserved
|
1249
|
-
0x00,
|
1250
|
-
0x01, // frame_count
|
1251
|
-
0x12,
|
1252
|
-
0x64,
|
1253
|
-
0x61,
|
1254
|
-
0x69,
|
1255
|
-
0x6c, // dailymotion/hls.js
|
1256
|
-
0x79,
|
1257
|
-
0x6d,
|
1258
|
-
0x6f,
|
1259
|
-
0x74,
|
1260
|
-
0x69,
|
1261
|
-
0x6f,
|
1262
|
-
0x6e,
|
1263
|
-
0x2f,
|
1264
|
-
0x68,
|
1265
|
-
0x6c,
|
1266
|
-
0x73,
|
1267
|
-
0x2e,
|
1268
|
-
0x6a,
|
1269
|
-
0x73,
|
1270
|
-
0x00,
|
1271
|
-
0x00,
|
1272
|
-
0x00,
|
1273
|
-
0x00,
|
1274
|
-
0x00,
|
1275
|
-
0x00,
|
1276
|
-
0x00,
|
1277
|
-
0x00,
|
1278
|
-
0x00,
|
1279
|
-
0x00,
|
1280
|
-
0x00,
|
1281
|
-
0x00,
|
1282
|
-
0x00, // compressorname
|
1283
|
-
0x00,
|
1284
|
-
0x18, // depth = 24
|
1285
|
-
0x11,
|
1286
|
-
0x11,
|
1287
|
-
]), // pre_defined = -1
|
1288
|
-
hvcc,
|
1289
|
-
MP4.box(
|
1290
|
-
MP4.types.btrt,
|
1291
|
-
new Uint8Array([
|
1292
|
-
0x00,
|
1293
|
-
0x1c,
|
1294
|
-
0x9c,
|
1295
|
-
0x80, // bufferSizeDB
|
1296
|
-
0x00,
|
1297
|
-
0x2d,
|
1298
|
-
0xc6,
|
1299
|
-
0xc0, // maxBitrate
|
1300
|
-
0x00,
|
1301
|
-
0x2d,
|
1302
|
-
0xc6,
|
1303
|
-
0xc0,
|
1304
|
-
]),
|
1305
|
-
), // avgBitrate
|
1306
|
-
MP4.box(
|
1307
|
-
MP4.types.pasp,
|
1308
|
-
new Uint8Array([
|
1309
|
-
hSpacing >> 24, // hSpacing
|
1310
|
-
(hSpacing >> 16) & 0xff,
|
1311
|
-
(hSpacing >> 8) & 0xff,
|
1312
|
-
hSpacing & 0xff,
|
1313
|
-
vSpacing >> 24, // vSpacing
|
1314
|
-
(vSpacing >> 16) & 0xff,
|
1315
|
-
(vSpacing >> 8) & 0xff,
|
1316
|
-
vSpacing & 0xff,
|
1317
|
-
]),
|
1318
|
-
),
|
1319
|
-
);
|
1320
|
-
}
|
1321
1126
|
}
|
1322
1127
|
|
1323
1128
|
export default MP4;
|
package/src/remux/mp4-remuxer.ts
CHANGED
@@ -4,7 +4,7 @@ import type { HlsEventEmitter } from '../events';
|
|
4
4
|
import { Events } from '../events';
|
5
5
|
import { ErrorTypes, ErrorDetails } from '../errors';
|
6
6
|
import { logger } from '../utils/logger';
|
7
|
-
import
|
7
|
+
import {
|
8
8
|
InitSegmentData,
|
9
9
|
Remuxer,
|
10
10
|
RemuxerResult,
|
@@ -29,7 +29,6 @@ import type { TrackSet } from '../types/track';
|
|
29
29
|
import type { SourceBufferName } from '../types/buffer';
|
30
30
|
import type { Fragment } from '../loader/fragment';
|
31
31
|
import type { HlsConfig } from '../config';
|
32
|
-
import type { TypeSupported } from '../utils/codecs';
|
33
32
|
|
34
33
|
const MAX_SILENT_FRAME_DURATION = 10 * 1000; // 10 seconds
|
35
34
|
const AAC_SAMPLES_PER_FRAME = 1024;
|
@@ -42,7 +41,7 @@ let safariWebkitVersion: number | null = null;
|
|
42
41
|
export default class MP4Remuxer implements Remuxer {
|
43
42
|
private observer: HlsEventEmitter;
|
44
43
|
private config: HlsConfig;
|
45
|
-
private typeSupported:
|
44
|
+
private typeSupported: any;
|
46
45
|
private ISGenerated: boolean = false;
|
47
46
|
private _initPTS: RationalTimestamp | null = null;
|
48
47
|
private _initDTS: RationalTimestamp | null = null;
|
@@ -516,7 +515,7 @@ export default class MP4Remuxer implements Remuxer {
|
|
516
515
|
if (foundHole || foundOverlap) {
|
517
516
|
if (foundHole) {
|
518
517
|
logger.warn(
|
519
|
-
|
518
|
+
`AVC: ${toMsFromMpegTsClock(
|
520
519
|
delta,
|
521
520
|
true,
|
522
521
|
)} ms (${delta}dts) hole between fragments detected at ${timeOffset.toFixed(
|
@@ -525,7 +524,7 @@ export default class MP4Remuxer implements Remuxer {
|
|
525
524
|
);
|
526
525
|
} else {
|
527
526
|
logger.warn(
|
528
|
-
|
527
|
+
`AVC: ${toMsFromMpegTsClock(
|
529
528
|
-delta,
|
530
529
|
true,
|
531
530
|
)} ms (${delta}dts) overlapping between fragments detected at ${timeOffset.toFixed(
|
@@ -544,27 +543,12 @@ export default class MP4Remuxer implements Remuxer {
|
|
544
543
|
inputSamples[0].dts = firstDTS;
|
545
544
|
inputSamples[0].pts = firstPTS;
|
546
545
|
} else {
|
547
|
-
let isPTSOrderRetained = true;
|
548
546
|
for (let i = 0; i < inputSamples.length; i++) {
|
549
|
-
if (inputSamples[i].dts > firstPTS
|
547
|
+
if (inputSamples[i].dts > firstPTS) {
|
550
548
|
break;
|
551
549
|
}
|
552
|
-
|
553
|
-
const prevPTS = inputSamples[i].pts;
|
554
550
|
inputSamples[i].dts -= delta;
|
555
551
|
inputSamples[i].pts -= delta;
|
556
|
-
|
557
|
-
// check to see if this sample's PTS order has changed
|
558
|
-
// relative to the next one
|
559
|
-
if (i < inputSamples.length - 1) {
|
560
|
-
const nextSamplePTS = inputSamples[i + 1].pts;
|
561
|
-
const currentSamplePTS = inputSamples[i].pts;
|
562
|
-
|
563
|
-
const currentOrder = nextSamplePTS <= currentSamplePTS;
|
564
|
-
const prevOrder = nextSamplePTS <= prevPTS;
|
565
|
-
|
566
|
-
isPTSOrderRetained = currentOrder == prevOrder;
|
567
|
-
}
|
568
552
|
}
|
569
553
|
}
|
570
554
|
logger.log(
|
@@ -759,7 +743,7 @@ export default class MP4Remuxer implements Remuxer {
|
|
759
743
|
}
|
760
744
|
}
|
761
745
|
}
|
762
|
-
// next AVC
|
746
|
+
// next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
|
763
747
|
mp4SampleDuration =
|
764
748
|
stretchedLastFrame || !mp4SampleDuration
|
765
749
|
? averageSampleDuration
|
@@ -943,7 +927,7 @@ export default class MP4Remuxer implements Remuxer {
|
|
943
927
|
for (let j = 0; j < missing; j++) {
|
944
928
|
const newStamp = Math.max(nextPts as number, 0);
|
945
929
|
let fillFrame = AAC.getSilentFrame(
|
946
|
-
track.
|
930
|
+
track.manifestCodec || track.codec,
|
947
931
|
track.channelCount,
|
948
932
|
);
|
949
933
|
if (!fillFrame) {
|
@@ -1093,7 +1077,7 @@ export default class MP4Remuxer implements Remuxer {
|
|
1093
1077
|
const nbSamples: number = Math.ceil((endDTS - startDTS) / frameDuration);
|
1094
1078
|
// silent frame
|
1095
1079
|
const silentFrame: Uint8Array | undefined = AAC.getSilentFrame(
|
1096
|
-
track.
|
1080
|
+
track.manifestCodec || track.codec,
|
1097
1081
|
track.channelCount,
|
1098
1082
|
);
|
1099
1083
|
|
package/src/task-loop.ts
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
import { type ILogger, Logger } from './utils/logger';
|
2
|
-
|
3
1
|
/**
|
4
2
|
* @ignore
|
5
3
|
* Sub-class specialization of EventHandler base class.
|
@@ -29,14 +27,13 @@ import { type ILogger, Logger } from './utils/logger';
|
|
29
27
|
* we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further
|
30
28
|
* task processing on the next main loop iteration (also known as "next tick" in the Node/JS runtime lingo).
|
31
29
|
*/
|
32
|
-
export default class TaskLoop
|
30
|
+
export default class TaskLoop {
|
33
31
|
private readonly _boundTick: () => void;
|
34
32
|
private _tickTimer: number | null = null;
|
35
33
|
private _tickInterval: number | null = null;
|
36
34
|
private _tickCallCount = 0;
|
37
35
|
|
38
|
-
constructor(
|
39
|
-
super(label, logger);
|
36
|
+
constructor() {
|
40
37
|
this._boundTick = this.tick.bind(this);
|
41
38
|
}
|
42
39
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import
|
1
|
+
import EwmaBandWidthEstimator from '../utils/ewma-bandwidth-estimator';
|
2
2
|
|
3
3
|
export interface ComponentAPI {
|
4
4
|
destroy(): void;
|
@@ -15,6 +15,4 @@ export interface AbrComponentAPI extends ComponentAPI {
|
|
15
15
|
export interface NetworkComponentAPI extends ComponentAPI {
|
16
16
|
startLoad(startPosition: number): void;
|
17
17
|
stopLoad(): void;
|
18
|
-
pauseBuffering?(): void;
|
19
|
-
resumeBuffering?(): void;
|
20
18
|
}
|
package/src/types/demuxer.ts
CHANGED
@@ -64,7 +64,6 @@ export interface DemuxedAudioTrack extends DemuxedTrack {
|
|
64
64
|
segmentCodec?: string;
|
65
65
|
channelCount?: number;
|
66
66
|
manifestCodec?: string;
|
67
|
-
parsedCodec?: string;
|
68
67
|
samples: AudioSample[];
|
69
68
|
}
|
70
69
|
|
@@ -73,14 +72,12 @@ export interface DemuxedVideoTrackBase extends DemuxedTrack {
|
|
73
72
|
height?: number;
|
74
73
|
pixelRatio?: [number, number];
|
75
74
|
audFound?: boolean;
|
76
|
-
vps?: Uint8Array[];
|
77
75
|
pps?: Uint8Array[];
|
78
76
|
sps?: Uint8Array[];
|
79
77
|
naluState?: number;
|
80
78
|
segmentCodec?: string;
|
81
79
|
manifestCodec?: string;
|
82
80
|
samples: VideoSample[] | Uint8Array;
|
83
|
-
params?: object;
|
84
81
|
}
|
85
82
|
|
86
83
|
export interface DemuxedVideoTrack extends DemuxedVideoTrackBase {
|
package/src/types/events.ts
CHANGED
package/src/types/remuxer.ts
CHANGED
@@ -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;
|
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
|
@@ -217,24 +209,3 @@ export function convertAVC1ToAVCOTI(codec: string) {
|
|
217
209
|
}
|
218
210
|
return codec;
|
219
211
|
}
|
220
|
-
|
221
|
-
export interface TypeSupported {
|
222
|
-
mpeg: boolean;
|
223
|
-
mp3: boolean;
|
224
|
-
ac3: boolean;
|
225
|
-
}
|
226
|
-
|
227
|
-
export function getM2TSSupportedAudioTypes(
|
228
|
-
preferManagedMediaSource: boolean,
|
229
|
-
): TypeSupported {
|
230
|
-
const MediaSource = getMediaSource(preferManagedMediaSource) || {
|
231
|
-
isTypeSupported: () => false,
|
232
|
-
};
|
233
|
-
return {
|
234
|
-
mpeg: MediaSource.isTypeSupported('audio/mpeg'),
|
235
|
-
mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
|
236
|
-
ac3: __USE_M2TS_ADVANCED_CODECS__
|
237
|
-
? MediaSource.isTypeSupported('audio/mp4; codecs="ac-3"')
|
238
|
-
: false,
|
239
|
-
};
|
240
|
-
}
|