-
Notifications
You must be signed in to change notification settings - Fork 64
feat: retry logic for 412 not playable errors #1106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
f63854d
832bc86
514b4d8
33c2e12
9d98d96
1ab17f1
3ae6cf7
cc7f2a1
1e92a7f
7afcef5
fb922e2
881f9f3
a14fda2
bf5fd44
3903933
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -289,7 +289,7 @@ const isAndroidLike = | |
| // NOTE: Exporting for testing | ||
| export const muxMediaState: WeakMap< | ||
| HTMLMediaElement, | ||
| Partial<MuxMediaProps> & { seekable?: TimeRanges; liveEdgeStartOffset?: number } | ||
| Partial<MuxMediaProps> & { seekable?: TimeRanges; liveEdgeStartOffset?: number; retryCount?: number } | ||
| > = new WeakMap(); | ||
|
|
||
| const MUX_VIDEO_DOMAIN = 'mux.com'; | ||
|
|
@@ -524,7 +524,7 @@ export const initialize = (props: Partial<MuxMediaPropsInternal>, mediaEl: HTMLM | |
|
|
||
| props.drmTypeCb = drmTypeCb; | ||
|
|
||
| muxMediaState.set(mediaEl as HTMLMediaElement, {}); | ||
| muxMediaState.set(mediaEl as HTMLMediaElement, { retryCount: 0 }); | ||
| const nextHlsInstance = setupHls(props, mediaEl); | ||
| const setPreload = setupPreload(props as Pick<MuxMediaProps, 'preload' | 'src'>, mediaEl, nextHlsInstance); | ||
| setupMux(props, mediaEl, nextHlsInstance); | ||
|
|
@@ -1251,8 +1251,71 @@ export const loadMedia = ( | |
| }); | ||
|
|
||
| hls.on(Hls.Events.ERROR, (_event, data) => { | ||
| saveAndDispatchError(mediaEl, getErrorFromHlsErrorData(data, props)); | ||
| const error = getErrorFromHlsErrorData(data, props); | ||
|
|
||
| if (error.muxCode === MuxErrorCode.NETWORK_NOT_READY) { | ||
| const maxRetries = 6; // 5 minutes and 5 seconds total (5s, 60s, 60s, 60s, 60s, 60s) | ||
| const state = muxMediaState.get(mediaEl) ?? {}; | ||
| const retryCount = state.retryCount ?? 0; | ||
|
|
||
| if (retryCount < maxRetries) { | ||
| // First retry is 5 seconds, subsequent retries are 60 seconds | ||
| const retryDelay = retryCount === 0 ? 5000 : 60000; | ||
|
|
||
| // New error with the retry delay | ||
| const retryDelayError = new MediaError( | ||
| `Retrying in ${retryDelay / 1000} seconds...`, | ||
| error.code, | ||
| error.fatal | ||
| ); | ||
| Object.assign(retryDelayError, error); | ||
| saveAndDispatchError(mediaEl, retryDelayError); | ||
|
|
||
| setTimeout(() => { | ||
| state.retryCount = retryCount + 1; | ||
| if (data.details === 'manifestLoadError' && data.url) { | ||
| hls.loadSource(data.url); | ||
| hls.attachMedia(mediaEl); | ||
luwes marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } else { | ||
| hls.recoverMediaError(); | ||
| } | ||
|
||
| }, retryDelay); | ||
| return; | ||
| } else { | ||
| state.retryCount = 0; | ||
| // New error with the retry link | ||
| const retryLinkError = new MediaError( | ||
| 'Try again later or <a href="#" onclick="window.location.reload(); return false;" style="color: #4a90e2;">click here to retry</a>', | ||
| error.code, | ||
| error.fatal | ||
| ); | ||
| Object.assign(retryLinkError, error); | ||
| saveAndDispatchError(mediaEl, retryLinkError); | ||
| return; | ||
| } | ||
| } | ||
| saveAndDispatchError(mediaEl, error); | ||
| }); | ||
|
|
||
| hls.on(Hls.Events.MANIFEST_LOADED, () => { | ||
| // Clear error state and UI | ||
| const state = muxMediaState.get(mediaEl); | ||
| if (state) { | ||
| state.error = null; | ||
| state.retryCount = 0; | ||
| } | ||
luwes marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| const clearError = new MediaError('', MediaError.MEDIA_ERR_CUSTOM, false); | ||
| clearError.muxCode = MuxErrorCode.NOT_AN_ERROR; | ||
Pintorado marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| mediaEl.dispatchEvent( | ||
| new CustomEvent('error', { | ||
| detail: clearError, | ||
| composed: true, | ||
| bubbles: true, | ||
| }) | ||
| ); | ||
luwes marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }); | ||
|
|
||
| mediaEl.addEventListener('error', handleInternalError); | ||
| addEventListenerWithTeardown(mediaEl, 'waiting', maybeDispatchEndedCallback); | ||
|
|
||
|
|
@@ -1386,6 +1449,7 @@ const getErrorFromHlsErrorData = ( | |
| props: Partial<Pick<MuxMediaPropsInternal, 'playbackId' | 'drmToken' | 'playbackToken' | 'tokens'>> | ||
| ) => { | ||
| console.error('getErrorFromHlsErrorData()', data); | ||
|
|
||
| const ErrorCodeMap: Partial<Record<ValueOf<typeof Hls.ErrorTypes>, 0 | 1 | 2 | 3 | 4 | 5>> = { | ||
| [Hls.ErrorTypes.NETWORK_ERROR]: MediaError.MEDIA_ERR_NETWORK, | ||
| [Hls.ErrorTypes.MEDIA_ERROR]: MediaError.MEDIA_ERR_DECODE, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.