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:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user