openvidu-testapp: refactor TrackComponent

pull/848/head
pabloFuente 2024-10-04 13:25:44 +02:00
parent 4354561d5a
commit 0ce0e2acde
7 changed files with 98 additions and 120 deletions

View File

@ -1,5 +1,5 @@
<div class="parent-div"> <div class="parent-div">
<audio #audioElement [id]="elementRefId" [ngClass]="getTrackOrigin()"></audio> <audio #mediaElement [id]="finalElementRefId" [ngClass]="getTrackOrigin()"></audio>
<span>{{trackPublication.source}}</span> <span>{{trackPublication.source}}</span>
<div class="bottom-div"> <div class="bottom-div">
<button *ngIf="localParticipant" (click)="muteUnmuteAudio()" class="audio-btn" matTooltip="Mute/Unmute audio" <button *ngIf="localParticipant" (click)="muteUnmuteAudio()" class="audio-btn" matTooltip="Mute/Unmute audio"

View File

@ -1,60 +1,22 @@
import { Component, ElementRef, Input, ViewChild } from '@angular/core'; import { Component } from '@angular/core';
import { LocalTrack, AudioTrack, AudioCaptureOptions } from 'livekit-client'; import { LocalTrack } from 'livekit-client';
import { TrackComponent } from '../track/track.component'; import { TrackComponent } from '../track/track.component';
@Component({ @Component({
selector: 'app-audio-track', selector: 'app-audio-track',
templateUrl: './audio-track.component.html', templateUrl: './audio-track.component.html',
styleUrls: ['./audio-track.component.css'] styleUrls: ['./audio-track.component.css'],
}) })
export class AudioTrackComponent extends TrackComponent { export class AudioTrackComponent extends TrackComponent {
@ViewChild('audioElement') elementRef: ElementRef;
elementRefId: string = '';
muteAudioIcon: string = 'mic'; muteAudioIcon: string = 'mic';
private _audioTrack: AudioTrack | undefined;
private audioCaptureOptions: AudioCaptureOptions;
@Input() set audioTrack(audioTrack: AudioTrack | undefined) {
this._audioTrack = audioTrack;
this.track = this._audioTrack;
this.setupTrackEventListeners();
let id = '';
id = `${this.getTrackOrigin()}--audio--${this._audioTrack?.source}--${this._audioTrack?.sid}`;
if (this._audioTrack?.sid !== this._audioTrack?.mediaStreamID) {
id += `--${this._audioTrack?.mediaStreamID}`;
}
id = id.replace(/[^0-9a-z-A-Z_-]+/g, '');
this.elementRefId = id;
if (this.elementRef && !this.localParticipant) {
this._audioTrack?.attach(this.elementRef.nativeElement);
}
}
ngAfterViewInit() {
if (!this.localParticipant) {
this._audioTrack?.attach(this.elementRef.nativeElement);
}
}
ngOnDestroy() {
this._audioTrack?.detach(this.elementRef.nativeElement);
}
async muteUnmuteAudio() { async muteUnmuteAudio() {
if (this._audioTrack?.isMuted) { if (this._track?.isMuted) {
this.muteAudioIcon = 'mic'; this.muteAudioIcon = 'mic';
await (this._audioTrack as LocalTrack).unmute(); await (this._track as LocalTrack).unmute();
} else { } else {
this.muteAudioIcon = 'mic_off'; this.muteAudioIcon = 'mic_off';
await (this._audioTrack as LocalTrack).mute(); await (this._track as LocalTrack).mute();
} }
} }
} }

View File

@ -51,13 +51,11 @@
</div> </div>
<div class="audio-tracks-container"> <div class="audio-tracks-container">
<app-audio-track *ngFor="let trackPublication of participant.audioTrackPublications| keyvalue" <app-audio-track *ngFor="let trackPublication of participant.audioTrackPublications| keyvalue"
[trackPublication]="trackPublication.value" [audioTrack]="trackPublication.value.audioTrack" [index]="index" [trackPublication]="trackPublication.value" [track]="trackPublication.value.audioTrack"
[localParticipant]="localParticipant" [index]="index" [localParticipant]="localParticipant" (newTrackEvent)="events.push($event)"></app-audio-track>
(newTrackEvent)="events.push($event)"></app-audio-track>
</div> </div>
<app-video-track *ngFor="let trackPublication of participant.videoTrackPublications | keyvalue ; index as i" <app-video-track *ngFor="let trackPublication of participant.videoTrackPublications | keyvalue"
[trackPublication]="trackPublication.value" [videoTrack]="trackPublication.value.videoTrack" [index]="index" [trackPublication]="trackPublication.value" [track]="trackPublication.value.videoTrack"
[localParticipant]="localParticipant" [index]="index" [localParticipant]="localParticipant" (newTrackEvent)="events.push($event)"></app-video-track>
(newTrackEvent)="events.push($event)" [attr.id]="'user-' + index + '-' + participant.identity + '-video-' + i"></app-video-track>
</div> </div>
</div> </div>

View File

@ -1,4 +1,11 @@
import { Component, EventEmitter, Input, Output } from '@angular/core'; import {
Component,
ElementRef,
EventEmitter,
Input,
Output,
ViewChild,
} from '@angular/core';
import { import {
TrackPublication, TrackPublication,
LocalParticipant, LocalParticipant,
@ -9,6 +16,8 @@ import {
TrackEventCallbacks, TrackEventCallbacks,
LocalTrackPublication, LocalTrackPublication,
RemoteTrackPublication, RemoteTrackPublication,
AudioTrack,
VideoTrack,
} from 'livekit-client'; } from 'livekit-client';
import { import {
TestAppEvent, TestAppEvent,
@ -35,18 +44,62 @@ export class TrackComponent {
@Input() @Input()
localParticipant: LocalParticipant | undefined; localParticipant: LocalParticipant | undefined;
@Input() protected finalElementRefId: string = '';
index: number; private indexId: string;
private trackId: string;
protected track: Track | undefined; protected _track: Track | undefined;
@ViewChild('mediaElement') protected elementRef: ElementRef;
trackSubscribed: boolean = true; trackSubscribed: boolean = true;
trackEnabled: boolean = true; trackEnabled: boolean = true;
constructor(protected testFeedService: TestFeedService) {} constructor(protected testFeedService: TestFeedService) {}
@Input() set index(index: number) {
this.indexId = index.toString();
this.finalElementRefId = `participant-${this.indexId}-${this.trackId}`;
}
@Input() set track(track: AudioTrack | VideoTrack | undefined) {
this._track = track;
this.setupTrackEventListeners();
this.trackId = `-${this.getTrackOrigin()}--${this._track?.kind}--${
this._track?.source
}--${this._track?.sid}`;
if (this._track?.sid !== this._track?.mediaStreamID) {
this.trackId += `--${this._track?.mediaStreamID}`;
}
this.trackId = this.trackId.replace(/[^0-9a-z-A-Z_-]+/g, '');
this.finalElementRefId = `participant-${this.indexId}-${this.trackId}`;
this.attachTrack();
}
ngAfterViewInit() {
this.attachTrack();
}
ngOnDestroy() {
if (this.elementRef) {
this._track?.detach(this.elementRef.nativeElement);
}
}
private attachTrack() {
if (
this.elementRef &&
(this._track?.kind === Track.Kind.Video ||
(this._track?.kind === Track.Kind.Audio && !this.localParticipant))
) {
this._track.attach(this.elementRef.nativeElement);
}
}
protected async unpublishTrack() { protected async unpublishTrack() {
await this.localParticipant?.unpublishTrack(this.track as LocalTrack); await this.localParticipant?.unpublishTrack(this._track as LocalTrack);
} }
protected async toggleSubscribeTrack() { protected async toggleSubscribeTrack() {
@ -68,13 +121,13 @@ export class TrackComponent {
let callbacks: TrackEventCallbacks; let callbacks: TrackEventCallbacks;
let events: TrackEvent; let events: TrackEvent;
this.track this._track
?.on(TrackEvent.Message, () => { ?.on(TrackEvent.Message, () => {
this.newTrackEvent.emit({ this.newTrackEvent.emit({
eventType: TrackEvent.Message, eventType: TrackEvent.Message,
eventCategory: 'TrackEvent', eventCategory: 'TrackEvent',
eventContent: {}, eventContent: {},
eventDescription: this.track!.source, eventDescription: this._track!.source,
}); });
}) })
.on(TrackEvent.Muted, () => { .on(TrackEvent.Muted, () => {
@ -82,7 +135,7 @@ export class TrackComponent {
eventType: TrackEvent.Muted, eventType: TrackEvent.Muted,
eventCategory: 'TrackEvent', eventCategory: 'TrackEvent',
eventContent: {}, eventContent: {},
eventDescription: this.track!.source, eventDescription: this._track!.source,
}); });
}) })
.on(TrackEvent.Unmuted, () => { .on(TrackEvent.Unmuted, () => {
@ -90,7 +143,7 @@ export class TrackComponent {
eventType: TrackEvent.Unmuted, eventType: TrackEvent.Unmuted,
eventCategory: 'TrackEvent', eventCategory: 'TrackEvent',
eventContent: {}, eventContent: {},
eventDescription: this.track!.source, eventDescription: this._track!.source,
}); });
}) })
.on(TrackEvent.AudioSilenceDetected, () => { .on(TrackEvent.AudioSilenceDetected, () => {
@ -98,7 +151,7 @@ export class TrackComponent {
eventType: TrackEvent.AudioSilenceDetected, eventType: TrackEvent.AudioSilenceDetected,
eventCategory: 'TrackEvent', eventCategory: 'TrackEvent',
eventContent: {}, eventContent: {},
eventDescription: this.track!.source, eventDescription: this._track!.source,
}); });
}) })
.on(TrackEvent.Restarted, () => { .on(TrackEvent.Restarted, () => {
@ -106,7 +159,7 @@ export class TrackComponent {
eventType: TrackEvent.Restarted, eventType: TrackEvent.Restarted,
eventCategory: 'TrackEvent', eventCategory: 'TrackEvent',
eventContent: {}, eventContent: {},
eventDescription: this.track!.source, eventDescription: this._track!.source,
}); });
}) })
.on(TrackEvent.Ended, () => { .on(TrackEvent.Ended, () => {
@ -114,23 +167,23 @@ export class TrackComponent {
eventType: TrackEvent.Ended, eventType: TrackEvent.Ended,
eventCategory: 'TrackEvent', eventCategory: 'TrackEvent',
eventContent: {}, eventContent: {},
eventDescription: this.track!.source, eventDescription: this._track!.source,
}); });
}) })
.on(TrackEvent.VisibilityChanged, (visible: boolean) => { .on(TrackEvent.VisibilityChanged, (visible: boolean) => {
this.newTrackEvent.emit({ this.newTrackEvent.emit({
eventType: TrackEvent.VisibilityChanged, eventType: TrackEvent.VisibilityChanged,
eventCategory: 'TrackEvent', eventCategory: 'TrackEvent',
eventContent: { visible, track: this.track }, eventContent: { visible, track: this._track },
eventDescription: `${this.track!.source} is visible: ${visible}`, eventDescription: `${this._track!.source} is visible: ${visible}`,
}); });
}) })
.on(TrackEvent.VideoDimensionsChanged, (dimensions: Track.Dimensions) => { .on(TrackEvent.VideoDimensionsChanged, (dimensions: Track.Dimensions) => {
this.newTrackEvent.emit({ this.newTrackEvent.emit({
eventType: TrackEvent.VideoDimensionsChanged, eventType: TrackEvent.VideoDimensionsChanged,
eventCategory: 'TrackEvent', eventCategory: 'TrackEvent',
eventContent: { dimensions, track: this.track }, eventContent: { dimensions, track: this._track },
eventDescription: `${this.track?.source} ${JSON.stringify( eventDescription: `${this._track?.source} ${JSON.stringify(
dimensions dimensions
)}`, )}`,
}); });
@ -140,7 +193,7 @@ export class TrackComponent {
eventType: TrackEvent.UpstreamPaused, eventType: TrackEvent.UpstreamPaused,
eventCategory: 'TrackEvent', eventCategory: 'TrackEvent',
eventContent: {}, eventContent: {},
eventDescription: this.track!.source, eventDescription: this._track!.source,
}); });
}) })
.on(TrackEvent.UpstreamResumed, () => { .on(TrackEvent.UpstreamResumed, () => {
@ -148,16 +201,16 @@ export class TrackComponent {
eventType: TrackEvent.UpstreamResumed, eventType: TrackEvent.UpstreamResumed,
eventCategory: 'TrackEvent', eventCategory: 'TrackEvent',
eventContent: {}, eventContent: {},
eventDescription: this.track!.source, eventDescription: this._track!.source,
}); });
}); });
} }
protected getTrackOrigin(): string { protected getTrackOrigin(): string {
let origin: string; let origin: string;
if (this.track instanceof RemoteTrack) { if (this._track instanceof RemoteTrack) {
origin = 'remote'; origin = 'remote';
} else if (this.track instanceof LocalTrack) { } else if (this._track instanceof LocalTrack) {
origin = 'local'; origin = 'local';
} else { } else {
origin = 'unknown'; origin = 'unknown';

View File

@ -10,7 +10,7 @@ import { AudioTrack, VideoTrack } from 'livekit-client';
@Component({ @Component({
selector: 'app-table-video', selector: 'app-table-video',
template: ` template: `
<video #videoElement [id]="videoId" autoplay playsinline></video> <video #mediaElement [id]="videoId" autoplay playsinline></video>
`, `,
styles: [ styles: [
` `
@ -21,7 +21,7 @@ import { AudioTrack, VideoTrack } from 'livekit-client';
], ],
}) })
export class TableVideoComponent implements AfterViewInit { export class TableVideoComponent implements AfterViewInit {
@ViewChild('videoElement') elementRef: ElementRef; @ViewChild('mediaElement') elementRef: ElementRef;
@Input() videoId: string; @Input() videoId: string;

View File

@ -1,5 +1,5 @@
<div class="parent-div"> <div class="parent-div">
<video #videoElement [id]="elementRefId" [ngClass]="getTrackOrigin()"></video> <video #mediaElement [id]="finalElementRefId" [ngClass]="getTrackOrigin()"></video>
<div class="bottom-div"> <div class="bottom-div">
<button *ngIf="localParticipant" (click)="muteUnmuteVideo()" class="video-btn mute-unmute-video" matTooltip="Mute/Unmute video" <button *ngIf="localParticipant" (click)="muteUnmuteVideo()" class="video-btn mute-unmute-video" matTooltip="Mute/Unmute video"
matTooltipClass="custom-tooltip"> matTooltipClass="custom-tooltip">

View File

@ -1,10 +1,7 @@
import { Component, ElementRef, Input, ViewChild } from '@angular/core'; import { Component } from '@angular/core';
import { import {
LocalTrack, LocalTrack,
VideoTrack, VideoTrack,
VideoCaptureOptions,
ScreenShareCaptureOptions,
RemoteTrack,
RemoteTrackPublication, RemoteTrackPublication,
VideoQuality, VideoQuality,
} from 'livekit-client'; } from 'livekit-client';
@ -19,36 +16,10 @@ import { InfoDialogComponent } from '../dialogs/info-dialog/info-dialog.componen
styleUrls: ['./video-track.component.css'], styleUrls: ['./video-track.component.css'],
}) })
export class VideoTrackComponent extends TrackComponent { export class VideoTrackComponent extends TrackComponent {
@ViewChild('videoElement') elementRef: ElementRef;
elementRefId: string = '';
muteVideoIcon: string = 'videocam'; muteVideoIcon: string = 'videocam';
blurIcon: string = 'blur_on'; blurIcon: string = 'blur_on';
private _videoTrack: VideoTrack | undefined;
maxVideoQuality: string; maxVideoQuality: string;
@Input() set videoTrack(videoTrack: VideoTrack | undefined) {
this._videoTrack = videoTrack;
this.track = this._videoTrack;
this.setupTrackEventListeners();
let id = '';
id = `${this.getTrackOrigin()}--video--${this._videoTrack?.source}--${
this._videoTrack?.sid
}`;
if (this._videoTrack?.sid !== this._videoTrack?.mediaStreamID) {
id += `--${this._videoTrack?.mediaStreamID}`;
}
id = id.replace(/[^0-9a-z-A-Z_-]+/g, '');
this.elementRefId = id;
if (this.elementRef) {
this._videoTrack?.attach(this.elementRef.nativeElement);
}
}
constructor( constructor(
protected override testFeedService: TestFeedService, protected override testFeedService: TestFeedService,
private dialog: MatDialog private dialog: MatDialog
@ -56,21 +27,13 @@ export class VideoTrackComponent extends TrackComponent {
super(testFeedService); super(testFeedService);
} }
ngAfterViewInit() {
this._videoTrack?.attach(this.elementRef.nativeElement);
}
ngOnDestroy() {
this._videoTrack?.detach(this.elementRef.nativeElement);
}
async muteUnmuteVideo() { async muteUnmuteVideo() {
if (this._videoTrack?.isMuted) { if (this._track?.isMuted) {
this.muteVideoIcon = 'videocam'; this.muteVideoIcon = 'videocam';
await (this._videoTrack as LocalTrack).unmute(); await (this._track as LocalTrack).unmute();
} else { } else {
this.muteVideoIcon = 'videocam_off'; this.muteVideoIcon = 'videocam_off';
await (this._videoTrack as LocalTrack).mute(); await (this._track as LocalTrack).mute();
} }
} }
@ -97,7 +60,7 @@ export class VideoTrackComponent extends TrackComponent {
openInfoDialog() { openInfoDialog() {
const updateFunction = async (): Promise<string> => { const updateFunction = async (): Promise<string> => {
const videoLayers: any[] = []; const videoLayers: any[] = [];
let stats = await this._videoTrack?.getRTCStatsReport(); let stats = await (this._track! as VideoTrack).getRTCStatsReport();
stats?.forEach((report) => { stats?.forEach((report) => {
if (report.type === 'outbound-rtp' || report.type === 'inbound-rtp') { if (report.type === 'outbound-rtp' || report.type === 'inbound-rtp') {
videoLayers.push({ videoLayers.push({
@ -108,6 +71,8 @@ export class VideoTrackComponent extends TrackComponent {
frameWidth: report.frameWidth, frameWidth: report.frameWidth,
frameHeight: report.frameHeight, frameHeight: report.frameHeight,
framesPerSecond: report.framesPerSecond, framesPerSecond: report.framesPerSecond,
bytesReceived: report.bytesReceived,
bytesSent: report.bytesSent,
}); });
} }
}); });
@ -116,7 +81,7 @@ export class VideoTrackComponent extends TrackComponent {
this.dialog.open(InfoDialogComponent, { this.dialog.open(InfoDialogComponent, {
data: { data: {
title: 'Video Track Layers Info', title: 'Video Track Layers Info',
subtitle: this.elementRefId, subtitle: this.finalElementRefId,
updateFunction, updateFunction,
}, },
}); });
@ -124,10 +89,10 @@ export class VideoTrackComponent extends TrackComponent {
async blur() { async blur() {
if (this.blurIcon == 'blur_on') { if (this.blurIcon == 'blur_on') {
// await (this._videoTrack! as LocalVideoTrack).setProcessor(BackgroundBlur()); // await (this._track! as LocalVideoTrack).setProcessor(BackgroundBlur());
this.blurIcon = 'blur_off'; this.blurIcon = 'blur_off';
} else { } else {
// await (this._videoTrack! as LocalVideoTrack).stopProcessor(); // await (this._track! as LocalVideoTrack).stopProcessor();
this.blurIcon = 'blur_on'; this.blurIcon = 'blur_on';
} }
} }