mirror of https://github.com/OpenVidu/openvidu.git
ov-components: Improves virtual background support handling
Adds a more robust mechanism to manage virtual background support, including: - Displaying a warning message when the browser does not support virtual backgrounds. - Disabling background selection buttons when virtual backgrounds are not supported. - Optimizing background processor initialization and attachment for different browsers (handling lazy loading for Firefox). - Centralizing the check for virtual background support in the OpenViduService. This change ensures a better user experience by clearly indicating when virtual backgrounds are unavailable and preventing users from attempting to use an unsupported feature.master
parent
6cfa44c4f1
commit
7208cb3a65
|
|
@ -13,6 +13,13 @@
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (!isVirtualBackgroundSupported()) {
|
||||||
|
<div class="not-supported-message">
|
||||||
|
<p class="warning-title">{{ 'PANEL.BACKGROUND.NOT_SUPPORTED' | translate }}</p>
|
||||||
|
<p class="warning-description">{{ 'PANEL.BACKGROUND.NOT_SUPPORTED_DESCRIPTION' | translate }}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="effects-container" fxFlex="100%" fxLayoutAlign="space-evenly none">
|
<div class="effects-container" fxFlex="100%" fxLayoutAlign="space-evenly none">
|
||||||
<div>
|
<div>
|
||||||
<h4 class="background-title">{{ 'PANEL.BACKGROUND.BLURRED_SECTION' | translate }}</h4>
|
<h4 class="background-title">{{ 'PANEL.BACKGROUND.BLURRED_SECTION' | translate }}</h4>
|
||||||
|
|
@ -24,6 +31,7 @@
|
||||||
[class.active-effect-btn]="backgroundSelectedId === effect.id"
|
[class.active-effect-btn]="backgroundSelectedId === effect.id"
|
||||||
(click)="applyBackground(effect)"
|
(click)="applyBackground(effect)"
|
||||||
[attr.id]="effect.id + '-btn'"
|
[attr.id]="effect.id + '-btn'"
|
||||||
|
[disabled]="!isVirtualBackgroundSupported()"
|
||||||
[matTooltip]="
|
[matTooltip]="
|
||||||
effect.type === effectType.NONE
|
effect.type === effectType.NONE
|
||||||
? ('PANEL.BACKGROUND.NO_EFFECTS' | translate)
|
? ('PANEL.BACKGROUND.NO_EFFECTS' | translate)
|
||||||
|
|
@ -44,7 +52,8 @@
|
||||||
class="effect-button"
|
class="effect-button"
|
||||||
[id]="'effect-' + effect.id"
|
[id]="'effect-' + effect.id"
|
||||||
[class.active-effect-btn]="backgroundSelectedId === effect.id"
|
[class.active-effect-btn]="backgroundSelectedId === effect.id"
|
||||||
(click)="applyBackground(effect)"
|
[class.disabled]="!isVirtualBackgroundSupported()"
|
||||||
|
(click)="isVirtualBackgroundSupported() && applyBackground(effect)"
|
||||||
>
|
>
|
||||||
<img [src]="effect.thumbnail" />
|
<img [src]="effect.thumbnail" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,26 @@
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.not-supported-message {
|
||||||
|
padding: 15px;
|
||||||
|
margin: 10px;
|
||||||
|
background-color: var(--ov-warn-color, #ff9800);
|
||||||
|
border-radius: var(--ov-surface-radius);
|
||||||
|
color: var(--ov-text-surface-color);
|
||||||
|
|
||||||
|
.warning-title {
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-description {
|
||||||
|
font-size: 0.9em;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.effects-container {
|
.effects-container {
|
||||||
display: block !important;
|
display: block !important;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
@ -33,6 +53,11 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.effect-button.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.active-effect-btn {
|
.active-effect-btn {
|
||||||
border: 2px solid var(--ov-accent-action-color);
|
border: 2px solid var(--ov-accent-action-color);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, EventEmitter, Input, OnInit, Output, Signal } from '@angular/core';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { BackgroundEffect, EffectType } from '../../../models/background-effect.model';
|
import { BackgroundEffect, EffectType } from '../../../models/background-effect.model';
|
||||||
import { PanelType } from '../../../models/panel.model';
|
import { PanelType } from '../../../models/panel.model';
|
||||||
|
|
@ -23,7 +23,7 @@ export class BackgroundEffectsPanelComponent implements OnInit {
|
||||||
effectType = EffectType;
|
effectType = EffectType;
|
||||||
backgroundImages: BackgroundEffect[] = [];
|
backgroundImages: BackgroundEffect[] = [];
|
||||||
noEffectAndBlurredBackground: BackgroundEffect[] = [];
|
noEffectAndBlurredBackground: BackgroundEffect[] = [];
|
||||||
private backgrounds: BackgroundEffect[];
|
private backgrounds: BackgroundEffect[] = [];
|
||||||
private backgroundSubs: Subscription;
|
private backgroundSubs: Subscription;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -38,6 +38,14 @@ export class BackgroundEffectsPanelComponent implements OnInit {
|
||||||
private cd: ChangeDetectorRef
|
private cd: ChangeDetectorRef
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computed signal that reactively tracks if virtual background is supported.
|
||||||
|
* Updates automatically when browser support changes.
|
||||||
|
*/
|
||||||
|
readonly isVirtualBackgroundSupported: Signal<boolean> = computed(() =>
|
||||||
|
this.backgroundService.isVirtualBackgroundSupported()
|
||||||
|
);
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.subscribeToBackgroundSelected();
|
this.subscribeToBackgroundSelected();
|
||||||
this.backgrounds = this.backgroundService.getBackgrounds();
|
this.backgrounds = this.backgroundService.getBackgrounds();
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,9 @@
|
||||||
"BLURRED_SECTION": "没有效果和模糊的背景",
|
"BLURRED_SECTION": "没有效果和模糊的背景",
|
||||||
"NO_EFFECTS": "没有背景效果",
|
"NO_EFFECTS": "没有背景效果",
|
||||||
"BLURRED_EFFECT": "模糊的背景",
|
"BLURRED_EFFECT": "模糊的背景",
|
||||||
"IMAGES_SECTION": "背景图像"
|
"IMAGES_SECTION": "背景图像",
|
||||||
|
"NOT_SUPPORTED": "此浏览器不支持虚拟背景",
|
||||||
|
"NOT_SUPPORTED_DESCRIPTION": "您的浏览器不支持背景效果。此功能需要 GPU 加速,可能已被禁用或不可用。"
|
||||||
},
|
},
|
||||||
"RECORDING": {
|
"RECORDING": {
|
||||||
"TITLE": "录音",
|
"TITLE": "录音",
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,9 @@
|
||||||
"BLURRED_SECTION": "Keine Effekte und unscharfer Hintergrund",
|
"BLURRED_SECTION": "Keine Effekte und unscharfer Hintergrund",
|
||||||
"NO_EFFECTS": "Kein Hintergrundeffekt",
|
"NO_EFFECTS": "Kein Hintergrundeffekt",
|
||||||
"BLURRED_EFFECT": "Unscharfer Hintergrund",
|
"BLURRED_EFFECT": "Unscharfer Hintergrund",
|
||||||
"IMAGES_SECTION": "Hintergrundbilder"
|
"IMAGES_SECTION": "Hintergrundbilder",
|
||||||
|
"NOT_SUPPORTED": "Virtuelle Hintergründe werden in diesem Browser nicht unterstützt",
|
||||||
|
"NOT_SUPPORTED_DESCRIPTION": "Ihr Browser unterstützt keine Hintergrundeffekte. Diese Funktion erfordert GPU-Beschleunigung, die möglicherweise deaktiviert oder nicht verfügbar ist."
|
||||||
},
|
},
|
||||||
"RECORDING": {
|
"RECORDING": {
|
||||||
"TITLE": "Aufnahme",
|
"TITLE": "Aufnahme",
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,9 @@
|
||||||
"BLURRED_SECTION": "No effects and blurred background",
|
"BLURRED_SECTION": "No effects and blurred background",
|
||||||
"NO_EFFECTS": "No background effect",
|
"NO_EFFECTS": "No background effect",
|
||||||
"BLURRED_EFFECT": "Blurred background",
|
"BLURRED_EFFECT": "Blurred background",
|
||||||
"IMAGES_SECTION": "Background images"
|
"IMAGES_SECTION": "Background images",
|
||||||
|
"NOT_SUPPORTED": "Virtual backgrounds are not supported in this browser",
|
||||||
|
"NOT_SUPPORTED_DESCRIPTION": "Your browser does not support background effects. This feature requires GPU acceleration which may be disabled or unavailable."
|
||||||
},
|
},
|
||||||
"RECORDING": {
|
"RECORDING": {
|
||||||
"TITLE": "Recording",
|
"TITLE": "Recording",
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,9 @@
|
||||||
"BLURRED_SECTION": "Sin efectos y fondo desenfocado",
|
"BLURRED_SECTION": "Sin efectos y fondo desenfocado",
|
||||||
"NO_EFFECTS": "Sin efecto",
|
"NO_EFFECTS": "Sin efecto",
|
||||||
"BLURRED_EFFECT": "Fondo desenfocado",
|
"BLURRED_EFFECT": "Fondo desenfocado",
|
||||||
"IMAGES_SECTION": "Imágenes de fondo"
|
"IMAGES_SECTION": "Imágenes de fondo",
|
||||||
|
"NOT_SUPPORTED": "Los efectos de fondo virtuales no son compatibles con este navegador",
|
||||||
|
"NOT_SUPPORTED_DESCRIPTION": "Tu navegador no admite efectos de fondo. Esta función requiere aceleración GPU que puede estar deshabilitada o no disponible."
|
||||||
},
|
},
|
||||||
"RECORDING": {
|
"RECORDING": {
|
||||||
"TITLE": "Grabación",
|
"TITLE": "Grabación",
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,9 @@
|
||||||
"BLURRED_SECTION": "Aucun effet et arrière-plan flou",
|
"BLURRED_SECTION": "Aucun effet et arrière-plan flou",
|
||||||
"NO_EFFECTS": "Aucun effet de fond",
|
"NO_EFFECTS": "Aucun effet de fond",
|
||||||
"BLURRED_EFFECT": "Arrière-plan flou",
|
"BLURRED_EFFECT": "Arrière-plan flou",
|
||||||
"IMAGES_SECTION": "Images d'arrière-plan"
|
"IMAGES_SECTION": "Images d'arrière-plan",
|
||||||
|
"NOT_SUPPORTED": "Les arrière-plans virtuels ne sont pas pris en charge dans ce navigateur",
|
||||||
|
"NOT_SUPPORTED_DESCRIPTION": "Votre navigateur ne prend pas en charge les effets d'arrière-plan. Cette fonctionnalité nécessite l'accélération GPU qui peut être désactivée ou non disponible."
|
||||||
},
|
},
|
||||||
"RECORDING": {
|
"RECORDING": {
|
||||||
"TITLE": "Enregistrement",
|
"TITLE": "Enregistrement",
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,9 @@
|
||||||
"BLURRED_SECTION": "कोई प्रभाव नहीं है और पृष्ठभूमि धुंधली है",
|
"BLURRED_SECTION": "कोई प्रभाव नहीं है और पृष्ठभूमि धुंधली है",
|
||||||
"NO_EFFECTS": "कोई पृष्ठभूमि प्रभाव नहीं है",
|
"NO_EFFECTS": "कोई पृष्ठभूमि प्रभाव नहीं है",
|
||||||
"BLURRED_EFFECT": "पृष्ठभूमि धुंधली है",
|
"BLURRED_EFFECT": "पृष्ठभूमि धुंधली है",
|
||||||
"IMAGES_SECTION": "पृष्ठभूमि छवियां"
|
"IMAGES_SECTION": "पृष्ठभूमि छवियां",
|
||||||
|
"NOT_SUPPORTED": "इस ब्राउज़र में वर्चुअल बैकग्राउंड समर्थित नहीं है",
|
||||||
|
"NOT_SUPPORTED_DESCRIPTION": "आपका ब्राउज़र बैकग्राउंड इफेक्ट्स का समर्थन नहीं करता है। इस सुविधा के लिए GPU त्वरण की आवश्यकता है जो अक्षम या अनुपलब्ध हो सकता है।"
|
||||||
},
|
},
|
||||||
"RECORDING": {
|
"RECORDING": {
|
||||||
"TITLE": "रिकॉर्डिंग",
|
"TITLE": "रिकॉर्डिंग",
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,9 @@
|
||||||
"BLURRED_SECTION": "Nessun effetto e sfondo sfocato",
|
"BLURRED_SECTION": "Nessun effetto e sfondo sfocato",
|
||||||
"NO_EFFECTS": "Nessun effetto di sfondo",
|
"NO_EFFECTS": "Nessun effetto di sfondo",
|
||||||
"BLURRED_EFFECT": "Sfondo sfocato",
|
"BLURRED_EFFECT": "Sfondo sfocato",
|
||||||
"IMAGES_SECTION": "Immagini di sfondo"
|
"IMAGES_SECTION": "Immagini di sfondo",
|
||||||
|
"NOT_SUPPORTED": "Gli sfondi virtuali non sono supportati in questo browser",
|
||||||
|
"NOT_SUPPORTED_DESCRIPTION": "Il tuo browser non supporta gli effetti di sfondo. Questa funzione richiede l'accelerazione GPU che potrebbe essere disabilitata o non disponibile."
|
||||||
},
|
},
|
||||||
"RECORDING": {
|
"RECORDING": {
|
||||||
"TITLE": "Registrazione",
|
"TITLE": "Registrazione",
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,9 @@
|
||||||
"BLURRED_SECTION": "エフェクトなし、ぼやけた背景",
|
"BLURRED_SECTION": "エフェクトなし、ぼやけた背景",
|
||||||
"NO_EFFECTS": "背景エフェクトなし",
|
"NO_EFFECTS": "背景エフェクトなし",
|
||||||
"BLURRED_EFFECT": "ぼやけた背景",
|
"BLURRED_EFFECT": "ぼやけた背景",
|
||||||
"IMAGES_SECTION": "背景画像"
|
"IMAGES_SECTION": "背景画像",
|
||||||
|
"NOT_SUPPORTED": "このブラウザでは仮想背景はサポートされていません",
|
||||||
|
"NOT_SUPPORTED_DESCRIPTION": "お使いのブラウザは背景効果をサポートしていません。この機能にはGPUアクセラレーションが必要ですが、無効化されているか利用できません。"
|
||||||
},
|
},
|
||||||
"RECORDING": {
|
"RECORDING": {
|
||||||
"TITLE": "レコーディング",
|
"TITLE": "レコーディング",
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,9 @@
|
||||||
"BLURRED_SECTION": "Geen effecten en onscherpe achtergrond",
|
"BLURRED_SECTION": "Geen effecten en onscherpe achtergrond",
|
||||||
"NO_EFFECTS": "Geen achtergrondeffect",
|
"NO_EFFECTS": "Geen achtergrondeffect",
|
||||||
"BLURRED_EFFECT": "Onscherpe achtergrond",
|
"BLURRED_EFFECT": "Onscherpe achtergrond",
|
||||||
"IMAGES_SECTION": "Achtergrondafbeeldingen"
|
"IMAGES_SECTION": "Achtergrondafbeeldingen",
|
||||||
|
"NOT_SUPPORTED": "Virtuele achtergronden worden niet ondersteund in deze browser",
|
||||||
|
"NOT_SUPPORTED_DESCRIPTION": "Uw browser ondersteunt geen achtergrondeffecten. Deze functie vereist GPU-versnelling die mogelijk uitgeschakeld of niet beschikbaar is."
|
||||||
},
|
},
|
||||||
"RECORDING": {
|
"RECORDING": {
|
||||||
"TITLE": "Opname",
|
"TITLE": "Opname",
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,9 @@
|
||||||
"BLURRED_SECTION": "Sem efeitos e fundo desfocado",
|
"BLURRED_SECTION": "Sem efeitos e fundo desfocado",
|
||||||
"NO_EFFECTS": "Sem efeito de fundo",
|
"NO_EFFECTS": "Sem efeito de fundo",
|
||||||
"BLURRED_EFFECT": "Fundo desfocado",
|
"BLURRED_EFFECT": "Fundo desfocado",
|
||||||
"IMAGES_SECTION": "Imagens de fundo"
|
"IMAGES_SECTION": "Imagens de fundo",
|
||||||
|
"NOT_SUPPORTED": "Fundos virtuais não são suportados neste navegador",
|
||||||
|
"NOT_SUPPORTED_DESCRIPTION": "Seu navegador não suporta efeitos de fundo. Este recurso requer aceleração de GPU que pode estar desabilitada ou não disponível."
|
||||||
},
|
},
|
||||||
"RECORDING": {
|
"RECORDING": {
|
||||||
"TITLE": "Gravação",
|
"TITLE": "Gravação",
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable, signal, Signal } from '@angular/core';
|
||||||
import { BackgroundProcessor, /*BackgroundProcessorWrapper,*/ SwitchBackgroundProcessorOptions } from '@livekit/track-processors';
|
import {
|
||||||
|
BackgroundProcessor,
|
||||||
|
supportsBackgroundProcessors,
|
||||||
|
supportsModernBackgroundProcessors,
|
||||||
|
/*BackgroundProcessorWrapper,*/ SwitchBackgroundProcessorOptions
|
||||||
|
} from '@livekit/track-processors';
|
||||||
import {
|
import {
|
||||||
AudioCaptureOptions,
|
AudioCaptureOptions,
|
||||||
ConnectionState,
|
ConnectionState,
|
||||||
|
createLocalTracks,
|
||||||
CreateLocalTracksOptions,
|
CreateLocalTracksOptions,
|
||||||
E2EEOptions,
|
E2EEOptions,
|
||||||
ExternalE2EEKeyProvider,
|
ExternalE2EEKeyProvider,
|
||||||
|
|
@ -13,14 +19,12 @@ import {
|
||||||
RoomOptions,
|
RoomOptions,
|
||||||
Track,
|
Track,
|
||||||
VideoCaptureOptions,
|
VideoCaptureOptions,
|
||||||
VideoPresets,
|
VideoPresets
|
||||||
createLocalTracks
|
|
||||||
} from 'livekit-client';
|
} from 'livekit-client';
|
||||||
import { ILogger } from '../../models/logger.model';
|
import { ILogger } from '../../models/logger.model';
|
||||||
import { OpenViduComponentsConfigService } from '../config/directive-config.service';
|
import { OpenViduComponentsConfigService } from '../config/directive-config.service';
|
||||||
import { DeviceService } from '../device/device.service';
|
import { DeviceService } from '../device/device.service';
|
||||||
import { LoggerService } from '../logger/logger.service';
|
import { LoggerService } from '../logger/logger.service';
|
||||||
import { PlatformService } from '../platform/platform.service';
|
|
||||||
import { StorageService } from '../storage/storage.service';
|
import { StorageService } from '../storage/storage.service';
|
||||||
|
|
||||||
// TODO: Remove this once livekit-client exports it
|
// TODO: Remove this once livekit-client exports it
|
||||||
|
|
@ -58,8 +62,20 @@ export class OpenViduService {
|
||||||
/**
|
/**
|
||||||
* Background processor for video tracks. Initialized in disabled mode.
|
* Background processor for video tracks. Initialized in disabled mode.
|
||||||
* This processor is shared between prejoin and in-room states.
|
* This processor is shared between prejoin and in-room states.
|
||||||
|
* Only initialized if browser supports background processing (GPU available).
|
||||||
*/
|
*/
|
||||||
private backgroundProcessor: BackgroundProcessorWrapper;
|
private backgroundProcessor?: BackgroundProcessorWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal to track if background processor is supported (requires GPU).
|
||||||
|
* Set to false if browser doesn't support it or processor initialization fails.
|
||||||
|
*/
|
||||||
|
private _isBackgroundProcessorSupported = signal(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public readonly signal for background processor support status.
|
||||||
|
*/
|
||||||
|
readonly isBackgroundProcessorSupported: Signal<boolean> = this._isBackgroundProcessorSupported.asReadonly();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
|
@ -69,11 +85,33 @@ export class OpenViduService {
|
||||||
private deviceService: DeviceService,
|
private deviceService: DeviceService,
|
||||||
private storageService: StorageService,
|
private storageService: StorageService,
|
||||||
private configService: OpenViduComponentsConfigService,
|
private configService: OpenViduComponentsConfigService,
|
||||||
private platformService: PlatformService
|
|
||||||
) {
|
) {
|
||||||
this.log = this.loggerSrv.get('OpenViduService');
|
this.log = this.loggerSrv.get('OpenViduService');
|
||||||
// this.isSttReadyObs = this._isSttReady.asObservable();
|
// this.isSttReadyObs = this._isSttReady.asObservable();
|
||||||
this.backgroundProcessor = BackgroundProcessor({ mode: 'disabled' });
|
|
||||||
|
// Check if browser supports background processors
|
||||||
|
if (!supportsBackgroundProcessors()) {
|
||||||
|
this.log.w('Background processors not supported in this browser (GPU may be disabled)');
|
||||||
|
this._isBackgroundProcessorSupported.set(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only initialize processor immediately for browsers supporting modern processors
|
||||||
|
// Browsers without modern support (e.g., Firefox) will initialize on-demand
|
||||||
|
if (supportsModernBackgroundProcessors()) {
|
||||||
|
try {
|
||||||
|
this.backgroundProcessor = BackgroundProcessor({ mode: 'disabled' });
|
||||||
|
this._isBackgroundProcessorSupported.set(true);
|
||||||
|
this.log.d('Background processor initialized at startup (modern processors supported)');
|
||||||
|
} catch (error: any) {
|
||||||
|
this.log.w('Failed to initialize background processor:', error?.message || error);
|
||||||
|
this._isBackgroundProcessorSupported.set(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Mark as supported but don't initialize yet - will be created on-demand
|
||||||
|
this._isBackgroundProcessorSupported.set(true);
|
||||||
|
this.log.d('Background processors supported but not modern - will initialize on-demand');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -275,6 +313,8 @@ export class OpenViduService {
|
||||||
return this.localTracks;
|
return this.localTracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switches the background mode on the local video track.
|
* Switches the background mode on the local video track.
|
||||||
* Works both in prejoin and in-room states.
|
* Works both in prejoin and in-room states.
|
||||||
|
|
@ -285,20 +325,36 @@ export class OpenViduService {
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
async switchBackgroundMode(options: SwitchBackgroundProcessorOptions): Promise<void> {
|
async switchBackgroundMode(options: SwitchBackgroundProcessorOptions): Promise<void> {
|
||||||
// For Firefox, attach processor only when an effect is activated
|
if (!this.isBackgroundProcessorSupported()) {
|
||||||
if (this.platformService.isFirefox()) {
|
this.log.w('Background processor not supported (GPU disabled). Virtual background is disabled.');
|
||||||
await this.handleFirefoxProcessor(options.mode);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.backgroundProcessor.switchTo(options);
|
try {
|
||||||
this.log.d('Background mode switched:', options);
|
// For browsers without modern processor support: attach processor on-demand when effect is activated
|
||||||
|
if (!supportsModernBackgroundProcessors()) {
|
||||||
|
await this.handleLazyProcessorAttachment(options.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If processor exists, switch mode (either pre-initialized or just created on-demand)
|
||||||
|
if (this.backgroundProcessor) {
|
||||||
|
await this.backgroundProcessor.switchTo(options);
|
||||||
|
this.log.d('Background mode switched:', options);
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
this.log.e('Failed to switch background mode:', error?.message || error);
|
||||||
|
this._isBackgroundProcessorSupported.set(false);
|
||||||
|
// Don't throw - gracefully disable virtual background instead of crashing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the background processor handling for Firefox browser.
|
* Handles lazy processor attachment for browsers without modern processor support.
|
||||||
|
* Creates and attaches processor on-demand when effect is activated.
|
||||||
|
* This is used for browsers like Firefox that don't support modern background processors.
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
private async handleFirefoxProcessor(mode: SwitchBackgroundProcessorOptions['mode']): Promise<void> {
|
private async handleLazyProcessorAttachment(mode: SwitchBackgroundProcessorOptions['mode']): Promise<void> {
|
||||||
const videoTrack = await this.getVideoTrack();
|
const videoTrack = await this.getVideoTrack();
|
||||||
if (!videoTrack) return;
|
if (!videoTrack) return;
|
||||||
|
|
||||||
|
|
@ -306,13 +362,25 @@ export class OpenViduService {
|
||||||
const isDisabled = mode === 'disabled';
|
const isDisabled = mode === 'disabled';
|
||||||
|
|
||||||
if (!isDisabled && !hasProcessor) {
|
if (!isDisabled && !hasProcessor) {
|
||||||
this.log.d('Firefox: Attaching processor on effect activation');
|
try {
|
||||||
await videoTrack.setProcessor(this.backgroundProcessor);
|
// Create processor on-demand if not already created
|
||||||
|
if (!this.backgroundProcessor) {
|
||||||
|
this.log.d('Creating background processor on-demand');
|
||||||
|
this.backgroundProcessor = BackgroundProcessor({ mode: 'disabled' });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.log.d('Attaching processor on effect activation (lazy loading)');
|
||||||
|
await videoTrack.setProcessor(this.backgroundProcessor);
|
||||||
|
} catch (error: any) {
|
||||||
|
this.log.w('Failed to attach background processor (GPU may be disabled):', error?.message || error);
|
||||||
|
this._isBackgroundProcessorSupported.set(false);
|
||||||
|
// Continue without crashing - virtual background will be disabled
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDisabled && hasProcessor) {
|
if (isDisabled && hasProcessor) {
|
||||||
this.log.d('Firefox: Stopping processor on effect deactivation');
|
this.log.d('Stopping processor on effect deactivation');
|
||||||
await videoTrack.stopProcessor();
|
await videoTrack.stopProcessor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -390,15 +458,24 @@ export class OpenViduService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply background processor to video track (initialized in disabled mode)
|
// Apply background processor to video track (initialized in disabled mode)
|
||||||
// For Firefox: skip processor attachment to avoid performance issues (applied only when effect is activated)
|
// For browsers with modern processor support: attach processor immediately for smooth transitions
|
||||||
// For other browsers: attach processor for smooth transitions
|
// For browsers without modern support: skip attachment, will be applied on-demand when effect is activated
|
||||||
const videoTrack = newLocalTracks.find((t) => t.kind === Track.Kind.Video) as LocalVideoTrack | undefined;
|
const videoTrack = newLocalTracks.find((t) => t.kind === Track.Kind.Video) as LocalVideoTrack | undefined;
|
||||||
debugger;
|
if (videoTrack && supportsModernBackgroundProcessors()) {
|
||||||
if (videoTrack && !this.platformService.isFirefox()) {
|
if (this.isBackgroundProcessorSupported() && this.backgroundProcessor) {
|
||||||
await videoTrack.setProcessor(this.backgroundProcessor);
|
try {
|
||||||
this.log.d('Background processor applied to newly created video track');
|
await videoTrack.setProcessor(this.backgroundProcessor);
|
||||||
} else if (videoTrack && this.platformService.isFirefox()) {
|
this.log.d('Background processor applied to newly created video track');
|
||||||
this.log.d('Firefox detected: skipping processor attachment for better performance');
|
} catch (error: any) {
|
||||||
|
this.log.w('Failed to apply background processor (GPU may be disabled):', error?.message || error);
|
||||||
|
this._isBackgroundProcessorSupported.set(false);
|
||||||
|
// Continue without crashing - virtual background will be disabled
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.log.d('Background processor not supported (GPU disabled or not available)');
|
||||||
|
}
|
||||||
|
} else if (videoTrack && !supportsModernBackgroundProcessors()) {
|
||||||
|
this.log.d('Modern background processors not supported - will apply processor on-demand when effect is activated');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mute tracks if devices are disabled
|
// Mute tracks if devices are disabled
|
||||||
|
|
|
||||||
|
|
@ -406,10 +406,9 @@ export class ParticipantService {
|
||||||
// Update Signal - create new reference to trigger reactivity
|
// Update Signal - create new reference to trigger reactivity
|
||||||
// The Observable will automatically emit via toObservable()
|
// The Observable will automatically emit via toObservable()
|
||||||
if (this.localParticipant) {
|
if (this.localParticipant) {
|
||||||
const updatedParticipant = Object.assign(
|
const updatedParticipant = Object.assign(Object.create(Object.getPrototypeOf(this.localParticipant)), {
|
||||||
Object.create(Object.getPrototypeOf(this.localParticipant)),
|
...this.localParticipant
|
||||||
{ ...this.localParticipant }
|
});
|
||||||
);
|
|
||||||
this.localParticipantWritableSignal.set(updatedParticipant);
|
this.localParticipantWritableSignal.set(updatedParticipant);
|
||||||
} else {
|
} else {
|
||||||
this.localParticipantWritableSignal.set(undefined);
|
this.localParticipantWritableSignal.set(undefined);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { computed, Injectable, Signal } from '@angular/core';
|
||||||
import { SwitchBackgroundProcessorOptions } from '@livekit/track-processors';
|
import { SwitchBackgroundProcessorOptions } from '@livekit/track-processors';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { BackgroundEffect, EffectType } from '../../models/background-effect.model';
|
import { BackgroundEffect, EffectType } from '../../models/background-effect.model';
|
||||||
|
|
@ -59,6 +59,14 @@ export class VirtualBackgroundService {
|
||||||
return this.backgrounds;
|
return this.backgrounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computed signal that checks if virtual background is supported (requires GPU).
|
||||||
|
* Reactively tracks the support status from OpenViduService.
|
||||||
|
*/
|
||||||
|
readonly isVirtualBackgroundSupported: Signal<boolean> = computed(() =>
|
||||||
|
this.openviduService.isBackgroundProcessorSupported()
|
||||||
|
);
|
||||||
|
|
||||||
isBackgroundApplied(): boolean {
|
isBackgroundApplied(): boolean {
|
||||||
const bgSelected = this.backgroundIdSelected.getValue();
|
const bgSelected = this.backgroundIdSelected.getValue();
|
||||||
return !!bgSelected && bgSelected !== 'no_effect';
|
return !!bgSelected && bgSelected !== 'no_effect';
|
||||||
|
|
@ -80,6 +88,12 @@ export class VirtualBackgroundService {
|
||||||
* The background processor is centralized in OpenViduService for consistency.
|
* The background processor is centralized in OpenViduService for consistency.
|
||||||
*/
|
*/
|
||||||
async applyBackground(bg: BackgroundEffect) {
|
async applyBackground(bg: BackgroundEffect) {
|
||||||
|
// Check if virtual background is supported before proceeding
|
||||||
|
if (!this.isVirtualBackgroundSupported()) {
|
||||||
|
this.log.w('Virtual background not supported (GPU disabled). Skipping background application.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If the background is already applied, do nothing
|
// If the background is already applied, do nothing
|
||||||
if (this.backgroundIsAlreadyApplied(bg.id)) return;
|
if (this.backgroundIsAlreadyApplied(bg.id)) return;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue