mirror of https://github.com/OpenVidu/openvidu.git
ov-components: optimize background processor application for video tracks
parent
435db94254
commit
87ec92ecc8
|
|
@ -15,6 +15,7 @@ import {
|
|||
VideoPresets,
|
||||
createLocalTracks
|
||||
} from 'livekit-client';
|
||||
import { BackgroundProcessor, BackgroundProcessorWrapper, SwitchBackgroundProcessorOptions } from '@livekit/track-processors';
|
||||
import { ILogger } from '../../models/logger.model';
|
||||
import { OpenViduComponentsConfigService } from '../config/directive-config.service';
|
||||
import { DeviceService } from '../device/device.service';
|
||||
|
|
@ -51,6 +52,12 @@ export class OpenViduService {
|
|||
private livekitUrl = '';
|
||||
private log: ILogger;
|
||||
|
||||
/**
|
||||
* Background processor for video tracks. Initialized in disabled mode.
|
||||
* This processor is shared between prejoin and in-room states.
|
||||
*/
|
||||
private backgroundProcessor: BackgroundProcessorWrapper;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
|
@ -62,6 +69,7 @@ export class OpenViduService {
|
|||
) {
|
||||
this.log = this.loggerSrv.get('OpenViduService');
|
||||
// this.isSttReadyObs = this._isSttReady.asObservable();
|
||||
this.backgroundProcessor = BackgroundProcessor({ mode: 'disabled' });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -265,6 +273,18 @@ export class OpenViduService {
|
|||
return this.localTracks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the background mode on the local video track.
|
||||
* Works both in prejoin and in-room states.
|
||||
* @param options - The switch options (mode, blurRadius, imagePath)
|
||||
* @returns Promise<void>
|
||||
* @internal
|
||||
*/
|
||||
async switchBackgroundMode(options: SwitchBackgroundProcessorOptions): Promise<void> {
|
||||
await this.backgroundProcessor.switchTo(options);
|
||||
this.log.d('Background mode switched:', options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
**/
|
||||
|
|
@ -337,6 +357,14 @@ export class OpenViduService {
|
|||
newLocalTracks = await createLocalTracks(options);
|
||||
}
|
||||
|
||||
// Apply background processor to video track (initialized in disabled mode)
|
||||
// This ensures the processor is attached before publishing for smooth transitions
|
||||
const videoTrack = newLocalTracks.find((t) => t.kind === Track.Kind.Video) as LocalVideoTrack | undefined;
|
||||
if (videoTrack) {
|
||||
await videoTrack.setProcessor(this.backgroundProcessor);
|
||||
this.log.d('Background processor applied to newly created video track');
|
||||
}
|
||||
|
||||
// Mute tracks if devices are disabled
|
||||
if (!this.deviceService.isCameraEnabled()) {
|
||||
newLocalTracks.find((t) => t.kind === Track.Kind.Video)?.mute();
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { BackgroundOptions, BackgroundProcessor, ProcessorWrapper } from '@livekit/track-processors';
|
||||
import { LocalVideoTrack, Track } from 'livekit-client';
|
||||
import { SwitchBackgroundProcessorOptions } from '@livekit/track-processors';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { BackgroundEffect, EffectType } from '../../models/background-effect.model';
|
||||
import { ILogger } from '../../models/logger.model';
|
||||
import { LoggerService } from '../logger/logger.service';
|
||||
import { OpenViduService } from '../openvidu/openvidu.service';
|
||||
import { ParticipantService } from '../participant/participant.service';
|
||||
import { StorageService } from '../storage/storage.service';
|
||||
|
||||
/**
|
||||
|
|
@ -47,17 +45,14 @@ export class VirtualBackgroundService {
|
|||
private HARD_BLUR_INTENSITY = 60;
|
||||
|
||||
private log: ILogger;
|
||||
private processor: ProcessorWrapper<BackgroundOptions>;
|
||||
|
||||
constructor(
|
||||
private participantService: ParticipantService,
|
||||
private openviduService: OpenViduService,
|
||||
private storageService: StorageService,
|
||||
private loggerSrv: LoggerService
|
||||
) {
|
||||
this.log = this.loggerSrv.get('VirtualBackgroundService');
|
||||
this.backgroundIdSelected$ = this.backgroundIdSelected.asObservable();
|
||||
this.processor = BackgroundProcessor({ mode: 'disabled' });
|
||||
}
|
||||
|
||||
getBackgrounds(): BackgroundEffect[] {
|
||||
|
|
@ -74,126 +69,59 @@ export class VirtualBackgroundService {
|
|||
if (!!bgId) {
|
||||
const background = this.backgrounds.find((bg) => bg.id === bgId);
|
||||
if (background) {
|
||||
this.applyBackground(background);
|
||||
await this.applyBackground(background);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a background effect to the local video track.
|
||||
* Works both in prejoin (using OpenViduService's processor) and in-room states.
|
||||
* The background processor is centralized in OpenViduService for consistency.
|
||||
*/
|
||||
async applyBackground(bg: BackgroundEffect) {
|
||||
// If the background is already applied, do nothing
|
||||
if (this.backgroundIsAlreadyApplied(bg.id)) return;
|
||||
|
||||
const cameraTrack = this.getCameraTrack();
|
||||
if (!cameraTrack) {
|
||||
this.log.e('No camera track found. Cannot apply background.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// If no effect is selected, remove the background
|
||||
if (bg.type === EffectType.NONE) {
|
||||
await this.removeBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
const currentProcessor = cameraTrack.getProcessor() as ProcessorWrapper<BackgroundOptions>;
|
||||
|
||||
if (currentProcessor) {
|
||||
await this.replaceBackground(currentProcessor, bg);
|
||||
} else {
|
||||
await this.applyProcessorToCameraTrack(cameraTrack, this.processor);
|
||||
await this.replaceBackground(this.processor, bg);
|
||||
}
|
||||
const options = this.getBackgroundOptions(bg);
|
||||
await this.openviduService.switchBackgroundMode(options);
|
||||
|
||||
this.storageService.setBackground(bg.id);
|
||||
this.backgroundIdSelected.next(bg.id);
|
||||
this.log.d('Background applied:', options);
|
||||
} catch (error) {
|
||||
this.log.e('Error applying background effect:', error);
|
||||
}
|
||||
}
|
||||
|
||||
private getBackgroundOptions(bg: BackgroundEffect): BackgroundOptions {
|
||||
if (bg.type === EffectType.IMAGE && bg.src) {
|
||||
return { imagePath: bg.src, blurRadius: undefined, backgroundDisabled: false };
|
||||
} else if (bg.type === EffectType.BLUR) {
|
||||
return {
|
||||
blurRadius: bg.id === 'soft_blur' ? this.SOFT_BLUR_INTENSITY : this.HARD_BLUR_INTENSITY,
|
||||
imagePath: undefined,
|
||||
backgroundDisabled: false
|
||||
};
|
||||
}
|
||||
return { backgroundDisabled: true };
|
||||
}
|
||||
|
||||
async removeBackground() {
|
||||
if (this.isBackgroundApplied()) {
|
||||
this.backgroundIdSelected.next('no_effect');
|
||||
const cameraTrack = this.getCameraTrack();
|
||||
const processor = cameraTrack?.getProcessor() as ProcessorWrapper<BackgroundOptions>;
|
||||
if (processor) {
|
||||
try {
|
||||
await processor.updateTransformerOptions({ backgroundDisabled: true });
|
||||
} catch (e) {
|
||||
this.log.w('Error disabling processor:', e);
|
||||
}
|
||||
try {
|
||||
await this.openviduService.switchBackgroundMode({ mode: 'disabled' });
|
||||
} catch (e) {
|
||||
this.log.w('Error disabling processor:', e);
|
||||
}
|
||||
this.storageService.removeBackground();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the camera track from either the published tracks (if in room) or local tracks (if in prejoin)
|
||||
* @returns The camera LocalTrack or undefined if not found
|
||||
* @private
|
||||
*/
|
||||
private getCameraTrack(): LocalVideoTrack | undefined {
|
||||
// First, try to get from published tracks (when in room)
|
||||
if (this.openviduService.isRoomConnected()) {
|
||||
const localParticipant = this.participantService.getLocalParticipant();
|
||||
const cameraTrackPublication = localParticipant?.cameraTracks?.[0];
|
||||
if (cameraTrackPublication?.track) {
|
||||
return cameraTrackPublication.track as LocalVideoTrack;
|
||||
}
|
||||
private getBackgroundOptions(bg: BackgroundEffect): SwitchBackgroundProcessorOptions {
|
||||
if (bg.type === EffectType.NONE) {
|
||||
return { mode: 'disabled' };
|
||||
} else if (bg.type === EffectType.IMAGE && bg.src) {
|
||||
return { mode: 'virtual-background', imagePath: bg.src };
|
||||
} else if (bg.type === EffectType.BLUR) {
|
||||
return {
|
||||
mode: 'background-blur',
|
||||
blurRadius: bg.id === 'soft_blur' ? this.SOFT_BLUR_INTENSITY : this.HARD_BLUR_INTENSITY
|
||||
};
|
||||
}
|
||||
|
||||
// Fallback to local tracks (when in prejoin or tracks not yet published)
|
||||
const localTracks = this.openviduService.getLocalTracks();
|
||||
const cameraTrack = localTracks.find((track) => track.kind === Track.Kind.Video);
|
||||
return cameraTrack as LocalVideoTrack | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a background processor to the camera track
|
||||
* @param cameraTrack The camera track to apply the processor to
|
||||
* @param processor The background processor to apply
|
||||
* @private
|
||||
*/
|
||||
private async applyProcessorToCameraTrack(cameraTrack: LocalVideoTrack, processor: ProcessorWrapper<BackgroundOptions>): Promise<void> {
|
||||
await cameraTrack.setProcessor(processor);
|
||||
return { mode: 'disabled' };
|
||||
}
|
||||
|
||||
private backgroundIsAlreadyApplied(backgroundId: string): boolean {
|
||||
return backgroundId === this.backgroundIdSelected.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the current background effect with a new one by updating the processor options.
|
||||
*
|
||||
* @private
|
||||
* @param currentProcessor - The current processor wrapper that handles background effects
|
||||
* @param bg - The new background effect to apply
|
||||
* @returns A Promise that resolves when the background options have been updated
|
||||
* @throws Will throw an error if updating the background options fails
|
||||
*/
|
||||
private async replaceBackground(currentProcessor: ProcessorWrapper<BackgroundOptions>, bg: BackgroundEffect) {
|
||||
try {
|
||||
const options = this.getBackgroundOptions(bg);
|
||||
// Update the processor with the new options
|
||||
await currentProcessor.updateTransformerOptions(options);
|
||||
this.log.d('Background options updated:', options);
|
||||
} catch (error) {
|
||||
this.log.e('Error updating background options:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue