hls.js 1.6.0-beta.1.0.canary.10808 → 1.6.0-beta.1.0.canary.10810

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
@@ -130,5 +130,5 @@
130
130
  "url-toolkit": "2.2.5",
131
131
  "wrangler": "3.91.0"
132
132
  },
133
- "version": "1.6.0-beta.1.0.canary.10808"
133
+ "version": "1.6.0-beta.1.0.canary.10810"
134
134
  }
@@ -251,7 +251,7 @@ class AudioStreamController
251
251
  if (this.initPTS[frag.cc] !== undefined) {
252
252
  this.waitingData = null;
253
253
  this.state = State.FRAG_LOADING;
254
- const payload = cache.flush();
254
+ const payload = cache.flush().buffer;
255
255
  const data: FragLoadedData = {
256
256
  frag,
257
257
  part,
@@ -8,6 +8,7 @@ import {
8
8
  findClosestLevelWithAudioGroup,
9
9
  findMatchingOption,
10
10
  matchesOption,
11
+ useAlternateAudio,
11
12
  } from '../utils/rendition-helper';
12
13
  import type Hls from '../hls';
13
14
  import type {
@@ -401,11 +402,10 @@ class AudioTrackController extends BasePlaylistController {
401
402
  if (!this.shouldLoadPlaylist(audioTrack)) {
402
403
  return;
403
404
  }
404
- if (audioTrack.url === this.hls.levels[this.hls.loadLevel]?.uri) {
405
- // Do not load audio rendition with URI matching main variant URI
406
- return;
405
+ // Do not load audio rendition with URI matching main variant URI
406
+ if (useAlternateAudio(audioTrack.url, this.hls)) {
407
+ this.scheduleLoading(audioTrack, hlsUrlParameters);
407
408
  }
408
- this.scheduleLoading(audioTrack, hlsUrlParameters);
409
409
  }
410
410
 
411
411
  protected loadingPlaylist(
@@ -347,7 +347,7 @@ class EMEController extends Logger implements ComponentAPI {
347
347
  this.generateRequestWithPreferredKeySession(
348
348
  keySessionContext,
349
349
  scheme,
350
- decryptdata.pssh,
350
+ decryptdata.pssh.buffer,
351
351
  'expired',
352
352
  );
353
353
  } else {
@@ -449,10 +449,11 @@ class EMEController extends Logger implements ComponentAPI {
449
449
  const keySessionContextPromise = (this.keyIdToKeySessionPromise[keyId] =
450
450
  keyContextPromise.then((keySessionContext) => {
451
451
  const scheme = 'cenc';
452
+ const initData = decryptdata.pssh ? decryptdata.pssh.buffer : null;
452
453
  return this.generateRequestWithPreferredKeySession(
453
454
  keySessionContext,
454
455
  scheme,
455
- decryptdata.pssh,
456
+ initData,
456
457
  'playlist-key',
457
458
  );
458
459
  }));
@@ -545,7 +546,7 @@ class EMEController extends Logger implements ComponentAPI {
545
546
  const json = bin2str(new Uint8Array(initData));
546
547
  try {
547
548
  const sinf = base64Decode(JSON.parse(json).sinf);
548
- const tenc = parseSinf(new Uint8Array(sinf));
549
+ const tenc = parseSinf(sinf);
549
550
  if (!tenc) {
550
551
  throw new Error(
551
552
  `'schm' box missing or not cbcs/cenc with schi > tenc`,
@@ -730,9 +731,8 @@ class EMEController extends Logger implements ComponentAPI {
730
731
  );
731
732
  }
732
733
  initDataType = mappedInitData.initDataType;
733
- initData = context.decryptdata.pssh = mappedInitData.initData
734
- ? new Uint8Array(mappedInitData.initData)
735
- : null;
734
+ initData = mappedInitData.initData ? mappedInitData.initData : null;
735
+ context.decryptdata.pssh = initData ? new Uint8Array(initData) : null;
736
736
  } catch (error) {
737
737
  this.warn(error.message);
738
738
  if (this.hls?.config.debug) {
@@ -1273,6 +1273,10 @@ MediaSource ${JSON.stringify(attachMediaSourceData)} from ${logFromSource}`,
1273
1273
  }
1274
1274
 
1275
1275
  private onLevelUpdated(event: Events.LEVEL_UPDATED, data: LevelUpdatedData) {
1276
+ if (data.level === -1) {
1277
+ // level was removed
1278
+ return;
1279
+ }
1276
1280
  const main = this.hls.levels[data.level];
1277
1281
  const currentSelection = {
1278
1282
  ...(this.mediaSelection || this.altSelection),
@@ -11,6 +11,7 @@ import { PlaylistContextType, PlaylistLevelType } from '../types/loader';
11
11
  import { ChunkMetadata } from '../types/transmuxer';
12
12
  import { BufferHelper } from '../utils/buffer-helper';
13
13
  import { pickMostCompleteCodecName } from '../utils/codecs';
14
+ import { useAlternateAudio } from '../utils/rendition-helper';
14
15
  import type { FragmentTracker } from './fragment-tracker';
15
16
  import type Hls from '../hls';
16
17
  import type { Fragment, MediaFragment } from '../loader/fragment';
@@ -851,9 +852,10 @@ export default class StreamController
851
852
  event: Events.AUDIO_TRACK_SWITCHING,
852
853
  data: AudioTrackSwitchingData,
853
854
  ) {
855
+ const hls = this.hls;
854
856
  // if any URL found on new audio track, it is an alternate audio track
855
857
  const fromAltAudio = this.altAudio === AlternateAudio.SWITCHED;
856
- const altAudio = !!data.url;
858
+ const altAudio = useAlternateAudio(data.url, hls);
857
859
  // if we switch on main audio, ensure that main fragment scheduling is synced with media.buffered
858
860
  // don't do anything if we switch to alt audio: audio stream controller is handling it.
859
861
  // we will just have to change buffer scheduling on audioTrackSwitched
@@ -878,7 +880,6 @@ export default class StreamController
878
880
  // Reset audio transmuxer so when switching back to main audio we're not still appending where we left off
879
881
  this.resetTransmuxer();
880
882
  }
881
- const hls = this.hls;
882
883
  // If switching from alt to main audio, flush all audio and trigger track switched
883
884
  if (fromAltAudio) {
884
885
  hls.trigger(Events.BUFFER_FLUSHING, {
@@ -898,8 +899,7 @@ export default class StreamController
898
899
  event: Events.AUDIO_TRACK_SWITCHED,
899
900
  data: AudioTrackSwitchedData,
900
901
  ) {
901
- const trackId = data.id;
902
- const altAudio = !!this.hls.audioTracks[trackId].url;
902
+ const altAudio = useAlternateAudio(data.url, this.hls);
903
903
  if (altAudio) {
904
904
  const videoBuffer = this.videoBuffer;
905
905
  // if we switched on alternate audio, ensure that main fragment scheduling is synced with video sourcebuffer buffered
@@ -577,7 +577,7 @@ export class TimelineController implements ComponentAPI {
577
577
  const hls = this.hls;
578
578
  // Parse the WebVTT file contents.
579
579
  const payloadWebVTT = frag.initSegment?.data
580
- ? appendUint8Array(frag.initSegment.data, new Uint8Array(payload))
580
+ ? appendUint8Array(frag.initSegment.data, new Uint8Array(payload)).buffer
581
581
  : payload;
582
582
  parseWebVTT(
583
583
  payloadWebVTT,
@@ -86,7 +86,8 @@ export default class Decrypter {
86
86
  ): Promise<ArrayBuffer> {
87
87
  if (this.useSoftware) {
88
88
  return new Promise((resolve, reject) => {
89
- this.softwareDecrypt(new Uint8Array(data), key, iv, aesMode);
89
+ const dataView = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
90
+ this.softwareDecrypt(dataView, key, iv, aesMode);
90
91
  const decryptResult = this.flush();
91
92
  if (decryptResult) {
92
93
  resolve(decryptResult.buffer);
@@ -9,7 +9,7 @@ export default class ChunkCache {
9
9
 
10
10
  flush(): Uint8Array {
11
11
  const { chunks, dataLength } = this;
12
- let result;
12
+ let result: Uint8Array;
13
13
  if (!chunks.length) {
14
14
  return new Uint8Array(0);
15
15
  } else if (chunks.length === 1) {
@@ -135,7 +135,8 @@ export default class Transmuxer {
135
135
  // For Low-Latency HLS Parts, decrypt in place, since part parsing is expected on push progress
136
136
  const loadingParts = chunkMeta.part > -1;
137
137
  if (loadingParts) {
138
- decryptedData = decrypter.flush();
138
+ const data = decrypter.flush();
139
+ decryptedData = data ? data.buffer : data;
139
140
  }
140
141
  if (!decryptedData) {
141
142
  stats.executeEnd = now();
@@ -248,7 +249,7 @@ export default class Transmuxer {
248
249
  if (decryptedData) {
249
250
  // Push always returns a TransmuxerResult if decryptdata is null
250
251
  transmuxResults.push(
251
- this.push(decryptedData, null, chunkMeta) as TransmuxerResult,
252
+ this.push(decryptedData.buffer, null, chunkMeta) as TransmuxerResult,
252
253
  );
253
254
  }
254
255
  }
@@ -1,3 +1,12 @@
1
+ declare global {
2
+ interface ArrayBuffer {
3
+ ' buffer_kind'?: 'array';
4
+ }
5
+ interface Uint8Array {
6
+ ' buffer_kind'?: 'uint8';
7
+ }
8
+ }
9
+
1
10
  export type SourceBufferName = 'video' | 'audio' | 'audiovideo';
2
11
 
3
12
  /* eslint-disable no-restricted-globals */
@@ -237,7 +237,7 @@ class FetchLoader implements Loader<LoaderContext> {
237
237
  .then((data) => {
238
238
  if (data.done) {
239
239
  if (chunkCache.dataLength) {
240
- onProgress(stats, context, chunkCache.flush(), response);
240
+ onProgress(stats, context, chunkCache.flush().buffer, response);
241
241
  }
242
242
 
243
243
  return Promise.resolve(new ArrayBuffer(0));
@@ -251,12 +251,12 @@ class FetchLoader implements Loader<LoaderContext> {
251
251
  chunkCache.push(chunk);
252
252
  if (chunkCache.dataLength >= highWaterMark) {
253
253
  // flush in order to join the typed arrays
254
- onProgress(stats, context, chunkCache.flush(), response);
254
+ onProgress(stats, context, chunkCache.flush().buffer, response);
255
255
  }
256
256
  } else {
257
257
  // If there's nothing cached already, and the chache is large enough
258
258
  // just emit the progress event
259
- onProgress(stats, context, chunk, response);
259
+ onProgress(stats, context, chunk.buffer, response);
260
260
  }
261
261
  return pump();
262
262
  })
@@ -1,6 +1,7 @@
1
1
  import { codecsSetSelectionPreferenceValue } from './codecs';
2
2
  import { getVideoSelectionOptions } from './hdr';
3
3
  import { logger } from './logger';
4
+ import type Hls from '../hls';
4
5
  import type { Level, VideoRange } from '../types/level';
5
6
  import type {
6
7
  AudioSelectionOption,
@@ -500,3 +501,7 @@ function searchDownAndUpList(
500
501
  }
501
502
  return -1;
502
503
  }
504
+
505
+ export function useAlternateAudio(audioTrackUrl: string, hls: Hls): boolean {
506
+ return !!audioTrackUrl && audioTrackUrl !== hls.levels[hls.loadLevel]?.uri;
507
+ }