openvidu/openvidu-testapp/src/app/components/participant/participant.component.ts

461 lines
12 KiB
TypeScript

import { Component, EventEmitter, Input, Output } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import {
AudioCaptureOptions,
ConnectionQuality,
CreateLocalTracksOptions,
DataPacket_Kind,
LocalAudioTrack,
LocalParticipant,
LocalTrack,
LocalTrackPublication,
LocalVideoTrack,
Participant,
ParticipantEvent,
RemoteTrack,
RemoteTrackPublication,
Room,
ScreenShareCaptureOptions,
SubscriptionError,
Track,
TrackEvent,
TrackPublication,
TrackPublishOptions,
VideoCaptureOptions,
createLocalAudioTrack,
createLocalScreenTracks,
createLocalVideoTrack,
} from 'livekit-client';
import { ParticipantEventCallbacks } from 'livekit-client/dist/src/room/participant/Participant';
import { ParticipantPermission } from 'livekit-server-sdk';
import {
TestAppEvent,
TestFeedService,
} from 'src/app/services/test-feed.service';
import { OptionsDialogComponent } from '../dialogs/options-dialog/options-dialog.component';
@Component({
selector: 'app-participant',
templateUrl: './participant.component.html',
styleUrls: ['./participant.component.css'],
})
export class ParticipantComponent {
@Input()
participant: Participant;
@Input()
room: Room;
@Input()
index: number;
@Output()
sendDataToOneParticipant = new EventEmitter<string>();
localParticipant: LocalParticipant | undefined;
events: TestAppEvent[] = [];
createLocalTracksOptions: CreateLocalTracksOptions;
screenShareCaptureOptions: ScreenShareCaptureOptions = {};
trackPublishOptions?: TrackPublishOptions;
private decoder = new TextDecoder();
constructor(
private testFeedService: TestFeedService,
private dialog: MatDialog
) {}
ngOnInit() {
this.setupParticipantEventListeners();
this.localParticipant = this.participant.isLocal
? (this.participant as LocalParticipant)
: undefined;
this.createLocalTracksOptions = {
audio: JSON.parse(JSON.stringify(this.room.options.audioCaptureDefaults)),
video: JSON.parse(JSON.stringify(this.room.options.videoCaptureDefaults)),
};
this.trackPublishOptions = JSON.parse(
JSON.stringify(this.room.options.publishDefaults)
);
}
async addVideoTrack() {
const options =
this.createLocalTracksOptions.video === true
? undefined
: (this.createLocalTracksOptions.video as VideoCaptureOptions);
const localVideoTrack: LocalVideoTrack = await createLocalVideoTrack(
options
);
(this.participant as LocalParticipant).publishTrack(
localVideoTrack,
this.trackPublishOptions
);
}
async addAudioTrack() {
const options =
this.createLocalTracksOptions.audio === true
? undefined
: (this.createLocalTracksOptions.audio as AudioCaptureOptions);
const localAudioTrack: LocalAudioTrack = await createLocalAudioTrack(
options
);
(this.participant as LocalParticipant).publishTrack(
localAudioTrack,
this.trackPublishOptions
);
}
async addScreenTrack() {
const localScreenTracks: LocalTrack[] = await createLocalScreenTracks(
this.screenShareCaptureOptions
);
localScreenTracks.forEach((track) =>
(this.participant as LocalParticipant).publishTrack(
track,
this.trackPublishOptions
)
);
}
openVideoTrackOptionsDialog() {
const dialogRef = this.dialog.open(OptionsDialogComponent, {
data: {
createLocalTracksOptions: {
video: this.createLocalTracksOptions.video,
},
allowDisablingVideo: false,
},
});
dialogRef.afterClosed().subscribe((result) => {
if (!!result) {
if (typeof result.createLocalTracksOptions.video === 'boolean') {
this.createLocalTracksOptions.video =
this.room.options.videoCaptureDefaults!;
} else {
this.createLocalTracksOptions.video = result.createLocalTracksOptions
.video as VideoCaptureOptions;
}
}
});
}
openAudioTrackOptionsDialog() {
const dialogRef = this.dialog.open(OptionsDialogComponent, {
data: {
createLocalTracksOptions: {
audio: this.createLocalTracksOptions.audio,
},
allowDisablingAudio: false,
},
});
dialogRef.afterClosed().subscribe((result) => {
if (!!result) {
this.createLocalTracksOptions.audio =
result.createLocalTracksOptions.audio;
}
});
}
openScreenTrackOptionsDialog() {
const dialogRef = this.dialog.open(OptionsDialogComponent, {
data: {
shareScreen: true,
screenShareCaptureOptions: this.screenShareCaptureOptions,
allowDisablingScreen: false,
},
});
dialogRef.afterClosed().subscribe((result) => {
if (!!result) {
this.screenShareCaptureOptions = result.screenShareCaptureOptions;
}
});
}
openTrackPublishOptionsDialog() {
const dialogRef = this.dialog.open(OptionsDialogComponent, {
data: {
trackPublishOptions: this.trackPublishOptions,
},
});
dialogRef.afterClosed().subscribe((result) => {
if (!!result) {
this.trackPublishOptions = result.trackPublishOptions;
}
});
}
/**
* [ParticipantEventCallbacks]
*/
setupParticipantEventListeners() {
// This is a link to the complete list of Participant events
let callbacks: ParticipantEventCallbacks;
let events: ParticipantEvent;
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) => {
const decodedPayload = this.decoder.decode(payload);
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
);
}
);
}
updateEventList(
eventType: ParticipantEvent | TrackEvent,
eventCategory: 'ParticipantEvent' | 'TrackEvent',
eventContent: any,
eventDescription: string
) {
const event: TestAppEvent = {
eventType,
eventCategory,
eventContent,
eventDescription,
};
this.events.push(event);
this.testFeedService.pushNewEvent({ user: this.index, event });
}
sendData() {
this.sendDataToOneParticipant.emit(this.participant.identity);
}
}