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/package.json CHANGED
@@ -134,5 +134,5 @@
134
134
  "url-toolkit": "2.2.5",
135
135
  "wrangler": "3.109.2"
136
136
  },
137
- "version": "1.6.0-beta.3.0.canary.10980"
137
+ "version": "1.6.0-beta.3.0.canary.10981"
138
138
  }
@@ -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?.substring(0, 4) === 'hvc1') // Force media capabilities check for HEVC to avoid failure on Windows
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
- case ErrorDetails.BUFFER_FULL_ERROR:
802
- if (!data.parent || data.parent !== 'audio') {
802
+ if (data.parent !== 'audio') {
803
803
  return;
804
804
  }
805
- if (data.details === ErrorDetails.BUFFER_APPEND_ERROR) {
806
- this.resetLoadingState();
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 = 'audio';
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
- break;
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
- case ErrorDetails.BUFFER_FULL_ERROR:
1020
- if (!data.parent || data.parent !== 'main') {
1020
+ if (data.parent !== 'main') {
1021
1021
  return;
1022
1022
  }
1023
- if (data.details === ErrorDetails.BUFFER_APPEND_ERROR) {
1024
- this.resetLoadingState();
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 = 'main';
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 = 'main';
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
@@ -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 preferenceValue = sampleEntryCodesISO.video[fourCC];
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 { fillInMissingAV01Params, mimeTypeForCodec } from './codecs';
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
- videoCodecs.split(',').map((videoCodec) => ({
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.split('"')[1];
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'
@@ -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 delimit = codec.indexOf('.');
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' ||