feat: Add experimental rewrite of go2rtc live provider (MSE/WebRTC/MP4/MJPEG) (#2580)

- Closes #2556
- Closes #2450

**Key intended features:**
 - go2rtc compatible
- 100% test coverage to significantly improve ability to test, maintain
and work around browser weirdnesses (e.g. Safari).
 - Written from the ground up in the style of the rest of the project.

**To use:**
 - Change `live_provider` from `go2rtc` to `go2rtc-experimental`.
This commit is contained in:
Dermot Duffy
2026-07-14 14:22:41 -07:00
committed by GitHub
parent 5662e48c22
commit c02c692f68
125 changed files with 9608 additions and 807 deletions
+4 -1
View File
@@ -6,6 +6,9 @@ export class PauseAction extends AdvancedCameraCardAction<GeneralActionConfig> {
public async execute(api: CardActionsAPI): Promise<void> {
await super.execute(api);
await api.getMediaLoadedInfoManager().get()?.mediaPlayerController?.pause();
await api
.getMediaLoadedInfoManager()
.get()
?.mediaPlayerController?.playback?.pause();
}
}
+1 -1
View File
@@ -6,6 +6,6 @@ export class PlayAction extends AdvancedCameraCardAction<GeneralActionConfig> {
public async execute(api: CardActionsAPI): Promise<void> {
await super.execute(api);
await api.getMediaLoadedInfoManager().get()?.mediaPlayerController?.play();
await api.getMediaLoadedInfoManager().get()?.mediaPlayerController?.playback?.play();
}
}
+5 -4
View File
@@ -1,6 +1,7 @@
import { createNotificationFromText } from '../../components-lib/notification/factory';
import type { ConditionStateChange } from '../../condition-trigger/conditions/types';
import { localize } from '../../localize/localize';
import { Generation } from '../../utils/concurrency/generation';
import { Timer } from '../../utils/timer';
import { getStreamCameraID } from '../../view/substream';
import type { View } from '../../view/view';
@@ -22,7 +23,7 @@ export class CallManager {
// resumed tail leaks audio onto the shared lock from an instance the user
// can no longer see or control, and may install state into a fresh
// lifecycle from a request that belongs to the previous one.
private _initEpoch = 0;
private _initGeneration = new Generation();
constructor(api: CardCallAPI) {
this._api = api;
@@ -103,7 +104,7 @@ export class CallManager {
return false;
}
const initEpoch = this._initEpoch;
const initGeneration = this._initGeneration.current();
const microphoneConnected = await this._connectMicrophone();
// If the init/uninit lifecycle advanced while the microphone connect was
// in flight, this request belongs to a previous lifecycle -- the view we
@@ -111,7 +112,7 @@ export class CallManager {
// clean teardown path for state we'd install here. Bail before touching
// `_call`, the ringtone lock, or surfacing a notification onto a torn-down
// NotificationManager.
if (initEpoch !== this._initEpoch) {
if (!this._initGeneration.isCurrent(initGeneration)) {
return false;
}
if (!microphoneConnected) {
@@ -241,7 +242,7 @@ export class CallManager {
//
// Safe to re-initialize afterwards via `initialize()`.
public uninitialize(): void {
this._initEpoch++;
this._initGeneration.invalidate();
this._ringtone.stop();
this._unansweredTimer.stop();
if (this._call) {
@@ -21,7 +21,10 @@ export type MediaUnavailableIssueReason =
| 'entity_unavailable'
| 'not_loading'
| 'playback_error'
| 'stalled';
| 'server_error'
| 'stalled'
| 'two_way_audio_error'
| 'unsupported';
declare module 'issue' {
interface IssueTriggerContext {
@@ -50,10 +53,22 @@ export const MEDIA_UNAVAILABLE_REASONS: Record<
localizationKey: 'issues.media_unavailable.reasons.playback_error',
icon: 'mdi:alert-circle',
},
server_error: {
localizationKey: 'issues.media_unavailable.reasons.server_error',
icon: 'mdi:server-network-off',
},
stalled: {
localizationKey: 'issues.media_unavailable.reasons.stalled',
icon: 'mdi:motion-pause',
},
two_way_audio_error: {
localizationKey: 'issues.media_unavailable.reasons.two_way_audio_error',
icon: 'mdi:microphone-off',
},
unsupported: {
localizationKey: 'issues.media_unavailable.reasons.unsupported',
icon: 'mdi:video-off-outline',
},
};
export class MediaUnavailableIssue implements Issue {