hls.js 1.6.6-0.canary.11352 → 1.6.6-0.canary.11354

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.mjs CHANGED
@@ -523,7 +523,7 @@ function enableLogs(debugConfig, context, id) {
523
523
  // Some browsers don't allow to use bind on console object anyway
524
524
  // fallback to default if needed
525
525
  try {
526
- newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.6-0.canary.11352"}`);
526
+ newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.6-0.canary.11354"}`);
527
527
  } catch (e) {
528
528
  /* log fn threw an exception. All logger methods are no-ops. */
529
529
  return createLogger();
@@ -2630,9 +2630,9 @@ function getUnsupportedResult(error, configurations) {
2630
2630
  error
2631
2631
  };
2632
2632
  }
2633
- const SUPPORTED_INFO_CACHE = {};
2634
2633
  function requiresMediaCapabilitiesDecodingInfo(level, audioTracksByGroup, currentVideoRange, currentFrameRate, currentBw, audioPreference) {
2635
2634
  // Only test support when configuration is exceeds minimum options
2635
+ const videoCodecs = level.videoCodec;
2636
2636
  const audioGroups = level.audioCodec ? level.audioGroups : null;
2637
2637
  const audioCodecPreference = audioPreference == null ? void 0 : audioPreference.audioCodec;
2638
2638
  const channelsPreference = audioPreference == null ? void 0 : audioPreference.channels;
@@ -2663,69 +2663,47 @@ function requiresMediaCapabilitiesDecodingInfo(level, audioTracksByGroup, curren
2663
2663
  return true;
2664
2664
  }
2665
2665
  }
2666
- return level.videoCodec !== undefined && (level.width > 1920 && level.height > 1088 || level.height > 1920 && level.width > 1088 || level.frameRate > Math.max(currentFrameRate, 30) || level.videoRange !== 'SDR' && level.videoRange !== currentVideoRange || level.bitrate > Math.max(currentBw, 8e6)) || !!audioChannels && isFiniteNumber(maxChannels) && Object.keys(audioChannels).some(channels => parseInt(channels) > maxChannels);
2666
+ return videoCodecs !== undefined && (
2667
+ // Force media capabilities check for HEVC to avoid failure on Windows
2668
+ videoCodecs.split(',').some(videoCodec => isHEVC(videoCodec)) || level.width > 1920 && level.height > 1088 || level.height > 1920 && level.width > 1088 || level.frameRate > Math.max(currentFrameRate, 30) || level.videoRange !== 'SDR' && level.videoRange !== currentVideoRange || level.bitrate > Math.max(currentBw, 8e6)) || !!audioChannels && isFiniteNumber(maxChannels) && Object.keys(audioChannels).some(channels => parseInt(channels) > maxChannels);
2667
2669
  }
2668
- function getMediaDecodingInfoPromise(level, audioTracksByGroup, mediaCapabilities) {
2670
+ function getMediaDecodingInfoPromise(level, audioTracksByGroup, mediaCapabilities, cache = {}) {
2669
2671
  const videoCodecs = level.videoCodec;
2670
- const audioCodecs = level.audioCodec;
2671
- if (!videoCodecs && !audioCodecs || !mediaCapabilities) {
2672
+ if (!videoCodecs && !level.audioCodec || !mediaCapabilities) {
2672
2673
  return Promise.resolve(SUPPORTED_INFO_DEFAULT);
2673
2674
  }
2674
2675
  const configurations = [];
2675
- if (videoCodecs) {
2676
- const baseVideoConfiguration = {
2677
- width: level.width,
2678
- height: level.height,
2679
- bitrate: Math.ceil(Math.max(level.bitrate * 0.9, level.averageBitrate)),
2680
- // Assume a framerate of 30fps since MediaCapabilities will not accept Level default of 0.
2681
- framerate: level.frameRate || 30
2676
+ const videoDecodeList = makeVideoConfigurations(level);
2677
+ const videoCount = videoDecodeList.length;
2678
+ const audioDecodeList = makeAudioConfigurations(level, audioTracksByGroup, videoCount > 0);
2679
+ const audioCount = audioDecodeList.length;
2680
+ for (let i = videoCount || 1 * audioCount || 1; i--;) {
2681
+ const configuration = {
2682
+ type: 'media-source'
2682
2683
  };
2683
- const videoRange = level.videoRange;
2684
- if (videoRange !== 'SDR') {
2685
- baseVideoConfiguration.transferFunction = videoRange.toLowerCase();
2684
+ if (videoCount) {
2685
+ configuration.video = videoDecodeList[i % videoCount];
2686
2686
  }
2687
- const videoCodecsArray = videoCodecs.split(',');
2687
+ if (audioCount) {
2688
+ configuration.audio = audioDecodeList[i % audioCount];
2689
+ const audioBitrate = configuration.audio.bitrate;
2690
+ if (configuration.video && audioBitrate) {
2691
+ configuration.video.bitrate -= audioBitrate;
2692
+ }
2693
+ }
2694
+ configurations.push(configuration);
2695
+ }
2696
+ if (videoCodecs) {
2688
2697
  // Override Windows Firefox HEVC MediaCapabilities result (https://github.com/video-dev/hls.js/issues/7046)
2689
2698
  const ua = navigator.userAgent;
2690
- if (videoCodecsArray.some(videoCodec => isHEVC(videoCodec)) && userAgentHevcSupportIsInaccurate()) {
2691
- return Promise.resolve(getUnsupportedResult(new Error(`Overriding Windows Firefox HEVC MediaCapabilities result based on user-agent sting: (${ua})`), configurations));
2699
+ if (videoCodecs.split(',').some(videoCodec => isHEVC(videoCodec)) && userAgentHevcSupportIsInaccurate()) {
2700
+ return Promise.resolve(getUnsupportedResult(new Error(`Overriding Windows Firefox HEVC MediaCapabilities result based on user-agent string: (${ua})`), configurations));
2692
2701
  }
2693
- configurations.push.apply(configurations, videoCodecsArray.map(videoCodec => ({
2694
- type: 'media-source',
2695
- video: _objectSpread2(_objectSpread2({}, baseVideoConfiguration), {}, {
2696
- contentType: mimeTypeForCodec(fillInMissingAV01Params(videoCodec), 'video')
2697
- })
2698
- })));
2699
- }
2700
- if (audioCodecs && level.audioGroups) {
2701
- level.audioGroups.forEach(audioGroupId => {
2702
- var _audioTracksByGroup$g;
2703
- if (!audioGroupId) {
2704
- return;
2705
- }
2706
- (_audioTracksByGroup$g = audioTracksByGroup.groups[audioGroupId]) == null ? void 0 : _audioTracksByGroup$g.tracks.forEach(audioTrack => {
2707
- if (audioTrack.groupId === audioGroupId) {
2708
- const channels = audioTrack.channels || '';
2709
- const channelsNumber = parseFloat(channels);
2710
- if (isFiniteNumber(channelsNumber) && channelsNumber > 2) {
2711
- configurations.push.apply(configurations, audioCodecs.split(',').map(audioCodec => ({
2712
- type: 'media-source',
2713
- audio: {
2714
- contentType: mimeTypeForCodec(audioCodec, 'audio'),
2715
- channels: '' + channelsNumber
2716
- // spatialRendering:
2717
- // audioCodec === 'ec-3' && channels.indexOf('JOC'),
2718
- }
2719
- })));
2720
- }
2721
- }
2722
- });
2723
- });
2724
2702
  }
2725
2703
  return Promise.all(configurations.map(configuration => {
2726
2704
  // Cache MediaCapabilities promises
2727
2705
  const decodingInfoKey = getMediaDecodingInfoKey(configuration);
2728
- return SUPPORTED_INFO_CACHE[decodingInfoKey] || (SUPPORTED_INFO_CACHE[decodingInfoKey] = mediaCapabilities.decodingInfo(configuration));
2706
+ return cache[decodingInfoKey] || (cache[decodingInfoKey] = mediaCapabilities.decodingInfo(configuration));
2729
2707
  })).then(decodingInfoResults => ({
2730
2708
  supported: !decodingInfoResults.some(info => !info.supported),
2731
2709
  configurations,
@@ -2737,22 +2715,90 @@ function getMediaDecodingInfoPromise(level, audioTracksByGroup, mediaCapabilitie
2737
2715
  error
2738
2716
  }));
2739
2717
  }
2718
+ function makeVideoConfigurations(level) {
2719
+ var _level$videoCodec;
2720
+ const videoCodecs = (_level$videoCodec = level.videoCodec) == null ? void 0 : _level$videoCodec.split(',');
2721
+ const bitrate = getVariantDecodingBitrate(level);
2722
+ const width = level.width || 640;
2723
+ const height = level.height || 480;
2724
+ // Assume a framerate of 30fps since MediaCapabilities will not accept Level default of 0.
2725
+ const framerate = level.frameRate || 30;
2726
+ const videoRange = level.videoRange.toLowerCase();
2727
+ return videoCodecs ? videoCodecs.map(videoCodec => {
2728
+ const videoConfiguration = {
2729
+ contentType: mimeTypeForCodec(fillInMissingAV01Params(videoCodec), 'video'),
2730
+ width,
2731
+ height,
2732
+ bitrate,
2733
+ framerate
2734
+ };
2735
+ if (videoRange !== 'sdr') {
2736
+ videoConfiguration.transferFunction = videoRange;
2737
+ }
2738
+ return videoConfiguration;
2739
+ }) : [];
2740
+ }
2741
+ function makeAudioConfigurations(level, audioTracksByGroup, hasVideo) {
2742
+ var _level$audioCodec;
2743
+ const audioCodecs = (_level$audioCodec = level.audioCodec) == null ? void 0 : _level$audioCodec.split(',');
2744
+ const combinedBitrate = getVariantDecodingBitrate(level);
2745
+ if (audioCodecs && level.audioGroups) {
2746
+ return level.audioGroups.reduce((configurations, audioGroupId) => {
2747
+ var _audioTracksByGroup$g;
2748
+ const tracks = audioGroupId ? (_audioTracksByGroup$g = audioTracksByGroup.groups[audioGroupId]) == null ? void 0 : _audioTracksByGroup$g.tracks : null;
2749
+ if (tracks) {
2750
+ return tracks.reduce((configs, audioTrack) => {
2751
+ if (audioTrack.groupId === audioGroupId) {
2752
+ const channelsNumber = parseFloat(audioTrack.channels || '');
2753
+ audioCodecs.forEach(audioCodec => {
2754
+ const audioConfiguration = {
2755
+ contentType: mimeTypeForCodec(audioCodec, 'audio'),
2756
+ bitrate: hasVideo ? estimatedAudioBitrate(audioCodec, combinedBitrate) : combinedBitrate
2757
+ };
2758
+ if (channelsNumber) {
2759
+ audioConfiguration.channels = '' + channelsNumber;
2760
+ }
2761
+ configs.push(audioConfiguration);
2762
+ });
2763
+ }
2764
+ return configs;
2765
+ }, configurations);
2766
+ }
2767
+ return configurations;
2768
+ }, []);
2769
+ }
2770
+ return [];
2771
+ }
2772
+ function estimatedAudioBitrate(audioCodec, levelBitrate) {
2773
+ if (levelBitrate <= 1) {
2774
+ return 1;
2775
+ }
2776
+ let audioBitrate = 128000;
2777
+ if (audioCodec === 'ec-3') {
2778
+ audioBitrate = 768000;
2779
+ } else if (audioCodec === 'ac-3') {
2780
+ audioBitrate = 640000;
2781
+ }
2782
+ return Math.min(levelBitrate / 2, audioBitrate); // Don't exceed some % of level bitrate
2783
+ }
2784
+ function getVariantDecodingBitrate(level) {
2785
+ return Math.ceil(Math.max(level.bitrate * 0.9, level.averageBitrate) / 1000) * 1000 || 1;
2786
+ }
2740
2787
  function getMediaDecodingInfoKey(config) {
2788
+ let key = '';
2741
2789
  const {
2742
2790
  audio,
2743
2791
  video
2744
2792
  } = config;
2745
- const mediaConfig = video || audio;
2746
- if (mediaConfig) {
2747
- const codec = getCodecsForMimeType(mediaConfig.contentType);
2748
- if (video) {
2749
- return `r${video.height}x${video.width}f${Math.ceil(video.framerate)}${video.transferFunction || 'sd'}_${codec}_${Math.ceil(video.bitrate / 1e5)}`;
2750
- }
2751
- if (audio) {
2752
- return `c${audio.channels}${audio.spatialRendering ? 's' : 'n'}_${codec}`;
2753
- }
2793
+ if (video) {
2794
+ const codec = getCodecsForMimeType(video.contentType);
2795
+ key += `${codec}_r${video.height}x${video.width}f${Math.ceil(video.framerate)}${video.transferFunction || 'sd'}_${Math.ceil(video.bitrate / 1e5)}`;
2796
+ }
2797
+ if (audio) {
2798
+ const codec = getCodecsForMimeType(audio.contentType);
2799
+ key += `${video ? '_' : ''}${codec}_c${audio.channels}`;
2754
2800
  }
2755
- return '';
2801
+ return key;
2756
2802
  }
2757
2803
 
2758
2804
  const HdcpLevels = ['NONE', 'TYPE-0', 'TYPE-1', null];
@@ -3345,6 +3391,7 @@ class AbrController extends Logger {
3345
3391
  this.partCurrent = null;
3346
3392
  this.bitrateTestDelay = 0;
3347
3393
  this.rebufferNotice = -1;
3394
+ this.supportedCache = {};
3348
3395
  this.bwEstimator = void 0;
3349
3396
  /*
3350
3397
  This method monitors the download rate of the current fragment, and will downswitch if that fragment will not load
@@ -3555,13 +3602,14 @@ class AbrController extends Logger {
3555
3602
  this.unregisterListeners();
3556
3603
  this.clearTimer();
3557
3604
  // @ts-ignore
3558
- this.hls = this._abandonRulesCheck = null;
3605
+ this.hls = this._abandonRulesCheck = this.supportedCache = null;
3559
3606
  this.fragCurrent = this.partCurrent = null;
3560
3607
  }
3561
3608
  onManifestLoading(event, data) {
3562
3609
  this.lastLoadedFragLevel = -1;
3563
3610
  this.firstSelection = -1;
3564
3611
  this.lastLevelLoadSec = 0;
3612
+ this.supportedCache = {};
3565
3613
  this.fragCurrent = this.partCurrent = null;
3566
3614
  this.onLevelsUpdated();
3567
3615
  this.clearTimer();
@@ -3922,7 +3970,7 @@ class AbrController extends Logger {
3922
3970
  const ttfbEstimateSec = this.bwEstimator.getEstimateTTFB() / 1000;
3923
3971
  const levelsSkipped = [];
3924
3972
  for (let i = maxAutoLevel; i >= minAutoLevel; i--) {
3925
- var _levelInfo$supportedR;
3973
+ var _levelInfo$supportedR, _levelInfo$supportedR2;
3926
3974
  const levelInfo = levels[i];
3927
3975
  const upSwitch = i > selectionBaseLevel;
3928
3976
  if (!levelInfo) {
@@ -3930,9 +3978,8 @@ class AbrController extends Logger {
3930
3978
  }
3931
3979
  if (config.useMediaCapabilities && !levelInfo.supportedResult && !levelInfo.supportedPromise) {
3932
3980
  const mediaCapabilities = navigator.mediaCapabilities;
3933
- if (typeof (mediaCapabilities == null ? void 0 : mediaCapabilities.decodingInfo) === 'function' && (requiresMediaCapabilitiesDecodingInfo(levelInfo, audioTracksByGroup, currentVideoRange, currentFrameRate, currentBw, audioPreference) || isHEVC(levelInfo.videoCodec)) // Force media capabilities check for HEVC to avoid failure on Windows
3934
- ) {
3935
- levelInfo.supportedPromise = getMediaDecodingInfoPromise(levelInfo, audioTracksByGroup, mediaCapabilities);
3981
+ if (typeof (mediaCapabilities == null ? void 0 : mediaCapabilities.decodingInfo) === 'function' && requiresMediaCapabilitiesDecodingInfo(levelInfo, audioTracksByGroup, currentVideoRange, currentFrameRate, currentBw, audioPreference)) {
3982
+ levelInfo.supportedPromise = getMediaDecodingInfoPromise(levelInfo, audioTracksByGroup, mediaCapabilities, this.supportedCache);
3936
3983
  levelInfo.supportedPromise.then(decodingInfo => {
3937
3984
  if (!this.hls) {
3938
3985
  return;
@@ -3951,6 +3998,8 @@ class AbrController extends Logger {
3951
3998
  this.hls.nextLoadLevel = 0;
3952
3999
  }
3953
4000
  }
4001
+ } else if (decodingInfo.decodingInfoResults.some(info => info.smooth === false || info.powerEfficient === false)) {
4002
+ this.log(`MediaCapabilities decodingInfo for level ${index} not smooth or powerEfficient: ${stringify(decodingInfo)}`);
3954
4003
  }
3955
4004
  });
3956
4005
  } else {
@@ -3960,7 +4009,7 @@ class AbrController extends Logger {
3960
4009
 
3961
4010
  // skip candidates which change codec-family or video-range,
3962
4011
  // and which decrease or increase frame-rate for up and down-switch respectfully
3963
- if (currentCodecSet && levelInfo.codecSet !== currentCodecSet || currentVideoRange && levelInfo.videoRange !== currentVideoRange || upSwitch && currentFrameRate > levelInfo.frameRate || !upSwitch && currentFrameRate > 0 && currentFrameRate < levelInfo.frameRate || levelInfo.supportedResult && !((_levelInfo$supportedR = levelInfo.supportedResult.decodingInfoResults) != null && _levelInfo$supportedR[0].smooth)) {
4012
+ if (currentCodecSet && levelInfo.codecSet !== currentCodecSet || currentVideoRange && levelInfo.videoRange !== currentVideoRange || upSwitch && currentFrameRate > levelInfo.frameRate || !upSwitch && currentFrameRate > 0 && currentFrameRate < levelInfo.frameRate || (_levelInfo$supportedR = levelInfo.supportedResult) != null && (_levelInfo$supportedR2 = _levelInfo$supportedR.decodingInfoResults) != null && _levelInfo$supportedR2.some(info => info.smooth === false)) {
3964
4013
  if (!firstSelection || i !== minStartIndex) {
3965
4014
  levelsSkipped.push(i);
3966
4015
  continue;
@@ -10218,7 +10267,7 @@ function requireEventemitter3 () {
10218
10267
  var eventemitter3Exports = requireEventemitter3();
10219
10268
  var EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports);
10220
10269
 
10221
- const version = "1.6.6-0.canary.11352";
10270
+ const version = "1.6.6-0.canary.11354";
10222
10271
 
10223
10272
  // ensure the worker ends up in the bundle
10224
10273
  // If the worker should not be included this gets aliased to empty.js
@@ -25210,6 +25259,14 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
25210
25259
  scheduleIndex: index,
25211
25260
  player
25212
25261
  });
25262
+ if (currentItem !== this.playingItem) {
25263
+ // Schedule change occured on INTERSTITIAL_ASSET_ENDED
25264
+ if (this.itemsMatch(currentItem, this.playingItem) && !this.playingAsset) {
25265
+ this.advanceAfterAssetEnded(interstitial, this.findItemIndex(this.playingItem), playingAssetListIndex);
25266
+ }
25267
+ // Navigation occured on INTERSTITIAL_ASSET_ENDED
25268
+ return;
25269
+ }
25213
25270
  this.retreiveMediaSource(assetId, scheduledItem);
25214
25271
  if (player.media && !((_this$detachedData3 = this.detachedData) != null && _this$detachedData3.mediaSource)) {
25215
25272
  player.detachMedia();
@@ -25231,7 +25288,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
25231
25288
  this.updateSchedule();
25232
25289
  const items = this.schedule.items;
25233
25290
  if (scheduledItem && items) {
25234
- const updatedIndex = this.schedule.findItemIndex(scheduledItem);
25291
+ const updatedIndex = this.findItemIndex(scheduledItem);
25235
25292
  this.advanceSchedule(updatedIndex, items, assetListIndex, currentItem, playingLastItem);
25236
25293
  }
25237
25294
  return;
@@ -26177,7 +26234,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
26177
26234
  // If all assets in interstitial fail, mark the interstitial with an error
26178
26235
  if (!interstitial.assetList.some(asset => !asset.error)) {
26179
26236
  interstitial.error = error;
26180
- } else if (interstitial.appendInPlace) {
26237
+ } else {
26181
26238
  // Reset level details and reload/parse media playlists to align with updated schedule
26182
26239
  for (let i = assetListIndex; i < interstitial.assetList.length; i++) {
26183
26240
  this.resetAssetPlayer(interstitial.assetList[i].identifier);