Improve robustness of hls media player (#11672)

This commit is contained in:
Allen Porter 2022-02-12 20:21:26 -08:00 committed by GitHub
parent d86a18b80b
commit a8c1fdd21e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -43,6 +43,8 @@ class HaHLSPlayer extends LitElement {
@state() private _error?: string; @state() private _error?: string;
@state() private _errorIsFatal = false;
private _hlsPolyfillInstance?: HlsLite; private _hlsPolyfillInstance?: HlsLite;
private _exoPlayer = false; private _exoPlayer = false;
@ -53,6 +55,7 @@ class HaHLSPlayer extends LitElement {
super.connectedCallback(); super.connectedCallback();
HaHLSPlayer.streamCount += 1; HaHLSPlayer.streamCount += 1;
if (this.hasUpdated) { if (this.hasUpdated) {
this._resetError();
this._startHls(); this._startHls();
} }
} }
@ -64,16 +67,23 @@ class HaHLSPlayer extends LitElement {
} }
protected render(): TemplateResult { protected render(): TemplateResult {
if (this._error) {
return html`<ha-alert alert-type="error">${this._error}</ha-alert>`;
}
return html` return html`
<video ${this._error
?autoplay=${this.autoPlay} ? html`<ha-alert
.muted=${this.muted} alert-type="error"
?playsinline=${this.playsInline} class=${this._errorIsFatal ? "fatal" : "retry"}
?controls=${this.controls} >
></video> ${this._error}
</ha-alert>`
: ""}
${!this._errorIsFatal
? html`<video
?autoplay=${this.autoPlay}
.muted=${this.muted}
?playsinline=${this.playsInline}
?controls=${this.controls}
></video>`
: ""}
`; `;
} }
@ -87,12 +97,11 @@ class HaHLSPlayer extends LitElement {
} }
this._cleanUp(); this._cleanUp();
this._resetError();
this._startHls(); this._startHls();
} }
private async _startHls(): Promise<void> { private async _startHls(): Promise<void> {
this._error = undefined;
const masterPlaylistPromise = fetch(this.url); const masterPlaylistPromise = fetch(this.url);
const Hls: typeof HlsType = (await import("hls.js/dist/hls.light.min")) const Hls: typeof HlsType = (await import("hls.js/dist/hls.light.min"))
@ -110,8 +119,8 @@ class HaHLSPlayer extends LitElement {
} }
if (!hlsSupported) { if (!hlsSupported) {
this._error = this.hass.localize( this._setFatalError(
"ui.components.media-browser.video_not_supported" this.hass.localize("ui.components.media-browser.video_not_supported")
); );
return; return;
} }
@ -219,9 +228,16 @@ class HaHLSPlayer extends LitElement {
this._hlsPolyfillInstance = hls; this._hlsPolyfillInstance = hls;
hls.attachMedia(videoEl); hls.attachMedia(videoEl);
hls.on(Hls.Events.MEDIA_ATTACHED, () => { hls.on(Hls.Events.MEDIA_ATTACHED, () => {
this._resetError();
hls.loadSource(url); hls.loadSource(url);
}); });
hls.on(Hls.Events.ERROR, (_, data: any) => { hls.on(Hls.Events.FRAG_LOADED, (_event, _data: any) => {
this._resetError();
});
hls.on(Hls.Events.ERROR, (_event, data: any) => {
// Some errors are recovered automatically by the hls player itself, and the others handled
// in this function require special actions to recover. Errors retried in this function
// are done with backoff to not cause unecessary failures.
if (!data.fatal) { if (!data.fatal) {
return; return;
} }
@ -241,22 +257,22 @@ class HaHLSPlayer extends LitElement {
error += " (" + data.response.code + ")"; error += " (" + data.response.code + ")";
} }
} }
this._error = error; this._setRetryableError(error);
return; break;
} }
case Hls.ErrorDetails.MANIFEST_LOAD_TIMEOUT: case Hls.ErrorDetails.MANIFEST_LOAD_TIMEOUT:
this._error = "Timeout while starting stream"; this._setRetryableError("Timeout while starting stream");
return; break;
default: default:
this._error = "Unknown stream network error (" + data.details + ")"; this._setRetryableError("Stream network error");
return; break;
} }
this._error = "Error with media stream contents (" + data.details + ")"; hls.startLoad();
} else if (data.type === Hls.ErrorTypes.MEDIA_ERROR) { } else if (data.type === Hls.ErrorTypes.MEDIA_ERROR) {
this._error = "Error with media stream contents (" + data.details + ")"; this._setRetryableError("Error with media stream contents");
hls.recoverMediaError();
} else { } else {
this._error = this._setFatalError("Error playing stream");
"Unknown error with stream (" + data.type + ", " + data.details + ")";
} }
}); });
} }
@ -284,6 +300,21 @@ class HaHLSPlayer extends LitElement {
} }
} }
private _resetError() {
this._error = undefined;
this._errorIsFatal = false;
}
private _setFatalError(errorMessage: string) {
this._error = errorMessage;
this._errorIsFatal = true;
}
private _setRetryableError(errorMessage: string) {
this._error = errorMessage;
this._errorIsFatal = false;
}
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return css` return css`
:host, :host,
@ -296,10 +327,14 @@ class HaHLSPlayer extends LitElement {
max-height: var(--video-max-height, calc(100vh - 97px)); max-height: var(--video-max-height, calc(100vh - 97px));
} }
ha-alert { .fatal {
display: block; display: block;
padding: 100px 16px; padding: 100px 16px;
} }
.retry {
display: block;
}
`; `;
} }
} }