openvidu/openvidu-testapp/src/app/components/video-track/video-track.component.ts

151 lines
4.7 KiB
TypeScript

import { Component, inject } from '@angular/core';
import { NgClass } from '@angular/common';
import {
LocalTrack,
VideoTrack,
RemoteTrackPublication,
VideoQuality,
} from 'livekit-client';
import { TrackComponent } from '../track/track.component';
import { MatDialog } from '@angular/material/dialog';
import { TestFeedService } from 'src/app/services/test-feed.service';
import { InfoDialogComponent } from '../dialogs/info-dialog/info-dialog.component';
import { ProcessorDialogComponent } from '../dialogs/processor-dialog/processor-dialog.component';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select';
@Component({
selector: 'app-video-track',
templateUrl: './video-track.component.html',
styleUrl: './video-track.component.css',
imports: [NgClass, MatIconModule, MatTooltipModule, MatFormFieldModule, MatSelectModule],
})
export class VideoTrackComponent extends TrackComponent {
muteVideoIcon: string = 'videocam';
maxVideoQuality: string;
videoZoom = false;
// Processor state
processor: any;
mode: 'disabled' | 'background-blur' | 'virtual-background' | undefined =
'virtual-background';
backgroundType: 'image' | 'screen' = 'image';
tracking: boolean = false;
scale: number = 1;
horizontalPosition: number = 0;
blurRadius: number = 10;
processorEnabled: boolean = false;
screenShareTrack: MediaStreamTrack | undefined;
segmentationMethod: 'mediapipe' | 'chroma' = 'mediapipe';
modelAssetPath: string = 'https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter/float16/latest/selfie_segmenter.tflite';
chromaKey = {
autoDetect: true,
autoDetectThreshold: [70, 70, 70] as [number, number, number],
hueRange: [60, 130] as [number, number],
saturationRange: [50, 255] as [number, number],
valueRange: [50, 255] as [number, number],
sampleRegion: { startX: 0.05, endX: 0.2, startY: 0.08, endY: 0.25 },
autoDetectFrameInterval: 30
};
private dialog = inject(MatDialog);
constructor(
protected override testFeedService: TestFeedService,
) {
super(testFeedService);
}
async muteUnmuteVideo() {
if (this._track?.isMuted) {
this.muteVideoIcon = 'videocam';
await (this._track as LocalTrack).unmute();
} else {
this.muteVideoIcon = 'videocam_off';
await (this._track as LocalTrack).mute();
}
}
async onQualityChange() {
let videoQuality: VideoQuality;
switch (this.maxVideoQuality) {
case 'LOW':
videoQuality = VideoQuality.LOW;
break;
case 'MEDIUM':
videoQuality = VideoQuality.MEDIUM;
break;
case 'HIGH':
videoQuality = VideoQuality.HIGH;
break;
default:
videoQuality = VideoQuality.HIGH;
}
await (this.trackPublication as RemoteTrackPublication).setVideoQuality(
videoQuality
);
}
openInfoDialog() {
const updateFunction = async (): Promise<string> => {
const videoLayers: any[] = [];
let stats = await (this._track! as VideoTrack).getRTCStatsReport();
let codecs = new Map();
stats?.forEach((report) => {
if (report.type === 'codec') {
// Store for matching with codecId in 'outbound-rtp' or 'inbound-rtp' reports
codecs.set(report.id, report);
}
if (report.type === 'outbound-rtp' || report.type === 'inbound-rtp') {
videoLayers.push({
codecId: report.codecId,
scalabilityMode: report.scalabilityMode,
rid: report.rid,
active: report.active,
frameWidth: report.frameWidth,
frameHeight: report.frameHeight,
framesPerSecond: report.framesPerSecond,
bytesReceived: report.bytesReceived,
bytesSent: report.bytesSent,
});
}
});
videoLayers.forEach((layer) => {
if (codecs.has(layer.codecId)) {
layer.codec = codecs.get(layer.codecId).mimeType;
}
});
return JSON.stringify(videoLayers, null, 2);
};
this.dialog.open(InfoDialogComponent, {
data: {
title: 'Video Track Layers Info',
subtitle: this.finalElementRefId,
updateFunction,
updateInterval: 700,
},
});
}
openProcessorDialog() {
this.dialog.open(ProcessorDialogComponent, {
data: {
videoTrack: this,
},
width: '1400px',
maxWidth: '95vw',
maxHeight: '95vh',
});
}
toggleVideoZoom() {
this.videoZoom = !this.videoZoom;
let newWidth = this.videoZoom ? '1500px' : '120px';
this.elementRef.nativeElement.style.width = newWidth;
}
}