diff --git a/.eslintrc.js b/.eslintrc.js index 386f3ca0586..913a65ffe7b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -16,6 +16,8 @@ module.exports = { __USE_CMCD__: true, __USE_CONTENT_STEERING__: true, __USE_VARIABLE_SUBSTITUTION__: true, + __USE_M2TS_ADVANCED_CODECS__: true, + __USE_MEDIA_CAPABILITIES__: true, }, // see https://github.com/standard/eslint-config-standard // 'prettier' (https://github.com/prettier/eslint-config-prettier) must be last @@ -23,6 +25,7 @@ module.exports = { parser: '@typescript-eslint/parser', parserOptions: { sourceType: 'module', + project: './tsconfig.json', }, plugins: ['@typescript-eslint', 'import'], rules: { diff --git a/.github/ISSUE_TEMPLATE/question.yaml b/.github/ISSUE_TEMPLATE/question.yaml index 9876cc3bd00..eb0dfbcfff2 100644 --- a/.github/ISSUE_TEMPLATE/question.yaml +++ b/.github/ISSUE_TEMPLATE/question.yaml @@ -1,5 +1,5 @@ name: Question -description: Need some help? +description: Need help with something not related to a Bug or Feature Request? labels: [Question, Needs Triage] body: - type: textarea diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index c7c27b125a4..14836d7f401 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -10,8 +10,11 @@ permissions: jobs: run: runs-on: ubuntu-latest + concurrency: + group: 'automerge:run:${{ github.head_ref }}' + cancel-in-progress: true steps: - - uses: tjenkinson/gh-action-auto-merge-dependency-updates@01b22a97735b01be6bc1d8a2705f7ee167472414 + - uses: tjenkinson/gh-action-auto-merge-dependency-updates@94a659f2eba4e787914b23e13ab2a28f46b5e1e6 # v1.3.5 with: repo-token: ${{ secrets.CI_GITHUB_TOKEN }} allowed-actors: renovate[bot] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9e32083a3d4..53b99de7db2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,6 +15,9 @@ defaults: jobs: config: runs-on: ubuntu-latest + concurrency: + group: 'build:config:${{ github.ref }}' + cancel-in-progress: true outputs: canUseSauce: ${{ steps.check_sauce_access.outputs.result == 'true' }} tag: ${{ steps.extract_tag.outputs.result }} @@ -24,7 +27,7 @@ jobs: id: check_sauce_access run: | if ! [[ -z "$SAUCE_USERNAME" ]] && ! [[ -z "$SAUCE_ACCESS_KEY" ]]; then - echo "::set-output name=result::true" + echo "result=true" >> $GITHUB_OUTPUT fi env: CI: true @@ -32,7 +35,7 @@ jobs: SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} - name: extract tag id: extract_tag - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const prefix = 'refs/tags/'; @@ -43,15 +46,16 @@ jobs: build: needs: config runs-on: ubuntu-latest - + concurrency: + group: 'build:build:${{ github.ref }}' + cancel-in-progress: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: check package-lock.json version - id: extract_tag - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const fs = require('fs'); @@ -77,13 +81,13 @@ jobs: ${{ runner.os }}- - name: use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version-file: '.node-version' - name: install run: | - npx -y npm-ci-please@^1.1.1 + npm ci env: CI: true @@ -112,7 +116,7 @@ jobs: CI: true - name: upload build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: build # version number is set in package.json so need to include that @@ -125,8 +129,11 @@ jobs: test_unit: needs: build runs-on: ubuntu-latest + concurrency: + group: 'build:test_unit:${{ github.ref }}' + cancel-in-progress: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: cache node_modules uses: actions/cache@v3 @@ -141,18 +148,18 @@ jobs: ${{ runner.os }}- - name: use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version-file: '.node-version' - name: download build - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: build - name: install run: | - npx -y npm-ci-please@^1.1.1 + npm ci env: CI: true @@ -166,8 +173,11 @@ jobs: needs: [config, test_unit] if: needs.config.outputs.tag || needs.config.outputs.isMainBranch == 'true' runs-on: ubuntu-latest + concurrency: + group: 'build:cloudflare_pages:${{ github.ref }}' + cancel-in-progress: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: cache node_modules uses: actions/cache@v3 @@ -182,18 +192,18 @@ jobs: ${{ runner.os }}- - name: use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version-file: '.node-version' - name: download build - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: build - name: install run: | - npx -y npm-ci-please@^1.1.1 + npm ci env: CI: true @@ -215,6 +225,9 @@ jobs: needs: [config, test_unit] if: needs.config.outputs.tag runs-on: ubuntu-latest + concurrency: + group: 'build:update_draft_release:${{ github.ref }}' + cancel-in-progress: true outputs: upload_url: ${{ steps.draft_release.outputs.upload_url }} permissions: @@ -233,18 +246,21 @@ jobs: needs: [config, test_unit, update_draft_release] if: needs.config.outputs.tag runs-on: ubuntu-latest + concurrency: + group: 'build:release_github:${{ github.ref }}' + cancel-in-progress: true permissions: contents: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version-file: '.node-version' - name: download build - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: build @@ -266,10 +282,13 @@ jobs: needs: [config, test_unit] if: needs.config.outputs.tag || needs.config.outputs.isMainBranch == 'true' runs-on: ubuntu-latest + concurrency: + group: 'build:release_npm:${{ github.ref }}' + cancel-in-progress: true permissions: id-token: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: cache node_modules uses: actions/cache@v3 @@ -284,18 +303,18 @@ jobs: ${{ runner.os }}- - name: use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version-file: '.node-version' - name: download build - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: build - name: install run: | - npx -y npm-ci-please@^1.1.1 + npm ci env: CI: true @@ -311,6 +330,9 @@ jobs: needs: [config, test_unit] if: needs.config.outputs.canUseSauce == 'true' runs-on: ubuntu-latest + concurrency: + group: 'build:test_functional_required:${{ matrix.config }}:${{ github.ref }}' + cancel-in-progress: true name: test_functional_required (${{ matrix.config }}) strategy: fail-fast: true @@ -330,7 +352,7 @@ jobs: uaVersion: '75.0' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: cache node_modules uses: actions/cache@v3 @@ -345,17 +367,17 @@ jobs: ${{ runner.os }}- - name: use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version-file: '.node-version' - name: download build - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: build - name: start SauceConnect tunnel - uses: saucelabs/sauce-connect-action@b632f7b7d4c5fce8bac327911322b033330b04ab + uses: saucelabs/sauce-connect-action@270345f214aca56f55c678ca59e626dfd43da153 #v2 with: username: ${{ secrets.SAUCE_USERNAME }} accessKey: ${{ secrets.SAUCE_ACCESS_KEY }} @@ -364,7 +386,7 @@ jobs: - name: install run: | - npx -y npm-ci-please@^1.1.1 + npm ci env: CI: true @@ -384,6 +406,9 @@ jobs: test_functional_optional: needs: test_functional_required runs-on: ubuntu-latest + concurrency: + group: 'build:test_functional_optional:${{ matrix.config }}:${{ github.ref }}' + cancel-in-progress: true continue-on-error: true name: test_functional_optional (${{ matrix.config }}) strategy: @@ -391,15 +416,15 @@ jobs: max-parallel: 8 matrix: include: - - config: safari-macOS_10.15 + - config: safari-macOS_13 ua: safari - os: macOS 10.15 + os: macOS 13 - config: firefox-win_10 ua: firefox os: Windows 10 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: cache node_modules uses: actions/cache@v3 @@ -414,17 +439,17 @@ jobs: ${{ runner.os }}- - name: use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version-file: '.node-version' - name: download build - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: build - name: start SauceConnect tunnel - uses: saucelabs/sauce-connect-action@b632f7b7d4c5fce8bac327911322b033330b04ab + uses: saucelabs/sauce-connect-action@270345f214aca56f55c678ca59e626dfd43da153 #v2 with: username: ${{ secrets.SAUCE_USERNAME }} accessKey: ${{ secrets.SAUCE_ACCESS_KEY }} @@ -433,7 +458,7 @@ jobs: - name: install run: | - npx -y npm-ci-please@^1.1.1 + npm ci env: CI: true diff --git a/.github/workflows/cancel.yml b/.github/workflows/cancel.yml deleted file mode 100644 index 0687301afaf..00000000000 --- a/.github/workflows/cancel.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Cancel -on: - workflow_run: - workflows: ['automerge', 'build', 'codeql-analysis'] - types: - - requested -permissions: - actions: write -jobs: - cancel: - runs-on: ubuntu-latest - steps: - - uses: styfle/cancel-workflow-action@3d86a7cc43670094ac248017207be0295edbc31d - name: Cancel Previous Workflow Runs - with: - workflow_id: ${{ github.event.workflow.id }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 2b1129e8cc1..7ffb21a4197 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -16,7 +16,9 @@ jobs: analyze: name: Analyze runs-on: ubuntu-latest - + concurrency: + group: 'codeql-analysis:analyze:${{ github.ref }}' + cancel-in-progress: true strategy: fail-fast: false matrix: @@ -24,12 +26,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.gitignore b/.gitignore index 2e9edb20833..8993e0dee37 100644 --- a/.gitignore +++ b/.gitignore @@ -26,11 +26,14 @@ coverage/ # eslint .eslintcache +/cache.tsbuildinfo -# Visual Studio 2015/2017 cache/options directory +# Visual Studio exclusions +*.suo +*.user .vs/ - -# Visual Studio 2017 auto generated files +bin/ +obj/ Generated\ Files/ /web.config diff --git a/.node-version b/.node-version index 3c032078a4a..209e3ef4b62 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -18 +20 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1621ce9b2af..e67b424c378 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,4 +47,4 @@ Pull requests are welcome and pair well with bug reports and feature requests. H ## Contact -If you aren't already a member, consider joining video-dev on Slack https://video-dev.herokuapp.com/ and chatting with us in the `#hlsjs` channel. +If you aren't already a member, consider joining video-dev on Slack https://www.video-dev.org/ and chatting with us in the `#hlsjs` channel. diff --git a/MIGRATING.md b/MIGRATING.md index 7f92afb817d..3c51ca9d779 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -1,3 +1,11 @@ +# Migrating from hls.js 1.x to 1.4+ + +The 1.4 version of hls.js now ships with an ESM version of the library (`dist/hls.mjs`) which requires that you specify the [`workerPath` config option](https://github.com/video-dev/hls.js/blob/master/docs/API.md#workerpath) in order for web workers to be used. This should point to the `dist/hls.worker.js` file included in the package. + +If you are using the UMD version (`dist/hls.js`), no changes are required. + +**Important Note:** If you are using a bundler, such as webpack, the ESM version of the package will likely be used by default. If this is the case, make sure to add the `workerPath` config option after upgrading to hls.js 1.4 or above. + # Migrating from hls.js 0.x to 1.x This guide provides an overview to migrating an application using hls.js from v0.14.x to v1.0.0. @@ -11,13 +19,18 @@ include your own Promise polyfill. ### Back Buffer Eviction -The new `backBufferLength` setting defaults to 90 seconds, and applies to Live and VOD streams. In version 1.0 and up, -the back buffer on VOD content will be cleared by hls.js rather than leaving it up to the browser by default. +The new `backBufferLength` setting applies to Live and VOD streams. It defaults to Infinity, leaving back buffer eviction to the browser to perform on SourceBuffer append (https://www.w3.org/TR/media-source-2/#sourcebuffer-coded-frame-eviction). The demo page includes a setting of 90 seconds for demonstration purposes. -Set `backBufferLength` to `Infinity` and `liveBackBufferLength` to `90` if you would like 1.0 to handle back buffer -eviction for Live and VOD streams as older versions did. While `liveBackBufferLength` can still be used, it has been +In v1.0 and up, the back buffer on VOD and Live content will be left up to the browser by default. Set `backBufferLength` to `Infinity` and `liveBackBufferLength` to `90` if you would like v1 to handle back buffer eviction for Live and VOD streams as older versions did. While `liveBackBufferLength` can still be used, it has been marked deprecated and may be removed in an upcoming minor release. +### Front Buffer Eviction + +The new `frontBufferFlushThreshold` setting defaults to Infinity seconds and governs active eviction of buffered ranges outside of +the current contiguous front buffer. For example, given currentTime=0 and bufferedRanges=[[0, 100], [150, 200]] with +a configured frontBufferFlushThreshold=60, we will only remove the range from [150, 200] as it lies outside of the target buffer length +and is not contiguous with the forward buffer from the currentTime of 0. + ### Low Latency Streams The new `lowLatencyMode` setting is enabled by default. Set to `false` to disable Low-latency part loading and target diff --git a/README.md b/README.md index 51f8041e5fc..364abf1b99a 100644 --- a/README.md +++ b/README.md @@ -39,12 +39,13 @@ HLS.js is written in [ECMAScript6] (`*.js`) and [TypeScript] (`*.ts`) (strongly - ITU-T Rec. H.264 and ISO/IEC 14496-10 Elementary Stream - ISO/IEC 13818-7 ADTS AAC Elementary Stream - ISO/IEC 11172-3 / ISO/IEC 13818-3 (MPEG-1/2 Audio Layer III) Elementary Stream + - ATSC A/52 / AC-3 / Dolby Digital Elementary Stream - Packetized metadata (ID3v2.3.0) Elementary Stream - AAC container (audio only streams) - MPEG Audio container (MPEG-1/2 Audio Layer III audio only streams) - Timed Metadata for HTTP Live Streaming (ID3 format carried in MPEG-2 TS, Emsg in CMAF/Fragmented MP4, and DATERANGE playlist tags) - AES-128 decryption -- SAMPLE-AES decryption (only supported if using MPEG-2 TS container) +- "identity" format SAMPLE-AES decryption of MPEG-2 TS segments only - Encrypted media extensions (EME) support for DRM (digital rights management) - FairPlay, PlayReady, Widevine CDMs with fmp4 segments - Level capping based on HTMLMediaElement resolution, dropped-frames, and HDCP-Level @@ -100,7 +101,7 @@ The following properties are added to their respective variants' attribute list - `#EXT-X-DISCONTINUITY-SEQUENCE=` - `#EXT-X-BYTERANGE=[@]` - `#EXT-X-MAP:` -- `#EXT-X-KEY:` (`METHOD=SAMPLE-AES` is only supports with MPEG-2 TS segments) +- `#EXT-X-KEY:` (`KEYFORMAT="identity",METHOD=SAMPLE-AES` is only supports with MPEG-2 TS segments) - `#EXT-X-PROGRAM-DATE-TIME:` - `#EXT-X-START:TIME-OFFSET=` - `#EXT-X-SERVER-CONTROL:` @@ -125,15 +126,11 @@ Parsed but missing feature support For a complete list of issues, see ["Top priorities" in the Release Planning and Backlog project tab](https://github.com/video-dev/hls.js/projects/6). Codec support is dependent on the runtime environment (for example, not all browsers on the same OS support HEVC). -- Advanced variant selection based on runtime media capabilities (See issues labeled [`media-capabilities`](https://github.com/video-dev/hls.js/labels/media-capabilities)) -- HLS Content Steering - HLS Interstitials -- `#EXT-X-GAP` filling [#2940](https://github.com/video-dev/hls.js/issues/2940) - `#EXT-X-I-FRAME-STREAM-INF` I-frame Media Playlist files -- `SAMPLE-AES` with fmp4, aac, mp3, vtt... segments (MPEG-2 TS only) -- FairPlay, PlayReady, Widevine DRM with MPEG-2 TS segments -- FairPlay legacy keys (For com.apple.fps.1_0 use native Safari playback) -- Advanced variant selection based on runtime media capabilities (See issues labeled [`media-capabilities`](https://github.com/video-dev/hls.js/labels/media-capabilities)) +- "identity" format `SAMPLE-AES` method keys with fmp4, aac, mp3, vtt... segments (MPEG-2 TS only) +- MPEG-2 TS segments with FairPlay Streaming, PlayReady, or Widevine encryption +- FairPlay Streaming legacy keys (For com.apple.fps.1_0 use native Safari playback) - MP3 elementary stream audio in IE and Edge (<=18) on Windows 10 (See [#1641](https://github.com/video-dev/hls.js/issues/1641) and [Microsoft answers forum](https://answers.microsoft.com/en-us/ie/forum/all/ie11-on-windows-10-cannot-play-hls-with-mp3/2da994b5-8dec-4ae9-9201-7d138ede49d9)) ### Server-side-rendering (SSR) and `require` from a Node.js runtime @@ -303,8 +300,9 @@ HLS.js is supported on: - Firefox 41+ for Android - Firefox 42+ for Desktop - Edge for Windows 10+ -- Safari 8+ for MacOS 10.10+ -- Safari for ipadOS 13+ +- Safari 9+ for macOS 10.11+ +- Safari for iPadOS 13+ +- Safari for iOS 17.1+ A [Promise polyfill](https://github.com/taylorhakes/promise-polyfill) is required in browsers missing native promise support. @@ -360,12 +358,6 @@ native browser support for HLS playback in HTMLMediaElements: // we can provide an HLS manifest (i.e. .m3u8 URL) directly to the video // element through the `src` property. This is using the built-in support // of the plain video element, without using HLS.js. - // - // Note: it would be more normal to wait on the 'canplay' event below however - // on Safari (where you are most likely to find built-in HLS support) the - // video.src URL must be on the user-driven white-list before a 'canplay' - // event will be emitted; the last video event that can be reliably - // listened-for when the URL is not on the white-list is 'loadedmetadata'. else if (video.canPlayType('application/vnd.apple.mpegurl')) { video.src = videoSrc; } diff --git a/api-extractor/report/hls.js.api.md b/api-extractor/report/hls.js.api.md index 43b20a26bad..24aa184abd6 100644 --- a/api-extractor/report/hls.js.api.md +++ b/api-extractor/report/hls.js.api.md @@ -13,7 +13,13 @@ export interface AbrComponentAPI extends ComponentAPI { // (undocumented) readonly bwEstimator?: EwmaBandWidthEstimator; // (undocumented) + firstAutoLevel: number; + // (undocumented) + forcedAutoLevel: number; + // (undocumented) nextAutoLevel: number; + // (undocumented) + resetEstimator(abrEwmaDefaultEstimate: number): any; } // Warning: (ae-missing-release-tag) "AbrController" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -22,17 +28,23 @@ export interface AbrComponentAPI extends ComponentAPI { export class AbrController implements AbrComponentAPI { constructor(hls: Hls); // (undocumented) - readonly bwEstimator: EwmaBandWidthEstimator; + bwEstimator: EwmaBandWidthEstimator; // (undocumented) clearTimer(): void; // (undocumented) destroy(): void; // (undocumented) + get firstAutoLevel(): number; + // (undocumented) + get forcedAutoLevel(): number; + // (undocumented) protected hls: Hls; // (undocumented) get nextAutoLevel(): number; set nextAutoLevel(nextLevel: number); // (undocumented) + protected onError(event: Events.ERROR, data: ErrorData): void; + // (undocumented) protected onFragBuffered(event: Events.FRAG_BUFFERED, data: FragBufferedData): void; // (undocumented) protected onFragLoaded(event: Events.FRAG_LOADED, { frag, part }: FragLoadedData): void; @@ -43,8 +55,12 @@ export class AbrController implements AbrComponentAPI { // (undocumented) protected onLevelSwitching(event: Events.LEVEL_SWITCHING, data: LevelSwitchingData): void; // (undocumented) + protected onManifestLoading(event: Events.MANIFEST_LOADING, data: ManifestLoadingData): void; + // (undocumented) protected registerListeners(): void; // (undocumented) + resetEstimator(abrEwmaDefaultEstimate?: number): void; + // (undocumented) protected unregisterListeners(): void; } @@ -57,6 +73,7 @@ export type ABRControllerConfig = { abrEwmaFastVoD: number; abrEwmaSlowVoD: number; abrEwmaDefaultEstimate: number; + abrEwmaDefaultEstimateMax: number; abrBandWidthFactor: number; abrBandWidthUpFactor: number; abrMaxWithRealBitrate: boolean; @@ -74,6 +91,8 @@ export class AttrList { // (undocumented) bool(attrName: string): boolean; // (undocumented) + get clientAttrs(): string[]; + // (undocumented) decimalFloatingPoint(attrName: string): number; // (undocumented) decimalInteger(attrName: string): number; @@ -99,6 +118,20 @@ export class AttrList { // @public (undocumented) export type AudioPlaylistType = 'AUDIO'; +// Warning: (ae-missing-release-tag) "AudioSelectionOption" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type AudioSelectionOption = { + lang?: string; + assocLang?: string; + characteristics?: string; + channels?: string; + name?: string; + audioCodec?: string; + groupId?: string; + default?: boolean; +}; + // Warning: (ae-missing-release-tag) "AudioStreamController" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -154,6 +187,8 @@ export class AudioStreamController extends BaseStreamController implements Netwo export class AudioTrackController extends BasePlaylistController { constructor(hls: Hls); // (undocumented) + get allAudioTracks(): MediaPlaylist[]; + // (undocumented) get audioTrack(): number; set audioTrack(newId: number); // (undocumented) @@ -174,6 +209,8 @@ export class AudioTrackController extends BasePlaylistController { protected onManifestLoading(): void; // (undocumented) protected onManifestParsed(event: Events.MANIFEST_PARSED, data: ManifestParsedData): void; + // (undocumented) + setAudioOption(audioOption: MediaPlaylist | AudioSelectionOption | undefined): MediaPlaylist | null; } // Warning: (ae-missing-release-tag) "AudioTrackLoadedData" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -257,11 +294,11 @@ export class BaseSegment { // (undocumented) readonly baseurl: string; // (undocumented) - get byteRange(): number[]; + get byteRange(): [number, number] | []; // (undocumented) - get byteRangeEndOffset(): number; + get byteRangeEndOffset(): number | undefined; // (undocumented) - get byteRangeStartOffset(): number; + get byteRangeStartOffset(): number | undefined; // (undocumented) elementaryStreams: ElementaryStreams; // (undocumented) @@ -282,13 +319,15 @@ export class BaseStreamController extends TaskLoop implements NetworkComponentAP // (undocumented) protected afterBufferFlushed(media: Bufferable, bufferType: SourceBufferName, playlistType: PlaylistLevelType): void; // (undocumented) - protected alignPlaylists(details: LevelDetails, previousDetails?: LevelDetails): number; + protected alignPlaylists(details: LevelDetails, previousDetails: LevelDetails | undefined, switchDetails: LevelDetails | undefined): number; // (undocumented) protected bitrateTest: boolean; // Warning: (ae-forgotten-export) The symbol "RemuxedTrack" needs to be exported by the entry point hls.d.ts // // (undocumented) - protected bufferFragmentData(data: RemuxedTrack, frag: Fragment, part: Part | null, chunkMeta: ChunkMetadata): void; + protected bufferFragmentData(data: RemuxedTrack, frag: Fragment, part: Part | null, chunkMeta: ChunkMetadata, noBacktracking?: boolean): void; + // (undocumented) + protected checkLiveUpdate(details: LevelDetails): void; // (undocumented) protected clearTrackerIfNeeded(frag: Fragment): void; // (undocumented) @@ -370,7 +409,7 @@ export class BaseStreamController extends TaskLoop implements NetworkComponentAP // (undocumented) protected lastCurrentTime: number; // (undocumented) - protected levelLastLoaded: number | null; + protected levelLastLoaded: Level | null; // (undocumented) protected levels: Array | null; // (undocumented) @@ -430,7 +469,7 @@ export class BaseStreamController extends TaskLoop implements NetworkComponentAP // (undocumented) protected resetLoadingState(): void; // (undocumented) - protected resetStartWhenNotLoaded(level: number): void; + protected resetStartWhenNotLoaded(level: Level | null): void; // (undocumented) protected resetTransmuxer(): void; // (undocumented) @@ -524,7 +563,11 @@ export class BufferController implements ComponentAPI { // (undocumented) protected appendChangeType(type: any, mimeType: any): void; // (undocumented) - appendError: number; + appendErrors: { + audio: number; + video: number; + audiovideo: number; + }; // (undocumented) bufferCodecEventsExpected: number; // (undocumented) @@ -534,10 +577,16 @@ export class BufferController implements ComponentAPI { // (undocumented) destroy(): void; // (undocumented) - flushBackBuffer(): void; + protected error: (msg: any, obj?: any) => void; + // (undocumented) + flushBackBuffer(currentTime: number, targetDuration: number, targetBackBufferPosition: number): void; + // (undocumented) + flushFrontBuffer(currentTime: number, targetDuration: number, targetFrontBufferPosition: number): void; // (undocumented) hasSourceTypes(): boolean; // (undocumented) + protected log: (msg: any) => void; + // (undocumented) media: HTMLMediaElement | null; // (undocumented) mediaSource: MediaSource | null; @@ -572,9 +621,13 @@ export class BufferController implements ComponentAPI { // (undocumented) tracks: TrackSet; // (undocumented) + trimBuffers(): void; + // (undocumented) protected unregisterListeners(): void; // (undocumented) updateSeekableRange(levelDetails: any): void; + // (undocumented) + protected warn: (msg: any, obj?: any) => void; } // Warning: (ae-missing-release-tag) "BufferControllerConfig" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -583,6 +636,7 @@ export class BufferController implements ComponentAPI { export type BufferControllerConfig = { appendErrorMaxRetry: number; backBufferLength: number; + frontBufferFlushThreshold: number; liveDurationInfinity: boolean; liveBackBufferLength: number | null; }; @@ -720,15 +774,8 @@ export class ChunkMetadata { // @public export class CMCDController implements ComponentAPI { constructor(hls: Hls); - static appendQueryToUri(uri: any, query: any): any; // (undocumented) destroy(): void; - // Warning: (ae-forgotten-export) The symbol "CMCD" needs to be exported by the entry point hls.d.ts - static serialize(data: CMCD): string; - // Warning: (ae-forgotten-export) The symbol "CMCDHeaders" needs to be exported by the entry point hls.d.ts - static toHeaders(data: CMCD): Partial; - static toQuery(data: CMCD): string; - static uuid(): string; } // Warning: (ae-missing-release-tag) "CMCDControllerConfig" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -738,6 +785,7 @@ export type CMCDControllerConfig = { sessionId?: string; contentId?: string; useHeaders?: boolean; + includeKeys?: string[]; }; // Warning: (ae-missing-release-tag) "ComponentAPI" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -754,6 +802,8 @@ export interface ComponentAPI { export class ContentSteeringController implements NetworkComponentAPI { constructor(hls: Hls); // (undocumented) + clearTimeout(): void; + // (undocumented) destroy(): void; // (undocumented) filterParsedLevels(levels: Level[]): Level[]; @@ -971,10 +1021,14 @@ export interface ErrorData { // (undocumented) parent?: PlaylistLevelType; // (undocumented) + part?: Part | null; + // (undocumented) reason?: string; // (undocumented) response?: LoaderResponse; // (undocumented) + sourceBufferName?: SourceBufferName; + // (undocumented) stats?: LoaderStats; // (undocumented) type: ErrorTypes; @@ -1179,6 +1233,8 @@ export enum Events { // (undocumented) MANIFEST_PARSED = "hlsManifestParsed", // (undocumented) + MAX_AUTO_LEVEL_UPDATED = "hlsMaxAutoLevelUpdated", + // (undocumented) MEDIA_ATTACHED = "hlsMediaAttached", // (undocumented) MEDIA_ATTACHING = "hlsMediaAttaching", @@ -1189,6 +1245,8 @@ export enum Events { // (undocumented) NON_NATIVE_TEXT_TRACKS_FOUND = "hlsNonNativeTextTracksFound", // (undocumented) + STEERING_MANIFEST_LOADED = "hlsSteeringManifestLoaded", + // (undocumented) SUBTITLE_FRAG_PROCESSED = "hlsSubtitleFragProcessed", // (undocumented) SUBTITLE_TRACK_LOADED = "hlsSubtitleTrackLoaded", @@ -1484,19 +1542,17 @@ export interface FragParsingUserdataData { samples: UserdataSample[]; } +// Warning: (ae-forgotten-export) The symbol "HdcpLevels" needs to be exported by the entry point hls.d.ts // Warning: (ae-missing-release-tag) "HdcpLevel" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export type HdcpLevel = (typeof HdcpLevels)[number]; -// Warning: (ae-missing-release-tag) "HdcpLevels" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export const HdcpLevels: readonly ["NONE", "TYPE-0", "TYPE-1", null]; - // @public class Hls implements HlsEventEmitter { constructor(userConfig?: Partial); + get allAudioTracks(): Array; + get allSubtitleTracks(): Array; attachMedia(media: HTMLMediaElement): void; get audioTrack(): number; // Warning: (ae-setter-with-docs) The doc comment for the property "audioTrack" must appear on the getter, not the setter. @@ -1507,6 +1563,7 @@ class Hls implements HlsEventEmitter { set autoLevelCapping(newLevel: number); get autoLevelEnabled(): boolean; get bandwidthEstimate(): number; + set bandwidthEstimate(abrEwmaDefaultEstimate: number); get capLevelToPlayerSize(): boolean; // Warning: (ae-setter-with-docs) The doc comment for the property "capLevelToPlayerSize" must appear on the getter, not the setter. set capLevelToPlayerSize(shouldStartCapping: boolean); @@ -1530,10 +1587,14 @@ class Hls implements HlsEventEmitter { static get ErrorTypes(): typeof ErrorTypes; // (undocumented) static get Events(): typeof Events; + // (undocumented) + get firstAutoLevel(): number; get firstLevel(): number; // Warning: (ae-setter-with-docs) The doc comment for the property "firstLevel" must appear on the getter, not the setter. set firstLevel(newLevel: number); get forceStartLoad(): boolean; + static getMediaSource(): typeof MediaSource | undefined; + static isMSESupported(): boolean; static isSupported(): boolean; get latency(): number; // (undocumented) @@ -1576,12 +1637,16 @@ class Hls implements HlsEventEmitter { on(event: E, listener: HlsListeners[E], context?: Context): void; // (undocumented) once(event: E, listener: HlsListeners[E], context?: Context): void; + pauseBuffering(): void; get playingDate(): Date | null; recoverMediaError(): void; // (undocumented) removeAllListeners(event?: E | undefined): void; // (undocumented) - removeLevel(levelIndex: any, urlId?: number): void; + removeLevel(levelIndex: number): void; + resumeBuffering(): void; + setAudioOption(audioOption: MediaPlaylist | AudioSelectionOption | undefined): MediaPlaylist | null; + setSubtitleOption(subtitleOption: MediaPlaylist | SubtitleSelectionOption | undefined): MediaPlaylist | null; get startLevel(): number; // Warning: (ae-setter-with-docs) The doc comment for the property "startLevel" must appear on the getter, not the setter. set startLevel(newLevel: number); @@ -1624,6 +1689,7 @@ export type HlsConfig = { enableSoftwareAES: boolean; minAutoBitrate: number; ignoreDevicePixelRatio: boolean; + preferManagedMediaSource: boolean; loader: { new (confg: HlsConfig): Loader; }; @@ -1640,6 +1706,7 @@ export type HlsConfig = { cmcd?: CMCDControllerConfig; cmcdController?: typeof CMCDController; contentSteeringController?: typeof ContentSteeringController; + useMediaCapabilities: boolean; abrController: typeof AbrController; bufferController: typeof BufferController; capLevelController: typeof CapLevelController; @@ -1647,7 +1714,7 @@ export type HlsConfig = { fpsController: typeof FPSController; progressive: boolean; lowLatencyMode: boolean; -} & ABRControllerConfig & BufferControllerConfig & CapLevelControllerConfig & EMEControllerConfig & FPSControllerConfig & LevelControllerConfig & MP4RemuxerConfig & StreamControllerConfig & LatencyControllerConfig & MetadataControllerConfig & TimelineControllerConfig & TSDemuxerConfig & HlsLoadPolicies & FragmentLoaderConfig & PlaylistLoaderConfig; +} & ABRControllerConfig & BufferControllerConfig & CapLevelControllerConfig & EMEControllerConfig & FPSControllerConfig & LevelControllerConfig & MP4RemuxerConfig & StreamControllerConfig & SelectionPreferences & LatencyControllerConfig & MetadataControllerConfig & TimelineControllerConfig & TSDemuxerConfig & HlsLoadPolicies & FragmentLoaderConfig & PlaylistLoaderConfig; // Warning: (ae-missing-release-tag) "HlsEventEmitter" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1759,6 +1826,10 @@ export interface HlsListeners { [Events.MANIFEST_LOADING]: (event: Events.MANIFEST_LOADING, data: ManifestLoadingData) => void; // (undocumented) [Events.MANIFEST_PARSED]: (event: Events.MANIFEST_PARSED, data: ManifestParsedData) => void; + // Warning: (ae-forgotten-export) The symbol "MaxAutoLevelUpdatedData" needs to be exported by the entry point hls.d.ts + // + // (undocumented) + [Events.MAX_AUTO_LEVEL_UPDATED]: (event: Events.MAX_AUTO_LEVEL_UPDATED, data: MaxAutoLevelUpdatedData) => void; // (undocumented) [Events.MEDIA_ATTACHED]: (event: Events.MEDIA_ATTACHED, data: MediaAttachedData) => void; // (undocumented) @@ -1770,6 +1841,8 @@ export interface HlsListeners { // (undocumented) [Events.NON_NATIVE_TEXT_TRACKS_FOUND]: (event: Events.NON_NATIVE_TEXT_TRACKS_FOUND, data: NonNativeTextTracksData) => void; // (undocumented) + [Events.STEERING_MANIFEST_LOADED]: (event: Events.STEERING_MANIFEST_LOADED, data: SteeringManifestLoadedData) => void; + // (undocumented) [Events.SUBTITLE_FRAG_PROCESSED]: (event: Events.SUBTITLE_FRAG_PROCESSED, data: SubtitleFragProcessedData) => void; // (undocumented) [Events.SUBTITLE_TRACK_LOADED]: (event: Events.SUBTITLE_TRACK_LOADED, data: SubtitleTrackLoadedData) => void; @@ -1950,9 +2023,11 @@ export type LatencyControllerConfig = { // // @public (undocumented) export class Level { - constructor(data: LevelParsed); + constructor(data: LevelParsed | MediaPlaylist); + // (undocumented) + addFallback(): void; // (undocumented) - addFallback(data: LevelParsed): void; + addGroupId(type: string, groupId: string | undefined): void; // (undocumented) get attrs(): LevelAttributes; // (undocumented) @@ -1962,16 +2037,28 @@ export class Level { // (undocumented) get audioGroupId(): string | undefined; // (undocumented) - audioGroupIds?: (string | undefined)[]; + get audioGroupIds(): (string | undefined)[] | undefined; + // (undocumented) + get audioGroups(): (string | undefined)[] | undefined; + // (undocumented) + get averageBitrate(): number; // (undocumented) readonly bitrate: number; // (undocumented) + get codecs(): string; + // (undocumented) readonly codecSet: string; // (undocumented) details?: LevelDetails; // (undocumented) fragmentError: number; // (undocumented) + readonly frameRate: number; + // (undocumented) + hasAudioGroup(groupId: string | undefined): boolean; + // (undocumented) + hasSubtitleGroup(groupId: string | undefined): boolean; + // (undocumented) readonly height: number; // (undocumented) readonly id: number; @@ -1985,26 +2072,36 @@ export class Level { // (undocumented) get maxBitrate(): number; // (undocumented) - readonly name: string | undefined; + readonly name: string; // (undocumented) get pathwayId(): string; // (undocumented) realBitrate: number; // (undocumented) - get textGroupId(): string | undefined; + get score(): number; + // (undocumented) + get subtitleGroups(): (string | undefined)[] | undefined; + // (undocumented) + supportedPromise?: Promise; + // (undocumented) + supportedResult?: MediaDecodingInfo; // (undocumented) - textGroupIds?: (string | undefined)[]; + get textGroupId(): string | undefined; // (undocumented) - readonly unknownCodecs: string[] | undefined; + get textGroupIds(): (string | undefined)[] | undefined; // (undocumented) get uri(): string; // (undocumented) - url: string[]; + readonly url: string[]; // (undocumented) get urlId(): number; set urlId(value: number); // (undocumented) readonly videoCodec: string | undefined; + // Warning: (ae-forgotten-export) The symbol "VideoRange" needs to be exported by the entry point hls.d.ts + // + // (undocumented) + get videoRange(): VideoRange; // (undocumented) readonly width: number; } @@ -2030,7 +2127,7 @@ export interface LevelAttributes extends AttrList { // (undocumented) 'SUPPLEMENTAL-CODECS'?: string; // (undocumented) - 'VIDEO-RANGE'?: 'SDR' | 'HLG' | 'PQ'; + 'VIDEO-RANGE'?: VideoRange; // (undocumented) AUDIO?: string; // (undocumented) @@ -2058,7 +2155,7 @@ export type LevelControllerConfig = { // // @public export class LevelDetails { - constructor(baseUrl: any); + constructor(baseUrl: string); // (undocumented) advanced: boolean; // (undocumented) @@ -2234,6 +2331,8 @@ export interface LevelLoadingData { // (undocumented) level: number; // (undocumented) + pathwayId: string | undefined; + // (undocumented) url: string; } @@ -2254,8 +2353,6 @@ export interface LevelParsed { // (undocumented) id?: number; // (undocumented) - level?: number; - // (undocumented) name: string; // (undocumented) textCodec?: string; @@ -2308,9 +2405,58 @@ export interface LevelSwitchedData { // Warning: (ae-missing-release-tag) "LevelSwitchingData" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface LevelSwitchingData extends Omit { +export interface LevelSwitchingData { + // (undocumented) + attrs: LevelAttributes; + // (undocumented) + audioCodec: string | undefined; + // (undocumented) + audioGroupIds: (string | undefined)[] | undefined; + // (undocumented) + audioGroups: (string | undefined)[] | undefined; + // (undocumented) + averageBitrate: number; + // (undocumented) + bitrate: number; + // (undocumented) + codecSet: string; + // (undocumented) + details: LevelDetails | undefined; + // (undocumented) + fragmentError: number; + // (undocumented) + height: number; + // (undocumented) + id: number; // (undocumented) level: number; + // (undocumented) + loaded: { + bytes: number; + duration: number; + } | undefined; + // (undocumented) + loadError: number; + // (undocumented) + maxBitrate: number; + // (undocumented) + name: string | undefined; + // (undocumented) + realBitrate: number; + // (undocumented) + subtitleGroups: (string | undefined)[] | undefined; + // (undocumented) + textGroupIds: (string | undefined)[] | undefined; + // (undocumented) + uri: string; + // (undocumented) + url: string[]; + // (undocumented) + urlId: 0; + // (undocumented) + videoCodec: string | undefined; + // (undocumented) + width: number; } // Warning: (ae-missing-release-tag) "LevelUpdatedData" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -2336,14 +2482,14 @@ export interface Loader { // (undocumented) abort(): void; // (undocumented) - context: T; + context: T | null; // (undocumented) destroy(): void; getCacheAge?: () => number | null; // (undocumented) getResponseHeader?: (name: string) => string | null; // (undocumented) - load(context: LoaderContext, config: LoaderConfiguration, callbacks: LoaderCallbacks): void; + load(context: T, config: LoaderConfiguration, callbacks: LoaderCallbacks): void; // (undocumented) stats: LoaderStats; } @@ -2582,6 +2728,8 @@ export interface ManifestParsedData { export interface MediaAttachedData { // (undocumented) media: HTMLMediaElement; + // (undocumented) + mediaSource?: MediaSource; } // Warning: (ae-missing-release-tag) "MediaAttachingData" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -2626,6 +2774,16 @@ export interface MediaAttributes extends AttrList { URI?: string; } +// Warning: (ae-missing-release-tag) "MediaDecodingInfo" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type MediaDecodingInfo = { + supported: boolean; + configurations: readonly MediaDecodingConfiguration[]; + decodingInfoResults: readonly MediaCapabilitiesDecodingInfo[]; + error?: Error; +}; + // Warning: (ae-missing-release-tag) "MediaKeyFunc" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2647,23 +2805,41 @@ export interface MediaKeySessionContext { mediaKeys: MediaKeys; // (undocumented) mediaKeysSession: MediaKeySession; + // (undocumented) + _onkeystatuseschange?: (this: MediaKeySession, ev: Event) => any; + // (undocumented) + _onmessage?: (this: MediaKeySession, ev: MediaKeyMessageEvent) => any; } // Warning: (ae-missing-release-tag) "MediaPlaylist" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface MediaPlaylist extends Omit { +export interface MediaPlaylist { + // (undocumented) + assocLang?: string; // (undocumented) attrs: MediaAttributes; // (undocumented) + audioCodec?: string; + // (undocumented) autoselect: boolean; // (undocumented) + bitrate: number; + // (undocumented) + channels?: string; + // (undocumented) + characteristics?: string; + // (undocumented) default: boolean; // (undocumented) + details?: LevelDetails; + // (undocumented) forced: boolean; // (undocumented) groupId: string; // (undocumented) + height?: number; + // (undocumented) id: number; // (undocumented) instreamId?: string; @@ -2672,7 +2848,17 @@ export interface MediaPlaylist extends Omit { // (undocumented) name: string; // (undocumented) + textCodec?: string; + // (undocumented) type: MediaPlaylistType | 'main'; + // (undocumented) + unknownCodecs?: string[]; + // (undocumented) + url: string; + // (undocumented) + videoCodec?: string; + // (undocumented) + width?: number; } // Warning: (ae-missing-release-tag) "MediaPlaylistType" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -2744,13 +2930,13 @@ export const enum NetworkErrorAction { // (undocumented) DoNothing = 0, // (undocumented) - InsertDiscontinuity = 4, + InsertDiscontinuity = 4,// Reserved for future use // (undocumented) RemoveAlternatePermanently = 3, // (undocumented) - RetryRequest = 5, + RetryRequest = 5,// Reserved for future use // (undocumented) - SendAlternateToPenaltyBox = 2, + SendAlternateToPenaltyBox = 2,// Reserved for future use // (undocumented) SendEndCallback = 1 } @@ -2810,6 +2996,15 @@ export class Part extends BaseSegment { stats: LoadStats; } +// Warning: (ae-missing-release-tag) "PathwayClone" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PathwayClone = { + 'BASE-ID': string; + ID: string; + 'URI-REPLACEMENT': UriReplacement; +}; + // Warning: (ae-missing-release-tag) "PlaylistContextType" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2873,6 +3068,8 @@ export interface PlaylistLoaderContext extends LoaderContext { // (undocumented) levelDetails?: LevelDetails; // (undocumented) + pathwayId?: string; + // (undocumented) type: PlaylistContextType; } @@ -2884,6 +3081,16 @@ export type RetryConfig = { retryDelayMs: number; maxRetryDelayMs: number; backoff?: 'exponential' | 'linear'; + shouldRetry?: (retryConfig: RetryConfig | null | undefined, retryCount: number, isTimeout: boolean, loaderResponse: LoaderResponse | undefined, retry: boolean) => boolean; +}; + +// Warning: (ae-missing-release-tag) "SelectionPreferences" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type SelectionPreferences = { + videoPreference?: VideoSelectionOption; + audioPreference?: AudioSelectionOption; + subtitlePreference?: SubtitleSelectionOption; }; // Warning: (ae-missing-release-tag) "SourceBufferName" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -2891,6 +3098,27 @@ export type RetryConfig = { // @public (undocumented) export type SourceBufferName = 'video' | 'audio' | 'audiovideo'; +// Warning: (ae-missing-release-tag) "SteeringManifest" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type SteeringManifest = { + VERSION: 1; + TTL: number; + 'RELOAD-URI'?: string; + 'PATHWAY-PRIORITY': string[]; + 'PATHWAY-CLONES'?: PathwayClone[]; +}; + +// Warning: (ae-missing-release-tag) "SteeringManifestLoadedData" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface SteeringManifestLoadedData { + // (undocumented) + steeringManifest: SteeringManifest; + // (undocumented) + url: string; +} + // Warning: (ae-missing-release-tag) "StreamControllerConfig" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2928,6 +3156,19 @@ export interface SubtitleFragProcessedData { // @public (undocumented) export type SubtitlePlaylistType = 'SUBTITLES' | 'CLOSED-CAPTIONS'; +// Warning: (ae-missing-release-tag) "SubtitleSelectionOption" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type SubtitleSelectionOption = { + lang?: string; + assocLang?: string; + characteristics?: string; + name?: string; + groupId?: string; + default?: boolean; + forced?: boolean; +}; + // Warning: (ae-missing-release-tag) "SubtitleStreamController" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2940,8 +3181,6 @@ export class SubtitleStreamController extends BaseStreamController implements Ne // (undocumented) _handleFragmentLoadComplete(fragLoadedData: FragLoadedData): void; // (undocumented) - protected levels: Array; - // (undocumented) protected loadFragment(frag: Fragment, level: Level, targetBufferTime: number): void; // (undocumented) get mediaBufferTimeRanges(): Bufferable; @@ -2981,6 +3220,8 @@ export class SubtitleStreamController extends BaseStreamController implements Ne export class SubtitleTrackController extends BasePlaylistController { constructor(hls: Hls); // (undocumented) + get allSubtitleTracks(): MediaPlaylist[]; + // (undocumented) destroy(): void; // (undocumented) protected loadPlaylist(hlsUrlParameters?: HlsUrlParameters): void; @@ -3001,6 +3242,8 @@ export class SubtitleTrackController extends BasePlaylistController { // (undocumented) protected onSubtitleTrackLoaded(event: Events.SUBTITLE_TRACK_LOADED, data: TrackLoadedData): void; // (undocumented) + setSubtitleOption(subtitleOption: MediaPlaylist | SubtitleSelectionOption | undefined): MediaPlaylist | null; + // (undocumented) get subtitleDisplay(): boolean; set subtitleDisplay(value: boolean); get subtitleTrack(): number; @@ -3143,6 +3386,22 @@ export type TSDemuxerConfig = { forceKeyFrameOnDiscontinuity: boolean; }; +// Warning: (ae-missing-release-tag) "UriReplacement" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type UriReplacement = { + HOST?: string; + PARAMS?: { + [queryParameter: string]: string; + }; + 'PER-VARIANT-URIS'?: { + [stableVariantId: string]: string; + }; + 'PER-RENDITION-URIS'?: { + [stableRenditionId: string]: string; + }; +}; + // Warning: (ae-missing-release-tag) "UserdataSample" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -3168,6 +3427,14 @@ export interface UserdataSample { // @public (undocumented) export type VariableMap = Record; +// Warning: (ae-missing-release-tag) "VideoSelectionOption" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type VideoSelectionOption = { + preferHDR?: boolean; + allowedVideoRanges?: Array; +}; + // (No @packageDocumentation comment for this package) ``` diff --git a/build-config.js b/build-config.js index b1b80baea86..d0a3a233c2c 100644 --- a/build-config.js +++ b/build-config.js @@ -10,7 +10,7 @@ const istanbul = require('rollup-plugin-istanbul'); const fs = require('fs'); const pkgJson = JSON.parse( - fs.readFileSync('./package.json', { encoding: 'utf-8' }) + fs.readFileSync('./package.json', { encoding: 'utf-8' }), ); const BUILD_TYPE = { @@ -29,7 +29,7 @@ const buildTypeToOutputName = { light: `hls.light`, }; -/* Allow to customise builds through env-vars */ +/* Allow to customize builds through env-vars */ // eslint-disable-next-line no-undef const env = process.env; @@ -41,6 +41,10 @@ const addContentSteeringSupport = !!env.CONTENT_STEERING || !!env.USE_CONTENT_STEERING; const addVariableSubstitutionSupport = !!env.VARIABLE_SUBSTITUTION || !!env.USE_VARIABLE_SUBSTITUTION; +const addM2TSAdvancedCodecSupport = + !!env.M2TS_ADVANCED_CODECS || !!env.USE_M2TS_ADVANCED_CODECS; +const addMediaCapabilitiesSupport = + !!env.MEDIA_CAPABILITIES || !!env.USE_MEDIA_CAPABILITIES; const shouldBundleWorker = (format) => format !== FORMAT.esm; @@ -49,23 +53,43 @@ const buildConstants = (type, additional = {}) => ({ values: { __VERSION__: JSON.stringify(pkgJson.version), __USE_SUBTITLES__: JSON.stringify( - type === BUILD_TYPE.full || addSubtitleSupport + type === BUILD_TYPE.full || addSubtitleSupport, ), __USE_ALT_AUDIO__: JSON.stringify( - type === BUILD_TYPE.full || addAltAudioSupport + type === BUILD_TYPE.full || addAltAudioSupport, ), __USE_EME_DRM__: JSON.stringify(type === BUILD_TYPE.full || addEMESupport), __USE_CMCD__: JSON.stringify(type === BUILD_TYPE.full || addCMCDSupport), __USE_CONTENT_STEERING__: JSON.stringify( - type === BUILD_TYPE.full || addContentSteeringSupport + type === BUILD_TYPE.full || BUILD_TYPE.light || addContentSteeringSupport, ), __USE_VARIABLE_SUBSTITUTION__: JSON.stringify( - type === BUILD_TYPE.full || addVariableSubstitutionSupport + type === BUILD_TYPE.full || addVariableSubstitutionSupport, ), + __USE_M2TS_ADVANCED_CODECS__: JSON.stringify( + type === BUILD_TYPE.full || addM2TSAdvancedCodecSupport, + ), + __USE_MEDIA_CAPABILITIES__: JSON.stringify( + type === BUILD_TYPE.full || addMediaCapabilitiesSupport, + ), + ...additional, }, }); +const buildOnLog = ({ allowCircularDeps } = {}) => { + return (level, log, handler) => { + if (allowCircularDeps && log.code === 'CIRCULAR_DEPENDENCY') return; + + if (level === 'warn') { + // treat warnings as errors + handler('error', log); + } else { + handler(level, log); + } + }; +}; + const workerFnBanner = '(function __HLS_WORKER_BUNDLE__(__IN_WORKER__){'; const workerFnFooter = '})(false);'; @@ -85,7 +109,7 @@ const babelTsWithPresetEnvTargets = ({ targets, stripConsole }) => babel({ extensions, babelHelpers: 'bundled', - exclude: 'node_modules/**', + exclude: /node_modules\/(?!(@svta)\/).*/, assumptions: { noDocumentAll: true, noClassCalls: true, @@ -121,7 +145,15 @@ const babelTsWithPresetEnvTargets = ({ targets, stripConsole }) => espath.node.callee = importHelper.addNamed( espath, 'isFiniteNumber', - path.resolve('src/polyfills/number') + path.resolve('src/polyfills/number'), + ); + } else if ( + espath.get('callee').matchesPattern('Number.isSafeInteger') + ) { + espath.node.callee = importHelper.addNamed( + espath, + 'isSafeInteger', + path.resolve('src/polyfills/number'), ); } else if ( espath.get('callee').matchesPattern('Number.MAX_SAFE_INTEGER') @@ -129,7 +161,7 @@ const babelTsWithPresetEnvTargets = ({ targets, stripConsole }) => espath.node.callee = importHelper.addNamed( espath, 'MAX_SAFE_INTEGER', - path.resolve('src/polyfills/number') + path.resolve('src/polyfills/number'), ); } }, @@ -212,6 +244,20 @@ function getAliasesForLightDist() { }; } + if (!addM2TSAdvancedCodecSupport) { + aliases = { + ...aliases, + './ac3-demuxer': '../empty.js', + }; + } + + if (!addMediaCapabilitiesSupport) { + aliases = { + ...aliases, + '../utils/mediacapabilities-helper': '../empty.js', + }; + } + return aliases; } @@ -223,25 +269,21 @@ const buildRollupConfig = ({ includeCoverage, sourcemap = true, outputFile = null, + input = './src/exports-default.ts', }) => { const outputName = buildTypeToOutputName[type]; const extension = format === FORMAT.esm ? 'mjs' : 'js'; return { - input: './src/hls.ts', - onwarn: (e) => { - if (allowCircularDeps && e.code === 'CIRCULAR_DEPENDENCY') return; - - // treat warnings as errors - throw new Error(e); - }, + input, + onLog: buildOnLog({ allowCircularDeps }), output: { name: 'Hls', file: outputFile ? outputFile : minified - ? `./dist/${outputName}.min.${extension}` - : `./dist/${outputName}.${extension}`, + ? `./dist/${outputName}.min.${extension}` + : `./dist/${outputName}.${extension}`, format, banner: shouldBundleWorker(format) ? workerFnBanner : null, footer: shouldBundleWorker(format) ? workerFnFooter : null, @@ -282,6 +324,7 @@ const configs = Object.entries({ minified: true, }), fullEsm: buildRollupConfig({ + input: './src/exports-named.ts', type: BUILD_TYPE.full, format: FORMAT.esm, minified: false, @@ -297,16 +340,14 @@ const configs = Object.entries({ minified: true, }), lightEsm: buildRollupConfig({ + input: './src/exports-named.ts', type: BUILD_TYPE.light, format: FORMAT.esm, minified: false, }), worker: { input: './src/demux/transmuxer-worker.ts', - onwarn: (e) => { - // treat warnings as errors - throw new Error(e); - }, + onLog: buildOnLog(), output: { name: 'HlsWorker', file: './dist/hls.worker.js', @@ -319,7 +360,7 @@ const configs = Object.entries({ replace( buildConstants(BUILD_TYPE.full, { __IN_WORKER__: JSON.stringify(true), - }) + }), ), buildBabelLegacyBrowsers({ stripConsole: true }), terser(), @@ -327,10 +368,7 @@ const configs = Object.entries({ }, demo: { input: './demo/main.js', - onwarn: (e) => { - // treat warnings as errors - throw new Error(e); - }, + onLog: buildOnLog(), output: { name: 'HlsDemo', file: './dist/hls-demo.js', @@ -352,7 +390,7 @@ const configs = Object.entries({ branch: env.CF_PAGES_BRANCH, commitRef: env.CF_PAGES_COMMIT_SHA, } - : null + : null, ), }, }), diff --git a/demo/.prettierrc b/demo/.prettierrc new file mode 100644 index 00000000000..c1a6f667131 --- /dev/null +++ b/demo/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/demo/benchmark.html b/demo/benchmark.html index 28952bcdb10..4add8e2d507 100644 --- a/demo/benchmark.html +++ b/demo/benchmark.html @@ -1,4 +1,4 @@ - + diff --git a/demo/chart/chartjs-horizontal-bar.ts b/demo/chart/chartjs-horizontal-bar.ts index 1b0f835e6e1..3c36125505b 100644 --- a/demo/chart/chartjs-horizontal-bar.ts +++ b/demo/chart/chartjs-horizontal-bar.ts @@ -19,14 +19,14 @@ Chart.controllers.horizontalBar.prototype.calculateBarValuePixels = function ( value.start === undefined ? 0 : value.max >= 0 && value.min >= 0 - ? value.min - : value.max; + ? value.min + : value.max; const length = value.start === undefined ? value.end : value.max >= 0 && value.min >= 0 - ? value.max - value.min - : value.min - value.max; + ? value.max - value.min + : value.min - value.max; const base = scale.getPixelForValue(start); const head = scale.getPixelForValue(start + length); const size = head - base; diff --git a/demo/index-light.html b/demo/index-light.html index 2a6b0c15568..478e8d86131 100644 --- a/demo/index-light.html +++ b/demo/index-light.html @@ -1,4 +1,4 @@ - + @@ -149,6 +149,7 @@

id="video" controls autoplay + playsinline class="videoCentered" style="width: 80%" > diff --git a/demo/index.html b/demo/index.html index 9e86fdefb58..4d1fe6a2308 100644 --- a/demo/index.html +++ b/demo/index.html @@ -1,4 +1,4 @@ - + @@ -148,6 +148,7 @@

id="video" controls autoplay + playsinline class="videoCentered" style="width: 80%" > diff --git a/demo/main.js b/demo/main.js index 286be542149..f3d7b2235ff 100644 --- a/demo/main.js +++ b/demo/main.js @@ -1465,8 +1465,8 @@ function getURLParam(sParam, defaultValue) { return sParameterName[1] === 'undefined' ? undefined : sParameterName[1] === 'false' - ? false - : sParameterName[1]; + ? false + : sParameterName[1]; } } return defaultValue; diff --git a/demo/metrics.html b/demo/metrics.html index 0e2e8c6d271..d6f0e3cac62 100644 --- a/demo/metrics.html +++ b/demo/metrics.html @@ -1,4 +1,4 @@ - + hls.js metrics page diff --git a/docs/API.md b/docs/API.md index 9b751b0ea09..42c1217fc0f 100644 --- a/docs/API.md +++ b/docs/API.md @@ -28,6 +28,7 @@ See [API Reference](https://hlsjs-dev.video-dev.org/api-docs/) for a complete li - [`initialLiveManifestSize`](#initiallivemanifestsize) - [`maxBufferLength`](#maxbufferlength) - [`backBufferLength`](#backbufferlength) + - [`frontBufferFlushThreshold`](#frontbufferflushthreshold) - [`maxBufferSize`](#maxbuffersize) - [`maxBufferHole`](#maxbufferhole) - [`maxStarvationDelay`](#maxstarvationdelay) @@ -45,6 +46,7 @@ See [API Reference](https://hlsjs-dev.video-dev.org/api-docs/) for a complete li - [`maxLiveSyncPlaybackRate`](#maxlivesyncplaybackrate) - [`liveDurationInfinity`](#livedurationinfinity) - [`liveBackBufferLength` (deprecated)](#livebackbufferlength-deprecated) + - [`preferManagedMediaSource`](#prefermanagedmediasource) - [`enableWorker`](#enableworker) - [`workerPath`](#workerpath) - [`enableSoftwareAES`](#enablesoftwareaes) @@ -64,6 +66,7 @@ See [API Reference](https://hlsjs-dev.video-dev.org/api-docs/) for a complete li - [`retryDelayMs: number`](#retrydelayms-number) - [`maxRetryDelayMs: number`](#maxretrydelayms-number) - [`backoff?: 'exponential' | 'linear'`](#backoff-exponential--linear) + - [`shouldRetry`](#shouldretry) - [`startFragPrefetch`](#startfragprefetch) - [`testBandwidth`](#testbandwidth) - [`progressive`](#progressive) @@ -76,6 +79,9 @@ See [API Reference](https://hlsjs-dev.video-dev.org/api-docs/) for a complete li - [`pLoader`](#ploader) - [`xhrSetup`](#xhrsetup) - [`fetchSetup`](#fetchsetup) + - [`videoPreference`](#videopreference) + - [`audioPreference`](#audiopreference) + - [`subtitlePreference`](#subtitlepreference) - [`abrController`](#abrcontroller) - [`bufferController`](#buffercontroller) - [`capLevelController`](#caplevelcontroller) @@ -104,6 +110,7 @@ See [API Reference](https://hlsjs-dev.video-dev.org/api-docs/) for a complete li - [`abrEwmaFastVoD`](#abrewmafastvod) - [`abrEwmaSlowVoD`](#abrewmaslowvod) - [`abrEwmaDefaultEstimate`](#abrewmadefaultestimate) + - [`abrEwmaDefaultEstimateMax`](#abrewmadefaultestimatemax) - [`abrBandWidthFactor`](#abrbandwidthfactor) - [`abrBandWidthUpFactor`](#abrbandwidthupfactor) - [`abrMaxWithRealBitrate`](#abrmaxwithrealbitrate) @@ -128,6 +135,7 @@ See [API Reference](https://hlsjs-dev.video-dev.org/api-docs/) for a complete li - [`hls.loadLevel`](#hlsloadlevel) - [`hls.nextLoadLevel`](#hlsnextloadlevel) - [`hls.firstLevel`](#hlsfirstlevel) + - [`hls.firstAutoLevel`](#hlsfirstautolevel) - [`hls.startLevel`](#hlsstartlevel) - [`hls.autoLevelEnabled`](#hlsautolevelenabled) - [`hls.autoLevelCapping`](#hlsautolevelcapping) @@ -141,9 +149,13 @@ See [API Reference](https://hlsjs-dev.video-dev.org/api-docs/) for a complete li - [`hls.startLoad(startPosition=-1)`](#hlsstartloadstartposition-1) - [`hls.stopLoad()`](#hlsstopload) - [Audio Tracks Control API](#audio-tracks-control-api) + - [`hls.setAudioOption(audioOption)`](#hlssetaudiooptionaudiooption) + - [`hls.allAudioTracks`](#hlsallaudiotracks) - [`hls.audioTracks`](#hlsaudiotracks) - [`hls.audioTrack`](#hlsaudiotrack) - [Subtitle Tracks Control API](#subtitle-tracks-control-api) + - [`hls.setSubtitleOption(subtitleOption)`](#hlssetsubtitleoptionsubtitleoption) + - [`hls.allSubtitleTracks`](#hlsallsubtitletracks) - [`hls.subtitleTracks`](#hlssubtitletracks) - [`hls.subtitleTrack`](#hlssubtitletrack) - [`hls.subtitleDisplay`](#hlssubtitledisplay) @@ -179,17 +191,28 @@ First include `https://cdn.jsdelivr.net/npm/hls.js@1` (or `/hls.js` for unminifi ``` -Invoke the following static method: `Hls.isSupported()` to check whether your browser is supporting [MediaSource Extensions](http://w3c.github.io/media-source/). +Invoke the following static method: `Hls.isSupported()` to check whether your browser supports [MediaSource Extensions](http://w3c.github.io/media-source/) with any baseline codecs. ```html ``` +If you want to test for MSE support without testing for baseline codecs, use `isMSESupported`: + +```js +if ( + Hls.isMSESupported() && + Hls.getMediaSource().isTypeSupported('video/mp4;codecs="av01.0.01M.08"') +) { + console.log('Hello AV1 playback! AVC who?'); +} +``` + ### Second step: instantiate Hls object and bind it to `