Refactor JSMPEG signing/creation logic.
This commit is contained in:
+6
-4
@@ -733,9 +733,10 @@ export class FrigateCard extends LitElement {
|
|||||||
>
|
>
|
||||||
</frigate-card-viewer>`
|
</frigate-card-viewer>`
|
||||||
: ``}
|
: ``}
|
||||||
<!-- Note the subtle difference in condition below vs the other views in order
|
${
|
||||||
to always render the live view for live_preload mode -->
|
// Note the subtle difference in condition below vs the other views in order
|
||||||
${(!this._message && this._view.is('live')) || this.config.live_preload
|
// to always render the live view for live_preload mode.
|
||||||
|
(!this._message && this._view.is('live')) || this.config.live_preload
|
||||||
? html`
|
? html`
|
||||||
<frigate-card-live
|
<frigate-card-live
|
||||||
.hass=${this._hass}
|
.hass=${this._hass}
|
||||||
@@ -749,7 +750,8 @@ export class FrigateCard extends LitElement {
|
|||||||
>
|
>
|
||||||
</frigate-card-live>
|
</frigate-card-live>
|
||||||
`
|
`
|
||||||
: ``}
|
: ``
|
||||||
|
}
|
||||||
${this.config.elements
|
${this.config.elements
|
||||||
? html`
|
? html`
|
||||||
<frigate-card-elements
|
<frigate-card-elements
|
||||||
|
|||||||
+64
-37
@@ -21,6 +21,12 @@ import JSMpeg from '@cycjimmy/jsmpeg-player';
|
|||||||
|
|
||||||
import liveStyle from '../scss/live.scss';
|
import liveStyle from '../scss/live.scss';
|
||||||
|
|
||||||
|
// Number of seconds a signed URL is valid for.
|
||||||
|
const URL_SIGN_EXPIRY_SECONDS = 24 * 60 * 60;
|
||||||
|
|
||||||
|
// Number of seconds before the expiry to trigger a refresh.
|
||||||
|
const URL_SIGN_REFRESH_THRESHOLD_SECONDS = 1 * 60 * 60;
|
||||||
|
|
||||||
@customElement('frigate-card-live')
|
@customElement('frigate-card-live')
|
||||||
export class FrigateCardLive extends LitElement {
|
export class FrigateCardLive extends LitElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
@@ -169,8 +175,9 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
|||||||
|
|
||||||
protected hass!: HomeAssistant & ExtendedHomeAssistant;
|
protected hass!: HomeAssistant & ExtendedHomeAssistant;
|
||||||
protected _jsmpegCanvasElement?: HTMLCanvasElement;
|
protected _jsmpegCanvasElement?: HTMLCanvasElement;
|
||||||
protected _jsmpegVideoPlayer?;
|
protected _jsmpegVideoPlayer?: JSMpeg.VideoElement;
|
||||||
protected _jsmpegURL?: string | null;
|
protected _jsmpegURL?: string | null;
|
||||||
|
protected _refreshPlayerTimerID?: number;
|
||||||
|
|
||||||
protected async _getURL(): Promise<string | null> {
|
protected async _getURL(): Promise<string | null> {
|
||||||
if (!this.hass) {
|
if (!this.hass) {
|
||||||
@@ -180,7 +187,7 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
|||||||
const request = {
|
const request = {
|
||||||
type: 'auth/sign_path',
|
type: 'auth/sign_path',
|
||||||
path: `/api/frigate/${this.clientId}` + `/jsmpeg/${this.cameraName}`,
|
path: `/api/frigate/${this.clientId}` + `/jsmpeg/${this.cameraName}`,
|
||||||
expires: 60 * 15,
|
expires: URL_SIGN_EXPIRY_SECONDS,
|
||||||
};
|
};
|
||||||
// Sign the path so it includes an authSig parameter.
|
// Sign the path so it includes an authSig parameter.
|
||||||
let response;
|
let response;
|
||||||
@@ -194,22 +201,17 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
|||||||
return url.replace(/^http/i, 'ws');
|
return url.replace(/^http/i, 'ws');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async _createJSMPEGPlayer(): Promise<void> {
|
protected _createJSMPEGPlayer(): JSMpeg.VideoElement {
|
||||||
let videoDecoded = false;
|
let videoDecoded = false;
|
||||||
return new Promise<void>((resolve) => {
|
return new JSMpeg.VideoElement(
|
||||||
this._jsmpegVideoPlayer = new JSMpeg.VideoElement(
|
|
||||||
this,
|
this,
|
||||||
this._jsmpegURL,
|
this._jsmpegURL,
|
||||||
{
|
{
|
||||||
preserveDrawingBuffer: true,
|
preserveDrawingBuffer: true,
|
||||||
canvas: this._jsmpegCanvasElement,
|
canvas: this._jsmpegCanvasElement,
|
||||||
hooks: {
|
hooks: {
|
||||||
// Don't resolve the promise until it's playing to minimize the
|
|
||||||
// amount of time the canvas is empty (and show the spinner
|
|
||||||
// instead).
|
|
||||||
play: () => {
|
play: () => {
|
||||||
dispatchPlayEvent(this);
|
dispatchPlayEvent(this);
|
||||||
resolve();
|
|
||||||
},
|
},
|
||||||
pause: () => {
|
pause: () => {
|
||||||
dispatchPauseEvent(this);
|
dispatchPauseEvent(this);
|
||||||
@@ -220,6 +222,7 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
|||||||
protocols: [],
|
protocols: [],
|
||||||
audio: false,
|
audio: false,
|
||||||
videoBufferSize: 1024 * 1024 * 4,
|
videoBufferSize: 1024 * 1024 * 4,
|
||||||
|
reconnectInterval: 10,
|
||||||
onVideoDecode: () => {
|
onVideoDecode: () => {
|
||||||
// This is the only callback that is called after the dimensions
|
// This is the only callback that is called after the dimensions
|
||||||
// are available. It's called on every frame decode, so just
|
// are available. It's called on every frame decode, so just
|
||||||
@@ -231,41 +234,65 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
protected _resetPlayer(): void {
|
||||||
|
if (this._refreshPlayerTimerID) {
|
||||||
|
window.clearTimeout(this._refreshPlayerTimerID);
|
||||||
|
this._refreshPlayerTimerID = undefined;
|
||||||
|
}
|
||||||
|
if (this._jsmpegVideoPlayer) {
|
||||||
|
this._jsmpegVideoPlayer.destroy();
|
||||||
|
this._jsmpegVideoPlayer = undefined;
|
||||||
|
}
|
||||||
|
if (this._jsmpegCanvasElement) {
|
||||||
|
this._jsmpegCanvasElement.remove();
|
||||||
|
this._jsmpegCanvasElement = undefined;
|
||||||
|
}
|
||||||
|
this._jsmpegURL = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback(): void {
|
||||||
|
super.connectedCallback();
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback(): void {
|
||||||
|
this._resetPlayer();
|
||||||
|
super.disconnectedCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async _refreshPlayer(): Promise<void> {
|
||||||
|
this._resetPlayer();
|
||||||
|
|
||||||
|
this._jsmpegCanvasElement = document.createElement('canvas');
|
||||||
|
this._jsmpegCanvasElement.className = 'media';
|
||||||
|
|
||||||
|
this._jsmpegURL = await this._getURL();
|
||||||
|
if (this._jsmpegURL) {
|
||||||
|
this._refreshPlayerTimerID = window.setTimeout(() => {
|
||||||
|
this._refreshPlayer();
|
||||||
|
}, (URL_SIGN_EXPIRY_SECONDS - URL_SIGN_REFRESH_THRESHOLD_SECONDS) * 1000);
|
||||||
|
|
||||||
|
this._jsmpegVideoPlayer = this._createJSMPEGPlayer();
|
||||||
|
}
|
||||||
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
if (!this._jsmpegCanvasElement) {
|
if (
|
||||||
this._jsmpegCanvasElement = document.createElement('canvas');
|
this._jsmpegURL === undefined ||
|
||||||
this._jsmpegCanvasElement.className = 'media';
|
!this._jsmpegVideoPlayer ||
|
||||||
}
|
!this._jsmpegCanvasElement
|
||||||
|
) {
|
||||||
if (this._jsmpegURL === undefined) {
|
return html`${until(this._refreshPlayer(), renderProgressIndicator())}`;
|
||||||
return html`${until(
|
|
||||||
(async () => {
|
|
||||||
this._jsmpegURL = await this._getURL();
|
|
||||||
this.requestUpdate();
|
|
||||||
})(),
|
|
||||||
renderProgressIndicator(),
|
|
||||||
)}`;
|
|
||||||
}
|
}
|
||||||
if (!this._jsmpegURL) {
|
if (!this._jsmpegURL) {
|
||||||
return dispatchErrorMessageEvent(
|
return dispatchErrorMessageEvent(this, localize('error.jsmpeg_no_sign'));
|
||||||
this,
|
|
||||||
'Could not retrieve or sign JSMPEG websocket path',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
if (!this._jsmpegVideoPlayer || !this._jsmpegCanvasElement) {
|
||||||
if (!this._jsmpegVideoPlayer) {
|
return dispatchErrorMessageEvent(this, localize('error.jsmpeg_no_player'));
|
||||||
return html`${until(
|
|
||||||
(async () => {
|
|
||||||
await this._createJSMPEGPlayer();
|
|
||||||
this.requestUpdate();
|
|
||||||
})(),
|
|
||||||
renderProgressIndicator(),
|
|
||||||
)}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`${this._jsmpegCanvasElement}`;
|
return html`${this._jsmpegCanvasElement}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,8 @@
|
|||||||
"missing_webrtc": "WebRTC component not found",
|
"missing_webrtc": "WebRTC component not found",
|
||||||
"no_frigate_camera_name": "Cannot autodetect Frigate camera name, you need to either set camera_entity and / or frigate_camera_name",
|
"no_frigate_camera_name": "Cannot autodetect Frigate camera name, you need to either set camera_entity and / or frigate_camera_name",
|
||||||
"could_not_render_elements": "Could not render picture elements",
|
"could_not_render_elements": "Could not render picture elements",
|
||||||
"invalid_elements_config": "Invalid picture elements configuration"
|
"invalid_elements_config": "Invalid picture elements configuration",
|
||||||
|
"jsmpeg_no_sign": "Could not retrieve or sign JSMPEG websocket path",
|
||||||
|
"jsmpeg_no_player": "Could not start JSMPEG player"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user