mirror of https://github.com/OpenVidu/openvidu.git
openvidu-testapp: update and refactor all RoomEvent, ParticipantEvent and TrackEvent management
- Updates all available events to latest - Refactor event listeners to use shared utilities with early registration and safe add/remove patternpull/900/head
parent
052b110776
commit
f037f31da1
|
|
@ -6,40 +6,49 @@ import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
|
||||
export interface EventGroup {
|
||||
label: string;
|
||||
eventCollection: Map<string, boolean>;
|
||||
eventArray: string[];
|
||||
checkAll: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-events-dialog',
|
||||
template: `
|
||||
<h2 mat-dialog-title>{{target}} events</h2>
|
||||
<mat-dialog-content>
|
||||
<mat-slide-toggle [(ngModel)]="checkAll" (change)="updateAll()" [color]="'warn'"><i>ALL</i></mat-slide-toggle>
|
||||
<mat-divider></mat-divider>
|
||||
<div class="row no-wrap-row">
|
||||
<div class="col-50">
|
||||
@for (event of eventArray | slice:0:(eventArray.length/2); track event) {
|
||||
<div class="toggle">
|
||||
<mat-slide-toggle
|
||||
(change)="toggleEvent($event)"
|
||||
[checked]="eventCollection.get(event)"
|
||||
[name]="event"
|
||||
color="warn">{{event}}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
}
|
||||
@for (group of eventGroups; track group.label) {
|
||||
<h3 class="group-label">{{group.label}}</h3>
|
||||
<mat-slide-toggle [(ngModel)]="group.checkAll" (change)="updateAll(group)" [color]="'warn'"><i>ALL</i></mat-slide-toggle>
|
||||
<mat-divider></mat-divider>
|
||||
<div class="row no-wrap-row">
|
||||
<div class="col-50">
|
||||
@for (event of group.eventArray | slice:0:Math.ceil(group.eventArray.length/2); track event) {
|
||||
<div class="toggle">
|
||||
<mat-slide-toggle
|
||||
(change)="toggleEvent($event, group)"
|
||||
[checked]="group.eventCollection.get(event)"
|
||||
[name]="event"
|
||||
color="warn">{{event}}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="col-50">
|
||||
@for (event of group.eventArray | slice:Math.ceil(group.eventArray.length/2):group.eventArray.length; track event) {
|
||||
<div class="toggle">
|
||||
<mat-slide-toggle
|
||||
(change)="toggleEvent($event, group)"
|
||||
[checked]="group.eventCollection.get(event)"
|
||||
[name]="event"
|
||||
color="warn">{{event}}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-50">
|
||||
@for (event of eventArray | slice:(eventArray.length/2 + 1):(eventArray.length); track event) {
|
||||
<div class="toggle">
|
||||
<mat-slide-toggle
|
||||
(change)="toggleEvent($event)"
|
||||
[checked]="eventCollection.get(event)"
|
||||
[name]="event"
|
||||
color="warn">{{event}}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<button mat-button id="close-dialog-btn" mat-dialog-close="">CLOSE</button>
|
||||
|
|
@ -49,34 +58,50 @@ import { MatButtonModule } from '@angular/material/button';
|
|||
'mat-dialog-content { display: inline; }',
|
||||
'mat-divider { margin-top: 5px; margin-bottom: 5px; }',
|
||||
'.col-50 {flex-basis: 50%; box-sizing: border-box; padding-left: 20px; }',
|
||||
'.toggle { }'
|
||||
'.toggle { }',
|
||||
'.group-label { margin-top: 15px; margin-bottom: 5px; }',
|
||||
'.group-label:first-child { margin-top: 0; }'
|
||||
],
|
||||
imports: [SlicePipe, FormsModule, MatDialogModule, MatSlideToggleModule, MatDividerModule, MatButtonModule],
|
||||
})
|
||||
export class EventsDialogComponent {
|
||||
|
||||
Math = Math;
|
||||
target = '';
|
||||
checkAll = true;
|
||||
eventCollection: Map<string, boolean>;
|
||||
eventArray: string[];
|
||||
eventGroups: EventGroup[] = [];
|
||||
|
||||
private dialogData = inject(MAT_DIALOG_DATA);
|
||||
|
||||
constructor(public dialogRef: MatDialogRef<EventsDialogComponent>) {
|
||||
const data = this.dialogData;
|
||||
this.target = data.target;
|
||||
this.eventCollection = data.eventCollection;
|
||||
this.eventArray = Array.from(this.eventCollection.keys());
|
||||
if (data.eventGroups) {
|
||||
this.eventGroups = data.eventGroups.map((g: { label: string; eventCollection: Map<string, boolean> }) => ({
|
||||
label: g.label,
|
||||
eventCollection: g.eventCollection,
|
||||
eventArray: Array.from(g.eventCollection.keys()),
|
||||
checkAll: Array.from(g.eventCollection.values()).every(v => v),
|
||||
}));
|
||||
} else {
|
||||
// Backward compatibility: single eventCollection
|
||||
const eventCollection = data.eventCollection;
|
||||
this.eventGroups = [{
|
||||
label: data.target,
|
||||
eventCollection,
|
||||
eventArray: Array.from(eventCollection.keys()),
|
||||
checkAll: Array.from(eventCollection.values()).every(v => v),
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
updateAll() {
|
||||
this.eventCollection.forEach((value: boolean, key: string) => {
|
||||
this.eventCollection.set(key, this.checkAll);
|
||||
updateAll(group: EventGroup) {
|
||||
group.eventCollection.forEach((_value: boolean, key: string) => {
|
||||
group.eventCollection.set(key, group.checkAll);
|
||||
});
|
||||
}
|
||||
|
||||
toggleEvent(event: any) {
|
||||
this.eventCollection.set(event.source.name, event.checked);
|
||||
toggleEvent(event: any, group: EventGroup) {
|
||||
group.eventCollection.set(event.source.name, event.checked);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
<mat-icon class="mat-icon-custom-ic" aria-label="Room API button">cloud_circle</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button title="Room events" [id]="'room-events-btn-' + index"
|
||||
class="mat-icon-custom" (click)="openRoomEventsDialog()">
|
||||
class="mat-icon-custom" (click)="openAllEventsDialog()">
|
||||
<mat-icon class="mat-icon-custom-ic"
|
||||
aria-label="Room events button">notifications</mat-icon>
|
||||
</button>
|
||||
|
|
@ -89,10 +89,15 @@
|
|||
</div>
|
||||
<div>
|
||||
<app-participant class="local-participant" [participant]="room.localParticipant" [room]="room"
|
||||
[index]="index"></app-participant>
|
||||
[index]="index" [participantEvents]="participantEvents" [trackEvents]="trackEvents"
|
||||
[earlyParticipantEvents]="earlyParticipantEvents" [earlyParticipantListeners]="earlyParticipantListeners"
|
||||
[earlyTrackEvents]="earlyTrackEvents" [earlyTrackListeners]="earlyTrackListeners"></app-participant>
|
||||
@for (participant of room.remoteParticipants | keyvalue; track participant) {
|
||||
<app-participant class="remote-participant"
|
||||
[participant]="participant.value" [room]="room" [index]="index"
|
||||
[participantEvents]="participantEvents" [trackEvents]="trackEvents"
|
||||
[earlyParticipantEvents]="earlyParticipantEvents" [earlyParticipantListeners]="earlyParticipantListeners"
|
||||
[earlyTrackEvents]="earlyTrackEvents" [earlyTrackListeners]="earlyTrackListeners"
|
||||
(sendReliableDataToOneParticipant)="sendDataReliable($event)"
|
||||
(sendLossyDataToOneParticipant)="sendDataLossy($event)"></app-participant>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import {
|
|||
LocalVideoTrack,
|
||||
MediaDeviceFailure,
|
||||
Participant,
|
||||
ParticipantEvent,
|
||||
RemoteAudioTrack,
|
||||
RemoteDataTrack,
|
||||
RemoteParticipant,
|
||||
|
|
@ -42,6 +43,7 @@ import {
|
|||
SubscriptionError,
|
||||
TextStreamReader,
|
||||
Track,
|
||||
TrackEvent,
|
||||
TrackPublication,
|
||||
TrackPublishOptions,
|
||||
} from 'livekit-client';
|
||||
|
|
@ -58,6 +60,11 @@ import { InfoDialogComponent } from '../dialogs/info-dialog/info-dialog.componen
|
|||
import { ParticipantComponent } from '../participant/participant.component';
|
||||
import { RoomEventCallbacks } from 'node_modules/livekit-client/dist/src/room/Room';
|
||||
import PCTransport from 'node_modules/livekit-client/dist/src/room/PCTransport';
|
||||
import {
|
||||
registerParticipantEventListeners,
|
||||
registerTrackEventListeners,
|
||||
removeAllManagedListeners,
|
||||
} from 'src/app/utils/event-listener-utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-openvidu-instance',
|
||||
|
|
@ -74,6 +81,18 @@ export class OpenviduInstanceComponent {
|
|||
|
||||
room?: Room;
|
||||
roomEvents: Map<RoomEvent, Boolean> = new Map<RoomEvent, boolean>();
|
||||
participantEvents: Map<ParticipantEvent, boolean> = new Map<ParticipantEvent, boolean>();
|
||||
trackEvents: Map<TrackEvent, boolean> = new Map<TrackEvent, boolean>();
|
||||
|
||||
private roomEventListeners: Map<string, (...args: any[]) => void> = new Map();
|
||||
|
||||
// Early event registration: buffers events for participant/track components
|
||||
// that don't yet exist when the SDK fires events during connect()
|
||||
earlyParticipantEvents: Map<string, TestAppEvent[]> = new Map();
|
||||
earlyParticipantListeners: Map<string, Map<string, (...args: any[]) => void>> = new Map();
|
||||
earlyTrackEvents: Map<string, TestAppEvent[]> = new Map();
|
||||
earlyTrackListeners: Map<string, Map<string, (...args: any[]) => void>> = new Map();
|
||||
private earlyParticipantConnectedListener: ((...args: any[]) => void) | undefined;
|
||||
|
||||
roomName: string = 'TestRoom';
|
||||
participantName: string = 'TestParticipant';
|
||||
|
|
@ -142,6 +161,14 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.set(RoomEvent[event as keyof typeof RoomEvent], true);
|
||||
this.roomEvents.set(RoomEvent.ActiveSpeakersChanged, false);
|
||||
}
|
||||
for (let event of Object.keys(ParticipantEvent)) {
|
||||
this.participantEvents.set(ParticipantEvent[event as keyof typeof ParticipantEvent], true);
|
||||
this.participantEvents.set(ParticipantEvent.IsSpeakingChanged, false);
|
||||
}
|
||||
for (let event of Object.keys(TrackEvent)) {
|
||||
this.trackEvents.set(TrackEvent[event as keyof typeof TrackEvent], true);
|
||||
this.trackEvents.set(TrackEvent.TimeSyncUpdate, false);
|
||||
}
|
||||
this.participantName += this.index;
|
||||
if (this.roomConf.startSession) {
|
||||
const token = await this.roomApiService.createToken(
|
||||
|
|
@ -177,6 +204,16 @@ export class OpenviduInstanceComponent {
|
|||
this.room = new Room(this.roomOptions);
|
||||
(window as any)['room_' + this.index] = this.room;
|
||||
|
||||
// Register early participant event listeners on local participant BEFORE connect
|
||||
this.registerEarlyParticipantListeners(this.room.localParticipant);
|
||||
|
||||
// Register early participant event listeners on remote participants as they connect
|
||||
// This fires during connect() for participants already in the room
|
||||
this.earlyParticipantConnectedListener = (participant: RemoteParticipant) => {
|
||||
this.registerEarlyParticipantListeners(participant);
|
||||
};
|
||||
this.room.addListener(RoomEvent.ParticipantConnected, this.earlyParticipantConnectedListener as any);
|
||||
|
||||
this.setupRoomEventListeners(new Map(), true);
|
||||
|
||||
// connect to room
|
||||
|
|
@ -216,6 +253,19 @@ export class OpenviduInstanceComponent {
|
|||
}
|
||||
}
|
||||
|
||||
private registerRoomListener(event: RoomEvent, listener: (...args: any[]) => void) {
|
||||
this.room!.addListener(event, listener as any);
|
||||
this.roomEventListeners.set(event, listener);
|
||||
}
|
||||
|
||||
private unregisterRoomListener(event: RoomEvent | string) {
|
||||
const existing = this.roomEventListeners.get(event as string);
|
||||
if (existing) {
|
||||
this.room?.removeListener(event as RoomEvent, existing as any);
|
||||
this.roomEventListeners.delete(event as string);
|
||||
}
|
||||
}
|
||||
|
||||
setupRoomEventListeners(oldValues: Map<string, boolean>, firstTime: boolean) {
|
||||
// This is a link to the complete list of Room events
|
||||
let callbacks: RoomEventCallbacks;
|
||||
|
|
@ -226,9 +276,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.Connected) !==
|
||||
oldValues.get(RoomEvent.Connected)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.Connected);
|
||||
this.unregisterRoomListener(RoomEvent.Connected);
|
||||
if (this.roomEvents.get(RoomEvent.Connected)) {
|
||||
this.room!.on(RoomEvent.Connected, () => {
|
||||
this.registerRoomListener(RoomEvent.Connected, () => {
|
||||
this.updateEventList(RoomEvent.Connected, {}, '');
|
||||
this.room!.remoteParticipants.forEach(
|
||||
(remoteParticipant: RemoteParticipant) => {
|
||||
|
|
@ -251,22 +301,35 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.Reconnecting) !==
|
||||
oldValues.get(RoomEvent.Reconnecting)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.Reconnecting);
|
||||
this.unregisterRoomListener(RoomEvent.Reconnecting);
|
||||
if (this.roomEvents.get(RoomEvent.Reconnecting)) {
|
||||
this.room!.on(RoomEvent.Reconnecting, () => {
|
||||
this.registerRoomListener(RoomEvent.Reconnecting, () => {
|
||||
this.updateEventList(RoomEvent.Reconnecting, {}, '');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
firstTime ||
|
||||
this.roomEvents.get(RoomEvent.SignalReconnecting) !==
|
||||
oldValues.get(RoomEvent.SignalReconnecting)
|
||||
) {
|
||||
this.unregisterRoomListener(RoomEvent.SignalReconnecting);
|
||||
if (this.roomEvents.get(RoomEvent.SignalReconnecting)) {
|
||||
this.registerRoomListener(RoomEvent.SignalReconnecting, () => {
|
||||
this.updateEventList(RoomEvent.SignalReconnecting, {}, '');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
firstTime ||
|
||||
this.roomEvents.get(RoomEvent.Reconnected) !==
|
||||
oldValues.get(RoomEvent.Reconnected)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.Reconnected);
|
||||
this.unregisterRoomListener(RoomEvent.Reconnected);
|
||||
if (this.roomEvents.get(RoomEvent.Reconnected)) {
|
||||
this.room!.on(RoomEvent.Reconnected, () => {
|
||||
this.registerRoomListener(RoomEvent.Reconnected, () => {
|
||||
this.updateEventList(RoomEvent.Reconnected, {}, '');
|
||||
});
|
||||
}
|
||||
|
|
@ -277,9 +340,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.Disconnected) !==
|
||||
oldValues.get(RoomEvent.Disconnected)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.Disconnected);
|
||||
this.unregisterRoomListener(RoomEvent.Disconnected);
|
||||
if (this.roomEvents.get(RoomEvent.Disconnected)) {
|
||||
this.room!.on(RoomEvent.Disconnected, (reason?: DisconnectReason) => {
|
||||
this.registerRoomListener(RoomEvent.Disconnected, (reason?: DisconnectReason) => {
|
||||
this.updateEventList(
|
||||
RoomEvent.Disconnected,
|
||||
{},
|
||||
|
|
@ -294,9 +357,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.ConnectionStateChanged) !==
|
||||
oldValues.get(RoomEvent.ConnectionStateChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.ConnectionStateChanged);
|
||||
this.unregisterRoomListener(RoomEvent.ConnectionStateChanged);
|
||||
if (this.roomEvents.get(RoomEvent.ConnectionStateChanged)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.ConnectionStateChanged,
|
||||
(state: ConnectionState) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -309,14 +372,31 @@ export class OpenviduInstanceComponent {
|
|||
}
|
||||
}
|
||||
|
||||
if (
|
||||
firstTime ||
|
||||
this.roomEvents.get(RoomEvent.Moved) !==
|
||||
oldValues.get(RoomEvent.Moved)
|
||||
) {
|
||||
this.unregisterRoomListener(RoomEvent.Moved);
|
||||
if (this.roomEvents.get(RoomEvent.Moved)) {
|
||||
this.registerRoomListener(RoomEvent.Moved, (name: string) => {
|
||||
this.updateEventList(
|
||||
RoomEvent.Moved,
|
||||
{ name },
|
||||
`moved to room: ${name}`
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
firstTime ||
|
||||
this.roomEvents.get(RoomEvent.MediaDevicesChanged) !==
|
||||
oldValues.get(RoomEvent.MediaDevicesChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.MediaDevicesChanged);
|
||||
this.unregisterRoomListener(RoomEvent.MediaDevicesChanged);
|
||||
if (this.roomEvents.get(RoomEvent.MediaDevicesChanged)) {
|
||||
this.room!.on(RoomEvent.MediaDevicesChanged, () => {
|
||||
this.registerRoomListener(RoomEvent.MediaDevicesChanged, () => {
|
||||
this.updateEventList(RoomEvent.MediaDevicesChanged, {}, '');
|
||||
});
|
||||
}
|
||||
|
|
@ -327,9 +407,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.ParticipantConnected) !==
|
||||
oldValues.get(RoomEvent.ParticipantConnected)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.ParticipantConnected);
|
||||
this.unregisterRoomListener(RoomEvent.ParticipantConnected);
|
||||
if (this.roomEvents.get(RoomEvent.ParticipantConnected)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.ParticipantConnected,
|
||||
(participant: RemoteParticipant) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -347,9 +427,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.ParticipantActive) !==
|
||||
oldValues.get(RoomEvent.ParticipantActive)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.ParticipantActive);
|
||||
this.unregisterRoomListener(RoomEvent.ParticipantActive);
|
||||
if (this.roomEvents.get(RoomEvent.ParticipantActive)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.ParticipantActive,
|
||||
(participant: Participant) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -367,9 +447,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.ParticipantDisconnected) !==
|
||||
oldValues.get(RoomEvent.ParticipantDisconnected)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.ParticipantDisconnected);
|
||||
this.unregisterRoomListener(RoomEvent.ParticipantDisconnected);
|
||||
if (this.roomEvents.get(RoomEvent.ParticipantDisconnected)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.ParticipantDisconnected,
|
||||
(participant: RemoteParticipant) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -388,9 +468,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.TrackPublished) !==
|
||||
oldValues.get(RoomEvent.TrackPublished)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.TrackPublished);
|
||||
this.unregisterRoomListener(RoomEvent.TrackPublished);
|
||||
if (this.roomEvents.get(RoomEvent.TrackPublished)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.TrackPublished,
|
||||
(
|
||||
publication: RemoteTrackPublication,
|
||||
|
|
@ -414,9 +494,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.TrackSubscribed) !==
|
||||
oldValues.get(RoomEvent.TrackSubscribed)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.TrackSubscribed);
|
||||
this.unregisterRoomListener(RoomEvent.TrackSubscribed);
|
||||
if (this.roomEvents.get(RoomEvent.TrackSubscribed)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.TrackSubscribed,
|
||||
(
|
||||
track: RemoteTrack,
|
||||
|
|
@ -453,9 +533,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.TrackSubscriptionFailed) !==
|
||||
oldValues.get(RoomEvent.TrackSubscriptionFailed)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.TrackSubscriptionFailed);
|
||||
this.unregisterRoomListener(RoomEvent.TrackSubscriptionFailed);
|
||||
if (this.roomEvents.get(RoomEvent.TrackSubscriptionFailed)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.TrackSubscriptionFailed,
|
||||
(
|
||||
trackSid: string,
|
||||
|
|
@ -479,9 +559,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.TrackUnpublished) !==
|
||||
oldValues.get(RoomEvent.TrackUnpublished)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.TrackUnpublished);
|
||||
this.unregisterRoomListener(RoomEvent.TrackUnpublished);
|
||||
if (this.roomEvents.get(RoomEvent.TrackUnpublished)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.TrackUnpublished,
|
||||
(
|
||||
publication: RemoteTrackPublication,
|
||||
|
|
@ -502,9 +582,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.TrackUnsubscribed) !==
|
||||
oldValues.get(RoomEvent.TrackUnsubscribed)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.TrackUnsubscribed);
|
||||
this.unregisterRoomListener(RoomEvent.TrackUnsubscribed);
|
||||
if (this.roomEvents.get(RoomEvent.TrackUnsubscribed)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.TrackUnsubscribed,
|
||||
(
|
||||
track: Track,
|
||||
|
|
@ -543,9 +623,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.TrackMuted) !==
|
||||
oldValues.get(RoomEvent.TrackMuted)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.TrackMuted);
|
||||
this.unregisterRoomListener(RoomEvent.TrackMuted);
|
||||
if (this.roomEvents.get(RoomEvent.TrackMuted)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.TrackMuted,
|
||||
(publication: TrackPublication, participant: Participant) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -563,9 +643,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.TrackUnmuted) !==
|
||||
oldValues.get(RoomEvent.TrackUnmuted)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.TrackUnmuted);
|
||||
this.unregisterRoomListener(RoomEvent.TrackUnmuted);
|
||||
if (this.roomEvents.get(RoomEvent.TrackUnmuted)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.TrackUnmuted,
|
||||
(publication: TrackPublication, participant: Participant) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -583,9 +663,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.LocalTrackPublished) !==
|
||||
oldValues.get(RoomEvent.LocalTrackPublished)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.LocalTrackPublished);
|
||||
this.unregisterRoomListener(RoomEvent.LocalTrackPublished);
|
||||
if (this.roomEvents.get(RoomEvent.LocalTrackPublished)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.LocalTrackPublished,
|
||||
(
|
||||
publication: LocalTrackPublication,
|
||||
|
|
@ -611,9 +691,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.LocalTrackUnpublished) !==
|
||||
oldValues.get(RoomEvent.LocalTrackUnpublished)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.LocalTrackUnpublished);
|
||||
this.unregisterRoomListener(RoomEvent.LocalTrackUnpublished);
|
||||
if (this.roomEvents.get(RoomEvent.LocalTrackUnpublished)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.LocalTrackUnpublished,
|
||||
(
|
||||
publication: LocalTrackPublication,
|
||||
|
|
@ -635,9 +715,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.LocalAudioSilenceDetected) !==
|
||||
oldValues.get(RoomEvent.LocalAudioSilenceDetected)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.LocalAudioSilenceDetected);
|
||||
this.unregisterRoomListener(RoomEvent.LocalAudioSilenceDetected);
|
||||
if (this.roomEvents.get(RoomEvent.LocalAudioSilenceDetected)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.LocalAudioSilenceDetected,
|
||||
(publication: LocalTrackPublication) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -655,9 +735,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.ParticipantMetadataChanged) !==
|
||||
oldValues.get(RoomEvent.ParticipantMetadataChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.ParticipantMetadataChanged);
|
||||
this.unregisterRoomListener(RoomEvent.ParticipantMetadataChanged);
|
||||
if (this.roomEvents.get(RoomEvent.ParticipantMetadataChanged)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.ParticipantMetadataChanged,
|
||||
(metadata: string | undefined, participant: Participant) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -675,9 +755,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.ParticipantNameChanged) !==
|
||||
oldValues.get(RoomEvent.ParticipantNameChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.ParticipantNameChanged);
|
||||
this.unregisterRoomListener(RoomEvent.ParticipantNameChanged);
|
||||
if (this.roomEvents.get(RoomEvent.ParticipantNameChanged)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.ParticipantNameChanged,
|
||||
(name: string, participant: Participant) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -690,14 +770,37 @@ export class OpenviduInstanceComponent {
|
|||
}
|
||||
}
|
||||
|
||||
if (
|
||||
firstTime ||
|
||||
this.roomEvents.get(RoomEvent.ParticipantAttributesChanged) !==
|
||||
oldValues.get(RoomEvent.ParticipantAttributesChanged)
|
||||
) {
|
||||
this.unregisterRoomListener(RoomEvent.ParticipantAttributesChanged);
|
||||
if (this.roomEvents.get(RoomEvent.ParticipantAttributesChanged)) {
|
||||
this.registerRoomListener(
|
||||
RoomEvent.ParticipantAttributesChanged,
|
||||
(
|
||||
changedAttributes: Record<string, string>,
|
||||
participant: RemoteParticipant | LocalParticipant
|
||||
) => {
|
||||
this.updateEventList(
|
||||
RoomEvent.ParticipantAttributesChanged,
|
||||
{ changedAttributes, participant },
|
||||
`${participant.identity} ${JSON.stringify(changedAttributes)}`
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
firstTime ||
|
||||
this.roomEvents.get(RoomEvent.ParticipantPermissionsChanged) !==
|
||||
oldValues.get(RoomEvent.ParticipantPermissionsChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.ParticipantPermissionsChanged);
|
||||
this.unregisterRoomListener(RoomEvent.ParticipantPermissionsChanged);
|
||||
if (this.roomEvents.get(RoomEvent.ParticipantPermissionsChanged)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.ParticipantPermissionsChanged,
|
||||
(
|
||||
prevPermissions: ParticipantPermission | undefined,
|
||||
|
|
@ -722,9 +825,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.ActiveSpeakersChanged) !==
|
||||
oldValues.get(RoomEvent.ActiveSpeakersChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.ActiveSpeakersChanged);
|
||||
this.unregisterRoomListener(RoomEvent.ActiveSpeakersChanged);
|
||||
if (this.roomEvents.get(RoomEvent.ActiveSpeakersChanged)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.ActiveSpeakersChanged,
|
||||
(speakers: Participant[]) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -746,9 +849,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.RoomMetadataChanged) !==
|
||||
oldValues.get(RoomEvent.RoomMetadataChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.RoomMetadataChanged);
|
||||
this.unregisterRoomListener(RoomEvent.RoomMetadataChanged);
|
||||
if (this.roomEvents.get(RoomEvent.RoomMetadataChanged)) {
|
||||
this.room!.on(RoomEvent.RoomMetadataChanged, (metadata: string) => {
|
||||
this.registerRoomListener(RoomEvent.RoomMetadataChanged, (metadata: string) => {
|
||||
this.updateEventList(
|
||||
RoomEvent.RoomMetadataChanged,
|
||||
{ metadata },
|
||||
|
|
@ -763,9 +866,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.DataReceived) !==
|
||||
oldValues.get(RoomEvent.DataReceived)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.DataReceived);
|
||||
this.unregisterRoomListener(RoomEvent.DataReceived);
|
||||
if (this.roomEvents.get(RoomEvent.DataReceived)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.DataReceived,
|
||||
(
|
||||
payload: Uint8Array,
|
||||
|
|
@ -785,14 +888,34 @@ export class OpenviduInstanceComponent {
|
|||
}
|
||||
}
|
||||
|
||||
if (
|
||||
firstTime ||
|
||||
this.roomEvents.get(RoomEvent.SipDTMFReceived) !==
|
||||
oldValues.get(RoomEvent.SipDTMFReceived)
|
||||
) {
|
||||
this.unregisterRoomListener(RoomEvent.SipDTMFReceived);
|
||||
if (this.roomEvents.get(RoomEvent.SipDTMFReceived)) {
|
||||
this.registerRoomListener(
|
||||
RoomEvent.SipDTMFReceived,
|
||||
(dtmf: any, participant?: RemoteParticipant) => {
|
||||
this.updateEventList(
|
||||
RoomEvent.SipDTMFReceived,
|
||||
{ dtmf, participant },
|
||||
`${participant?.identity ?? 'unknown'} ${JSON.stringify(dtmf)}`
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
firstTime ||
|
||||
this.roomEvents.get(RoomEvent.ConnectionQualityChanged) !==
|
||||
oldValues.get(RoomEvent.ConnectionQualityChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.ConnectionQualityChanged);
|
||||
this.unregisterRoomListener(RoomEvent.ConnectionQualityChanged);
|
||||
if (this.roomEvents.get(RoomEvent.ConnectionQualityChanged)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.ConnectionQualityChanged,
|
||||
(quality: ConnectionQuality, participant: Participant) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -810,9 +933,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.MediaDevicesError) !==
|
||||
oldValues.get(RoomEvent.MediaDevicesError)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.MediaDevicesError);
|
||||
this.unregisterRoomListener(RoomEvent.MediaDevicesError);
|
||||
if (this.roomEvents.get(RoomEvent.MediaDevicesError)) {
|
||||
this.room!.on(RoomEvent.MediaDevicesError, (error: Error) => {
|
||||
this.registerRoomListener(RoomEvent.MediaDevicesError, (error: Error) => {
|
||||
this.updateEventList(
|
||||
RoomEvent.MediaDevicesError,
|
||||
{
|
||||
|
|
@ -830,9 +953,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.TrackStreamStateChanged) !==
|
||||
oldValues.get(RoomEvent.TrackStreamStateChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.TrackStreamStateChanged);
|
||||
this.unregisterRoomListener(RoomEvent.TrackStreamStateChanged);
|
||||
if (this.roomEvents.get(RoomEvent.TrackStreamStateChanged)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.TrackStreamStateChanged,
|
||||
(
|
||||
publication: RemoteTrackPublication,
|
||||
|
|
@ -854,11 +977,11 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.TrackSubscriptionPermissionChanged) !==
|
||||
oldValues.get(RoomEvent.TrackSubscriptionPermissionChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(
|
||||
this.unregisterRoomListener(
|
||||
RoomEvent.TrackSubscriptionPermissionChanged
|
||||
);
|
||||
if (this.roomEvents.get(RoomEvent.TrackSubscriptionPermissionChanged)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.TrackSubscriptionPermissionChanged,
|
||||
(
|
||||
publication: RemoteTrackPublication,
|
||||
|
|
@ -880,9 +1003,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.TrackSubscriptionStatusChanged) !==
|
||||
oldValues.get(RoomEvent.TrackSubscriptionStatusChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.TrackSubscriptionStatusChanged);
|
||||
this.unregisterRoomListener(RoomEvent.TrackSubscriptionStatusChanged);
|
||||
if (this.roomEvents.get(RoomEvent.TrackSubscriptionStatusChanged)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.TrackSubscriptionStatusChanged,
|
||||
(
|
||||
publication: RemoteTrackPublication,
|
||||
|
|
@ -904,9 +1027,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.AudioPlaybackStatusChanged) !==
|
||||
oldValues.get(RoomEvent.AudioPlaybackStatusChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.AudioPlaybackStatusChanged);
|
||||
this.unregisterRoomListener(RoomEvent.AudioPlaybackStatusChanged);
|
||||
if (this.roomEvents.get(RoomEvent.AudioPlaybackStatusChanged)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.AudioPlaybackStatusChanged,
|
||||
(playing: boolean) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -919,14 +1042,34 @@ export class OpenviduInstanceComponent {
|
|||
}
|
||||
}
|
||||
|
||||
if (
|
||||
firstTime ||
|
||||
this.roomEvents.get(RoomEvent.VideoPlaybackStatusChanged) !==
|
||||
oldValues.get(RoomEvent.VideoPlaybackStatusChanged)
|
||||
) {
|
||||
this.unregisterRoomListener(RoomEvent.VideoPlaybackStatusChanged);
|
||||
if (this.roomEvents.get(RoomEvent.VideoPlaybackStatusChanged)) {
|
||||
this.registerRoomListener(
|
||||
RoomEvent.VideoPlaybackStatusChanged,
|
||||
(playing: boolean) => {
|
||||
this.updateEventList(
|
||||
RoomEvent.VideoPlaybackStatusChanged,
|
||||
{ playing },
|
||||
`canPlaybackVideo: ${playing}`
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
firstTime ||
|
||||
this.roomEvents.get(RoomEvent.SignalConnected) !==
|
||||
oldValues.get(RoomEvent.SignalConnected)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.SignalConnected);
|
||||
this.unregisterRoomListener(RoomEvent.SignalConnected);
|
||||
if (this.roomEvents.get(RoomEvent.SignalConnected)) {
|
||||
this.room!.on(RoomEvent.SignalConnected, () => {
|
||||
this.registerRoomListener(RoomEvent.SignalConnected, () => {
|
||||
this.updateEventList(RoomEvent.SignalConnected, {}, '');
|
||||
});
|
||||
}
|
||||
|
|
@ -937,9 +1080,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.RecordingStatusChanged) !==
|
||||
oldValues.get(RoomEvent.RecordingStatusChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.RecordingStatusChanged);
|
||||
this.unregisterRoomListener(RoomEvent.RecordingStatusChanged);
|
||||
if (this.roomEvents.get(RoomEvent.RecordingStatusChanged)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.RecordingStatusChanged,
|
||||
(recording: boolean) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -957,11 +1100,11 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.ParticipantEncryptionStatusChanged) !==
|
||||
oldValues.get(RoomEvent.ParticipantEncryptionStatusChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(
|
||||
this.unregisterRoomListener(
|
||||
RoomEvent.ParticipantEncryptionStatusChanged
|
||||
);
|
||||
if (this.roomEvents.get(RoomEvent.ParticipantEncryptionStatusChanged)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.ParticipantEncryptionStatusChanged,
|
||||
(encrypted: boolean, participant?: Participant) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -979,9 +1122,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.EncryptionError) !==
|
||||
oldValues.get(RoomEvent.EncryptionError)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.EncryptionError);
|
||||
this.unregisterRoomListener(RoomEvent.EncryptionError);
|
||||
if (this.roomEvents.get(RoomEvent.EncryptionError)) {
|
||||
this.room!.on(RoomEvent.EncryptionError, (error: Error) => {
|
||||
this.registerRoomListener(RoomEvent.EncryptionError, (error: Error) => {
|
||||
this.updateEventList(
|
||||
RoomEvent.EncryptionError,
|
||||
{ error: error.message },
|
||||
|
|
@ -996,9 +1139,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.DCBufferStatusChanged) !==
|
||||
oldValues.get(RoomEvent.DCBufferStatusChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.DCBufferStatusChanged);
|
||||
this.unregisterRoomListener(RoomEvent.DCBufferStatusChanged);
|
||||
if (this.roomEvents.get(RoomEvent.DCBufferStatusChanged)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.DCBufferStatusChanged,
|
||||
(isLow: boolean, kind: any) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -1016,9 +1159,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.ActiveDeviceChanged) !==
|
||||
oldValues.get(RoomEvent.ActiveDeviceChanged)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.ActiveDeviceChanged);
|
||||
this.unregisterRoomListener(RoomEvent.ActiveDeviceChanged);
|
||||
if (this.roomEvents.get(RoomEvent.ActiveDeviceChanged)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.ActiveDeviceChanged,
|
||||
(kind: MediaDeviceKind, deviceId: string) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -1036,9 +1179,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.LocalTrackSubscribed) !==
|
||||
oldValues.get(RoomEvent.LocalTrackSubscribed)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.LocalTrackSubscribed);
|
||||
this.unregisterRoomListener(RoomEvent.LocalTrackSubscribed);
|
||||
if (this.roomEvents.get(RoomEvent.LocalTrackSubscribed)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.LocalTrackSubscribed,
|
||||
(
|
||||
publication: LocalTrackPublication,
|
||||
|
|
@ -1054,6 +1197,46 @@ export class OpenviduInstanceComponent {
|
|||
}
|
||||
}
|
||||
|
||||
if (
|
||||
firstTime ||
|
||||
this.roomEvents.get(RoomEvent.ChatMessage) !==
|
||||
oldValues.get(RoomEvent.ChatMessage)
|
||||
) {
|
||||
this.unregisterRoomListener(RoomEvent.ChatMessage);
|
||||
if (this.roomEvents.get(RoomEvent.ChatMessage)) {
|
||||
this.registerRoomListener(
|
||||
RoomEvent.ChatMessage,
|
||||
(message: any, participant?: RemoteParticipant | LocalParticipant) => {
|
||||
this.updateEventList(
|
||||
RoomEvent.ChatMessage,
|
||||
{ message, participant },
|
||||
`${participant?.identity ?? 'unknown'}: ${message.message}`
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
firstTime ||
|
||||
this.roomEvents.get(RoomEvent.MetricsReceived) !==
|
||||
oldValues.get(RoomEvent.MetricsReceived)
|
||||
) {
|
||||
this.unregisterRoomListener(RoomEvent.MetricsReceived);
|
||||
if (this.roomEvents.get(RoomEvent.MetricsReceived)) {
|
||||
this.registerRoomListener(
|
||||
RoomEvent.MetricsReceived,
|
||||
(metrics: any, participant?: Participant) => {
|
||||
this.updateEventList(
|
||||
RoomEvent.MetricsReceived,
|
||||
{ metrics, participant },
|
||||
`${participant?.identity ?? 'unknown'}`
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
firstTime ||
|
||||
this.roomEvents.get(RoomEvent.TranscriptionReceived) !==
|
||||
|
|
@ -1087,9 +1270,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.DataTrackPublished) !==
|
||||
oldValues.get(RoomEvent.DataTrackPublished)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.DataTrackPublished);
|
||||
this.unregisterRoomListener(RoomEvent.DataTrackPublished);
|
||||
if (this.roomEvents.get(RoomEvent.DataTrackPublished)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.DataTrackPublished,
|
||||
(track: RemoteDataTrack) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -1107,9 +1290,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.DataTrackUnpublished) !==
|
||||
oldValues.get(RoomEvent.DataTrackUnpublished)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.DataTrackUnpublished);
|
||||
this.unregisterRoomListener(RoomEvent.DataTrackUnpublished);
|
||||
if (this.roomEvents.get(RoomEvent.DataTrackUnpublished)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.DataTrackUnpublished,
|
||||
(sid: string) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -1127,9 +1310,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.LocalDataTrackPublished) !==
|
||||
oldValues.get(RoomEvent.LocalDataTrackPublished)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.LocalDataTrackPublished);
|
||||
this.unregisterRoomListener(RoomEvent.LocalDataTrackPublished);
|
||||
if (this.roomEvents.get(RoomEvent.LocalDataTrackPublished)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.LocalDataTrackPublished,
|
||||
(track: LocalDataTrack) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -1147,9 +1330,9 @@ export class OpenviduInstanceComponent {
|
|||
this.roomEvents.get(RoomEvent.LocalDataTrackUnpublished) !==
|
||||
oldValues.get(RoomEvent.LocalDataTrackUnpublished)
|
||||
) {
|
||||
this.room?.removeAllListeners(RoomEvent.LocalDataTrackUnpublished);
|
||||
this.unregisterRoomListener(RoomEvent.LocalDataTrackUnpublished);
|
||||
if (this.roomEvents.get(RoomEvent.LocalDataTrackUnpublished)) {
|
||||
this.room!.on(
|
||||
this.registerRoomListener(
|
||||
RoomEvent.LocalDataTrackUnpublished,
|
||||
(sid: string) => {
|
||||
this.updateEventList(
|
||||
|
|
@ -1181,6 +1364,32 @@ export class OpenviduInstanceComponent {
|
|||
|
||||
async disconnectRoom() {
|
||||
if (this.room) {
|
||||
// Clean up early listeners that were never handed off
|
||||
if (this.earlyParticipantConnectedListener) {
|
||||
this.room.removeListener(
|
||||
RoomEvent.ParticipantConnected,
|
||||
this.earlyParticipantConnectedListener as any
|
||||
);
|
||||
this.earlyParticipantConnectedListener = undefined;
|
||||
}
|
||||
for (const [key, listeners] of this.earlyParticipantListeners) {
|
||||
const participant =
|
||||
this.room.localParticipant.sid === key || this.room.localParticipant.identity === key
|
||||
? this.room.localParticipant
|
||||
: this.room.remoteParticipants.get(key);
|
||||
if (participant) {
|
||||
removeAllManagedListeners(participant, listeners);
|
||||
}
|
||||
}
|
||||
this.earlyParticipantEvents.clear();
|
||||
this.earlyParticipantListeners.clear();
|
||||
for (const [, listeners] of this.earlyTrackListeners) {
|
||||
// Track listeners are cleaned up by disconnect, but clear maps
|
||||
listeners.clear();
|
||||
}
|
||||
this.earlyTrackEvents.clear();
|
||||
this.earlyTrackListeners.clear();
|
||||
|
||||
await this.room.disconnect();
|
||||
delete this.room;
|
||||
delete this.localTracks.audioTrack;
|
||||
|
|
@ -1189,6 +1398,58 @@ export class OpenviduInstanceComponent {
|
|||
}
|
||||
}
|
||||
|
||||
private registerEarlyParticipantListeners(participant: Participant) {
|
||||
const key = participant.sid || participant.identity;
|
||||
const buffer: TestAppEvent[] = [];
|
||||
this.earlyParticipantEvents.set(key, buffer);
|
||||
|
||||
const listeners = registerParticipantEventListeners(
|
||||
participant,
|
||||
(eventType, eventContent, eventDescription) => {
|
||||
if (this.participantEvents.size > 0 && !this.participantEvents.get(eventType)) return;
|
||||
const event: TestAppEvent = {
|
||||
eventType,
|
||||
eventCategory: 'ParticipantEvent',
|
||||
eventContent,
|
||||
eventDescription,
|
||||
};
|
||||
buffer.push(event);
|
||||
this.testFeedService.pushNewEvent({ user: this.index, event });
|
||||
|
||||
// When a track becomes available during early registration, register early track listeners too
|
||||
if (eventType === ParticipantEvent.TrackSubscribed && eventContent.track) {
|
||||
this.registerEarlyTrackListeners(eventContent.track);
|
||||
} else if (eventType === ParticipantEvent.LocalTrackPublished && eventContent.publication?.track) {
|
||||
this.registerEarlyTrackListeners(eventContent.publication.track);
|
||||
}
|
||||
},
|
||||
this.decoder
|
||||
);
|
||||
this.earlyParticipantListeners.set(key, listeners);
|
||||
}
|
||||
|
||||
private registerEarlyTrackListeners(track: Track) {
|
||||
const key = track.sid || track.mediaStreamID;
|
||||
const buffer: TestAppEvent[] = [];
|
||||
this.earlyTrackEvents.set(key, buffer);
|
||||
|
||||
const listeners = registerTrackEventListeners(
|
||||
track,
|
||||
(eventType, eventContent, eventDescription) => {
|
||||
if (this.trackEvents.size > 0 && !this.trackEvents.get(eventType)) return;
|
||||
const event: TestAppEvent = {
|
||||
eventType,
|
||||
eventCategory: 'TrackEvent',
|
||||
eventContent,
|
||||
eventDescription,
|
||||
};
|
||||
buffer.push(event);
|
||||
this.testFeedService.pushNewEvent({ user: this.index, event });
|
||||
}
|
||||
);
|
||||
this.earlyTrackListeners.set(key, listeners);
|
||||
}
|
||||
|
||||
async setCameraEnabled() {
|
||||
this.room!.localParticipant.setCameraEnabled(true);
|
||||
}
|
||||
|
|
@ -1253,15 +1514,18 @@ export class OpenviduInstanceComponent {
|
|||
});
|
||||
}
|
||||
|
||||
openRoomEventsDialog() {
|
||||
const oldValues: Map<string, boolean> = new Map(
|
||||
openAllEventsDialog() {
|
||||
const oldRoomValues: Map<string, boolean> = new Map(
|
||||
JSON.parse(JSON.stringify([...this.roomEvents]))
|
||||
);
|
||||
|
||||
const dialogRef = this.dialog.open(EventsDialogComponent, {
|
||||
data: {
|
||||
eventCollection: this.roomEvents,
|
||||
target: 'Session',
|
||||
eventGroups: [
|
||||
{ label: 'RoomEvent', eventCollection: this.roomEvents },
|
||||
{ label: 'ParticipantEvent', eventCollection: this.participantEvents },
|
||||
{ label: 'TrackEvent', eventCollection: this.trackEvents },
|
||||
],
|
||||
target: 'All',
|
||||
},
|
||||
width: '800px',
|
||||
autoFocus: false,
|
||||
|
|
@ -1272,9 +1536,9 @@ export class OpenviduInstanceComponent {
|
|||
if (
|
||||
!!this.room &&
|
||||
JSON.stringify(Array.from(this.roomEvents.entries())) !==
|
||||
JSON.stringify(Array.from(oldValues.entries()))
|
||||
JSON.stringify(Array.from(oldRoomValues.entries()))
|
||||
) {
|
||||
this.setupRoomEventListeners(oldValues, false);
|
||||
this.setupRoomEventListeners(oldRoomValues, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,13 +105,17 @@
|
|||
@for (trackPublication of participant.audioTrackPublications| keyvalue; track trackPublication) {
|
||||
<app-audio-track
|
||||
[index]="index" [trackPublication]="trackPublication.value" [track]="trackPublication.value.audioTrack"
|
||||
[localParticipant]="localParticipant" (newTrackEvent)="onTrackEvent($event)"></app-audio-track>
|
||||
[localParticipant]="localParticipant"
|
||||
[earlyTrackEvents]="earlyTrackEvents" [earlyTrackListeners]="earlyTrackListeners"
|
||||
(newTrackEvent)="onTrackEvent($event)"></app-audio-track>
|
||||
}
|
||||
</div>
|
||||
@for (trackPublication of participant.videoTrackPublications | keyvalue; track trackPublication) {
|
||||
<app-video-track
|
||||
[index]="index" [trackPublication]="trackPublication.value" [track]="trackPublication.value.videoTrack"
|
||||
[localParticipant]="localParticipant" (newTrackEvent)="onTrackEvent($event)"></app-video-track>
|
||||
[localParticipant]="localParticipant"
|
||||
[earlyTrackEvents]="earlyTrackEvents" [earlyTrackListeners]="earlyTrackListeners"
|
||||
(newTrackEvent)="onTrackEvent($event)"></app-video-track>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
|
|||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import {
|
||||
AudioCaptureOptions,
|
||||
ConnectionQuality,
|
||||
CreateLocalTracksOptions,
|
||||
DataPacket_Kind,
|
||||
LocalAudioTrack,
|
||||
LocalDataTrack,
|
||||
LocalParticipant,
|
||||
|
|
@ -23,7 +21,6 @@ import {
|
|||
Room,
|
||||
RoomEvent,
|
||||
ScreenShareCaptureOptions,
|
||||
SubscriptionError,
|
||||
Track,
|
||||
TrackEvent,
|
||||
TrackPublication,
|
||||
|
|
@ -33,7 +30,6 @@ import {
|
|||
createLocalScreenTracks,
|
||||
createLocalVideoTrack,
|
||||
} from 'livekit-client';
|
||||
import { ParticipantPermission } from 'livekit-server-sdk';
|
||||
import {
|
||||
TestAppEvent,
|
||||
TestFeedService,
|
||||
|
|
@ -42,7 +38,10 @@ import { OptionsDialogComponent } from '../dialogs/options-dialog/options-dialog
|
|||
import { VideoTrackComponent } from '../video-track/video-track.component';
|
||||
import { AudioTrackComponent } from '../audio-track/audio-track.component';
|
||||
import { DataTrackComponent } from '../data-track/data-track.component';
|
||||
import { ParticipantEventCallbacks } from 'node_modules/livekit-client/dist/src/room/participant/Participant';
|
||||
import {
|
||||
registerParticipantEventListeners,
|
||||
removeAllManagedListeners,
|
||||
} from 'src/app/utils/event-listener-utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-participant',
|
||||
|
|
@ -60,6 +59,24 @@ export class ParticipantComponent {
|
|||
@Input()
|
||||
index: number;
|
||||
|
||||
@Input()
|
||||
participantEvents: Map<string, boolean> = new Map();
|
||||
|
||||
@Input()
|
||||
trackEvents: Map<string, boolean> = new Map();
|
||||
|
||||
@Input()
|
||||
earlyParticipantEvents: Map<string, TestAppEvent[]> = new Map();
|
||||
|
||||
@Input()
|
||||
earlyParticipantListeners: Map<string, Map<string, (...args: any[]) => void>> = new Map();
|
||||
|
||||
@Input()
|
||||
earlyTrackEvents: Map<string, TestAppEvent[]> = new Map();
|
||||
|
||||
@Input()
|
||||
earlyTrackListeners: Map<string, Map<string, (...args: any[]) => void>> = new Map();
|
||||
|
||||
@Output()
|
||||
sendReliableDataToOneParticipant = new EventEmitter<string>();
|
||||
|
||||
|
|
@ -80,6 +97,8 @@ export class ParticipantComponent {
|
|||
trackPublishOptions?: TrackPublishOptions;
|
||||
|
||||
private decoder = new TextDecoder();
|
||||
private participantEventListeners: Map<string, (...args: any[]) => void> = new Map();
|
||||
private roomListenersFromParticipant: Map<string, (...args: any[]) => void> = new Map();
|
||||
|
||||
private dialog = inject(MatDialog);
|
||||
|
||||
|
|
@ -89,6 +108,19 @@ export class ParticipantComponent {
|
|||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
// Drain early participant events buffered before this component existed
|
||||
const key = this.participant.sid || this.participant.identity;
|
||||
const earlyEvents = this.earlyParticipantEvents?.get(key);
|
||||
if (earlyEvents) {
|
||||
this.events.push(...earlyEvents);
|
||||
this.earlyParticipantEvents.delete(key);
|
||||
}
|
||||
// Remove early listeners and replace with component-owned ones
|
||||
const earlyListeners = this.earlyParticipantListeners?.get(key);
|
||||
if (earlyListeners) {
|
||||
removeAllManagedListeners(this.participant, earlyListeners);
|
||||
this.earlyParticipantListeners.delete(key);
|
||||
}
|
||||
this.setupParticipantEventListeners();
|
||||
this.localParticipant = this.participant.isLocal
|
||||
? (this.participant as LocalParticipant)
|
||||
|
|
@ -106,7 +138,11 @@ export class ParticipantComponent {
|
|||
}
|
||||
|
||||
onTrackEvent(event: TestAppEvent) {
|
||||
if (this.trackEvents.size > 0 && !this.trackEvents.get(event.eventType)) {
|
||||
return;
|
||||
}
|
||||
this.events.push(event);
|
||||
this.testFeedService.pushNewEvent({ user: this.index, event });
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
|
||||
|
|
@ -246,251 +282,16 @@ export class ParticipantComponent {
|
|||
* [ParticipantEventCallbacks]
|
||||
*/
|
||||
setupParticipantEventListeners() {
|
||||
// This is a link to the complete list of Participant events
|
||||
let callbacks: ParticipantEventCallbacks;
|
||||
let events: ParticipantEvent;
|
||||
// Remove any previous listeners
|
||||
removeAllManagedListeners(this.participant, this.participantEventListeners);
|
||||
|
||||
this.participant
|
||||
|
||||
.on(
|
||||
ParticipantEvent.TrackPublished,
|
||||
(publication: RemoteTrackPublication) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.TrackPublished,
|
||||
'ParticipantEvent',
|
||||
{ publication },
|
||||
publication.source
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
.on(
|
||||
ParticipantEvent.TrackSubscribed,
|
||||
(track: RemoteTrack, publication: RemoteTrackPublication) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.TrackSubscribed,
|
||||
'ParticipantEvent',
|
||||
{ track, publication },
|
||||
publication.source
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
.on(
|
||||
ParticipantEvent.TrackSubscriptionFailed,
|
||||
(trackSid: string, reason?: SubscriptionError) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.TrackSubscriptionFailed,
|
||||
'ParticipantEvent',
|
||||
{ trackSid, reason },
|
||||
trackSid +
|
||||
' . Reason: ' +
|
||||
(reason ? SubscriptionError[reason] : reason)
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
.on(
|
||||
ParticipantEvent.TrackUnpublished,
|
||||
(publication: RemoteTrackPublication) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.TrackUnpublished,
|
||||
'ParticipantEvent',
|
||||
{ publication },
|
||||
publication.source
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
.on(
|
||||
ParticipantEvent.TrackUnsubscribed,
|
||||
(track: RemoteTrack, publication: RemoteTrackPublication) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.TrackUnsubscribed,
|
||||
'ParticipantEvent',
|
||||
{ track, publication },
|
||||
track.source
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
.on(ParticipantEvent.TrackMuted, (publication: TrackPublication) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.TrackMuted,
|
||||
'ParticipantEvent',
|
||||
{ publication },
|
||||
publication.source
|
||||
);
|
||||
})
|
||||
|
||||
.on(ParticipantEvent.TrackUnmuted, (publication: TrackPublication) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.TrackUnmuted,
|
||||
'ParticipantEvent',
|
||||
{ publication },
|
||||
publication.source
|
||||
);
|
||||
})
|
||||
|
||||
.on(
|
||||
ParticipantEvent.LocalTrackPublished,
|
||||
(publication: LocalTrackPublication) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.LocalTrackPublished,
|
||||
'ParticipantEvent',
|
||||
{ publication },
|
||||
publication.source
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
.on(
|
||||
ParticipantEvent.LocalTrackUnpublished,
|
||||
(publication: LocalTrackPublication) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.LocalTrackUnpublished,
|
||||
'ParticipantEvent',
|
||||
{ publication },
|
||||
publication.source
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
.on(
|
||||
ParticipantEvent.ParticipantMetadataChanged,
|
||||
(prevMetadata: string | undefined) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.ParticipantMetadataChanged,
|
||||
'ParticipantEvent',
|
||||
{ prevMetadata },
|
||||
`previous: ${prevMetadata}, new: ${this.participant.metadata}`
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
.on(ParticipantEvent.ParticipantNameChanged, (name: string) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.ParticipantNameChanged,
|
||||
'ParticipantEvent',
|
||||
{ name },
|
||||
`${name}`
|
||||
);
|
||||
})
|
||||
|
||||
.on(
|
||||
ParticipantEvent.DataReceived,
|
||||
(payload: Uint8Array, kind: DataPacket_Kind) => {
|
||||
let decodedPayload = this.decoder.decode(payload);
|
||||
decodedPayload += ` (kind: ${DataPacket_Kind[kind]})`;
|
||||
this.updateEventList(
|
||||
ParticipantEvent.DataReceived,
|
||||
'ParticipantEvent',
|
||||
{ payload: decodedPayload, kind },
|
||||
decodedPayload
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
.on(ParticipantEvent.IsSpeakingChanged, (speaking: boolean) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.IsSpeakingChanged,
|
||||
'ParticipantEvent',
|
||||
{ speaking },
|
||||
`${speaking}`
|
||||
);
|
||||
})
|
||||
|
||||
.on(
|
||||
ParticipantEvent.ConnectionQualityChanged,
|
||||
(connectionQuality: ConnectionQuality) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.ConnectionQualityChanged,
|
||||
'ParticipantEvent',
|
||||
{ connectionQuality },
|
||||
`${connectionQuality}`
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
.on(
|
||||
ParticipantEvent.TrackStreamStateChanged,
|
||||
(
|
||||
publication: RemoteTrackPublication,
|
||||
streamState: Track.StreamState
|
||||
) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.TrackStreamStateChanged,
|
||||
'ParticipantEvent',
|
||||
{ publication, streamState },
|
||||
`${publication.source}: ${streamState}`
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
.on(
|
||||
ParticipantEvent.TrackSubscriptionPermissionChanged,
|
||||
(
|
||||
publication: RemoteTrackPublication,
|
||||
status: TrackPublication.PermissionStatus
|
||||
) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.TrackSubscriptionPermissionChanged,
|
||||
'ParticipantEvent',
|
||||
{ publication, status },
|
||||
`${publication.source}: ${status}`
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
.on(ParticipantEvent.MediaDevicesError, (error: Error) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.MediaDevicesError,
|
||||
'ParticipantEvent',
|
||||
{ error },
|
||||
`${error.message}`
|
||||
);
|
||||
})
|
||||
|
||||
.on(
|
||||
ParticipantEvent.ParticipantPermissionsChanged,
|
||||
(prevPermissions?: ParticipantPermission) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.ParticipantPermissionsChanged,
|
||||
'ParticipantEvent',
|
||||
{ prevPermissions },
|
||||
`previous: ${prevPermissions}, new: ${JSON.stringify(
|
||||
this.participant.permissions
|
||||
)}`
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
.on(
|
||||
ParticipantEvent.TrackSubscriptionStatusChanged,
|
||||
(
|
||||
publication: RemoteTrackPublication,
|
||||
status: TrackPublication.SubscriptionStatus
|
||||
) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.TrackSubscriptionStatusChanged,
|
||||
'ParticipantEvent',
|
||||
{ publication, status },
|
||||
`${publication.source}: ${status}`
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
.on(
|
||||
ParticipantEvent.LocalTrackSubscribed,
|
||||
(trackPublication: LocalTrackPublication) => {
|
||||
this.updateEventList(
|
||||
ParticipantEvent.LocalTrackSubscribed,
|
||||
'ParticipantEvent',
|
||||
{ trackPublication },
|
||||
trackPublication.source
|
||||
);
|
||||
}
|
||||
);
|
||||
this.participantEventListeners = registerParticipantEventListeners(
|
||||
this.participant,
|
||||
(eventType, eventContent, eventDescription) => {
|
||||
this.updateEventList(eventType, 'ParticipantEvent', eventContent, eventDescription);
|
||||
},
|
||||
this.decoder
|
||||
);
|
||||
}
|
||||
|
||||
updateEventList(
|
||||
|
|
@ -499,6 +300,9 @@ export class ParticipantComponent {
|
|||
eventContent: any,
|
||||
eventDescription: string
|
||||
) {
|
||||
if (this.participantEvents.size > 0 && !this.participantEvents.get(eventType)) {
|
||||
return;
|
||||
}
|
||||
const event: TestAppEvent = {
|
||||
eventType,
|
||||
eventCategory,
|
||||
|
|
@ -518,23 +322,22 @@ export class ParticipantComponent {
|
|||
}
|
||||
|
||||
private setupDataTrackListeners() {
|
||||
this.room.on(
|
||||
RoomEvent.DataTrackPublished,
|
||||
(track: RemoteDataTrack) => {
|
||||
if (track.publisherIdentity === this.participant.identity) {
|
||||
this.remoteDataTracks.push(track);
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
}
|
||||
);
|
||||
this.room.on(
|
||||
RoomEvent.DataTrackUnpublished,
|
||||
(sid: string) => {
|
||||
this.remoteDataTracks = this.remoteDataTracks.filter(
|
||||
(t) => t.info.sid !== sid
|
||||
);
|
||||
const publishedListener = (track: RemoteDataTrack) => {
|
||||
if (track.publisherIdentity === this.participant.identity) {
|
||||
this.remoteDataTracks.push(track);
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
);
|
||||
};
|
||||
this.room.addListener(RoomEvent.DataTrackPublished, publishedListener);
|
||||
this.roomListenersFromParticipant.set(RoomEvent.DataTrackPublished, publishedListener as any);
|
||||
|
||||
const unpublishedListener = (sid: string) => {
|
||||
this.remoteDataTracks = this.remoteDataTracks.filter(
|
||||
(t) => t.info.sid !== sid
|
||||
);
|
||||
this.cdr.detectChanges();
|
||||
};
|
||||
this.room.addListener(RoomEvent.DataTrackUnpublished, unpublishedListener);
|
||||
this.roomListenersFromParticipant.set(RoomEvent.DataTrackUnpublished, unpublishedListener as any);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import {
|
|||
TrackEvent,
|
||||
LocalTrack,
|
||||
RemoteTrack,
|
||||
TrackEventCallbacks,
|
||||
RemoteTrackPublication,
|
||||
AudioTrack,
|
||||
VideoTrack,
|
||||
|
|
@ -22,6 +21,10 @@ import {
|
|||
TestAppEvent,
|
||||
TestFeedService,
|
||||
} from 'src/app/services/test-feed.service';
|
||||
import {
|
||||
registerTrackEventListeners,
|
||||
removeAllManagedListeners,
|
||||
} from 'src/app/utils/event-listener-utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-track',
|
||||
|
|
@ -42,6 +45,12 @@ export class TrackComponent {
|
|||
@Input()
|
||||
localParticipant: LocalParticipant | undefined;
|
||||
|
||||
@Input()
|
||||
earlyTrackEvents: Map<string, TestAppEvent[]> = new Map();
|
||||
|
||||
@Input()
|
||||
earlyTrackListeners: Map<string, Map<string, (...args: any[]) => void>> = new Map();
|
||||
|
||||
protected finalElementRefId: string = '';
|
||||
private indexId: string;
|
||||
private trackId: string;
|
||||
|
|
@ -52,6 +61,8 @@ export class TrackComponent {
|
|||
trackSubscribed: boolean = true;
|
||||
trackEnabled: boolean = true;
|
||||
|
||||
private trackEventListeners: Map<string, (...args: any[]) => void> = new Map();
|
||||
|
||||
constructor(protected testFeedService: TestFeedService) {}
|
||||
|
||||
@Input() set index(index: number) {
|
||||
|
|
@ -62,6 +73,23 @@ export class TrackComponent {
|
|||
@Input() set track(track: AudioTrack | VideoTrack | undefined) {
|
||||
this._track = track;
|
||||
|
||||
// Drain early track events buffered before this component existed
|
||||
if (this._track) {
|
||||
const key = this._track.sid || this._track.mediaStreamID;
|
||||
const earlyEvents = this.earlyTrackEvents?.get(key);
|
||||
if (earlyEvents) {
|
||||
for (const event of earlyEvents) {
|
||||
this.newTrackEvent.emit(event as any);
|
||||
}
|
||||
this.earlyTrackEvents.delete(key);
|
||||
}
|
||||
const earlyListeners = this.earlyTrackListeners?.get(key);
|
||||
if (earlyListeners) {
|
||||
removeAllManagedListeners(this._track, earlyListeners);
|
||||
this.earlyTrackListeners.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
this.setupTrackEventListeners();
|
||||
|
||||
this.trackId = `-${this.getTrackOrigin()}--${this._track?.kind}--${
|
||||
|
|
@ -115,93 +143,22 @@ export class TrackComponent {
|
|||
}
|
||||
|
||||
protected setupTrackEventListeners() {
|
||||
// This is a link to the complete list of Track events
|
||||
let callbacks: TrackEventCallbacks;
|
||||
let events: TrackEvent;
|
||||
if (!this._track) return;
|
||||
|
||||
this._track
|
||||
?.on(TrackEvent.Message, () => {
|
||||
// Clear previous listeners (in case track changed)
|
||||
removeAllManagedListeners(this._track, this.trackEventListeners);
|
||||
|
||||
this.trackEventListeners = registerTrackEventListeners(
|
||||
this._track,
|
||||
(eventType, eventContent, eventDescription) => {
|
||||
this.newTrackEvent.emit({
|
||||
eventType: TrackEvent.Message,
|
||||
eventType,
|
||||
eventCategory: 'TrackEvent',
|
||||
eventContent: {},
|
||||
eventDescription: this._track!.source,
|
||||
eventContent,
|
||||
eventDescription,
|
||||
});
|
||||
})
|
||||
.on(TrackEvent.Muted, () => {
|
||||
this.newTrackEvent.emit({
|
||||
eventType: TrackEvent.Muted,
|
||||
eventCategory: 'TrackEvent',
|
||||
eventContent: {},
|
||||
eventDescription: this._track!.source,
|
||||
});
|
||||
})
|
||||
.on(TrackEvent.Unmuted, () => {
|
||||
this.newTrackEvent.emit({
|
||||
eventType: TrackEvent.Unmuted,
|
||||
eventCategory: 'TrackEvent',
|
||||
eventContent: {},
|
||||
eventDescription: this._track!.source,
|
||||
});
|
||||
})
|
||||
.on(TrackEvent.AudioSilenceDetected, () => {
|
||||
this.newTrackEvent.emit({
|
||||
eventType: TrackEvent.AudioSilenceDetected,
|
||||
eventCategory: 'TrackEvent',
|
||||
eventContent: {},
|
||||
eventDescription: this._track!.source,
|
||||
});
|
||||
})
|
||||
.on(TrackEvent.Restarted, () => {
|
||||
this.newTrackEvent.emit({
|
||||
eventType: TrackEvent.Restarted,
|
||||
eventCategory: 'TrackEvent',
|
||||
eventContent: {},
|
||||
eventDescription: this._track!.source,
|
||||
});
|
||||
})
|
||||
.on(TrackEvent.Ended, () => {
|
||||
this.newTrackEvent.emit({
|
||||
eventType: TrackEvent.Ended,
|
||||
eventCategory: 'TrackEvent',
|
||||
eventContent: {},
|
||||
eventDescription: this._track!.source,
|
||||
});
|
||||
})
|
||||
.on(TrackEvent.VisibilityChanged, (visible: boolean) => {
|
||||
this.newTrackEvent.emit({
|
||||
eventType: TrackEvent.VisibilityChanged,
|
||||
eventCategory: 'TrackEvent',
|
||||
eventContent: { visible, track: this._track },
|
||||
eventDescription: `${this._track!.source} is visible: ${visible}`,
|
||||
});
|
||||
})
|
||||
.on(TrackEvent.VideoDimensionsChanged, (dimensions: Track.Dimensions) => {
|
||||
this.newTrackEvent.emit({
|
||||
eventType: TrackEvent.VideoDimensionsChanged,
|
||||
eventCategory: 'TrackEvent',
|
||||
eventContent: { dimensions, track: this._track },
|
||||
eventDescription: `${this._track?.source} ${JSON.stringify(
|
||||
dimensions
|
||||
)}`,
|
||||
});
|
||||
})
|
||||
.on(TrackEvent.UpstreamPaused, () => {
|
||||
this.newTrackEvent.emit({
|
||||
eventType: TrackEvent.UpstreamPaused,
|
||||
eventCategory: 'TrackEvent',
|
||||
eventContent: {},
|
||||
eventDescription: this._track!.source,
|
||||
});
|
||||
})
|
||||
.on(TrackEvent.UpstreamResumed, () => {
|
||||
this.newTrackEvent.emit({
|
||||
eventType: TrackEvent.UpstreamResumed,
|
||||
eventCategory: 'TrackEvent',
|
||||
eventContent: {},
|
||||
eventDescription: this._track!.source,
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected getTrackOrigin(): string {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,331 @@
|
|||
import {
|
||||
ConnectionQuality,
|
||||
DataPacket_Kind,
|
||||
LocalTrackPublication,
|
||||
LocalVideoTrack,
|
||||
Participant,
|
||||
ParticipantEvent,
|
||||
RemoteTrack,
|
||||
RemoteTrackPublication,
|
||||
SubscriptionError,
|
||||
Track,
|
||||
TrackEvent,
|
||||
TrackPublication,
|
||||
} from 'livekit-client';
|
||||
import type { TranscriptionSegment, ChatMessage } from 'livekit-client';
|
||||
import { ParticipantPermission } from 'livekit-server-sdk';
|
||||
|
||||
export type ParticipantOnEvent = (
|
||||
eventType: ParticipantEvent,
|
||||
eventContent: any,
|
||||
eventDescription: string
|
||||
) => void;
|
||||
|
||||
export type TrackOnEvent = (
|
||||
eventType: TrackEvent,
|
||||
eventContent: any,
|
||||
eventDescription: string
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* Registers ALL participant event listeners on the given participant.
|
||||
* Returns the listener map for later cleanup/replacement.
|
||||
*/
|
||||
export function registerParticipantEventListeners(
|
||||
participant: Participant,
|
||||
onEvent: ParticipantOnEvent,
|
||||
decoder: TextDecoder
|
||||
): Map<string, (...args: any[]) => void> {
|
||||
const listeners = new Map<string, (...args: any[]) => void>();
|
||||
|
||||
const reg = (event: ParticipantEvent, listener: (...args: any[]) => void) => {
|
||||
participant.addListener(event as any, listener as any);
|
||||
listeners.set(event, listener);
|
||||
};
|
||||
|
||||
reg(ParticipantEvent.TrackPublished, (publication: RemoteTrackPublication) => {
|
||||
onEvent(ParticipantEvent.TrackPublished, { publication }, publication.source);
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.TrackSubscribed, (track: RemoteTrack, publication: RemoteTrackPublication) => {
|
||||
onEvent(ParticipantEvent.TrackSubscribed, { track, publication }, publication.source);
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.TrackSubscriptionFailed, (trackSid: string, reason?: SubscriptionError) => {
|
||||
onEvent(
|
||||
ParticipantEvent.TrackSubscriptionFailed,
|
||||
{ trackSid, reason },
|
||||
trackSid + ' . Reason: ' + (reason ? SubscriptionError[reason] : reason)
|
||||
);
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.TrackUnpublished, (publication: RemoteTrackPublication) => {
|
||||
onEvent(ParticipantEvent.TrackUnpublished, { publication }, publication.source);
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.TrackUnsubscribed, (track: RemoteTrack, publication: RemoteTrackPublication) => {
|
||||
onEvent(ParticipantEvent.TrackUnsubscribed, { track, publication }, track.source);
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.TrackMuted, (publication: TrackPublication) => {
|
||||
onEvent(ParticipantEvent.TrackMuted, { publication }, publication.source);
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.TrackUnmuted, (publication: TrackPublication) => {
|
||||
onEvent(ParticipantEvent.TrackUnmuted, { publication }, publication.source);
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.LocalTrackPublished, (publication: LocalTrackPublication) => {
|
||||
onEvent(ParticipantEvent.LocalTrackPublished, { publication }, publication.source);
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.LocalTrackUnpublished, (publication: LocalTrackPublication) => {
|
||||
onEvent(ParticipantEvent.LocalTrackUnpublished, { publication }, publication.source);
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.ParticipantMetadataChanged, (prevMetadata: string | undefined) => {
|
||||
onEvent(
|
||||
ParticipantEvent.ParticipantMetadataChanged,
|
||||
{ prevMetadata },
|
||||
`previous: ${prevMetadata}, new: ${participant.metadata}`
|
||||
);
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.ParticipantNameChanged, (name: string) => {
|
||||
onEvent(ParticipantEvent.ParticipantNameChanged, { name }, `${name}`);
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.DataReceived, (payload: Uint8Array, kind: DataPacket_Kind) => {
|
||||
let decodedPayload = decoder.decode(payload);
|
||||
decodedPayload += ` (kind: ${DataPacket_Kind[kind]})`;
|
||||
onEvent(ParticipantEvent.DataReceived, { payload: decodedPayload, kind }, decodedPayload);
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.IsSpeakingChanged, (speaking: boolean) => {
|
||||
onEvent(ParticipantEvent.IsSpeakingChanged, { speaking }, `${speaking}`);
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.ConnectionQualityChanged, (connectionQuality: ConnectionQuality) => {
|
||||
onEvent(ParticipantEvent.ConnectionQualityChanged, { connectionQuality }, `${connectionQuality}`);
|
||||
});
|
||||
|
||||
reg(
|
||||
ParticipantEvent.TrackStreamStateChanged,
|
||||
(publication: RemoteTrackPublication, streamState: Track.StreamState) => {
|
||||
onEvent(
|
||||
ParticipantEvent.TrackStreamStateChanged,
|
||||
{ publication, streamState },
|
||||
`${publication.source}: ${streamState}`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
reg(
|
||||
ParticipantEvent.TrackSubscriptionPermissionChanged,
|
||||
(publication: RemoteTrackPublication, status: TrackPublication.PermissionStatus) => {
|
||||
onEvent(
|
||||
ParticipantEvent.TrackSubscriptionPermissionChanged,
|
||||
{ publication, status },
|
||||
`${publication.source}: ${status}`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
reg(ParticipantEvent.MediaDevicesError, (error: Error) => {
|
||||
onEvent(ParticipantEvent.MediaDevicesError, { error }, `${error.message}`);
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.ParticipantPermissionsChanged, (prevPermissions?: ParticipantPermission) => {
|
||||
onEvent(
|
||||
ParticipantEvent.ParticipantPermissionsChanged,
|
||||
{ prevPermissions },
|
||||
`previous: ${prevPermissions}, new: ${JSON.stringify(participant.permissions)}`
|
||||
);
|
||||
});
|
||||
|
||||
reg(
|
||||
ParticipantEvent.TrackSubscriptionStatusChanged,
|
||||
(publication: RemoteTrackPublication, status: TrackPublication.SubscriptionStatus) => {
|
||||
onEvent(
|
||||
ParticipantEvent.TrackSubscriptionStatusChanged,
|
||||
{ publication, status },
|
||||
`${publication.source}: ${status}`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
reg(ParticipantEvent.LocalTrackSubscribed, (trackPublication: LocalTrackPublication) => {
|
||||
onEvent(ParticipantEvent.LocalTrackSubscribed, { trackPublication }, trackPublication.source);
|
||||
});
|
||||
|
||||
reg(
|
||||
ParticipantEvent.LocalTrackCpuConstrained,
|
||||
(track: LocalVideoTrack, publication: LocalTrackPublication) => {
|
||||
onEvent(ParticipantEvent.LocalTrackCpuConstrained, { track, publication }, publication.source);
|
||||
}
|
||||
);
|
||||
|
||||
reg(ParticipantEvent.SipDTMFReceived, (dtmf: any) => {
|
||||
onEvent(ParticipantEvent.SipDTMFReceived, { dtmf }, JSON.stringify(dtmf));
|
||||
});
|
||||
|
||||
reg(
|
||||
ParticipantEvent.TranscriptionReceived,
|
||||
(transcription: TranscriptionSegment[], publication?: TrackPublication) => {
|
||||
onEvent(
|
||||
ParticipantEvent.TranscriptionReceived,
|
||||
{ transcription, publication },
|
||||
`segments: ${transcription.length}, source: ${publication?.source ?? 'unknown'}`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
reg(ParticipantEvent.AudioStreamAcquired, () => {
|
||||
onEvent(ParticipantEvent.AudioStreamAcquired, {}, '');
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.AttributesChanged, (changedAttributes: Record<string, string>) => {
|
||||
onEvent(ParticipantEvent.AttributesChanged, { changedAttributes }, JSON.stringify(changedAttributes));
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.ChatMessage, (msg: ChatMessage) => {
|
||||
onEvent(ParticipantEvent.ChatMessage, { msg }, msg.message);
|
||||
});
|
||||
|
||||
reg(ParticipantEvent.Active, () => {
|
||||
onEvent(ParticipantEvent.Active, {}, '');
|
||||
});
|
||||
|
||||
return listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all listeners in the given map from the target emitter.
|
||||
*/
|
||||
export function removeAllManagedListeners(
|
||||
target: any,
|
||||
listeners: Map<string, (...args: any[]) => void>
|
||||
) {
|
||||
for (const [event, listener] of listeners) {
|
||||
target.removeListener(event, listener);
|
||||
}
|
||||
listeners.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers ALL track event listeners on the given track.
|
||||
* Returns the listener map for later cleanup/replacement.
|
||||
*/
|
||||
export function registerTrackEventListeners(
|
||||
track: Track,
|
||||
onEvent: TrackOnEvent
|
||||
): Map<string, (...args: any[]) => void> {
|
||||
const listeners = new Map<string, (...args: any[]) => void>();
|
||||
|
||||
const reg = (event: TrackEvent, listener: (...args: any[]) => void) => {
|
||||
track.addListener(event as any, listener as any);
|
||||
listeners.set(event, listener);
|
||||
};
|
||||
|
||||
reg(TrackEvent.Message, () => {
|
||||
onEvent(TrackEvent.Message, {}, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.Muted, () => {
|
||||
onEvent(TrackEvent.Muted, {}, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.Unmuted, () => {
|
||||
onEvent(TrackEvent.Unmuted, {}, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.Restarted, () => {
|
||||
onEvent(TrackEvent.Restarted, {}, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.Ended, () => {
|
||||
onEvent(TrackEvent.Ended, {}, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.CpuConstrained, () => {
|
||||
onEvent(TrackEvent.CpuConstrained, {}, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.UpdateSettings, () => {
|
||||
onEvent(TrackEvent.UpdateSettings, {}, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.UpdateSubscription, () => {
|
||||
onEvent(TrackEvent.UpdateSubscription, {}, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.AudioPlaybackStarted, () => {
|
||||
onEvent(TrackEvent.AudioPlaybackStarted, {}, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.AudioPlaybackFailed, (error?: Error) => {
|
||||
onEvent(TrackEvent.AudioPlaybackFailed, { error }, `${track.source} ${error?.message ?? ''}`);
|
||||
});
|
||||
|
||||
reg(TrackEvent.AudioSilenceDetected, () => {
|
||||
onEvent(TrackEvent.AudioSilenceDetected, {}, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.VisibilityChanged, (visible: boolean) => {
|
||||
onEvent(TrackEvent.VisibilityChanged, { visible, track }, `${track.source} is visible: ${visible}`);
|
||||
});
|
||||
|
||||
reg(TrackEvent.VideoDimensionsChanged, (dimensions: Track.Dimensions) => {
|
||||
onEvent(
|
||||
TrackEvent.VideoDimensionsChanged,
|
||||
{ dimensions, track },
|
||||
`${track.source} ${JSON.stringify(dimensions)}`
|
||||
);
|
||||
});
|
||||
|
||||
reg(TrackEvent.VideoPlaybackStarted, () => {
|
||||
onEvent(TrackEvent.VideoPlaybackStarted, {}, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.VideoPlaybackFailed, (error?: Error) => {
|
||||
onEvent(TrackEvent.VideoPlaybackFailed, { error }, `${track.source} ${error?.message ?? ''}`);
|
||||
});
|
||||
|
||||
reg(TrackEvent.ElementAttached, (element: HTMLMediaElement) => {
|
||||
onEvent(TrackEvent.ElementAttached, { element }, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.ElementDetached, (element: HTMLMediaElement) => {
|
||||
onEvent(TrackEvent.ElementDetached, { element }, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.UpstreamPaused, () => {
|
||||
onEvent(TrackEvent.UpstreamPaused, {}, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.UpstreamResumed, () => {
|
||||
onEvent(TrackEvent.UpstreamResumed, {}, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.TrackProcessorUpdate, (processor?: any) => {
|
||||
onEvent(TrackEvent.TrackProcessorUpdate, { processor }, track.source);
|
||||
});
|
||||
|
||||
reg(TrackEvent.AudioTrackFeatureUpdate, (_track: any, feature: any, enabled: boolean) => {
|
||||
onEvent(
|
||||
TrackEvent.AudioTrackFeatureUpdate,
|
||||
{ feature, enabled },
|
||||
`${track.source} feature: ${feature}, enabled: ${enabled}`
|
||||
);
|
||||
});
|
||||
|
||||
reg(TrackEvent.TimeSyncUpdate, (update: { timestamp: number; rtpTimestamp: number }) => {
|
||||
onEvent(TrackEvent.TimeSyncUpdate, { update }, `${track.source} timestamp: ${update.timestamp}`);
|
||||
});
|
||||
|
||||
reg(TrackEvent.PreConnectBufferFlushed, (buffer: any) => {
|
||||
onEvent(TrackEvent.PreConnectBufferFlushed, { buffer }, track.source);
|
||||
});
|
||||
|
||||
return listeners;
|
||||
}
|
||||
Loading…
Reference in New Issue