fix: Correctly display MJPEG streams (#1734)
* fix: Correctly display MJPEG streams * Request update after load event arrives
This commit is contained in:
@@ -9,11 +9,14 @@
|
|||||||
// available as compilation time.
|
// available as compilation time.
|
||||||
// ====================================================================
|
// ====================================================================
|
||||||
|
|
||||||
import { css, CSSResultGroup, html, unsafeCSS, TemplateResult } from 'lit';
|
import { css, CSSResultGroup, html, nothing, PropertyValues, unsafeCSS } from 'lit';
|
||||||
import { customElement } from 'lit/decorators.js';
|
import { customElement } from 'lit/decorators.js';
|
||||||
import { query } from 'lit/decorators/query.js';
|
import { query } from 'lit/decorators/query.js';
|
||||||
import { FrigateCardMediaPlayer } from '../types.js';
|
import { FrigateCardMediaPlayer, MediaLoadedInfo } from '../types.js';
|
||||||
import { dispatchMediaLoadedEvent } from '../utils/media-info.js';
|
import {
|
||||||
|
createMediaLoadedInfo,
|
||||||
|
dispatchExistingMediaLoadedInfoAsEvent,
|
||||||
|
} from '../utils/media-info.js';
|
||||||
import './ha-hls-player';
|
import './ha-hls-player';
|
||||||
import './ha-web-rtc-player';
|
import './ha-web-rtc-player';
|
||||||
import liveHAComponentsStyle from '../scss/live-ha-components.scss';
|
import liveHAComponentsStyle from '../scss/live-ha-components.scss';
|
||||||
@@ -30,6 +33,8 @@ customElements.whenDefined('ha-camera-stream').then(() => {
|
|||||||
|
|
||||||
const STREAM_TYPE_HLS = 'hls';
|
const STREAM_TYPE_HLS = 'hls';
|
||||||
const STREAM_TYPE_WEB_RTC = 'web_rtc';
|
const STREAM_TYPE_WEB_RTC = 'web_rtc';
|
||||||
|
const STREAM_TYPE_MJPEG = 'mjpeg';
|
||||||
|
type StreamType = STREAM_TYPE_HLS | STREAM_TYPE_WEB_RTC | STREAM_TYPE_MJPEG;
|
||||||
|
|
||||||
@customElement('frigate-card-ha-camera-stream')
|
@customElement('frigate-card-ha-camera-stream')
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
@@ -42,6 +47,9 @@ customElements.whenDefined('ha-camera-stream').then(() => {
|
|||||||
@query('#player')
|
@query('#player')
|
||||||
protected _player: FrigateCardMediaPlayer;
|
protected _player: FrigateCardMediaPlayer;
|
||||||
|
|
||||||
|
protected _mediaLoadedInfoPerStream: Record<StreamType, MediaLoadedInfo> = {};
|
||||||
|
protected _mediaLoadedInfoDispatched: MediaLoadedInfo | null = null;
|
||||||
|
|
||||||
// ========================================================================================
|
// ========================================================================================
|
||||||
// Minor modifications from:
|
// Minor modifications from:
|
||||||
// - https://github.com/home-assistant/frontend/blob/dev/src/components/ha-camera-stream.ts
|
// - https://github.com/home-assistant/frontend/blob/dev/src/components/ha-camera-stream.ts
|
||||||
@@ -85,45 +93,60 @@ customElements.whenDefined('ha-camera-stream').then(() => {
|
|||||||
return this._player ? await this._player.getScreenshotURL() : null;
|
return this._player ? await this._player.getScreenshotURL() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected _storeMediaLoadedInfoHandler(
|
||||||
* Master render method.
|
stream: StreamType,
|
||||||
* @returns A rendered template.
|
ev: CustomEvent<MediaLoadedInfo>,
|
||||||
*/
|
) {
|
||||||
protected render(): TemplateResult {
|
this._storeMediaLoadedInfo(stream, ev.detail);
|
||||||
if (!this.stateObj) {
|
ev.stopPropagation();
|
||||||
return html``;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (this._shouldRenderMJPEG) {
|
protected _storeMediaLoadedInfo(
|
||||||
|
stream: StreamType,
|
||||||
|
mediaLoadedInfo: MediaLoadedInfo,
|
||||||
|
) {
|
||||||
|
this._mediaLoadedInfoPerStream[stream] = mediaLoadedInfo;
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _renderStream(stream: Stream) {
|
||||||
|
if (!this.stateObj) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
if (stream.type === STREAM_TYPE_MJPEG) {
|
||||||
return html`
|
return html`
|
||||||
<img
|
<img
|
||||||
@load=${(ev: Event) => {
|
@load=${(ev) =>
|
||||||
dispatchMediaLoadedEvent(this, ev, {
|
this._storeMediaLoadedInfo(
|
||||||
player: this,
|
STREAM_TYPE_MJPEG,
|
||||||
technology: ['mjpeg'],
|
createMediaLoadedInfo(ev, { player: this, technology: ['mjpeg'] }),
|
||||||
});
|
)}
|
||||||
}}
|
|
||||||
.src=${typeof this._connected == 'undefined' || this._connected
|
.src=${typeof this._connected == 'undefined' || this._connected
|
||||||
? computeMJPEGStreamUrl(this.stateObj)
|
? computeMJPEGStreamUrl(this.stateObj)
|
||||||
: ''}
|
: this._posterUrl || ''}
|
||||||
/>
|
/>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
if (this.stateObj.attributes.frontend_stream_type === STREAM_TYPE_HLS) {
|
|
||||||
return this._url
|
if (stream.type === STREAM_TYPE_HLS) {
|
||||||
? html` <frigate-card-ha-hls-player
|
return html` <frigate-card-ha-hls-player
|
||||||
id="player"
|
id="player"
|
||||||
?autoplay=${false}
|
?autoplay=${false}
|
||||||
playsinline
|
playsinline
|
||||||
.allowExoPlayer=${this.allowExoPlayer}
|
.allowExoPlayer=${this.allowExoPlayer}
|
||||||
.muted=${this.muted}
|
.muted=${this.muted}
|
||||||
.controls=${this.controls}
|
.controls=${this.controls}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.url=${this._url}
|
.entityid=${this.stateObj.entity_id}
|
||||||
></frigate-card-ha-hls-player>`
|
.posterUrl=${this._posterUrl}
|
||||||
: html``;
|
@frigate-card:media:loaded=${(ev) =>
|
||||||
|
this._storeMediaLoadedInfoHandler(STREAM_TYPE_HLS, ev)}
|
||||||
|
@streams=${this._handleHlsStreams}
|
||||||
|
class=${stream.visible ? '' : 'hidden'}
|
||||||
|
></frigate-card-ha-hls-player>`;
|
||||||
}
|
}
|
||||||
if (this.stateObj.attributes.frontend_stream_type === STREAM_TYPE_WEB_RTC) {
|
|
||||||
|
if (stream.type === STREAM_TYPE_WEB_RTC) {
|
||||||
return html`<frigate-card-ha-web-rtc-player
|
return html`<frigate-card-ha-web-rtc-player
|
||||||
id="player"
|
id="player"
|
||||||
?autoplay=${false}
|
?autoplay=${false}
|
||||||
@@ -132,8 +155,34 @@ customElements.whenDefined('ha-camera-stream').then(() => {
|
|||||||
.controls=${this.controls}
|
.controls=${this.controls}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.entityid=${this.stateObj.entity_id}
|
.entityid=${this.stateObj.entity_id}
|
||||||
|
.posterUrl=${this._posterUrl}
|
||||||
|
@frigate-card:media:loaded=${(ev) =>
|
||||||
|
this._storeMediaLoadedInfoHandler(STREAM_TYPE_WEB_RTC, ev)}
|
||||||
|
@streams=${this._handleWebRtcStreams}
|
||||||
|
class=${stream.visible ? '' : 'hidden'}
|
||||||
></frigate-card-ha-web-rtc-player>`;
|
></frigate-card-ha-web-rtc-player>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public updated(changedProps: PropertyValues): void {
|
||||||
|
super.updated(changedProps);
|
||||||
|
|
||||||
|
const streams = this._streams(
|
||||||
|
this._capabilities?.frontend_stream_types,
|
||||||
|
this._hlsStreams,
|
||||||
|
this._webRtcStreams,
|
||||||
|
);
|
||||||
|
|
||||||
|
const visibleStream = streams.find((stream) => stream.visible) ?? null;
|
||||||
|
if (visibleStream) {
|
||||||
|
const mediaLoadedInfo = this._mediaLoadedInfoPerStream[visibleStream.type];
|
||||||
|
if (mediaLoadedInfo && mediaLoadedInfo !== this._mediaLoadedInfoDispatched) {
|
||||||
|
this._mediaLoadedInfoDispatched = mediaLoadedInfo;
|
||||||
|
dispatchExistingMediaLoadedInfoAsEvent(this, mediaLoadedInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
|
|||||||
Reference in New Issue
Block a user