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.
- 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
@@ -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
@@ -3,7 +3,8 @@ import MP4 from './mp4-generator';
|
|
3
3
|
import type { HlsEventEmitter } from '../events';
|
4
4
|
import { Events } from '../events';
|
5
5
|
import { ErrorTypes, ErrorDetails } from '../errors';
|
6
|
-
import
|
6
|
+
import { logger } from '../utils/logger';
|
7
|
+
import {
|
7
8
|
InitSegmentData,
|
8
9
|
Remuxer,
|
9
10
|
RemuxerResult,
|
@@ -26,9 +27,8 @@ import type {
|
|
26
27
|
} from '../types/demuxer';
|
27
28
|
import type { TrackSet } from '../types/track';
|
28
29
|
import type { SourceBufferName } from '../types/buffer';
|
30
|
+
import type { Fragment } from '../loader/fragment';
|
29
31
|
import type { HlsConfig } from '../config';
|
30
|
-
import type { TypeSupported } from '../utils/codecs';
|
31
|
-
import type { ILogger } from '../utils/logger';
|
32
32
|
|
33
33
|
const MAX_SILENT_FRAME_DURATION = 10 * 1000; // 10 seconds
|
34
34
|
const AAC_SAMPLES_PER_FRAME = 1024;
|
@@ -39,10 +39,9 @@ let chromeVersion: number | null = null;
|
|
39
39
|
let safariWebkitVersion: number | null = null;
|
40
40
|
|
41
41
|
export default class MP4Remuxer implements Remuxer {
|
42
|
-
private
|
43
|
-
private
|
44
|
-
private
|
45
|
-
private readonly typeSupported: TypeSupported;
|
42
|
+
private observer: HlsEventEmitter;
|
43
|
+
private config: HlsConfig;
|
44
|
+
private typeSupported: any;
|
46
45
|
private ISGenerated: boolean = false;
|
47
46
|
private _initPTS: RationalTimestamp | null = null;
|
48
47
|
private _initDTS: RationalTimestamp | null = null;
|
@@ -60,13 +59,12 @@ export default class MP4Remuxer implements Remuxer {
|
|
60
59
|
constructor(
|
61
60
|
observer: HlsEventEmitter,
|
62
61
|
config: HlsConfig,
|
63
|
-
typeSupported
|
64
|
-
|
62
|
+
typeSupported,
|
63
|
+
vendor = '',
|
65
64
|
) {
|
66
65
|
this.observer = observer;
|
67
66
|
this.config = config;
|
68
67
|
this.typeSupported = typeSupported;
|
69
|
-
this.logger = logger;
|
70
68
|
this.ISGenerated = false;
|
71
69
|
|
72
70
|
if (chromeVersion === null) {
|
@@ -86,18 +84,18 @@ export default class MP4Remuxer implements Remuxer {
|
|
86
84
|
}
|
87
85
|
|
88
86
|
resetTimeStamp(defaultTimeStamp: RationalTimestamp | null) {
|
89
|
-
|
87
|
+
logger.log('[mp4-remuxer]: initPTS & initDTS reset');
|
90
88
|
this._initPTS = this._initDTS = defaultTimeStamp;
|
91
89
|
}
|
92
90
|
|
93
91
|
resetNextTimestamp() {
|
94
|
-
|
92
|
+
logger.log('[mp4-remuxer]: reset next timestamp');
|
95
93
|
this.isVideoContiguous = false;
|
96
94
|
this.isAudioContiguous = false;
|
97
95
|
}
|
98
96
|
|
99
97
|
resetInitSegment() {
|
100
|
-
|
98
|
+
logger.log('[mp4-remuxer]: ISGenerated flag reset');
|
101
99
|
this.ISGenerated = false;
|
102
100
|
this.videoTrackConfig = undefined;
|
103
101
|
}
|
@@ -117,7 +115,7 @@ export default class MP4Remuxer implements Remuxer {
|
|
117
115
|
}
|
118
116
|
}, videoSamples[0].pts);
|
119
117
|
if (rolloverDetected) {
|
120
|
-
|
118
|
+
logger.debug('PTS rollover detected');
|
121
119
|
}
|
122
120
|
return startPTS;
|
123
121
|
}
|
@@ -161,18 +159,15 @@ export default class MP4Remuxer implements Remuxer {
|
|
161
159
|
if (this.ISGenerated) {
|
162
160
|
const config = this.videoTrackConfig;
|
163
161
|
if (
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
(!config && enoughVideoSamples) ||
|
170
|
-
(this.nextAudioPts === null && enoughAudioSamples)
|
162
|
+
config &&
|
163
|
+
(videoTrack.width !== config.width ||
|
164
|
+
videoTrack.height !== config.height ||
|
165
|
+
videoTrack.pixelRatio?.[0] !== config.pixelRatio?.[0] ||
|
166
|
+
videoTrack.pixelRatio?.[1] !== config.pixelRatio?.[1])
|
171
167
|
) {
|
172
168
|
this.resetInitSegment();
|
173
169
|
}
|
174
|
-
}
|
175
|
-
if (!this.ISGenerated) {
|
170
|
+
} else {
|
176
171
|
initSegment = this.generateIS(
|
177
172
|
audioTrack,
|
178
173
|
videoTrack,
|
@@ -190,7 +185,7 @@ export default class MP4Remuxer implements Remuxer {
|
|
190
185
|
if (!isVideoContiguous && this.config.forceKeyFrameOnDiscontinuity) {
|
191
186
|
independent = true;
|
192
187
|
if (firstKeyFrameIndex > 0) {
|
193
|
-
|
188
|
+
logger.warn(
|
194
189
|
`[mp4-remuxer]: Dropped ${firstKeyFrameIndex} out of ${length} video samples due to a missing keyframe`,
|
195
190
|
);
|
196
191
|
const startPTS = this.getVideoStartPts(videoTrack.samples);
|
@@ -201,7 +196,7 @@ export default class MP4Remuxer implements Remuxer {
|
|
201
196
|
videoTrack.inputTimeScale;
|
202
197
|
firstKeyFramePTS = videoTimeOffset;
|
203
198
|
} else if (firstKeyFrameIndex === -1) {
|
204
|
-
|
199
|
+
logger.warn(
|
205
200
|
`[mp4-remuxer]: No keyframe found out of ${length} video samples`,
|
206
201
|
);
|
207
202
|
independent = false;
|
@@ -227,7 +222,7 @@ export default class MP4Remuxer implements Remuxer {
|
|
227
222
|
if (enoughAudioSamples) {
|
228
223
|
// if initSegment was generated without audio samples, regenerate it again
|
229
224
|
if (!audioTrack.samplerate) {
|
230
|
-
|
225
|
+
logger.warn(
|
231
226
|
'[mp4-remuxer]: regenerate InitSegment as audio detected',
|
232
227
|
);
|
233
228
|
initSegment = this.generateIS(
|
@@ -252,7 +247,7 @@ export default class MP4Remuxer implements Remuxer {
|
|
252
247
|
const audioTrackLength = audio ? audio.endPTS - audio.startPTS : 0;
|
253
248
|
// if initSegment was generated without video samples, regenerate it again
|
254
249
|
if (!videoTrack.inputTimeScale) {
|
255
|
-
|
250
|
+
logger.warn(
|
256
251
|
'[mp4-remuxer]: regenerate InitSegment as video detected',
|
257
252
|
);
|
258
253
|
initSegment = this.generateIS(
|
@@ -519,8 +514,8 @@ export default class MP4Remuxer implements Remuxer {
|
|
519
514
|
const foundOverlap = delta < -1;
|
520
515
|
if (foundHole || foundOverlap) {
|
521
516
|
if (foundHole) {
|
522
|
-
|
523
|
-
|
517
|
+
logger.warn(
|
518
|
+
`AVC: ${toMsFromMpegTsClock(
|
524
519
|
delta,
|
525
520
|
true,
|
526
521
|
)} ms (${delta}dts) hole between fragments detected at ${timeOffset.toFixed(
|
@@ -528,8 +523,8 @@ export default class MP4Remuxer implements Remuxer {
|
|
528
523
|
)}`,
|
529
524
|
);
|
530
525
|
} else {
|
531
|
-
|
532
|
-
|
526
|
+
logger.warn(
|
527
|
+
`AVC: ${toMsFromMpegTsClock(
|
533
528
|
-delta,
|
534
529
|
true,
|
535
530
|
)} ms (${delta}dts) overlapping between fragments detected at ${timeOffset.toFixed(
|
@@ -548,30 +543,15 @@ export default class MP4Remuxer implements Remuxer {
|
|
548
543
|
inputSamples[0].dts = firstDTS;
|
549
544
|
inputSamples[0].pts = firstPTS;
|
550
545
|
} else {
|
551
|
-
let isPTSOrderRetained = true;
|
552
546
|
for (let i = 0; i < inputSamples.length; i++) {
|
553
|
-
if (inputSamples[i].dts > firstPTS
|
547
|
+
if (inputSamples[i].dts > firstPTS) {
|
554
548
|
break;
|
555
549
|
}
|
556
|
-
|
557
|
-
const prevPTS = inputSamples[i].pts;
|
558
550
|
inputSamples[i].dts -= delta;
|
559
551
|
inputSamples[i].pts -= delta;
|
560
|
-
|
561
|
-
// check to see if this sample's PTS order has changed
|
562
|
-
// relative to the next one
|
563
|
-
if (i < inputSamples.length - 1) {
|
564
|
-
const nextSamplePTS = inputSamples[i + 1].pts;
|
565
|
-
const currentSamplePTS = inputSamples[i].pts;
|
566
|
-
|
567
|
-
const currentOrder = nextSamplePTS <= currentSamplePTS;
|
568
|
-
const prevOrder = nextSamplePTS <= prevPTS;
|
569
|
-
|
570
|
-
isPTSOrderRetained = currentOrder == prevOrder;
|
571
|
-
}
|
572
552
|
}
|
573
553
|
}
|
574
|
-
|
554
|
+
logger.log(
|
575
555
|
`Video: Initial PTS/DTS adjusted: ${toMsFromMpegTsClock(
|
576
556
|
firstPTS,
|
577
557
|
true,
|
@@ -693,7 +673,7 @@ export default class MP4Remuxer implements Remuxer {
|
|
693
673
|
} else {
|
694
674
|
stretchedLastFrame = true;
|
695
675
|
}
|
696
|
-
|
676
|
+
logger.log(
|
697
677
|
`[mp4-remuxer]: It is approximately ${
|
698
678
|
deltaToFrameEnd / 90
|
699
679
|
} ms to the next segment; using duration ${
|
@@ -742,7 +722,7 @@ export default class MP4Remuxer implements Remuxer {
|
|
742
722
|
averageSampleDuration / maxDtsDelta < 0.025 &&
|
743
723
|
outputSamples[0].cts === 0
|
744
724
|
) {
|
745
|
-
|
725
|
+
logger.warn(
|
746
726
|
'Found irregular gaps in sample duration. Using PTS instead of DTS to determine MP4 sample duration.',
|
747
727
|
);
|
748
728
|
let dts = firstDTS;
|
@@ -763,7 +743,7 @@ export default class MP4Remuxer implements Remuxer {
|
|
763
743
|
}
|
764
744
|
}
|
765
745
|
}
|
766
|
-
// next AVC
|
746
|
+
// next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
|
767
747
|
mp4SampleDuration =
|
768
748
|
stretchedLastFrame || !mp4SampleDuration
|
769
749
|
? averageSampleDuration
|
@@ -905,7 +885,7 @@ export default class MP4Remuxer implements Remuxer {
|
|
905
885
|
alignedWithVideo
|
906
886
|
) {
|
907
887
|
if (i === 0) {
|
908
|
-
|
888
|
+
logger.warn(
|
909
889
|
`Audio frame @ ${(pts / inputTimeScale).toFixed(
|
910
890
|
3,
|
911
891
|
)}s overlaps nextAudioPts by ${Math.round(
|
@@ -937,7 +917,7 @@ export default class MP4Remuxer implements Remuxer {
|
|
937
917
|
if (i === 0) {
|
938
918
|
this.nextAudioPts = nextAudioPts = nextPts;
|
939
919
|
}
|
940
|
-
|
920
|
+
logger.warn(
|
941
921
|
`[mp4-remuxer]: Injecting ${missing} audio frame @ ${(
|
942
922
|
nextPts / inputTimeScale
|
943
923
|
).toFixed(3)}s due to ${Math.round(
|
@@ -947,11 +927,11 @@ export default class MP4Remuxer implements Remuxer {
|
|
947
927
|
for (let j = 0; j < missing; j++) {
|
948
928
|
const newStamp = Math.max(nextPts as number, 0);
|
949
929
|
let fillFrame = AAC.getSilentFrame(
|
950
|
-
track.
|
930
|
+
track.manifestCodec || track.codec,
|
951
931
|
track.channelCount,
|
952
932
|
);
|
953
933
|
if (!fillFrame) {
|
954
|
-
|
934
|
+
logger.log(
|
955
935
|
'[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead.',
|
956
936
|
);
|
957
937
|
fillFrame = sample.unit.subarray();
|
@@ -1070,6 +1050,55 @@ export default class MP4Remuxer implements Remuxer {
|
|
1070
1050
|
this.isAudioContiguous = true;
|
1071
1051
|
return audioData;
|
1072
1052
|
}
|
1053
|
+
|
1054
|
+
remuxEmptyAudio(
|
1055
|
+
track: DemuxedAudioTrack,
|
1056
|
+
timeOffset: number,
|
1057
|
+
contiguous: boolean,
|
1058
|
+
videoData: Fragment,
|
1059
|
+
): RemuxedTrack | undefined {
|
1060
|
+
const inputTimeScale: number = track.inputTimeScale;
|
1061
|
+
const mp4timeScale: number = track.samplerate
|
1062
|
+
? track.samplerate
|
1063
|
+
: inputTimeScale;
|
1064
|
+
const scaleFactor: number = inputTimeScale / mp4timeScale;
|
1065
|
+
const nextAudioPts: number | null = this.nextAudioPts;
|
1066
|
+
// sync with video's timestamp
|
1067
|
+
const initDTS = this._initDTS as RationalTimestamp;
|
1068
|
+
const init90kHz = (initDTS.baseTime * 90000) / initDTS.timescale;
|
1069
|
+
const startDTS: number =
|
1070
|
+
(nextAudioPts !== null
|
1071
|
+
? nextAudioPts
|
1072
|
+
: videoData.startDTS * inputTimeScale) + init90kHz;
|
1073
|
+
const endDTS: number = videoData.endDTS * inputTimeScale + init90kHz;
|
1074
|
+
// one sample's duration value
|
1075
|
+
const frameDuration: number = scaleFactor * AAC_SAMPLES_PER_FRAME;
|
1076
|
+
// samples count of this segment's duration
|
1077
|
+
const nbSamples: number = Math.ceil((endDTS - startDTS) / frameDuration);
|
1078
|
+
// silent frame
|
1079
|
+
const silentFrame: Uint8Array | undefined = AAC.getSilentFrame(
|
1080
|
+
track.manifestCodec || track.codec,
|
1081
|
+
track.channelCount,
|
1082
|
+
);
|
1083
|
+
|
1084
|
+
logger.warn('[mp4-remuxer]: remux empty Audio');
|
1085
|
+
// Can't remux if we can't generate a silent frame...
|
1086
|
+
if (!silentFrame) {
|
1087
|
+
logger.trace(
|
1088
|
+
'[mp4-remuxer]: Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec',
|
1089
|
+
);
|
1090
|
+
return;
|
1091
|
+
}
|
1092
|
+
|
1093
|
+
const samples: Array<any> = [];
|
1094
|
+
for (let i = 0; i < nbSamples; i++) {
|
1095
|
+
const stamp = startDTS + i * frameDuration;
|
1096
|
+
samples.push({ unit: silentFrame, pts: stamp, dts: stamp });
|
1097
|
+
}
|
1098
|
+
track.samples = samples;
|
1099
|
+
|
1100
|
+
return this.remuxAudio(track, timeOffset, contiguous, false);
|
1101
|
+
}
|
1073
1102
|
}
|
1074
1103
|
|
1075
1104
|
export function normalizePts(value: number, reference: number | null): number {
|
@@ -14,9 +14,8 @@ import {
|
|
14
14
|
parseInitSegment,
|
15
15
|
} from '../utils/mp4-tools';
|
16
16
|
import { ElementaryStreamTypes } from '../loader/fragment';
|
17
|
+
import { logger } from '../utils/logger';
|
17
18
|
import { getCodecCompatibleName } from '../utils/codecs';
|
18
|
-
import type { HlsEventEmitter } from '../events';
|
19
|
-
import type { HlsConfig } from '../config';
|
20
19
|
import type { TrackSet } from '../types/track';
|
21
20
|
import type {
|
22
21
|
InitSegmentData,
|
@@ -31,12 +30,9 @@ import type {
|
|
31
30
|
PassthroughTrack,
|
32
31
|
} from '../types/demuxer';
|
33
32
|
import type { DecryptData } from '../loader/level-key';
|
34
|
-
import type { TypeSupported } from '../utils/codecs';
|
35
|
-
import type { ILogger } from '../utils/logger';
|
36
33
|
import type { RationalTimestamp } from '../utils/timescale-conversion';
|
37
34
|
|
38
35
|
class PassThroughRemuxer implements Remuxer {
|
39
|
-
private readonly logger: ILogger;
|
40
36
|
private emitInitSegment: boolean = false;
|
41
37
|
private audioCodec?: string;
|
42
38
|
private videoCodec?: string;
|
@@ -45,15 +41,6 @@ class PassThroughRemuxer implements Remuxer {
|
|
45
41
|
private initTracks?: TrackSet;
|
46
42
|
private lastEndTime: number | null = null;
|
47
43
|
|
48
|
-
constructor(
|
49
|
-
observer: HlsEventEmitter,
|
50
|
-
config: HlsConfig,
|
51
|
-
typeSupported: TypeSupported,
|
52
|
-
logger: ILogger,
|
53
|
-
) {
|
54
|
-
this.logger = logger;
|
55
|
-
}
|
56
|
-
|
57
44
|
public destroy() {}
|
58
45
|
|
59
46
|
public resetTimeStamp(defaultInitPTS: RationalTimestamp | null) {
|
@@ -124,7 +111,7 @@ class PassThroughRemuxer implements Remuxer {
|
|
124
111
|
id: 'main',
|
125
112
|
};
|
126
113
|
} else {
|
127
|
-
|
114
|
+
logger.warn(
|
128
115
|
'[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes.',
|
129
116
|
);
|
130
117
|
}
|
@@ -173,9 +160,7 @@ class PassThroughRemuxer implements Remuxer {
|
|
173
160
|
}
|
174
161
|
if (!initData?.length) {
|
175
162
|
// We can't remux if the initSegment could not be generated
|
176
|
-
|
177
|
-
'[passthrough-remuxer.ts]: Failed to generate initSegment.',
|
178
|
-
);
|
163
|
+
logger.warn('[passthrough-remuxer.ts]: Failed to generate initSegment.');
|
179
164
|
return result;
|
180
165
|
}
|
181
166
|
if (this.emitInitSegment) {
|
@@ -192,8 +177,8 @@ class PassThroughRemuxer implements Remuxer {
|
|
192
177
|
) {
|
193
178
|
initSegment.initPTS = decodeTime - timeOffset;
|
194
179
|
if (initPTS && initPTS.timescale === 1) {
|
195
|
-
|
196
|
-
`Adjusting initPTS
|
180
|
+
logger.warn(
|
181
|
+
`Adjusting initPTS by ${initSegment.initPTS - initPTS.baseTime}`,
|
197
182
|
);
|
198
183
|
}
|
199
184
|
this.initPTS = initPTS = {
|
@@ -211,7 +196,7 @@ class PassThroughRemuxer implements Remuxer {
|
|
211
196
|
if (duration > 0) {
|
212
197
|
this.lastEndTime = endTime;
|
213
198
|
} else {
|
214
|
-
|
199
|
+
logger.warn('Duration parsed from mp4 should be greater than zero');
|
215
200
|
this.resetNextTimestamp();
|
216
201
|
}
|
217
202
|
|
@@ -299,14 +284,14 @@ function getParsedTrackCodec(
|
|
299
284
|
return getCodecCompatibleName(parsedCodec, preferManagedMediaSource);
|
300
285
|
}
|
301
286
|
const result = 'mp4a.40.5';
|
302
|
-
|
287
|
+
logger.info(
|
303
288
|
`Parsed audio codec "${parsedCodec}" or audio object type not handled. Using "${result}"`,
|
304
289
|
);
|
305
290
|
return result;
|
306
291
|
}
|
307
292
|
// Provide defaults based on codec type
|
308
293
|
// This allows for some playback of some fmp4 playlists without CODECS defined in manifest
|
309
|
-
|
294
|
+
logger.warn(`Unhandled video codec "${parsedCodec}"`);
|
310
295
|
if (parsedCodec === 'hvc1' || parsedCodec === 'hev1') {
|
311
296
|
return 'hvc1.1.6.L120.90';
|
312
297
|
}
|
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 {
|