FenXiNspUniapp/packDetail1/component/hls.js/src/config.ts

654 lines
20 KiB
TypeScript

import AbrController from './controller/abr-controller';
import AudioStreamController from './controller/audio-stream-controller';
import AudioTrackController from './controller/audio-track-controller';
import { SubtitleStreamController } from './controller/subtitle-stream-controller';
import SubtitleTrackController from './controller/subtitle-track-controller';
import BufferController from './controller/buffer-controller';
import { TimelineController } from './controller/timeline-controller';
import CapLevelController from './controller/cap-level-controller';
import FPSController from './controller/fps-controller';
import EMEController, {
MediaKeySessionContext,
} from './controller/eme-controller';
import CMCDController from './controller/cmcd-controller';
import ContentSteeringController from './controller/content-steering-controller';
import ErrorController from './controller/error-controller';
import XhrLoader from './utils/xhr-loader';
import FetchLoader, { fetchSupported } from './utils/fetch-loader';
import Cues from './utils/cues';
import { requestMediaKeySystemAccess } from './utils/mediakeys-helper';
import { ILogger, logger } from './utils/logger';
import type Hls from './hls';
import type { CuesInterface } from './utils/cues';
import type { MediaKeyFunc, KeySystems } from './utils/mediakeys-helper';
import type {
FragmentLoaderContext,
Loader,
LoaderContext,
PlaylistLoaderContext,
} from './types/loader';
export type ABRControllerConfig = {
abrEwmaFastLive: number;
abrEwmaSlowLive: number;
abrEwmaFastVoD: number;
abrEwmaSlowVoD: number;
/**
* Default bandwidth estimate in bits/s prior to collecting fragment bandwidth samples
*/
abrEwmaDefaultEstimate: number;
abrBandWidthFactor: number;
abrBandWidthUpFactor: number;
abrMaxWithRealBitrate: boolean;
maxStarvationDelay: number;
maxLoadingDelay: number;
};
export type BufferControllerConfig = {
appendErrorMaxRetry: number;
backBufferLength: number;
liveDurationInfinity: boolean;
/**
* @deprecated use backBufferLength
*/
liveBackBufferLength: number | null;
};
export type CapLevelControllerConfig = {
capLevelToPlayerSize: boolean;
};
export type CMCDControllerConfig = {
sessionId?: string;
contentId?: string;
useHeaders?: boolean;
};
export type DRMSystemOptions = {
audioRobustness?: string;
videoRobustness?: string;
audioEncryptionScheme?: string | null;
videoEncryptionScheme?: string | null;
persistentState?: MediaKeysRequirement;
distinctiveIdentifier?: MediaKeysRequirement;
sessionTypes?: string[];
sessionType?: string;
};
export type DRMSystemConfiguration = {
licenseUrl: string;
serverCertificateUrl?: string;
generateRequest?: (
this: Hls,
initDataType: string,
initData: ArrayBuffer | null,
keyContext: MediaKeySessionContext
) =>
| { initDataType: string; initData: ArrayBuffer | null }
| undefined
| never;
};
export type DRMSystemsConfiguration = Partial<
Record<KeySystems, DRMSystemConfiguration>
>;
export type EMEControllerConfig = {
licenseXhrSetup?: (
this: Hls,
xhr: XMLHttpRequest,
url: string,
keyContext: MediaKeySessionContext,
licenseChallenge: Uint8Array
) => void | Uint8Array | Promise<Uint8Array | void>;
licenseResponseCallback?: (
this: Hls,
xhr: XMLHttpRequest,
url: string,
keyContext: MediaKeySessionContext
) => ArrayBuffer;
emeEnabled: boolean;
widevineLicenseUrl?: string;
drmSystems: DRMSystemsConfiguration;
drmSystemOptions: DRMSystemOptions;
requestMediaKeySystemAccessFunc: MediaKeyFunc | null;
};
export interface FragmentLoaderConstructor {
new (confg: HlsConfig): Loader<FragmentLoaderContext>;
}
/**
* @deprecated use fragLoadPolicy.default
*/
export type FragmentLoaderConfig = {
fragLoadingTimeOut: number;
fragLoadingMaxRetry: number;
fragLoadingRetryDelay: number;
fragLoadingMaxRetryTimeout: number;
};
export type FPSControllerConfig = {
capLevelOnFPSDrop: boolean;
fpsDroppedMonitoringPeriod: number;
fpsDroppedMonitoringThreshold: number;
};
export type LevelControllerConfig = {
startLevel?: number;
};
export type MP4RemuxerConfig = {
stretchShortVideoTrack: boolean;
maxAudioFramesDrift: number;
};
export interface PlaylistLoaderConstructor {
new (confg: HlsConfig): Loader<PlaylistLoaderContext>;
}
/**
* @deprecated use manifestLoadPolicy.default and playlistLoadPolicy.default
*/
export type PlaylistLoaderConfig = {
manifestLoadingTimeOut: number;
manifestLoadingMaxRetry: number;
manifestLoadingRetryDelay: number;
manifestLoadingMaxRetryTimeout: number;
levelLoadingTimeOut: number;
levelLoadingMaxRetry: number;
levelLoadingRetryDelay: number;
levelLoadingMaxRetryTimeout: number;
};
export type HlsLoadPolicies = {
fragLoadPolicy: LoadPolicy;
keyLoadPolicy: LoadPolicy;
certLoadPolicy: LoadPolicy;
playlistLoadPolicy: LoadPolicy;
manifestLoadPolicy: LoadPolicy;
steeringManifestLoadPolicy: LoadPolicy;
};
export type LoadPolicy = {
default: LoaderConfig;
};
export type LoaderConfig = {
maxTimeToFirstByteMs: number; // Max time to first byte
maxLoadTimeMs: number; // Max time for load completion
timeoutRetry: RetryConfig | null;
errorRetry: RetryConfig | null;
};
export type RetryConfig = {
maxNumRetry: number; // Maximum number of retries
retryDelayMs: number; // Retry delay = 2^retryCount * retryDelayMs (exponential) or retryCount * retryDelayMs (linear)
maxRetryDelayMs: number; // Maximum delay between retries
backoff?: 'exponential' | 'linear'; // used to determine retry backoff duration (see retryDelayMs)
};
export type StreamControllerConfig = {
autoStartLoad: boolean;
startPosition: number;
defaultAudioCodec?: string;
initialLiveManifestSize: number;
maxBufferLength: number;
maxBufferSize: number;
maxBufferHole: number;
highBufferWatchdogPeriod: number;
nudgeOffset: number;
nudgeMaxRetry: number;
maxFragLookUpTolerance: number;
maxMaxBufferLength: number;
startFragPrefetch: boolean;
testBandwidth: boolean;
};
export type LatencyControllerConfig = {
liveSyncDurationCount: number;
liveMaxLatencyDurationCount: number;
liveSyncDuration?: number;
liveMaxLatencyDuration?: number;
maxLiveSyncPlaybackRate: number;
};
export type MetadataControllerConfig = {
enableDateRangeMetadataCues: boolean;
enableEmsgMetadataCues: boolean;
enableID3MetadataCues: boolean;
};
export type TimelineControllerConfig = {
cueHandler: CuesInterface;
enableWebVTT: boolean;
enableIMSC1: boolean;
enableCEA708Captions: boolean;
captionsTextTrack1Label: string;
captionsTextTrack1LanguageCode: string;
captionsTextTrack2Label: string;
captionsTextTrack2LanguageCode: string;
captionsTextTrack3Label: string;
captionsTextTrack3LanguageCode: string;
captionsTextTrack4Label: string;
captionsTextTrack4LanguageCode: string;
renderTextTracksNatively: boolean;
};
export type TSDemuxerConfig = {
forceKeyFrameOnDiscontinuity: boolean;
};
export type HlsConfig = {
debug: boolean | ILogger;
enableWorker: boolean;
workerPath: null | string;
enableSoftwareAES: boolean;
minAutoBitrate: number;
ignoreDevicePixelRatio: boolean;
loader: { new (confg: HlsConfig): Loader<LoaderContext> };
fLoader?: FragmentLoaderConstructor;
pLoader?: PlaylistLoaderConstructor;
fetchSetup?: (context: LoaderContext, initParams: any) => Request;
xhrSetup?: (xhr: XMLHttpRequest, url: string) => Promise<void> | void;
// Alt Audio
audioStreamController?: typeof AudioStreamController;
audioTrackController?: typeof AudioTrackController;
// Subtitle
subtitleStreamController?: typeof SubtitleStreamController;
subtitleTrackController?: typeof SubtitleTrackController;
timelineController?: typeof TimelineController;
// EME
emeController?: typeof EMEController;
// CMCD
cmcd?: CMCDControllerConfig;
cmcdController?: typeof CMCDController;
// Content Steering
contentSteeringController?: typeof ContentSteeringController;
abrController: typeof AbrController;
bufferController: typeof BufferController;
capLevelController: typeof CapLevelController;
errorController: typeof ErrorController;
fpsController: typeof FPSController;
progressive: boolean;
lowLatencyMode: boolean;
} & ABRControllerConfig &
BufferControllerConfig &
CapLevelControllerConfig &
EMEControllerConfig &
FPSControllerConfig &
LevelControllerConfig &
MP4RemuxerConfig &
StreamControllerConfig &
LatencyControllerConfig &
MetadataControllerConfig &
TimelineControllerConfig &
TSDemuxerConfig &
HlsLoadPolicies &
FragmentLoaderConfig &
PlaylistLoaderConfig;
const defaultLoadPolicy: LoaderConfig = {
maxTimeToFirstByteMs: 8000,
maxLoadTimeMs: 20000,
timeoutRetry: null,
errorRetry: null,
};
/**
* @ignore
* If possible, keep hlsDefaultConfig shallow
* It is cloned whenever a new Hls instance is created, by keeping the config
* shallow the properties are cloned, and we don't end up manipulating the default
*/
export const hlsDefaultConfig: HlsConfig = {
autoStartLoad: true, // used by stream-controller
startPosition: -1, // used by stream-controller
defaultAudioCodec: undefined, // used by stream-controller
debug: false, // used by logger
capLevelOnFPSDrop: false, // used by fps-controller
capLevelToPlayerSize: false, // used by cap-level-controller
ignoreDevicePixelRatio: false, // used by cap-level-controller
initialLiveManifestSize: 1, // used by stream-controller
maxBufferLength: 30, // used by stream-controller
backBufferLength: Infinity, // used by buffer-controller
maxBufferSize: 60 * 1000 * 1000, // used by stream-controller
maxBufferHole: 0.1, // used by stream-controller
highBufferWatchdogPeriod: 2, // used by stream-controller
nudgeOffset: 0.1, // used by stream-controller
nudgeMaxRetry: 3, // used by stream-controller
maxFragLookUpTolerance: 0.25, // used by stream-controller
liveSyncDurationCount: 3, // used by latency-controller
liveMaxLatencyDurationCount: Infinity, // used by latency-controller
liveSyncDuration: undefined, // used by latency-controller
liveMaxLatencyDuration: undefined, // used by latency-controller
maxLiveSyncPlaybackRate: 1, // used by latency-controller
liveDurationInfinity: false, // used by buffer-controller
/**
* @deprecated use backBufferLength
*/
liveBackBufferLength: null, // used by buffer-controller
maxMaxBufferLength: 600, // used by stream-controller
enableWorker: true, // used by transmuxer
workerPath: null, // used by transmuxer
enableSoftwareAES: true, // used by decrypter
startLevel: undefined, // used by level-controller
startFragPrefetch: false, // used by stream-controller
fpsDroppedMonitoringPeriod: 5000, // used by fps-controller
fpsDroppedMonitoringThreshold: 0.2, // used by fps-controller
appendErrorMaxRetry: 3, // used by buffer-controller
loader: XhrLoader,
// loader: FetchLoader,
fLoader: undefined, // used by fragment-loader
pLoader: undefined, // used by playlist-loader
xhrSetup: undefined, // used by xhr-loader
licenseXhrSetup: undefined, // used by eme-controller
licenseResponseCallback: undefined, // used by eme-controller
abrController: AbrController,
bufferController: BufferController,
capLevelController: CapLevelController,
errorController: ErrorController,
fpsController: FPSController,
stretchShortVideoTrack: false, // used by mp4-remuxer
maxAudioFramesDrift: 1, // used by mp4-remuxer
forceKeyFrameOnDiscontinuity: true, // used by ts-demuxer
abrEwmaFastLive: 3, // used by abr-controller
abrEwmaSlowLive: 9, // used by abr-controller
abrEwmaFastVoD: 3, // used by abr-controller
abrEwmaSlowVoD: 9, // used by abr-controller
abrEwmaDefaultEstimate: 5e5, // 500 kbps // used by abr-controller
abrBandWidthFactor: 0.95, // used by abr-controller
abrBandWidthUpFactor: 0.7, // used by abr-controller
abrMaxWithRealBitrate: false, // used by abr-controller
maxStarvationDelay: 4, // used by abr-controller
maxLoadingDelay: 4, // used by abr-controller
minAutoBitrate: 0, // used by hls
emeEnabled: false, // used by eme-controller
widevineLicenseUrl: undefined, // used by eme-controller
drmSystems: {}, // used by eme-controller
drmSystemOptions: {}, // used by eme-controller
requestMediaKeySystemAccessFunc: __USE_EME_DRM__
? requestMediaKeySystemAccess
: null, // used by eme-controller
testBandwidth: true,
progressive: false,
lowLatencyMode: true,
cmcd: undefined,
enableDateRangeMetadataCues: true,
enableEmsgMetadataCues: true,
enableID3MetadataCues: true,
certLoadPolicy: {
default: defaultLoadPolicy,
},
keyLoadPolicy: {
default: {
maxTimeToFirstByteMs: 8000,
maxLoadTimeMs: 20000,
timeoutRetry: {
maxNumRetry: 1,
retryDelayMs: 1000,
maxRetryDelayMs: 20000,
backoff: 'linear',
},
errorRetry: {
maxNumRetry: 8,
retryDelayMs: 1000,
maxRetryDelayMs: 20000,
backoff: 'linear',
},
},
},
manifestLoadPolicy: {
default: {
maxTimeToFirstByteMs: Infinity,
maxLoadTimeMs: 20000,
timeoutRetry: {
maxNumRetry: 2,
retryDelayMs: 0,
maxRetryDelayMs: 0,
},
errorRetry: {
maxNumRetry: 1,
retryDelayMs: 1000,
maxRetryDelayMs: 8000,
},
},
},
playlistLoadPolicy: {
default: {
maxTimeToFirstByteMs: 10000,
maxLoadTimeMs: 20000,
timeoutRetry: {
maxNumRetry: 2,
retryDelayMs: 0,
maxRetryDelayMs: 0,
},
errorRetry: {
maxNumRetry: 2,
retryDelayMs: 1000,
maxRetryDelayMs: 8000,
},
},
},
fragLoadPolicy: {
default: {
maxTimeToFirstByteMs: 10000,
maxLoadTimeMs: 120000,
timeoutRetry: {
maxNumRetry: 4,
retryDelayMs: 0,
maxRetryDelayMs: 0,
},
errorRetry: {
maxNumRetry: 6,
retryDelayMs: 1000,
maxRetryDelayMs: 8000,
},
},
},
steeringManifestLoadPolicy: {
default: __USE_CONTENT_STEERING__
? {
maxTimeToFirstByteMs: 10000,
maxLoadTimeMs: 20000,
timeoutRetry: {
maxNumRetry: 2,
retryDelayMs: 0,
maxRetryDelayMs: 0,
},
errorRetry: {
maxNumRetry: 1,
retryDelayMs: 1000,
maxRetryDelayMs: 8000,
},
}
: defaultLoadPolicy,
},
// These default settings are deprecated in favor of the above policies
// and are maintained for backwards compatibility
manifestLoadingTimeOut: 10000,
manifestLoadingMaxRetry: 1,
manifestLoadingRetryDelay: 1000,
manifestLoadingMaxRetryTimeout: 64000,
levelLoadingTimeOut: 10000,
levelLoadingMaxRetry: 4,
levelLoadingRetryDelay: 1000,
levelLoadingMaxRetryTimeout: 64000,
fragLoadingTimeOut: 20000,
fragLoadingMaxRetry: 6,
fragLoadingRetryDelay: 1000,
fragLoadingMaxRetryTimeout: 64000,
// Dynamic Modules
...timelineConfig(),
subtitleStreamController: __USE_SUBTITLES__
? SubtitleStreamController
: undefined,
subtitleTrackController: __USE_SUBTITLES__
? SubtitleTrackController
: undefined,
timelineController: __USE_SUBTITLES__ ? TimelineController : undefined,
audioStreamController: __USE_ALT_AUDIO__ ? AudioStreamController : undefined,
audioTrackController: __USE_ALT_AUDIO__ ? AudioTrackController : undefined,
emeController: __USE_EME_DRM__ ? EMEController : undefined,
cmcdController: __USE_CMCD__ ? CMCDController : undefined,
contentSteeringController: __USE_CONTENT_STEERING__
? ContentSteeringController
: undefined,
};
function timelineConfig(): TimelineControllerConfig {
return {
cueHandler: Cues, // used by timeline-controller
enableWebVTT: __USE_SUBTITLES__, // used by timeline-controller
enableIMSC1: __USE_SUBTITLES__, // used by timeline-controller
enableCEA708Captions: __USE_SUBTITLES__, // used by timeline-controller
captionsTextTrack1Label: 'English', // used by timeline-controller
captionsTextTrack1LanguageCode: 'en', // used by timeline-controller
captionsTextTrack2Label: 'Spanish', // used by timeline-controller
captionsTextTrack2LanguageCode: 'es', // used by timeline-controller
captionsTextTrack3Label: 'Unknown CC', // used by timeline-controller
captionsTextTrack3LanguageCode: '', // used by timeline-controller
captionsTextTrack4Label: 'Unknown CC', // used by timeline-controller
captionsTextTrack4LanguageCode: '', // used by timeline-controller
renderTextTracksNatively: true,
};
}
/**
* @ignore
*/
export function mergeConfig(
defaultConfig: HlsConfig,
userConfig: Partial<HlsConfig>
): HlsConfig {
if (
(userConfig.liveSyncDurationCount ||
userConfig.liveMaxLatencyDurationCount) &&
(userConfig.liveSyncDuration || userConfig.liveMaxLatencyDuration)
) {
throw new Error(
"Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration"
);
}
if (
userConfig.liveMaxLatencyDurationCount !== undefined &&
(userConfig.liveSyncDurationCount === undefined ||
userConfig.liveMaxLatencyDurationCount <=
userConfig.liveSyncDurationCount)
) {
throw new Error(
'Illegal hls.js config: "liveMaxLatencyDurationCount" must be greater than "liveSyncDurationCount"'
);
}
if (
userConfig.liveMaxLatencyDuration !== undefined &&
(userConfig.liveSyncDuration === undefined ||
userConfig.liveMaxLatencyDuration <= userConfig.liveSyncDuration)
) {
throw new Error(
'Illegal hls.js config: "liveMaxLatencyDuration" must be greater than "liveSyncDuration"'
);
}
const defaultsCopy = deepCpy(defaultConfig);
// Backwards compatibility with deprecated config values
const deprecatedSettingTypes = ['manifest', 'level', 'frag'];
const deprecatedSettings = [
'TimeOut',
'MaxRetry',
'RetryDelay',
'MaxRetryTimeout',
];
deprecatedSettingTypes.forEach((type) => {
const policyName = `${type === 'level' ? 'playlist' : type}LoadPolicy`;
const policyNotSet = userConfig[policyName] === undefined;
const report: string[] = [];
deprecatedSettings.forEach((setting) => {
const deprecatedSetting = `${type}Loading${setting}`;
const value = userConfig[deprecatedSetting];
if (value !== undefined && policyNotSet) {
report.push(deprecatedSetting);
const settings: LoaderConfig = defaultsCopy[policyName].default;
userConfig[policyName] = { default: settings };
switch (setting) {
case 'TimeOut':
settings.maxLoadTimeMs = value;
settings.maxTimeToFirstByteMs = value;
break;
case 'MaxRetry':
settings.errorRetry!.maxNumRetry = value;
settings.timeoutRetry!.maxNumRetry = value;
break;
case 'RetryDelay':
settings.errorRetry!.retryDelayMs = value;
settings.timeoutRetry!.retryDelayMs = value;
break;
case 'MaxRetryTimeout':
settings.errorRetry!.maxRetryDelayMs = value;
settings.timeoutRetry!.maxRetryDelayMs = value;
break;
}
}
});
if (report.length) {
logger.warn(
`hls.js config: "${report.join(
'", "'
)}" setting(s) are deprecated, use "${policyName}": ${JSON.stringify(
userConfig[policyName]
)}`
);
}
});
return {
...defaultsCopy,
...userConfig,
};
}
function deepCpy(obj: any): any {
if (obj && typeof obj === 'object') {
if (Array.isArray(obj)) {
return obj.map(deepCpy);
}
return Object.keys(obj).reduce((result, key) => {
result[key] = deepCpy(obj[key]);
return result;
}, {});
}
return obj;
}
/**
* @ignore
*/
export function enableStreamingMode(config) {
const currentLoader = config.loader;
if (currentLoader !== FetchLoader && currentLoader !== XhrLoader) {
// If a developer has configured their own loader, respect that choice
logger.log(
'[config]: Custom loader detected, cannot enable progressive streaming'
);
config.progressive = false;
} else {
const canStreamProgressively = fetchSupported();
if (canStreamProgressively) {
config.loader = FetchLoader;
config.progressive = true;
config.enableSoftwareAES = true;
logger.log('[config]: Progressive streaming enabled, using FetchLoader');
}
}
}