1.24.3 • Published 5 days ago

@azure/communication-calling v1.24.3

Weekly downloads
594
License
Microsoft Softwar...
Repository
-
Last release
5 days ago

Azure Communication Calling client library for JavaScript

Get started with Azure Communication Services by using the Communication Services calling client library to add voice and video calling to your app. Read more about Azure Communication Services here

Getting started

Prerequisites

Setting up

Install the client library

Use the npm install command to install the Azure Communication Services Calling and Common client libraries for JavaScript.

npm install @azure/communication-common --save

npm install @azure/communication-calling --save

Object model

The following classes and interfaces handle some of the major features of the Azure Communication Services Calling client library:

NameDescription
CallClientThe CallClient is the main entry point to the Calling client library.
CallAgentThe CallAgent is used to start and manage calls.
DeviceManagerThe DeviceManager is used to manage media devices
AzureCommunicationTokenCredentialThe AzureCommunicationTokenCredential class implements the CommunicationTokenCredential interface which is used to instantiate the CallAgent.

Initialize the CallClient, create CallAgent, and access DeviceManager

Instantiate a new CallClient instance. You can configure it with custom options like a Logger instance. Once a CallClient is instantiated, you can create a CallAgent instance by calling the createCallAgent method on the CallClient instance. This asynchronously returns a CallAgent instance object. The createCallAgent method takes a CommunicationTokenCredential as an argument, which accepts a user access token.

// Set the logger's log level
setLogLevel('verbose');
// Redirect logger output to wherever desired. By default it logs to console
AzureLogger.log = (...args) => { console.log(...args) };
const userToken = '<user token>';
callClient = new CallClient(options);
const tokenCredential = new AzureCommunicationTokenCredential(userToken);
const callAgent = await callClient.createCallAgent(tokenCredential, {displayName: 'optional ACS user name'});
const deviceManager = await callClient.getDeviceManager()

Place an outgoing call

To create and start a call you need to use one of the APIs on CallAgent and provide a user that you've created through the Communication Services administration client library.

Call creation and start is synchronous. The Call instance allows you to subscribe to call events.

Place a call

Place a 1:1 call to a user or PSTN

To place a call to another Communication Services user, invoke the startCall method on callAgent and pass the callee's CommunicationUserIdentifier that you've created with the Communication Services Administration library.

const userCallee = { communicationUserId: '<ACS_USER_ID>' }
const oneToOneCall = callAgent.startCall([userCallee]);

To place a call to a PSTN, invoke the startCall method on callAgent and pass the callee's PhoneNumberIdentifier. Your Communication Services resource must be configured to allow PSTN calling. When calling a PSTN number, you must specify your alternate caller ID. An alternate caller Id refers to a phone number (based on the E.164 standard) identifiying the caller in a PSTN Call. For example, when you supply an alternate caller Id to the PSTN call, that phone number will be the one shown to the callee when the call is incoming.

!WARNING PSTN calling is currently in private preview. For access, apply to early adopter program.

const pstnCalee = { phoneNumber: '<ACS_USER_ID>' }
const alternateCallerId = {alternateCallerId: '<Alternate caller Id>'};
const oneToOneCall = callAgent.startCall([pstnCallee], {alternateCallerId});

Place a 1:n call with users and PSTN

const userCallee = { communicationUserId: <ACS_USER_ID> }
const pstnCallee = { phoneNumber: <PHONE_NUMBER>};
const alternateCallerId = {alternateCallerId: '<Alternate caller Id>'};
const groupCall = callAgent.startCall([userCallee, pstnCallee], {alternateCallerId});

Place a 1:1 call with video camera

!WARNING There can currently be no more than one outgoing local video stream. To place a video call, you have to enumerate local cameras using the deviceManager getCameras() API. Once you select the desired camera, use it to construct a LocalVideoStream instance and pass it within videoOptions as an item within the localVideoStream array to the startCall method. Once your call connects it'll automatically start sending a video stream from the selected camera to the other participant(s). This also applies to the Call.Accept() video options and CallAgent.join() video options.

const deviceManager = await callClient.getDeviceManager();
const cameras = await deviceManager.getCameras();
const camera = cameras[0]
localVideoStream = new LocalVideoStream(camera);
const placeCallOptions = {videoOptions: {localVideoStreams:[localVideoStream]}};
const call = callAgent.startCall(['acsUserId'], placeCallOptions);
### Join a group call
To start a new group call or join an ongoing group call, use the 'join' method
and pass an object with a `groupId` property. The value has to be a GUID.
```js

const context = { groupId: <GUID>}
const call = callAgent.join(context);

Join a Teams Meeting

!NOTE This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. To use this api please use 'beta' release of ACS Calling Web SDK

To join a Teams meeting, use 'join' method and pass a meeting link or a meeting's coordinates

// Join using meeting link
const locator = { meetingLink: <meeting link>}
const call = callAgent.join(locator);

// Join using meeting coordinates
const locator = {
	threadId: <thread id>,
	organizerId: <organizer id>,
	tenantId: <tenant id>,
	messageId: <message id>
}
const call = callAgent.join(locator);

Receiving an incoming call

The CallAgent instance emits an incomingCall event when the logged in identity is receiving an incoming call. To listen to this event, subscribe in the following way:

const incomingCallHander = async (args: { incomingCall: IncomingCall }) => {

	//Get incoming call id
	var incomingCallId = incomingCall.id
	
	// Get information about caller
	var callerInfo = incomingCall.callerInfo

	// Accept the call
	var call = await incomingCall.accept();

	// Reject the call
	incomingCall.reject();

	// Subscribe to callEnded event and get the call end reason
	 incomingCall.on('callEnded', args => {
		console.log(args.callEndReason);
	});

	// callEndReason is also a property of IncomingCall
	var callEndReason = incomingCall.callEndReason;
};
callAgentInstance.on('incomingCall', incomingCallHander);

The incomingCall event will provide with an instance of IncomingCall on which you can accept or reject a call.

Call Management

You can access call properties and perform various operations during a call to manage settings related to video and audio.

Call properties

  • Get the unique ID (string) for this Call.
const callId: string = call.id;
  • To learn about other participants in the call, inspect the remoteParticipants collection on the call instance. Array contains list RemoteParticipant objects
const remoteParticipants = call.remoteParticipants;
  • The identifier of caller if the call is incoming. Identifier is one of the CommunicationIdentifier types
const callerIdentity = call.callerInfo.identifier;
  • Get the state of the Call.
const callState = call.state;

This returns a string representing the current state of a call:

  • 'None' - initial call state
  • 'Connecting' - initial transition state once call is placed or accepted
  • 'Ringing' - for an outgoing call - indicates call is ringing for remote participants
  • 'EarlyMedia' - indicates a state in which an announcement is played before the call is connected
  • 'Connected' - call is connected
  • 'LocalHold' - call is put on hold by local participant, no media is flowing between local endpoint and remote participant(s)
  • 'RemoteHold' - call is put on hold by remote participant, no media is flowing between local endpoint and remote participant(s)
  • 'InLobby' - indicates that user is in lobby
  • 'Disconnecting' - transition state before the call goes to 'Disconnected' state
  • 'Disconnected' - final call state

    • If network connection is lost, state goes to 'Disconnected' after about 2 minutes.
  • To see why a given call ended, inspect the callEndReason property.

const callEndReason = call.callEndReason;
const callEndReasonCode = callEndReason.code // (number) code associated with the reason
const callEndReasonSubCode = callEndReason.subCode // (number) subCode associated with the reason
  • To learn if the current call is an incoming or outgoing call, inspect the direction property, it returns CallDirection.
const isIncoming = call.direction == 'Incoming';
const isOutgoing = call.direction == 'Outgoing';
  • To check if the current microphone is muted, inspect the isMuted property, it returns Boolean.
const muted = call.isMuted;
  • To see if the screen sharing stream is being sent from a given endpoint, check the isScreenSharingOn property, it returns Boolean.
const isScreenSharingOn = call.isScreenSharingOn;
  • To inspect active video streams, check the localVideoStreams collection, it contains LocalVideoStream objects
const localVideoStreams = call.localVideoStreams;

Mute and unmute

To mute or unmute the local endpoint you can use the mute and unmute asynchronous APIs:

//mute local device 
await call.mute();

//unmute local device 
await call.unmute();

Start and stop sending local video

To start a video, you have to enumerate cameras using the getCameras method on the deviceManager object. Then create a new instance of LocalVideoStream passing the desired camera into the startVideo method as an argument:

const deviceManager = await callClient.getDeviceManager();
const cameras = await deviceManager.getCameras();
const camera = cameras[0]
const localVideoStream = new LocalVideoStream(camera);
await call.startVideo(localVideoStream);

Once you successfully start sending video, a LocalVideoStream instance will be added to the localVideoStreams collection on a call instance.

call.localVideoStreams[0] === localVideoStream;

To stop local video, pass the localVideoStream instance available in the localVideoStreams collection:

await call.stopVideo(localVideoStream);

You can switch to a different camera device while video is being sent by invoking switchSource on a localVideoStream instance:

const cameras = await callClient.getDeviceManager().getCameras();
const camera = cameras[1];
localVideoStream.switchSource(camera);

Remote participants management

All remote participants are represented by RemoteParticipant type and available through remoteParticipants collection on a call instance.

List participants in a call

The remoteParticipants collection returns a list of remote participants in given call:

call.remoteParticipants; // [remoteParticipant, remoteParticipant....]

Remote participant properties

Remote participant has a set of properties and collections associated with it

CommunicationIdentifier

Get the identifier for this remote participant. Identity is one of the 'CommunicationIdentifier' types:

const identifier = remoteParticipant.identifier;

It can be one of 'CommunicationIdentifier' types:

  • { communicationUserId: '<ACS_USER_ID'> } - object representing ACS User
  • { phoneNumber: '<E.164>' } - object representing phone number in E.164 format
  • { microsoftTeamsUserId: '<TEAMS_USER_ID>', isAnonymous?: boolean; cloud?: "public" | "dod" | "gcch" } - object representing Teams user
  • { id: '<USER_ID>' } - object repredenting identifier that doesn't fit any of the other identifier types

State

Get state of this remote participant.

const state = remoteParticipant.state;

State can be one of

  • 'Idle' - initial state
  • 'Connecting' - transition state while participant is connecting to the call
  • 'Ringing' - participant is ringing
  • 'Connected' - participant is connected to the call
  • 'Hold' - participant is on hold
  • 'EarlyMedia' - announcement is played before participant is connected to the call
  • 'InLobby' - indicates that remote participant is in lobby
  • 'Disconnected' - final state - participant is disconnected from the call
    • If remote participant loses their network connectivity, then remote participant state goes to 'Disconnected' after about 2 minutes.

Call End reason

To learn why participant left the call, inspect callEndReason property:

const callEndReason = remoteParticipant.callEndReason;
const callEndReasonCode = callEndReason.code // (number) code associated with the reason
const callEndReasonSubCode = callEndReason.subCode // (number) subCode associated with the reason

Is Muted

To check whether this remote participant is muted or not, inspect isMuted property, it returns Boolean

const isMuted = remoteParticipant.isMuted;

Is Speaking

To check whether this remote participant is speaking or not, inspect isSpeaking property it returns Boolean

const isSpeaking = remoteParticipant.isSpeaking;

Video Streams

To inspect all video streams that a given participant is sending in this call, check videoStreams collection, it contains RemoteVideoStream objects

const videoStreams = remoteParticipant.videoStreams; // [RemoteVideoStream, ...]

Display Name

To get display name for this remote participant, inspect displayName property it return string

const displayName = remoteParticipant.displayName;

Add a participant to a call

To add a participant to a call (either a user or a phone number) you can invoke addParticipant. Provide one of the 'Identifier' types. This will synchronously return the remote participant instance.

const userIdentifier = { communicationUserId: <ACS_USER_ID> };
const pstnIdentifier = { phoneNumber: <PHONE_NUMBER>}
const remoteParticipant = call.addParticipant(userIdentifier);
const remoteParticipant = call.addParticipant(pstnIdentifier, {alternateCallerId: '<Alternate Caller ID>'});

Remove participant from a call

To remove a participant from a call (either a user or a phone number) you can invoke removeParticipant. You have to pass one of the 'Identifier' types This will resolve asynchronously once the participant is removed from the call. The participant will also be removed from the remoteParticipants collection.

const userIdentifier = { communicationUserId: <ACS_USER_ID> };
const pstnIdentifier = { phoneNumber: <PHONE_NUMBER>}
await call.removeParticipant(userIdentifier);
await call.removeParticipant(pstnIdentifier);

Render remote participant video streams

To list the video streams and screen sharing streams of remote participants, inspect the videoStreams collections:

const remoteVideoStream: RemoteVideoStream = call.remoteParticipants[0].videoStreams[0];
const streamType: MediaStreamType = remoteVideoStream.mediaStreamType;

To render a RemoteVideoStream, you have to subscribe to a isAvailableChanged event. If the isAvailable property changes to true, a remote participant is sending a stream. Once that happens, create a new instance of VideoStreamRenderer, and then create a new VideoStreamRendererView instance using the asynchronous createView method. You may then attach view.target to any UI element. Whenever availability of a remote stream changes you can choose to destroy the whole VideoStreamRenderer, a specific VideoStreamRendererView or keep them, but this will result in displaying blank video frame.

function subscribeToRemoteVideoStream(remoteVideoStream: RemoteVideoStream) {
	let renderer: VideoStreamRenderer;

	const displayVideo = () => {
		renderer = new VideoStreamRenderer(remoteVideoStream);
		this._streamRenderersMap.set(stream, renderer);
		const view = await videoStreamRenderer.createView();
		htmlElement.appendChild(view.target);
	}

	remoteVideoStream.on('isAvailableChanged', async () => {
		renderer = this._streamRenderersMap.get(stream);
		if (remoteVideoStream.isAvailable && !renderer) {
			displayVideo();
		} else {
			this._streamRenderersMap.delete(stream);
			renderer.dispose();
		}
	});

	renderer = this._streamRenderersMap.get(stream);
	if (remoteVideoStream.isAvailable && !renderer) {
		displayVideo();
	}
}

Remote video stream properties

Remote video streams have the following properties:

  • Id - ID of a remote video stream
const id: number = remoteVideoStream.id;
  • MediaStreamType - can be 'Video' or 'ScreenSharing'
const type: MediaStreamType = remoteVideoStream.mediaStreamType;
  • isAvailable - Indicates if remote participant endpoint is actively sending stream
const type: boolean = remoteVideoStream.isAvailable;

VideoStreamRenderer methods and properties

  • StreamSize - dimensions ( width/height ) of the renderer
const size: {width: number; height: number} = videoStreamRenderer.size;
  • Create a VideoStreamRendererView instance that can be later attached in the application UI to render the remote video stream.
videoStreamRenderer.createView()
  • Dispose of the videoStreamRenderer and all associated VideoStreamRendererView instances.
videoStreamRenderer.dispose()

VideoStreamRendererView methods and properties

When creating a VideoStreamRendererView you can specify scalingMode and isMirrored properties. Scaling mode can be 'Stretch', 'Crop', or 'Fit' If isMirrored is specified, the rendered stream will be flipped vertically.

const videoStreamRendererView: VideoStreamRendererView = videoStreamRenderer.createView({ scalingMode, isMirrored });

Any given VideoStreamRendererView instance has a target property that represents the rendering surface. This has to be attached in the application UI:

htmlElement.appendChild(view.target);

You can later update the scaling mode by invoking the updateScalingMode method.

view.updateScalingMode('Crop')

Device management

DeviceManager lets you enumerate local devices that can be used in a call to transmit your audio/video streams. It also allows you to request permission from a user to access their microphone and camera using the native browser API.

You can access the deviceManager by calling callClient.getDeviceManager() method.

const deviceManager = await callClient.getDeviceManager();

Enumerate local devices

To access local devices, you can use enumeration methods on the Device Manager. Enumeration is an asynchronous action.

//  Get a list of available video devices for use.
const localCameras = await deviceManager.getCameras(); // [VideoDeviceInfo, VideoDeviceInfo...]

// Get a list of available microphone devices for use.
const localMicrophones = await deviceManager.getMicrophones(); // [AudioDeviceInfo, AudioDeviceInfo...]

// Get a list of available speaker devices for use.
const localSpeakers = await deviceManager.getSpeakers(); // [AudioDeviceInfo, AudioDeviceInfo...]

Set default microphone/speaker

Device manager allows you to set a default device that will be used when starting a call. If client defaults are not set, Communication Services will fall back to OS defaults.

// Get the microphone device that is being used.
const defaultMicrophone = deviceManager.selectedMicrophone;

// Set the microphone device to use.
await deviceManager.selectMicrophone(localMicrophones[0]);

// Get the speaker device that is being used.
const defaultSpeaker = deviceManager.selectedSpeaker;

// Set the speaker device to use.
await deviceManager.selectSpeaker(localSpeakers[0]);

Local camera preview

You can use DeviceManager and VideoStreamRenderer to begin rendering streams from your local camera. This stream won't be sent to other participants; it's a local preview feed. This is an asynchronous action.

const cameras = await deviceManager.getCameras();
const camera = cameras[0];
const localCameraStream = new LocalVideoStream(camera);
const videoStreamRenderer = new VideoStreamRenderer(localCameraStream);
const view = await videoStreamRenderer.createView();
htmlElement.appendChild(view.target);

Request permission to camera/microphone

Prompt a user to grant camera/microphone permissions with the following:

const result = await deviceManager.askDevicePermission({audio: true, video: true});

This will resolve asynchronously with an object indicating if audio and video permissions were granted:

console.log(result.audio);
console.log(result.video);

Call recording management

!NOTE This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. To use this api please use 'beta' release of ACS Calling Web SDK

Call recording is an extended feature of the core Call API. You first need to obtain the recording feature API object:

const callRecordingApi = call.api(Features.Recording);

Then, to can check if the call is being recorded, inspect the isRecordingActive property of callRecordingApi, it returns Boolean.

const isResordingActive = callRecordingApi.isRecordingActive;

You can also subscribe to recording changes:

const isRecordingActiveChangedHandler = () => {
  console.log(callRecordingApi.isRecordingActive);
};

callRecordingApi.on('isRecordingActiveChanged', isRecordingActiveChangedHandler);
               

Call Transfer management

!NOTE This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. To use this api please use 'beta' release of ACS Calling Web SDK

Call transfer is an extended feature of the core Call API. You first need to obtain the transfer feature API object:

const callTransferApi = call.api(Features.Transfer);

Call transfer involves three parties transferor, transferee, and transfer target. Transfer flow is working as following:

  1. There is already a connected call between transferor and transferee
  2. transferor decide to transfer the call (transferee -> transfer target)
  3. transferor call transfer API
  4. transferee decide to whether accept or reject the transfer request to transfer target via transferRequested event.
  5. transfer target will receive an incoming call only if transferee did accept the transfer request

Transfer terminology

  • Transferor - The one who initiates the transfer request
  • Transferee - The one who is being transferred by the transferor to the transfer target
  • Transfer target - The one who is the target that is being transferred to

To transfer current call, you can use transfer synchronous API. transfer takes optional TransferCallOptions which allows you to set disableForwardingAndUnanswered flag:

  • disableForwardingAndUnanswered = false - if transfer target doesn't answer the transfer call, then it will follow the transfer target forwarding and unanswered settings
  • disableForwardingAndUnanswered = true - if transfer target doesn't answer the transfer call, then the transfer attempt will end
// transfer target can be ACS user
const id = { communicationUserId: <ACS_USER_ID> };
// call transfer API
const transfer = callTransferApi.transfer({targetParticipant: id});

Transfer allows you to subscribe to transferStateChanged and transferRequested events. transferRequsted event comes from call instance, transferStateChanged event and transfer state and error comes from transfer instance

// transfer state
const transferState = transfer.state; // None | Transferring | Transferred | Failed

// to check the transfer failure reason
const transferError = transfer.error; // transfer error code that describes the failure if transfer request failed

Transferee can accept or reject the transfer request initiated by transferor in transferRequested event via accept() or reject() in transferRequestedEventArgs. You can access targetParticipant information, accept, reject methods in transferRequestedEventArgs.

// Transferee to accept the transfer request
callTransferApi.on('transferRequested', args => {
  args.accept();
});

// Transferee to reject the transfer request
callTransferApi.on('transferRequested', args => {
  args.reject();
});

Dominant speakers of a call

Dominant speakers for a call is an extended feature of the core Call API and allows you obtain a list of the active speakers in the call.

This is a ranked list, where the first element in the list represents the last active speaker on the call and so on.

In order to obtain the dominant speakers in a call, you first need to obtain the call dominant speakers feature API object:

const callDominantSpeakersApi = call.api(Features.CallDominantSpeakers);

Then, obtain the list of the dominant speakers by calling dominantSpeakers. This has a type of DominantSpeakersInfo, which has the following members:

  • speakersList contains the list of the ranked dominant speakers in the call. These are represented by their participant id.
  • timestamp is the latest update time for the dominant speakers in the call.
let dominantSpeakers: DominantSpeakersInfo = callDominantSpeakersApi.dominantSpeakers;

Also, you can subscribe to the dominantSpeakersChanged event to know when the dominant speakers list has changed

const dominantSpeakersChangedHandler = () => {
	// Get the most up to date list of dominant speakers
	let dominantSpeakers = callDominantSpeakersApi.dominantSpeakers;
};
callDominantSpeakersApi.api(SDK.Features.CallDominantSpeakers).on('dominantSpeakersChanged', dominantSpeakersChangedHandler);

Handle the Dominant Speaker's video streams

Application can leverage DominantSpeakers feature to render the one or more of dominant speaker's video streams, and keep updating UI whenever dominant speaker list updates. This can be achieved with the following code example.

// RemoteParticipant obj representation of the dominant speaker
let dominantRemoteParticipant: RemoteParticipant;
// It is recommended to use a map to keep track of a stream's associated renderer
let streamRenderersMap: new Map<RemoteVideoStream, VideoStreamRenderer>();

function getRemoteParticipantForDominantSpeaker(dominantSpeakerIdentifier) {
	let dominantRemoteParticipant: RemoteParticipant;
	switch(dominantSpeakerIdentifier.kind) {
		case 'communicationUser': {
			dominantRemoteParticipant = currentCall.remoteParticipants.find(rm => {
				return (rm.identifier as CommunicationUserIdentifier).communicationUserId === dominantSpeakerIdentifier.communicationUserId
			});
			break;
		}
		case 'microsoftTeamsUser': {
			dominantRemoteParticipant = currentCall.remoteParticipants.find(rm => {
				return (rm.identifier as MicrosoftTeamsUserIdentifier).microsoftTeamsUserId === dominantSpeakerIdentifier.microsoftTeamsUserId
			});
			break;
		}
		case 'unknown': {
			dominantRemoteParticipant = currentCall.remoteParticipants.find(rm => {
				return (rm.identifier as UnknownIdentifier).id === dominantSpeakerIdentifier.id
			});
			break;
		}
	}
	return dominantRemoteParticipant;
}
// Handler function for when the dominant speaker changes
const dominantSpeakersChangedHandler = async () => {
	// Get the new dominant speaker's identifier
	const newDominantSpeakerIdentifier = currentCall.api(SDK.Features.DominantSpeakers).dominantSpeakers.speakersList[0];
	
	 if (newDominantSpeakerIdentifier) {
		// Get the remote participant object that matches newDominantSpeakerIdentifier
		const newDominantRemoteParticipant = getRemoteParticipantForDominantSpeaker(newDominantSpeakerIdentifier);
		
		// Create the new dominant speaker's stream renderers
		const streamViews = [];
		for (const stream of newDominantRemoteParticipant.videoStreams) {
			if (stream.isAvailable && !streamRenderersMap.get(stream)) {
				const renderer = new VideoStreamRenderer(stream);
				streamRenderersMap.set(stream, renderer);
				const view = await videoStreamRenderer.createView();
				streamViews.push(view);
			}
		}
		// Remove the old dominant speaker's video streams by disposing of their associated renderers
		if (dominantRemoteParticipant) {
			for (const stream of dominantRemoteParticipant.videoStreams) {
				const renderer = streamRenderersMap.get(stream);
				if (renderer) {
					streamRenderersMap.delete(stream);
					renderer.dispose();
				}
			}
		}

		// Set the new dominant remote participant obj
		dominantRemoteParticipant = newDominantRemoteParticipant

		// Render the new dominant remote participant's streams
		for (const view of streamViewsToRender) {
			htmlElement.appendChild(view.target);
		}
	 }
};

// When call is disconnected, set the dominant speaker to undefined
currentCall.on('stateChanged', () => {
	if (currentCall === 'Disconnected') {
		dominantRemoteParticipant = undefined;
	}
});

const dominantSpeakerIdentifier = currentCall.api(SDK.Features.DominantSpeakers).dominantSpeakers.speakersList[0];
dominantRemoteParticipant = getRemoteParticipantForDominantSpeaker(dominantSpeakerIdentifier);
currentCall.api(SDK.Features.DominantSpeakers).on('dominantSpeakersChanged', dominantSpeakersChangedHandler);

function subscribeToRemoteVideoStream(stream: RemoteVideoStream, participant: RemoteParticipant) {
	let renderer: VideoStreamRenderer;

	const displayVideo = () => {
		renderer = new VideoStreamRenderer(stream);
		streamRenderersMap.set(stream, renderer);
		const view = await renderer.createView();
		htmlElement.appendChild(view.target);
	}

	stream.on('isAvailableChanged', async () => {
		if (dominantRemoteParticipant !== participant) {
			return;
		}

		renderer = streamRenderersMap.get(stream);
		if (stream.isAvailable && !renderer) {
			displayVideo();
		} else {
			streamRenderersMap.delete(stream);
			renderer.dispose();
		}
	});

	if (dominantRemoteParticipant !== participant) {
		return;
	}

	renderer = streamRenderersMap.get(stream);
	if (stream.isAvailable && !renderer) {
		displayVideo();
	}
}

Eventing model

You have to inspect current values and subscribe to update events for future values.

Properties

// Inspect current value
console.log(object.property);

// Subscribe to value updates
object.on('propertyChanged', () => {
	// Inspect new value
	console.log(object.property)
});

// Unsubscribe from updates:
object.off('propertyChanged', () => {});



// Example for inspecting call state
console.log(call.state);
call.on('stateChanged', () => {
	console.log(call.state);
});
call.off('stateChanged', () => {});

Collections

// Inspect current collection
object.collection.forEach(v => {
	console.log(v);
});

// Subscribe to collection updates
object.on('collectionUpdated', e => {
	// Inspect new values added to the collection
	e.added.forEach(v => {
		console.log(v);
	});
	// Inspect values removed from the collection
	e.removed.forEach(v => {
		console.log(v);
	});
});

// Unsubscribe from updates:
object.off('collectionUpdated', () => {});



// Example for subscribing to remote participants and their video streams
call.remoteParticipants.forEach(p => {
	subscribeToRemoteParticipant(p);
})

call.on('remoteParticipantsUpdated', e => {
	e.added.forEach(p => { subscribeToRemoteParticipant(p) })
	e.removed.forEach(p => { unsubscribeFromRemoteParticipant(p) })
});

function subscribeToRemoteParticipant(p) {
	console.log(p.state);
	p.on('stateChanged', () => { console.log(p.state); });
	p.videoStreams.forEach(v => { subscribeToRemoteVideoStream(v) });
	p.on('videoStreamsUpdated', e => { e.added.forEach(v => { subscribeToRemoteVideoStream(v) }) })
}
1.25.2-beta.2

5 days ago

1.24.3

7 days ago

1.24.2-beta.1

20 days ago

1.25.1-beta.1

19 days ago

1.24.1

20 days ago

1.24.1-beta.2

1 month ago

1.23.2

2 months ago

1.23.1

2 months ago

1.21.3

2 months ago

1.22.4

2 months ago

1.22.3-beta.1

2 months ago

1.23.2-beta.1

2 months ago

1.21.2-beta.1

2 months ago

1.22.3

3 months ago

1.23.1-beta.2

3 months ago

1.20.3

3 months ago

1.21.2

3 months ago

1.22.2

3 months ago

1.22.2-beta.1

3 months ago

1.22.1

3 months ago

1.21.1

3 months ago

1.22.1-beta.1

3 months ago

1.20.2

4 months ago

1.21.1-beta.4

4 months ago

1.19.2-beta.2

4 months ago

1.21.1-beta.3

4 months ago

1.20.1

5 months ago

1.20.1-beta.2

6 months ago

1.19.1

6 months ago

1.20.1-beta.1

6 months ago

1.18.1

7 months ago

1.19.1-beta.2

7 months ago

1.16.3

9 months ago

1.18.2-beta.1

7 months ago

1.18.1-beta.1

8 months ago

1.15.1-beta.1

11 months ago

1.15.2-beta.1

10 months ago

1.17.1

8 months ago

1.15.3

9 months ago

1.15.2

10 months ago

1.17.1-beta.5

9 months ago

1.16.3-beta.1

9 months ago

1.16.2-beta.1

9 months ago

1.16.1-beta.1

10 months ago

1.14.1

11 months ago

1.14.1-beta.1

12 months ago

1.12.1

1 year ago

1.13.0-beta.4

1 year ago

1.13.1

1 year ago

1.13.2-beta.1

1 year ago

1.13.1-beta.1

1 year ago

1.12.0-beta.1

1 year ago

1.12.0-beta.2

1 year ago

1.10.1-beta.1

1 year ago

1.11.1

1 year ago

1.11.0-beta.1

1 year ago

1.10.1

1 year ago

1.10.0-beta.1

1 year ago

1.9.1

1 year ago

1.9.1-beta.1

2 years ago

1.8.1-beta.1

2 years ago

1.7.2-beta.1

2 years ago

1.6.3

2 years ago

1.8.0-beta.1

2 years ago

1.7.1-beta.1

2 years ago

1.6.3-beta.1

2 years ago

1.7.0-beta.1

2 years ago

1.5.4

2 years ago

1.6.2

2 years ago

1.6.0-beta.1

2 years ago

1.6.2-beta.2

2 years ago

1.6.1-beta.1

2 years ago

1.5.4-beta.1

2 years ago

1.4.4

2 years ago

1.4.3

2 years ago

1.3.2

2 years ago

1.3.2-beta.1

2 years ago

1.4.2-beta.1

2 years ago

1.4.1-beta.1

2 years ago

1.4.3-beta.1

2 years ago

1.3.1-beta.1

3 years ago

1.2.3-beta.1

3 years ago

1.2.2-beta.1

3 years ago

1.2.1-beta.1

3 years ago

1.2.0-beta.1

3 years ago

1.1.0

3 years ago

1.1.0-beta.2

3 years ago

1.1.0-beta.1

3 years ago

1.0.1-beta.2

3 years ago

1.0.1-beta.1

3 years ago

1.0.0

3 years ago

1.0.0-beta.10

3 years ago

1.0.0-beta.8

3 years ago

1.0.0-beta.9

3 years ago

1.0.0-beta.7

3 years ago

1.0.0-beta.6

3 years ago

1.0.0-beta.5

3 years ago

1.0.0-beta.4

3 years ago

1.0.0-beta.3

3 years ago

1.0.0-beta.2

4 years ago

1.0.0-beta.1

4 years ago