diff --git a/api-extractor/report/hls.js.api.md b/api-extractor/report/hls.js.api.md index 58337c0e4fc..4dda0ba6d6e 100644 --- a/api-extractor/report/hls.js.api.md +++ b/api-extractor/report/hls.js.api.md @@ -1230,6 +1230,14 @@ export class ErrorController extends Logger implements NetworkComponentAPI { stopLoad(): void; } +// Warning: (ae-missing-release-tag) "ErrorControllerConfig" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ErrorControllerConfig = { + onErrorHandler?: (data: ErrorData) => boolean; + onErrorOutHandler?: (data: ErrorData) => boolean; +}; + // Warning: (ae-missing-release-tag) "ErrorData" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2297,7 +2305,7 @@ export type HlsConfig = { progressive: boolean; lowLatencyMode: boolean; primarySessionId?: string; -} & ABRControllerConfig & BufferControllerConfig & CapLevelControllerConfig & EMEControllerConfig & FPSControllerConfig & GapControllerConfig & LevelControllerConfig & MP4RemuxerConfig & StreamControllerConfig & SelectionPreferences & LatencyControllerConfig & MetadataControllerConfig & TimelineControllerConfig & TSDemuxerConfig & HlsLoadPolicies & FragmentLoaderConfig & PlaylistLoaderConfig; +} & ABRControllerConfig & BufferControllerConfig & CapLevelControllerConfig & EMEControllerConfig & ErrorControllerConfig & FPSControllerConfig & GapControllerConfig & 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) // diff --git a/docs/API.md b/docs/API.md index 0dc1e7779b0..f7f0eb60cdb 100644 --- a/docs/API.md +++ b/docs/API.md @@ -96,6 +96,8 @@ See [API Reference](https://hlsjs-dev.video-dev.org/api-docs/) for a complete li - [`capLevelController`](#caplevelcontroller) - [`fpsController`](#fpscontroller) - [`errorController`](#errorcontroller) + - [`onErrorHandler`](#onerrorhandler) + - [`onErrorOutHandler`](#onerrorouthandler) - [`timelineController`](#timelinecontroller) - [`enableDateRangeMetadataCues`](#enabledaterangemetadatacues) - [`enableEmsgMetadataCues`](#enableemsgmetadatacues) @@ -1393,6 +1395,70 @@ Customized error controller. A class in charge of handling errors and error recovery logic. The error controller processes error events and implements recovery strategies such as level switching and fragment retry logic. +### `onErrorHandler` + +(default: `undefined`, type: `(data: ErrorData) => boolean`) + +Early error handler callback invoked **before** HLS.js attempts any error recovery. + +This callback is called immediately when an error occurs, giving you the earliest opportunity to inspect and handle errors. When you return `true`, HLS.js will **not attempt any error recovery** and you are responsible for handling the error manually. Return `false` or `undefined` to allow HLS.js to proceed with its normal error recovery logic. + +**Important:** Returning `true` prevents all error recovery attempts including fragment retries, level switching, and other recovery strategies. + +```js +var config = { + onErrorHandler: function (data) { + console.log( + 'Error handler called before recovery:', + data.type, + data.details, + ); + + // Example: Handle specific errors manually + if (data.details === Hls.ErrorDetails.FRAG_LOAD_ERROR) { + // Custom handling logic here + console.log('Manually handling fragment load error'); + return true; // Prevent HLS.js from attempting recovery + } + + // Let HLS.js handle other errors + return false; + }, +}; +``` + +### `onErrorOutHandler` + +(default: `undefined`, type: `(data: ErrorData) => boolean`) + +Error handler callback invoked **after** HLS.js has completed its error recovery attempts. + +This callback is called after HLS.js has exhausted its recovery strategies (such as retries and level switching). When you return `true`, HLS.js will **not execute its default error handling** and you are responsible for handling the error manually. Return `false` or `undefined` to allow HLS.js to proceed with its default error handling (which may include destroying the player or switching streams). + +**Important:** Returning `true` prevents default error handling after recovery has been attempted. This is useful for implementing custom fallback logic when HLS.js cannot recover from an error. + +```js +var config = { + onErrorOutHandler: function (data) { + console.log( + 'Error handler called after recovery:', + data.type, + data.details, + ); + + // Example: Custom fallback for unrecoverable errors + if (data.fatal && data.type === Hls.ErrorTypes.NETWORK_ERROR) { + // Custom handling logic here + console.log('Manually handling fragment load error'); + return true; // Prevent HLS.js default handling + } + + // Let HLS.js handle other errors with its default behavior + return false; + }, +}; +``` + ### `timelineController` (default: internal track timeline controller) diff --git a/src/config.ts b/src/config.ts index 15627fd4a7d..12e3ed87ead 100644 --- a/src/config.ts +++ b/src/config.ts @@ -19,6 +19,7 @@ import { stringify } from './utils/safe-json-stringify'; import XhrLoader from './utils/xhr-loader'; import type { MediaKeySessionContext } from './controller/eme-controller'; import type Hls from './hls'; +import type { ErrorData } from './hls'; import type { FragmentLoaderContext, Loader, @@ -125,6 +126,11 @@ export type EMEControllerConfig = { requireKeySystemAccessOnStart: boolean; }; +export type ErrorControllerConfig = { + onErrorHandler?: (data: ErrorData) => boolean; + onErrorOutHandler?: (data: ErrorData) => boolean; +}; + export interface FragmentLoaderConstructor { new (confg: HlsConfig): Loader; } @@ -335,6 +341,7 @@ export type HlsConfig = { BufferControllerConfig & CapLevelControllerConfig & EMEControllerConfig & + ErrorControllerConfig & FPSControllerConfig & GapControllerConfig & LevelControllerConfig & diff --git a/src/controller/error-controller.ts b/src/controller/error-controller.ts index 2650db155eb..42e7167ed2c 100644 --- a/src/controller/error-controller.ts +++ b/src/controller/error-controller.ts @@ -134,10 +134,14 @@ export default class ErrorController } private onError(event: Events.ERROR, data: ErrorData) { - if (data.fatal) { + const hls = this.hls; + // If the error is handled by onErrorHandler or is fatal, do not proceed with error recovery + if (hls.config.onErrorHandler?.(data) || data.fatal) { + if (data.errorAction) { + data.errorAction.resolved = true; + } return; } - const hls = this.hls; const context = data.context; switch (data.details) { @@ -465,6 +469,14 @@ export default class ErrorController } public onErrorOut(event: Events.ERROR, data: ErrorData) { + const hls = this.hls; + // If the error is handled by onErrorOutHandler, do not proceed with the default error handling + if (hls.config.onErrorOutHandler?.(data)) { + if (data.errorAction) { + data.errorAction.resolved = true; + } + return; + } switch (data.errorAction?.action) { case NetworkErrorAction.DoNothing: break; diff --git a/src/hls.ts b/src/hls.ts index cf61a67b01a..28016e6ca8e 100644 --- a/src/hls.ts +++ b/src/hls.ts @@ -1299,6 +1299,7 @@ export type { CapLevelControllerConfig, CMCDControllerConfig, EMEControllerConfig, + ErrorControllerConfig, DRMSystemConfiguration, DRMSystemsConfiguration, DRMSystemOptions,