2025-03-24 18:57:55 +00:00

222 lines
7.6 KiB
JavaScript

import { AzureCommunicationTokenCredential } from '@azure/communication-common';
import { CallClient, LocalVideoStream, VideoStreamRenderer } from '@azure/communication-calling';
class VideoCall {
constructor() {
this.callClient = null;
this.callAgent = null;
this.deviceManager = null;
this.localVideoStream = null;
this.call = null;
this.videoElement = document.createElement('video');
this.videoElement.setAttribute('autoplay', '');
this.videoElement.setAttribute('muted', '');
this.stateChangedCallback = null;
this.remoteParticipantsUpdated = null;
this.isLocalVideoStartedChanged = null;
this.localVideoStreamsUpdated = null;
this.idChanged = null;
this.onCreateLocalVideoStream = null;
this.remoteParticipantStateChanged = null;
this.remoteVideoIsAvailableChanged = null;
this.onGetServerCallID = null;
}
async init(userAccessToken, options) {
this.stateChangedCallback = options.stateChangedCallback;
this.remoteParticipantsUpdated = options.remoteParticipantsUpdated;
this.isLocalVideoStartedChanged = options.isLocalVideoStartedChanged;
this.localVideoStreamsUpdated = options.localVideoStreamsUpdated;
this.idChanged = options.idChanged;
this.onCreateLocalVideoStream = options.onCreateLocalVideoStream;
this.remoteParticipantStateChanged = options.remoteParticipantStateChanged;
this.remoteVideoIsAvailableChanged = options.remoteVideoIsAvailableChanged;
const tokenCredential = new AzureCommunicationTokenCredential(userAccessToken);
this.callClient = new CallClient();
this.callAgent = await this.callClient.createCallAgent(tokenCredential);
this.deviceManager = await this.callClient.getDeviceManager();
await this.deviceManager.askDevicePermission({ audio: true, video: true });
const cameras = await this.deviceManager.getCameras();
this.localVideoStream = new LocalVideoStream(cameras[0]);
}
stopLocalVideo() {
if (this.call) {
this.call.stopVideo(this.localVideoStream);
}
}
async joinRoom(roomId) {
// Assuming you have a room ID and call the appropriate ACS API to join a room
// This is just a placeholder, replace with actual logic
console.log('Joining room:', roomId);
if (this.callAgent) {
try {
const localVideoStream = await this.createLocalVideoStream();
const videoOptions = localVideoStream ? { localVideoStreams: [localVideoStream] } : undefined;
const roomCallLocator = { roomId: roomId };
let call = this.callAgent.join(roomCallLocator, { videoOptions });
this.subscribeToCall(call);
this.callAgent.on('callsUpdated', e => {
e.added.forEach((addedCall) => {
addedCall.on('stateChanged', () => {
if (addedCall.state === 'Connected') {
debugger;
addedCall.info.getServerCallId().then(result => {
this.onGetServerCallID?.(result);
}).catch(err => {
console.log(err);
});
}
});
});
});
} catch (error) {
console.error(error);
}
}
}
async subscribeToCall(call) {
try {
call.on('idChanged', () => {
this.idChanged?.(call.id);
});
call.on('stateChanged', async () => {
this.stateChangedCallback?.(call.state);
});
call.on('isLocalVideoStartedChanged', () => {
this.isLocalVideoStartedChanged?.(call.isLocalVideoStarted);
});
call.on('localVideoStreamsUpdated', e => {
this.localVideoStreamsUpdated?.(e);
});
// Subscribe to the call's 'remoteParticipantsUpdated' event to be
// notified when new participants are added to the call or removed from the call.
call.on('remoteParticipantsUpdated', e => {
this.remoteParticipantsUpdated?.(e);
e.added.forEach(remoteParticipant => {
this.subscribeToRemoteParticipant(remoteParticipant)
});
// Unsubscribe from participants that are removed from the call
e.removed.forEach(remoteParticipant => {
console.log('Remote participant removed from the call.');
});
});
call.localVideoStreams.forEach(async (lvs) => {
this.localVideoStream = lvs;
await this.displayLocalVideoStream(lvs);
});
// Inspect the call's current remote participants and subscribe to them.
call.remoteParticipants.forEach(remoteParticipant => {
this.subscribeToRemoteParticipant(remoteParticipant);
});
} catch (error) {
console.error(error);
}
}
subscribeToRemoteParticipant(remoteParticipant) {
try {
// Inspect the initial remoteParticipant.state value.
console.log(`Remote participant state: ${remoteParticipant.state}`);
// Subscribe to remoteParticipant's 'stateChanged' event for value changes.
remoteParticipant.on('stateChanged', () => {
console.log(`Remote participant state changed: ${remoteParticipant.state}`, JSON.stringify(remoteParticipant));
this.remoteParticipantStateChanged?.(remoteParticipant.state);
});
// Inspect the remoteParticipants's current videoStreams and subscribe to them.
remoteParticipant.videoStreams.forEach(remoteVideoStream => {
this.subscribeToRemoteVideoStream(remoteVideoStream);
});
// Subscribe to the remoteParticipant's 'videoStreamsUpdated' event to be
// notified when the remoteParticipant adds new videoStreams and removes video streams.
remoteParticipant.on('videoStreamsUpdated', e => {
// Subscribe to new remote participant's video streams that were added.
e.added.forEach(remoteVideoStream => {
this.subscribeToRemoteVideoStream(remoteVideoStream);
});
// Unsubscribe from remote participant's video streams that were removed.
e.removed.forEach(remoteVideoStream => {
console.log('Remote participant video stream was removed.');
})
});
} catch (error) {
console.error(error);
}
}
async subscribeToRemoteVideoStream(remoteVideoStream) {
let renderer = new VideoStreamRenderer(remoteVideoStream);
let view;
const createView = async () => {
// Create a renderer view for the remote video stream.
view = await renderer.createView();
this.remoteVideoIsAvailableChanged?.({
isAvailable: remoteVideoStream.isAvailable,
participantId: remoteVideoStream.tsParticipantId,
el: view.target
});
}
// Remote participant has switched video on/off
remoteVideoStream.on('isAvailableChanged', async () => {
try {
if (remoteVideoStream.isAvailable) {
await createView();
} else {
view?.dispose();
}
} catch (e) {
console.error(e);
}
});
// Remote participant has video on initially.
if (remoteVideoStream.isAvailable) {
try {
await createView();
} catch (e) {
console.error(e);
}
}
}
async createLocalVideoStream() {
const camera = (await this.deviceManager.getCameras())[0];
if (camera) {
return new LocalVideoStream(camera);
} else {
console.error(`No camera device found on the system`);
}
}
leaveRoom() {
if (this.call) {
this.call.hangUp();
}
}
async displayLocalVideoStream(lvs) {
try {
let localVideoStreamRenderer = new VideoStreamRenderer(lvs);
const view = await localVideoStreamRenderer.createView();
this.onCreateLocalVideoStream?.(view.target);
} catch (error) {
console.error(error);
}
}
}
export default VideoCall;