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; this.onGetServerCallID = options.onGetServerCallID; 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; this.callAgent.on('callsUpdated', e => { e.added.forEach((addedCall) => { addedCall.on('stateChanged', () => { if (addedCall.state === 'Connected') { addedCall.info.getServerCallId().then(result => { this.onGetServerCallID?.(result); }).catch(err => { console.log(err); }); } }); }); }); const roomCallLocator = { roomId: roomId }; let call = this.callAgent.join(roomCallLocator, { videoOptions }); this.subscribeToCall(call); } 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;