hls.js 1.6.0-beta.3.0.canary.10980 → 1.6.0-beta.3.0.canary.10981
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/dist/hls.js +4408 -4364
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +5641 -5598
- 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 +3797 -3756
- 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 +4426 -4384
- 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 +1 -1
- package/src/controller/abr-controller.ts +2 -1
- package/src/controller/audio-stream-controller.ts +7 -5
- package/src/controller/buffer-controller.ts +8 -3
- package/src/controller/error-controller.ts +14 -0
- package/src/controller/stream-controller.ts +8 -6
- package/src/utils/codecs.ts +14 -1
- package/src/utils/mediacapabilities-helper.ts +31 -4
- package/src/utils/mp4-tools.ts +2 -3
package/package.json
CHANGED
@@ -8,6 +8,7 @@ import {
|
|
8
8
|
requiresMediaCapabilitiesDecodingInfo,
|
9
9
|
SUPPORTED_INFO_DEFAULT,
|
10
10
|
} from '../utils/mediacapabilities-helper';
|
11
|
+
import { isHEVC } from '../utils/mp4-tools';
|
11
12
|
import {
|
12
13
|
type AudioTracksByGroup,
|
13
14
|
type CodecSetTier,
|
@@ -804,7 +805,7 @@ class AbrController extends Logger implements AbrComponentAPI {
|
|
804
805
|
currentBw,
|
805
806
|
audioPreference,
|
806
807
|
) ||
|
807
|
-
levelInfo.videoCodec
|
808
|
+
isHEVC(levelInfo.videoCodec)) // Force media capabilities check for HEVC to avoid failure on Windows
|
808
809
|
) {
|
809
810
|
levelInfo.supportedPromise = getMediaDecodingInfoPromise(
|
810
811
|
levelInfo,
|
@@ -797,13 +797,15 @@ class AudioStreamController
|
|
797
797
|
this.state = State.IDLE;
|
798
798
|
}
|
799
799
|
break;
|
800
|
+
case ErrorDetails.BUFFER_ADD_CODEC_ERROR:
|
800
801
|
case ErrorDetails.BUFFER_APPEND_ERROR:
|
801
|
-
|
802
|
-
if (!data.parent || data.parent !== 'audio') {
|
802
|
+
if (data.parent !== 'audio') {
|
803
803
|
return;
|
804
804
|
}
|
805
|
-
|
806
|
-
|
805
|
+
this.resetLoadingState();
|
806
|
+
break;
|
807
|
+
case ErrorDetails.BUFFER_FULL_ERROR:
|
808
|
+
if (data.parent !== 'audio') {
|
807
809
|
return;
|
808
810
|
}
|
809
811
|
if (this.reduceLengthAndFlushBuffer(data)) {
|
@@ -954,7 +956,7 @@ class AudioStreamController
|
|
954
956
|
}
|
955
957
|
const track = tracks.audio;
|
956
958
|
|
957
|
-
track.id =
|
959
|
+
track.id = PlaylistLevelType.AUDIO;
|
958
960
|
|
959
961
|
const variantAudioCodecs = currentLevel.audioCodec;
|
960
962
|
this.log(
|
@@ -1336,7 +1336,6 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe
|
|
1336
1336
|
} else {
|
1337
1337
|
// ok, let's create them now !
|
1338
1338
|
this.createSourceBuffers();
|
1339
|
-
this.bufferCreated();
|
1340
1339
|
}
|
1341
1340
|
}
|
1342
1341
|
}
|
@@ -1409,6 +1408,10 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe
|
|
1409
1408
|
this.error(
|
1410
1409
|
`error while trying to add sourceBuffer: ${error.message}`,
|
1411
1410
|
);
|
1411
|
+
// remove init segment from queue and delete track info
|
1412
|
+
this.shiftAndExecuteNext(type);
|
1413
|
+
this.operationQueue?.removeBlockers();
|
1414
|
+
delete this.tracks[type];
|
1412
1415
|
this.hls.trigger(Events.ERROR, {
|
1413
1416
|
type: ErrorTypes.MEDIA_ERROR,
|
1414
1417
|
details: ErrorDetails.BUFFER_ADD_CODEC_ERROR,
|
@@ -1416,12 +1419,14 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe
|
|
1416
1419
|
error,
|
1417
1420
|
sourceBufferName: type,
|
1418
1421
|
mimeType: mimeType,
|
1422
|
+
parent: track.id as PlaylistLevelType,
|
1419
1423
|
});
|
1420
|
-
|
1424
|
+
return;
|
1421
1425
|
}
|
1422
1426
|
this.trackSourceBuffer(type, track);
|
1423
1427
|
}
|
1424
1428
|
}
|
1429
|
+
this.bufferCreated();
|
1425
1430
|
}
|
1426
1431
|
|
1427
1432
|
private getTrackCodec(track: BaseTrack, trackName: SourceBufferName): string {
|
@@ -1450,7 +1455,7 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe
|
|
1450
1455
|
id: track.id,
|
1451
1456
|
listeners: [],
|
1452
1457
|
};
|
1453
|
-
|
1458
|
+
this.removeBufferListeners(type);
|
1454
1459
|
this.addBufferListener(type, 'updatestart', this.onSBUpdateStart);
|
1455
1460
|
this.addBufferListener(type, 'updateend', this.onSBUpdateEnd);
|
1456
1461
|
this.addBufferListener(type, 'error', this.onSBUpdateError);
|
@@ -3,6 +3,7 @@ import { ErrorDetails, ErrorTypes } from '../errors';
|
|
3
3
|
import { Events } from '../events';
|
4
4
|
import { HdcpLevels } from '../types/level';
|
5
5
|
import { PlaylistContextType, PlaylistLevelType } from '../types/loader';
|
6
|
+
import { getCodecsForMimeType } from '../utils/codecs';
|
6
7
|
import {
|
7
8
|
getRetryConfig,
|
8
9
|
isTimeoutError,
|
@@ -506,6 +507,19 @@ export default class ErrorController
|
|
506
507
|
data.errorAction.resolved = true;
|
507
508
|
// Stream controller is responsible for this but won't switch on false start
|
508
509
|
this.hls.nextLoadLevel = this.hls.nextAutoLevel;
|
510
|
+
if (
|
511
|
+
data.details === ErrorDetails.BUFFER_ADD_CODEC_ERROR &&
|
512
|
+
data.mimeType &&
|
513
|
+
data.sourceBufferName !== 'audiovideo'
|
514
|
+
) {
|
515
|
+
const codec = getCodecsForMimeType(data.mimeType);
|
516
|
+
const levels = this.hls.levels;
|
517
|
+
for (let i = levels.length; i--; ) {
|
518
|
+
if (levels[i][`${data.sourceBufferName}Codec`] === codec) {
|
519
|
+
this.hls.removeLevel(i);
|
520
|
+
}
|
521
|
+
}
|
522
|
+
}
|
509
523
|
}
|
510
524
|
}
|
511
525
|
}
|
@@ -1015,13 +1015,15 @@ export default class StreamController
|
|
1015
1015
|
this.state = State.IDLE;
|
1016
1016
|
}
|
1017
1017
|
break;
|
1018
|
+
case ErrorDetails.BUFFER_ADD_CODEC_ERROR:
|
1018
1019
|
case ErrorDetails.BUFFER_APPEND_ERROR:
|
1019
|
-
|
1020
|
-
if (!data.parent || data.parent !== 'main') {
|
1020
|
+
if (data.parent !== 'main') {
|
1021
1021
|
return;
|
1022
1022
|
}
|
1023
|
-
|
1024
|
-
|
1023
|
+
this.resetLoadingState();
|
1024
|
+
break;
|
1025
|
+
case ErrorDetails.BUFFER_FULL_ERROR:
|
1026
|
+
if (data.parent !== 'main') {
|
1025
1027
|
return;
|
1026
1028
|
}
|
1027
1029
|
if (this.reduceLengthAndFlushBuffer(data)) {
|
@@ -1416,7 +1418,7 @@ export default class StreamController
|
|
1416
1418
|
);
|
1417
1419
|
}
|
1418
1420
|
audio.levelCodec = audioCodec;
|
1419
|
-
audio.id =
|
1421
|
+
audio.id = PlaylistLevelType.MAIN;
|
1420
1422
|
this.log(
|
1421
1423
|
`Init audio buffer, container:${
|
1422
1424
|
audio.container
|
@@ -1428,7 +1430,7 @@ export default class StreamController
|
|
1428
1430
|
}
|
1429
1431
|
if (video) {
|
1430
1432
|
video.levelCodec = currentLevel.videoCodec;
|
1431
|
-
video.id =
|
1433
|
+
video.id = PlaylistLevelType.MAIN;
|
1432
1434
|
const parsedVideoCodec = video.codec;
|
1433
1435
|
if (parsedVideoCodec?.length === 4) {
|
1434
1436
|
// Make up for passthrough-remuxer not being able to parse full codec
|
package/src/utils/codecs.ts
CHANGED
@@ -1,4 +1,9 @@
|
|
1
1
|
import { getMediaSource } from './mediasource-helper';
|
2
|
+
import { isHEVC } from './mp4-tools';
|
3
|
+
|
4
|
+
export const userAgentHevcSupportIsInaccurate = () => {
|
5
|
+
return /\(Windows.+Firefox\//i.test(navigator.userAgent);
|
6
|
+
};
|
2
7
|
|
3
8
|
// from http://mp4ra.org/codecs.html
|
4
9
|
// values indicate codec selection preference (lower is higher priority)
|
@@ -121,8 +126,12 @@ export function videoCodecPreferenceValue(
|
|
121
126
|
}
|
122
127
|
|
123
128
|
export function codecsSetSelectionPreferenceValue(codecSet: string): number {
|
129
|
+
const limitedHevcSupport = userAgentHevcSupportIsInaccurate();
|
124
130
|
return codecSet.split(',').reduce((num, fourCC) => {
|
125
|
-
const
|
131
|
+
const lowerPriority = limitedHevcSupport && isHEVC(fourCC);
|
132
|
+
const preferenceValue = lowerPriority
|
133
|
+
? 9
|
134
|
+
: sampleEntryCodesISO.video[fourCC];
|
126
135
|
if (preferenceValue) {
|
127
136
|
return (preferenceValue * 2 + num) / (num ? 3 : 2);
|
128
137
|
}
|
@@ -272,3 +281,7 @@ export function getM2TSSupportedAudioTypes(
|
|
272
281
|
: false,
|
273
282
|
};
|
274
283
|
}
|
284
|
+
|
285
|
+
export function getCodecsForMimeType(mimeType: string): string {
|
286
|
+
return mimeType.replace(/^.+codecs=["']?([^"']+).*$/, '$1');
|
287
|
+
}
|
@@ -1,4 +1,10 @@
|
|
1
|
-
import {
|
1
|
+
import {
|
2
|
+
fillInMissingAV01Params,
|
3
|
+
getCodecsForMimeType,
|
4
|
+
mimeTypeForCodec,
|
5
|
+
userAgentHevcSupportIsInaccurate,
|
6
|
+
} from './codecs';
|
7
|
+
import { isHEVC } from './mp4-tools';
|
2
8
|
import type { AudioTracksByGroup } from './rendition-helper';
|
3
9
|
import type { Level, VideoRange } from '../types/level';
|
4
10
|
import type { AudioSelectionOption } from '../types/media-playlist';
|
@@ -115,10 +121,31 @@ export function getMediaDecodingInfoPromise(
|
|
115
121
|
baseVideoConfiguration.transferFunction =
|
116
122
|
videoRange.toLowerCase() as TransferFunction;
|
117
123
|
}
|
118
|
-
|
124
|
+
const videoCodecsArray = videoCodecs.split(',');
|
125
|
+
// Override Windows Firefox HEVC MediaCapabilities result (https://github.com/video-dev/hls.js/issues/7046)
|
126
|
+
const ua = navigator.userAgent;
|
127
|
+
if (
|
128
|
+
videoCodecsArray.some((videoCodec) => isHEVC(videoCodec)) &&
|
129
|
+
userAgentHevcSupportIsInaccurate()
|
130
|
+
) {
|
131
|
+
return Promise.resolve({
|
132
|
+
supported: false,
|
133
|
+
configurations,
|
134
|
+
decodingInfoResults: [
|
135
|
+
{
|
136
|
+
supported: false,
|
137
|
+
smooth: false,
|
138
|
+
powerEfficient: false,
|
139
|
+
},
|
140
|
+
],
|
141
|
+
error: new Error(
|
142
|
+
`Overriding Windows Firefox HEVC MediaCapabilities result based on user-agent sting: (${ua})`,
|
143
|
+
),
|
144
|
+
});
|
145
|
+
}
|
119
146
|
configurations.push.apply(
|
120
147
|
configurations,
|
121
|
-
|
148
|
+
videoCodecsArray.map((videoCodec) => ({
|
122
149
|
type: 'media-source',
|
123
150
|
video: {
|
124
151
|
...baseVideoConfiguration,
|
@@ -187,7 +214,7 @@ function getMediaDecodingInfoKey(config: MediaDecodingConfiguration): string {
|
|
187
214
|
const { audio, video } = config;
|
188
215
|
const mediaConfig = video || audio;
|
189
216
|
if (mediaConfig) {
|
190
|
-
const codec = mediaConfig.contentType
|
217
|
+
const codec = getCodecsForMimeType(mediaConfig.contentType);
|
191
218
|
if (video) {
|
192
219
|
return `r${video.height}x${video.width}f${Math.ceil(video.framerate)}${
|
193
220
|
video.transferFunction || 'sd'
|
package/src/utils/mp4-tools.ts
CHANGED
@@ -1012,12 +1012,11 @@ export function parseSamples(
|
|
1012
1012
|
return seiSamples;
|
1013
1013
|
}
|
1014
1014
|
|
1015
|
-
function isHEVC(codec: string) {
|
1015
|
+
export function isHEVC(codec: string | undefined) {
|
1016
1016
|
if (!codec) {
|
1017
1017
|
return false;
|
1018
1018
|
}
|
1019
|
-
const
|
1020
|
-
const baseCodec = delimit < 0 ? codec : codec.substring(0, delimit);
|
1019
|
+
const baseCodec = codec.substring(0, 4);
|
1021
1020
|
return (
|
1022
1021
|
baseCodec === 'hvc1' ||
|
1023
1022
|
baseCodec === 'hev1' ||
|