release: v3.0.0
This commit is contained in:
+22
-4
@@ -3,11 +3,26 @@
|
||||
* Separated to avoid circular dependencies and enable better tree-shaking
|
||||
*/
|
||||
|
||||
const hasQualityControls = (
|
||||
hls: HlsInstance | PlayerInstance | null | undefined
|
||||
): hls is HlsInstance => {
|
||||
return Boolean(hls && 'levels' in hls && Array.isArray(hls.levels) && 'currentLevel' in hls)
|
||||
}
|
||||
|
||||
const hasAudioControls = (
|
||||
hls: HlsInstance | PlayerInstance | null | undefined
|
||||
): hls is HlsInstance => {
|
||||
return Boolean(hls && 'audioTracks' in hls && Array.isArray(hls.audioTracks) && 'audioTrack' in hls)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update active quality level in HLS instance. Passing null re-enables auto.
|
||||
*/
|
||||
export const setHlsQualityLevel = (hls: any, levelIndex: number | null | undefined): void => {
|
||||
if (!hls || !Array.isArray(hls.levels)) {
|
||||
export const setHlsQualityLevel = (
|
||||
hls: HlsInstance | PlayerInstance | null | undefined,
|
||||
levelIndex: number | null | undefined
|
||||
): void => {
|
||||
if (!hasQualityControls(hls)) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -30,8 +45,11 @@ export const setHlsQualityLevel = (hls: any, levelIndex: number | null | undefin
|
||||
/**
|
||||
* Set active audio track in HLS instance
|
||||
*/
|
||||
export const setHlsAudioTrack = (hls: any, audioTrackIndex: number): void => {
|
||||
if (!hls || !hls.audioTracks) {
|
||||
export const setHlsAudioTrack = (
|
||||
hls: HlsInstance | PlayerInstance | null | undefined,
|
||||
audioTrackIndex: number
|
||||
): void => {
|
||||
if (!hasAudioControls(hls)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+15
-17
@@ -10,16 +10,16 @@ import { logger } from './logger'
|
||||
// Re-export control functions for backward compatibility
|
||||
export { setHlsQualityLevel, setHlsAudioTrack } from './hlsControl'
|
||||
|
||||
const HLS_CDN_URL = 'https://cdn.jsdelivr.net/npm/hls.js@1.5.13/dist/hls.min.js'
|
||||
const HLS_CDN_URL = 'https://cdn.jsdelivr.net/npm/hls.js@1.6.13/dist/hls.min.js'
|
||||
|
||||
/**
|
||||
* Load hls.js from CDN as fallback
|
||||
*/
|
||||
const loadHlsFromCDN = (): Promise<any> => {
|
||||
const loadHlsFromCDN = (): Promise<HlsConstructor> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Check if already loaded globally
|
||||
if (typeof (window as any).Hls !== 'undefined') {
|
||||
resolve((window as any).Hls)
|
||||
if (window.Hls) {
|
||||
resolve(window.Hls)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ const loadHlsFromCDN = (): Promise<any> => {
|
||||
script.async = true
|
||||
|
||||
script.onload = () => {
|
||||
if (typeof (window as any).Hls !== 'undefined') {
|
||||
resolve((window as any).Hls)
|
||||
if (window.Hls) {
|
||||
resolve(window.Hls)
|
||||
} else {
|
||||
reject(new Error('HLS.js CDN loaded but Hls global not found'))
|
||||
}
|
||||
@@ -46,7 +46,7 @@ const loadHlsFromCDN = (): Promise<any> => {
|
||||
/**
|
||||
* Load hls.js with npm fallback to CDN
|
||||
*/
|
||||
export const loadHls = async (): Promise<any> => {
|
||||
export const loadHls = async (): Promise<HlsConstructor> => {
|
||||
try {
|
||||
logger.log('[HLS Loader] Attempting to load from npm package...')
|
||||
// Try loading from npm package first
|
||||
@@ -70,7 +70,7 @@ export const loadHls = async (): Promise<any> => {
|
||||
/**
|
||||
* Check if HLS.js is supported in current browser
|
||||
*/
|
||||
export const isHlsSupported = (Hls: any): boolean => {
|
||||
export const isHlsSupported = (Hls: HlsConstructor): boolean => {
|
||||
return Hls && typeof Hls.isSupported === 'function' && Hls.isSupported()
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ export const hasNativeHlsSupport = (): boolean => {
|
||||
/**
|
||||
* Extract audio tracks from HLS instance
|
||||
*/
|
||||
export const getHlsAudioTracks = (hls: any): AudioTrack[] => {
|
||||
export const getHlsAudioTracks = (hls: HlsInstance): AudioTrack[] => {
|
||||
try {
|
||||
if (!hls) {
|
||||
logger.warn('[HLS Loader] getHlsAudioTracks: No HLS instance provided')
|
||||
@@ -100,7 +100,7 @@ export const getHlsAudioTracks = (hls: any): AudioTrack[] => {
|
||||
|
||||
logger.log('[HLS Loader] getHlsAudioTracks: Raw audioTracks from HLS:', hls.audioTracks)
|
||||
|
||||
const audioTracks: AudioTrack[] = hls.audioTracks.map((track: any, index: number) => {
|
||||
const audioTracks: AudioTrack[] = hls.audioTracks.map((track: HlsAudioTrack, index: number) => {
|
||||
const audioTrack = {
|
||||
name: track.name || track.label || `Audio ${index + 1}`,
|
||||
language: track.lang || track.language || 'unknown',
|
||||
@@ -123,7 +123,7 @@ export const getHlsAudioTracks = (hls: any): AudioTrack[] => {
|
||||
/**
|
||||
* Extract subtitle tracks from HLS instance
|
||||
*/
|
||||
export const getHlsSubtitleTracks = (hls: any): SubtitleTrack[] => {
|
||||
export const getHlsSubtitleTracks = (hls: HlsInstance): SubtitleTrack[] => {
|
||||
try {
|
||||
if (!hls) {
|
||||
return []
|
||||
@@ -134,7 +134,7 @@ export const getHlsSubtitleTracks = (hls: any): SubtitleTrack[] => {
|
||||
return []
|
||||
}
|
||||
|
||||
const subtitleTracks: SubtitleTrack[] = hls.subtitleTracks.map((track: any, index: number) => {
|
||||
const subtitleTracks: SubtitleTrack[] = hls.subtitleTracks.map((track: HlsSubtitleTrack, index: number) => {
|
||||
return {
|
||||
label: track.name || track.label || `Subtitle ${index + 1}`,
|
||||
lang: track.lang || track.language || 'unknown',
|
||||
@@ -152,7 +152,7 @@ export const getHlsSubtitleTracks = (hls: any): SubtitleTrack[] => {
|
||||
/**
|
||||
* Extract available quality levels from HLS instance
|
||||
*/
|
||||
export const getHlsQualities = (hls: any): VideoQuality[] => {
|
||||
export const getHlsQualities = (hls: HlsInstance): VideoQuality[] => {
|
||||
try {
|
||||
if (!hls) {
|
||||
logger.warn('[HLS Loader] getHlsQualities: No HLS instance provided')
|
||||
@@ -166,7 +166,7 @@ export const getHlsQualities = (hls: any): VideoQuality[] => {
|
||||
|
||||
logger.log('[HLS Loader] getHlsQualities: Raw levels from HLS:', hls.levels)
|
||||
|
||||
const qualities: VideoQuality[] = hls.levels.map((level: any, index: number) => {
|
||||
const qualities: VideoQuality[] = hls.levels.map((level: HlsLevel, index: number) => {
|
||||
const resolution = typeof level.attrs?.RESOLUTION === 'string' ? level.attrs.RESOLUTION : undefined
|
||||
const [widthFromResolution, heightFromResolution] = resolution
|
||||
? resolution.split('x').map((value: string) => parseInt(value, 10))
|
||||
@@ -175,7 +175,7 @@ export const getHlsQualities = (hls: any): VideoQuality[] => {
|
||||
const translations = getTranslations(detectBrowserLanguage());
|
||||
const width = level.width || widthFromResolution
|
||||
const height = level.height || heightFromResolution
|
||||
const bitrate = typeof level.bitrate === 'number' ? level.bitrate : level.attrs?.BANDWIDTH
|
||||
const bitrate = typeof level.bitrate === 'number' ? level.bitrate : (level.attrs?.BANDWIDTH ? parseInt(level.attrs.BANDWIDTH, 10) : undefined)
|
||||
|
||||
let label: string
|
||||
if (typeof level.name === 'string' && level.name.trim().length > 0) {
|
||||
@@ -213,5 +213,3 @@ export const getHlsQualities = (hls: any): VideoQuality[] => {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ export const setupHlsInstance = async ({
|
||||
}
|
||||
})
|
||||
|
||||
hls.on(Hls.Events.ERROR, (_event: any, data: any) => {
|
||||
hls.on(Hls.Events.ERROR, (_event: unknown, data: { fatal: boolean; type: string }) => {
|
||||
if (data.fatal) {
|
||||
switch (data.type) {
|
||||
case Hls.ErrorTypes.NETWORK_ERROR:
|
||||
@@ -131,13 +131,13 @@ export const setupHlsInstance = async ({
|
||||
}
|
||||
})
|
||||
|
||||
;(video as any).__hlsInstance = hls
|
||||
video.__hlsInstance = hls as PlayerInstance
|
||||
|
||||
return () => {
|
||||
if (hls) {
|
||||
hls.destroy()
|
||||
}
|
||||
delete (video as any).__hlsInstance
|
||||
delete video.__hlsInstance
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+53
-11
@@ -1,9 +1,11 @@
|
||||
/**
|
||||
* MPEG-TS loader utility
|
||||
* Dynamically loads mpegts.js library
|
||||
* Dynamically loads mpegts.js library with CDN fallback
|
||||
*/
|
||||
import { logger } from './logger'
|
||||
|
||||
const MPEGTS_CDN_URL = 'https://cdn.jsdelivr.net/npm/mpegts.js@1.7.3/dist/mpegts.js'
|
||||
|
||||
export interface MpegtsConfig {
|
||||
enableWorker?: boolean
|
||||
enableStashBuffer?: boolean
|
||||
@@ -20,14 +22,45 @@ export interface MpegtsConfig {
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
|
||||
let mpegtsInstance: any = null
|
||||
let loadingPromise: Promise<any> | null = null
|
||||
let mpegtsInstance: MpegtsStatic | null = null
|
||||
let loadingPromise: Promise<MpegtsStatic> | null = null
|
||||
|
||||
/**
|
||||
* Load mpegts.js from CDN as fallback
|
||||
*/
|
||||
const loadMpegtsFromCDN = (): Promise<MpegtsStatic> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (window.mpegts) {
|
||||
resolve(window.mpegts)
|
||||
return
|
||||
}
|
||||
|
||||
const script = document.createElement('script')
|
||||
script.src = MPEGTS_CDN_URL
|
||||
script.async = true
|
||||
|
||||
script.onload = () => {
|
||||
if (window.mpegts) {
|
||||
resolve(window.mpegts)
|
||||
} else {
|
||||
reject(new Error('mpegts.js loaded but not available on window'))
|
||||
}
|
||||
}
|
||||
|
||||
script.onerror = () => {
|
||||
reject(new Error(`Failed to load mpegts.js from CDN: ${MPEGTS_CDN_URL}`))
|
||||
}
|
||||
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Load mpegts.js library dynamically
|
||||
* Tries NPM package first, falls back to CDN if unavailable
|
||||
* @returns Promise that resolves to mpegts.js module
|
||||
*/
|
||||
export const loadMpegts = async (): Promise<any> => {
|
||||
export const loadMpegts = async (): Promise<MpegtsStatic> => {
|
||||
// Return cached instance if available
|
||||
if (mpegtsInstance) {
|
||||
logger.log('[MPEG-TS Loader] Using cached mpegts.js instance')
|
||||
@@ -47,10 +80,20 @@ export const loadMpegts = async (): Promise<any> => {
|
||||
const module = await import('mpegts.js')
|
||||
mpegtsInstance = module.default || module
|
||||
logger.log('[MPEG-TS Loader] Successfully loaded from npm package')
|
||||
return mpegtsInstance
|
||||
} catch (error) {
|
||||
logger.error('[MPEG-TS Loader] Failed to load mpegts.js:', error)
|
||||
throw new Error('Failed to load mpegts.js. Make sure it is installed: npm install mpegts.js')
|
||||
return mpegtsInstance!
|
||||
} catch (npmError) {
|
||||
logger.warn('[MPEG-TS Loader] npm package not available, loading from CDN...', npmError)
|
||||
|
||||
try {
|
||||
mpegtsInstance = await loadMpegtsFromCDN()
|
||||
logger.log('[MPEG-TS Loader] Successfully loaded from CDN')
|
||||
return mpegtsInstance!
|
||||
} catch (cdnError) {
|
||||
logger.error('[MPEG-TS Loader] Failed to load from both npm and CDN', cdnError)
|
||||
throw new Error(
|
||||
'Failed to load mpegts.js library. Please ensure mpegts.js is available or check your network connection.'
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
loadingPromise = null
|
||||
}
|
||||
@@ -64,7 +107,7 @@ export const loadMpegts = async (): Promise<any> => {
|
||||
* @param mpegts - The mpegts.js module
|
||||
* @returns True if supported
|
||||
*/
|
||||
export const isMpegtsSupported = (mpegts: any): boolean => {
|
||||
export const isMpegtsSupported = (mpegts: MpegtsStatic): boolean => {
|
||||
return mpegts && mpegts.isSupported()
|
||||
}
|
||||
|
||||
@@ -94,7 +137,7 @@ export const createDefaultMpegtsConfig = (isLive: boolean = false): MpegtsConfig
|
||||
* Get the cached mpegts.js instance
|
||||
* @returns The mpegts.js module or null
|
||||
*/
|
||||
export const getMpegtsInstance = (): any | null => {
|
||||
export const getMpegtsInstance = (): MpegtsStatic | null => {
|
||||
return mpegtsInstance
|
||||
}
|
||||
|
||||
@@ -106,4 +149,3 @@ export const clearMpegtsCache = (): void => {
|
||||
loadingPromise = null
|
||||
logger.log('[MPEG-TS Loader] Cache cleared')
|
||||
}
|
||||
|
||||
|
||||
+11
-12
@@ -69,10 +69,10 @@ export const setupMpegtsInstance = async ({
|
||||
player.load()
|
||||
|
||||
// Store player instance on video element for later access
|
||||
;(video as any).__mpegtsInstance = player
|
||||
video.__mpegtsInstance = player
|
||||
|
||||
// Event handlers
|
||||
player.on(mpegts.Events.ERROR, (errorType: string, errorDetail: string, errorInfo: any) => {
|
||||
player.on(mpegts.Events.ERROR, (errorType: string, errorDetail: string, errorInfo: unknown) => {
|
||||
logger.error('mpegts.js error:', { errorType, errorDetail, errorInfo })
|
||||
|
||||
const error = new Error(`MPEG-TS Player Error: ${errorType} - ${errorDetail}`)
|
||||
@@ -125,7 +125,7 @@ export const setupMpegtsInstance = async ({
|
||||
logger.log('mpegts.js: Recovered from early EOF')
|
||||
})
|
||||
|
||||
player.on(mpegts.Events.METADATA_ARRIVED, (metadata: any) => {
|
||||
player.on(mpegts.Events.METADATA_ARRIVED, (metadata: Record<string, unknown>) => {
|
||||
logger.log('mpegts.js: Metadata arrived', metadata)
|
||||
|
||||
// Trigger onLoadedMetadata callback
|
||||
@@ -134,10 +134,10 @@ export const setupMpegtsInstance = async ({
|
||||
}
|
||||
})
|
||||
|
||||
player.on(mpegts.Events.STATISTICS_INFO, (stats: any) => {
|
||||
player.on(mpegts.Events.STATISTICS_INFO, (stats: Record<string, unknown>) => {
|
||||
// Statistics info for debugging/monitoring
|
||||
if (stats) {
|
||||
;(video as any).__mpegtsStats = stats
|
||||
video.__mpegtsStats = stats
|
||||
}
|
||||
})
|
||||
|
||||
@@ -174,8 +174,8 @@ export const setupMpegtsInstance = async ({
|
||||
player.destroy()
|
||||
|
||||
// Clean up stored references
|
||||
delete (video as any).__mpegtsInstance
|
||||
delete (video as any).__mpegtsStats
|
||||
delete video.__mpegtsInstance
|
||||
delete video.__mpegtsStats
|
||||
} catch (cleanupError) {
|
||||
logger.error('Error during mpegts.js cleanup:', cleanupError)
|
||||
}
|
||||
@@ -199,11 +199,11 @@ export const setupMpegtsInstance = async ({
|
||||
* @param video - The video element
|
||||
* @returns The mpegts.js player instance or null
|
||||
*/
|
||||
export const getMpegtsInstance = (video: HTMLVideoElement | null): any | null => {
|
||||
export const getMpegtsInstance = (video: HTMLVideoElement | null): MpegtsPlayer | PlayerInstance | null => {
|
||||
if (!video) {
|
||||
return null
|
||||
}
|
||||
return (video as any).__mpegtsInstance || null
|
||||
return video.__mpegtsInstance ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,11 +211,11 @@ export const getMpegtsInstance = (video: HTMLVideoElement | null): any | null =>
|
||||
* @param video - The video element
|
||||
* @returns The statistics object or null
|
||||
*/
|
||||
export const getMpegtsStats = (video: HTMLVideoElement | null): any | null => {
|
||||
export const getMpegtsStats = (video: HTMLVideoElement | null): Record<string, unknown> | null => {
|
||||
if (!video) {
|
||||
return null
|
||||
}
|
||||
return (video as any).__mpegtsStats || null
|
||||
return video.__mpegtsStats ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,4 +226,3 @@ export const getMpegtsStats = (video: HTMLVideoElement | null): any | null => {
|
||||
export const hasMpegtsInstance = (video: HTMLVideoElement | null): boolean => {
|
||||
return getMpegtsInstance(video) !== null
|
||||
}
|
||||
|
||||
|
||||
+4
-16
@@ -9,21 +9,15 @@
|
||||
*/
|
||||
export const setupFullscreenPolyfill = () => {
|
||||
if (!document.exitFullscreen) {
|
||||
// @ts-ignore - Legacy API
|
||||
document.exitFullscreen = document.webkitExitFullscreen ||
|
||||
// @ts-ignore
|
||||
document.exitFullscreen = (document.webkitExitFullscreen ||
|
||||
document.mozCancelFullScreen ||
|
||||
// @ts-ignore
|
||||
document.msExitFullscreen
|
||||
document.msExitFullscreen) as typeof document.exitFullscreen
|
||||
}
|
||||
|
||||
if (!Element.prototype.requestFullscreen) {
|
||||
// @ts-ignore - Legacy API
|
||||
Element.prototype.requestFullscreen = Element.prototype.webkitRequestFullscreen ||
|
||||
// @ts-ignore
|
||||
Element.prototype.requestFullscreen = (Element.prototype.webkitRequestFullscreen ||
|
||||
Element.prototype.mozRequestFullScreen ||
|
||||
// @ts-ignore
|
||||
Element.prototype.msRequestFullscreen
|
||||
Element.prototype.msRequestFullscreen) as typeof Element.prototype.requestFullscreen
|
||||
}
|
||||
|
||||
// Fullscreen change event polyfill
|
||||
@@ -41,11 +35,8 @@ export const setupFullscreenPolyfill = () => {
|
||||
if (!Object.prototype.hasOwnProperty.call(document, 'fullscreenElement')) {
|
||||
Object.defineProperty(document, 'fullscreenElement', {
|
||||
get: function() {
|
||||
// @ts-ignore
|
||||
return this.webkitFullscreenElement ||
|
||||
// @ts-ignore
|
||||
this.mozFullScreenElement ||
|
||||
// @ts-ignore
|
||||
this.msFullscreenElement
|
||||
}
|
||||
})
|
||||
@@ -157,11 +148,8 @@ export const features = {
|
||||
if (!isBrowser) return false
|
||||
return !!(
|
||||
document.fullscreenEnabled ||
|
||||
// @ts-ignore
|
||||
document.webkitFullscreenEnabled ||
|
||||
// @ts-ignore
|
||||
document.mozFullScreenEnabled ||
|
||||
// @ts-ignore
|
||||
document.msFullscreenEnabled
|
||||
)
|
||||
},
|
||||
|
||||
+16
-17
@@ -11,10 +11,10 @@ const FLVJS_CDN_URL = 'https://cdn.jsdelivr.net/npm/flv.js@1.6.2/dist/flv.min.js
|
||||
* Dynamically loads flv.js from CDN
|
||||
* @returns Promise that resolves to the flv.js library
|
||||
*/
|
||||
const loadFlvjsFromCDN = async (): Promise<any> => {
|
||||
const loadFlvjsFromCDN = async (): Promise<FlvjsStatic> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if ((window as any).flvjs) {
|
||||
resolve((window as any).flvjs)
|
||||
if (window.flvjs) {
|
||||
resolve(window.flvjs)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ const loadFlvjsFromCDN = async (): Promise<any> => {
|
||||
script.async = true
|
||||
|
||||
script.onload = () => {
|
||||
if ((window as any).flvjs) {
|
||||
resolve((window as any).flvjs)
|
||||
if (window.flvjs) {
|
||||
resolve(window.flvjs)
|
||||
} else {
|
||||
reject(new Error('flv.js loaded but not available on window'))
|
||||
}
|
||||
@@ -43,7 +43,7 @@ const loadFlvjsFromCDN = async (): Promise<any> => {
|
||||
* Tries NPM package first, falls back to CDN if unavailable
|
||||
* @returns Promise that resolves to the flv.js library
|
||||
*/
|
||||
export const loadFlvjs = async (): Promise<any> => {
|
||||
export const loadFlvjs = async (): Promise<FlvjsStatic> => {
|
||||
try {
|
||||
// Try loading from NPM package first
|
||||
const flvModule = await import('flv.js')
|
||||
@@ -69,7 +69,7 @@ export const loadFlvjs = async (): Promise<any> => {
|
||||
* @param flvjs - The flv.js library instance
|
||||
* @returns True if supported
|
||||
*/
|
||||
export const isFlvjsSupported = (flvjs: any): boolean => {
|
||||
export const isFlvjsSupported = (flvjs: FlvjsStatic): boolean => {
|
||||
if (!flvjs) {
|
||||
return false
|
||||
}
|
||||
@@ -83,7 +83,7 @@ export const isFlvjsSupported = (flvjs: any): boolean => {
|
||||
* @param flvjs - The flv.js library instance
|
||||
* @returns Support information object
|
||||
*/
|
||||
export const getFlvjsSupportInfo = (flvjs: any): {
|
||||
export const getFlvjsSupportInfo = (flvjs: FlvjsStatic): {
|
||||
mseSupported: boolean
|
||||
networkStreamIOSupported: boolean
|
||||
httpsSupported: boolean
|
||||
@@ -138,7 +138,7 @@ export const createDefaultFlvConfig = (isLive: boolean = true) => {
|
||||
* @param player - The flv.js player instance
|
||||
* @returns Basic quality information
|
||||
*/
|
||||
export const extractFlvQualityInfo = (player: any): {
|
||||
export const extractFlvQualityInfo = (player: FlvjsPlayer): {
|
||||
width?: number
|
||||
height?: number
|
||||
videoCodec?: string
|
||||
@@ -154,17 +154,16 @@ export const extractFlvQualityInfo = (player: any): {
|
||||
try {
|
||||
const stats = player.statisticsInfo
|
||||
return {
|
||||
width: stats.videoWidth,
|
||||
height: stats.videoHeight,
|
||||
videoCodec: stats.videoCodec,
|
||||
audioCodec: stats.audioCodec,
|
||||
fps: stats.fps,
|
||||
videoBitrate: stats.videoBitrate,
|
||||
audioBitrate: stats.audioBitrate,
|
||||
width: stats.videoWidth as number | undefined,
|
||||
height: stats.videoHeight as number | undefined,
|
||||
videoCodec: stats.videoCodec as string | undefined,
|
||||
audioCodec: stats.audioCodec as string | undefined,
|
||||
fps: stats.fps as number | undefined,
|
||||
videoBitrate: stats.videoBitrate as number | undefined,
|
||||
audioBitrate: stats.audioBitrate as number | undefined,
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn('Failed to extract flv.js quality info:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-12
@@ -81,10 +81,10 @@ export const setupRtmpInstance = async ({
|
||||
player.load()
|
||||
|
||||
// Store player instance on video element for later access
|
||||
;(video as any).__rtmpInstance = player
|
||||
video.__rtmpInstance = player
|
||||
|
||||
// Event handlers
|
||||
player.on(flvjs.Events.ERROR, (errorType: string, errorDetail: string, errorInfo: any) => {
|
||||
player.on(flvjs.Events.ERROR, (errorType: string, errorDetail: string, errorInfo: unknown) => {
|
||||
logger.error('flv.js error:', { errorType, errorDetail, errorInfo })
|
||||
|
||||
const error = new Error(`FLV Player Error: ${errorType} - ${errorDetail}`)
|
||||
@@ -137,7 +137,7 @@ export const setupRtmpInstance = async ({
|
||||
logger.log('flv.js: Recovered from early EOF')
|
||||
})
|
||||
|
||||
player.on(flvjs.Events.METADATA_ARRIVED, (metadata: any) => {
|
||||
player.on(flvjs.Events.METADATA_ARRIVED, (metadata: Record<string, unknown>) => {
|
||||
logger.log('flv.js: Metadata arrived', metadata)
|
||||
|
||||
// Trigger onLoadedMetadata callback
|
||||
@@ -146,11 +146,11 @@ export const setupRtmpInstance = async ({
|
||||
}
|
||||
})
|
||||
|
||||
player.on(flvjs.Events.STATISTICS_INFO, (stats: any) => {
|
||||
player.on(flvjs.Events.STATISTICS_INFO, (stats: Record<string, unknown>) => {
|
||||
// Statistics info for debugging/monitoring
|
||||
// Can be used to display stream quality, bitrate, etc.
|
||||
if (stats) {
|
||||
;(video as any).__rtmpStats = stats
|
||||
video.__rtmpStats = stats
|
||||
}
|
||||
})
|
||||
|
||||
@@ -187,8 +187,8 @@ export const setupRtmpInstance = async ({
|
||||
player.destroy()
|
||||
|
||||
// Clean up stored references
|
||||
delete (video as any).__rtmpInstance
|
||||
delete (video as any).__rtmpStats
|
||||
delete video.__rtmpInstance
|
||||
delete video.__rtmpStats
|
||||
} catch (cleanupError) {
|
||||
logger.error('Error during flv.js cleanup:', cleanupError)
|
||||
}
|
||||
@@ -212,11 +212,11 @@ export const setupRtmpInstance = async ({
|
||||
* @param video - The video element
|
||||
* @returns The flv.js player instance or null
|
||||
*/
|
||||
export const getRtmpInstance = (video: HTMLVideoElement | null): any | null => {
|
||||
export const getRtmpInstance = (video: HTMLVideoElement | null): FlvjsPlayer | PlayerInstance | null => {
|
||||
if (!video) {
|
||||
return null
|
||||
}
|
||||
return (video as any).__rtmpInstance || null
|
||||
return video.__rtmpInstance ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,11 +224,11 @@ export const getRtmpInstance = (video: HTMLVideoElement | null): any | null => {
|
||||
* @param video - The video element
|
||||
* @returns The statistics object or null
|
||||
*/
|
||||
export const getRtmpStats = (video: HTMLVideoElement | null): any | null => {
|
||||
export const getRtmpStats = (video: HTMLVideoElement | null): Record<string, unknown> | null => {
|
||||
if (!video) {
|
||||
return null
|
||||
}
|
||||
return (video as any).__rtmpStats || null
|
||||
return video.__rtmpStats ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,4 +239,3 @@ export const getRtmpStats = (video: HTMLVideoElement | null): any | null => {
|
||||
export const hasRtmpInstance = (video: HTMLVideoElement | null): boolean => {
|
||||
return getRtmpInstance(video) !== null
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user