Support custom JSMPEG options.
This commit is contained in:
@@ -116,6 +116,7 @@ All variables listed are under a `live:` section.
|
|||||||
| `webrtc.entity` | | The RTSP entity to pass WebRTC. Specify this OR `webrtc.url` (above). |
|
| `webrtc.entity` | | The RTSP entity to pass WebRTC. Specify this OR `webrtc.url` (above). |
|
||||||
| `webrtc.*`| | Any other options in a `webrtc:` YAML dictionary are silently passed through to WebRTC. See [WebRTC Configuration](https://github.com/AlexxIT/WebRTC#configuration) for full details this external card provides.|
|
| `webrtc.*`| | Any other options in a `webrtc:` YAML dictionary are silently passed through to WebRTC. See [WebRTC Configuration](https://github.com/AlexxIT/WebRTC#configuration) for full details this external card provides.|
|
||||||
| `actions` | | Actions to use for the `live` view. See [actions](#actions) below.|
|
| `actions` | | Actions to use for the `live` view. See [actions](#actions) below.|
|
||||||
|
| `jsmpeg.options.{audio, video, pauseWhenHidden, disableGl, disableWebAssembly, preserveDrawingBuffer, progressive, throttled, chunkSize, maxAudioLag, videoBufferSize, audioBufferSize}` | | **Advanced users only**: Control the underlying [JSMPEG library options](https://github.com/phoboslab/jsmpeg#usage). This is not necessary for the vast majority of users: only set these flags if you know what you're doing, as you may entirely break video rendering in the card.|
|
||||||
|
|
||||||
#### Available Live Providers
|
#### Available Live Providers
|
||||||
|
|
||||||
|
|||||||
+34
-18
@@ -1,5 +1,11 @@
|
|||||||
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
||||||
import type { ExtendedHomeAssistant, FrigateCardConfig, MediaShowInfo, WebRTCConfig } from '../types.js';
|
import type {
|
||||||
|
ExtendedHomeAssistant,
|
||||||
|
FrigateCardConfig,
|
||||||
|
JSMPEGConfig,
|
||||||
|
MediaShowInfo,
|
||||||
|
WebRTCConfig,
|
||||||
|
} from '../types.js';
|
||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from 'custom-card-helpers';
|
||||||
import { customElement, property } from 'lit/decorators.js';
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
import { until } from 'lit/directives/until.js';
|
import { until } from 'lit/directives/until.js';
|
||||||
@@ -93,6 +99,7 @@ export class FrigateCardLive extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.cameraName=${this.frigateCameraName}
|
.cameraName=${this.frigateCameraName}
|
||||||
.clientId=${this.config.frigate.client_id}
|
.clientId=${this.config.frigate.client_id}
|
||||||
|
.jsmpegConfig=${this.config.live.jsmpeg}
|
||||||
@frigate-card:media-show=${this._mediaShowHandler}
|
@frigate-card:media-show=${this._mediaShowHandler}
|
||||||
>
|
>
|
||||||
</frigate-card-live-jsmpeg>`}`;
|
</frigate-card-live-jsmpeg>`}`;
|
||||||
@@ -242,6 +249,9 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected clientId?: string;
|
protected clientId?: string;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
protected jsmpegConfig?: JSMPEGConfig;
|
||||||
|
|
||||||
protected hass?: HomeAssistant & ExtendedHomeAssistant;
|
protected hass?: HomeAssistant & ExtendedHomeAssistant;
|
||||||
protected _jsmpegCanvasElement?: HTMLCanvasElement;
|
protected _jsmpegCanvasElement?: HTMLCanvasElement;
|
||||||
protected _jsmpegVideoPlayer?: JSMpeg.VideoElement;
|
protected _jsmpegVideoPlayer?: JSMpeg.VideoElement;
|
||||||
@@ -262,7 +272,8 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
|||||||
response = await homeAssistantSignPath(
|
response = await homeAssistantSignPath(
|
||||||
this.hass,
|
this.hass,
|
||||||
`/api/frigate/${this.clientId}` + `/jsmpeg/${this.cameraName}`,
|
`/api/frigate/${this.clientId}` + `/jsmpeg/${this.cameraName}`,
|
||||||
URL_SIGN_EXPIRY_SECONDS);
|
URL_SIGN_EXPIRY_SECONDS,
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(err);
|
console.warn(err);
|
||||||
return null;
|
return null;
|
||||||
@@ -279,11 +290,30 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
|||||||
*/
|
*/
|
||||||
protected _createJSMPEGPlayer(): JSMpeg.VideoElement {
|
protected _createJSMPEGPlayer(): JSMpeg.VideoElement {
|
||||||
let videoDecoded = false;
|
let videoDecoded = false;
|
||||||
|
|
||||||
|
const jsmpegOptions = {
|
||||||
|
pauseWhenHidden: false,
|
||||||
|
protocols: [],
|
||||||
|
audio: false,
|
||||||
|
videoBufferSize: 1024 * 1024 * 4,
|
||||||
|
onVideoDecode: () => {
|
||||||
|
// This is the only callback that is called after the dimensions
|
||||||
|
// are available. It's called on every frame decode, so just
|
||||||
|
// ignore any subsequent calls.
|
||||||
|
if (!videoDecoded && this._jsmpegCanvasElement) {
|
||||||
|
videoDecoded = true;
|
||||||
|
dispatchMediaShowEvent(this, this._jsmpegCanvasElement);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Override with user-specified options.
|
||||||
|
Object.assign(jsmpegOptions, this.jsmpegConfig?.options);
|
||||||
|
|
||||||
return new JSMpeg.VideoElement(
|
return new JSMpeg.VideoElement(
|
||||||
this,
|
this,
|
||||||
this._jsmpegURL,
|
this._jsmpegURL,
|
||||||
{
|
{
|
||||||
preserveDrawingBuffer: true,
|
|
||||||
canvas: this._jsmpegCanvasElement,
|
canvas: this._jsmpegCanvasElement,
|
||||||
hooks: {
|
hooks: {
|
||||||
play: () => {
|
play: () => {
|
||||||
@@ -294,21 +324,7 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
jsmpegOptions,
|
||||||
pauseWhenHidden: false,
|
|
||||||
protocols: [],
|
|
||||||
audio: false,
|
|
||||||
videoBufferSize: 1024 * 1024 * 4,
|
|
||||||
onVideoDecode: () => {
|
|
||||||
// This is the only callback that is called after the dimensions
|
|
||||||
// are available. It's called on every frame decode, so just
|
|
||||||
// ignore any subsequent calls.
|
|
||||||
if (!videoDecoded && this._jsmpegCanvasElement) {
|
|
||||||
videoDecoded = true;
|
|
||||||
dispatchMediaShowEvent(this, this._jsmpegCanvasElement);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+31
-5
@@ -127,11 +127,13 @@ const actionSchema = z.union([
|
|||||||
]);
|
]);
|
||||||
export type ActionType = z.infer<typeof actionSchema>;
|
export type ActionType = z.infer<typeof actionSchema>;
|
||||||
|
|
||||||
const actionBaseSchema = z.object({
|
const actionBaseSchema = z
|
||||||
tap_action: actionSchema.optional(),
|
.object({
|
||||||
hold_action: actionSchema.optional(),
|
tap_action: actionSchema.optional(),
|
||||||
double_tap_action: actionSchema.optional(),
|
hold_action: actionSchema.optional(),
|
||||||
}).passthrough();
|
double_tap_action: actionSchema.optional(),
|
||||||
|
})
|
||||||
|
.passthrough();
|
||||||
export type Actions = z.infer<typeof actionBaseSchema>;
|
export type Actions = z.infer<typeof actionBaseSchema>;
|
||||||
|
|
||||||
const actionsSchema = z.object({
|
const actionsSchema = z.object({
|
||||||
@@ -369,11 +371,35 @@ const webrtcConfigSchema = z
|
|||||||
.optional();
|
.optional();
|
||||||
export type WebRTCConfig = z.infer<typeof webrtcConfigSchema>;
|
export type WebRTCConfig = z.infer<typeof webrtcConfigSchema>;
|
||||||
|
|
||||||
|
const jsmpegConfigSchema = z
|
||||||
|
.object({
|
||||||
|
options: z
|
||||||
|
.object({
|
||||||
|
// https://github.com/phoboslab/jsmpeg#usage
|
||||||
|
audio: z.boolean().optional(),
|
||||||
|
video: z.boolean().optional(),
|
||||||
|
pauseWhenHidden: z.boolean().optional(),
|
||||||
|
disableGl: z.boolean().optional(),
|
||||||
|
disableWebAssembly: z.boolean().optional(),
|
||||||
|
preserveDrawingBuffer: z.boolean().optional(),
|
||||||
|
progressive: z.boolean().optional(),
|
||||||
|
throttled: z.boolean().optional(),
|
||||||
|
chunkSize: z.number().optional(),
|
||||||
|
maxAudioLag: z.number().optional(),
|
||||||
|
videoBufferSize: z.number().optional(),
|
||||||
|
audioBufferSize: z.number().optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
})
|
||||||
|
.optional();
|
||||||
|
export type JSMPEGConfig = z.infer<typeof jsmpegConfigSchema>;
|
||||||
|
|
||||||
const liveConfigSchema = z
|
const liveConfigSchema = z
|
||||||
.object({
|
.object({
|
||||||
provider: z.enum(LIVE_PROVIDERS).default(liveConfigDefault.provider),
|
provider: z.enum(LIVE_PROVIDERS).default(liveConfigDefault.provider),
|
||||||
preload: z.boolean().default(liveConfigDefault.preload),
|
preload: z.boolean().default(liveConfigDefault.preload),
|
||||||
webrtc: webrtcConfigSchema,
|
webrtc: webrtcConfigSchema,
|
||||||
|
jsmpeg: jsmpegConfigSchema,
|
||||||
})
|
})
|
||||||
.merge(actionsSchema)
|
.merge(actionsSchema)
|
||||||
.default(liveConfigDefault);
|
.default(liveConfigDefault);
|
||||||
|
|||||||
Reference in New Issue
Block a user