-
-
-
-
-
ACS User Identity
-
-
-
-
-
The ACS Identity SDK can be used to create a user access token which authenticates the calling clients.
-
The example code shows how to use the ACS Identity SDK from a backend service. A walkthrough of integrating the ACS Identity SDK can be found on
Microsoft Docs
-
-
-
-
- { this.displayName = e.target.value }}/>
- { this.clientTag = e.target.value }}/>
- { this.state.token = e.target.value }}/>
- { this.state.communicationUserId = e.target.value }}/>
-
-
-
-
-
this.logIn()}>
- Login ACS user and initialize SDK
-
-
-
+
+
+
+
+
Teams User Identity
+
+
+
+
+
Microsoft Authentication Library (MSAL) is used to retrieve user token which is then exchanged to get an access
+ to get an access token from the communication service. The access token is then used to initialize the ACS SDK
+
Information and steps on how to generate access token for a Teams user can be found in the
Microsoft Docs
+
On clicking the Login Teams User and Initialize SDK, if the Teams user email or password is not provided, Microsoft signin pop-up will be used
-
-
+ {
+ (!this.state.showSpinner && !this.state.loggedIn) &&
+
-
-
Teams User Identity
+
+ { this.teamsUserEmail = e.target.value }} />
+ { this.teamsUserPassword = e.target.value }} />
-
Microsoft Authentication Library (MSAL) is used to retrieve user token which is then exchanged to get an access
- to get an access token from the communication service. The access token is then used to initialize the ACS SDK
-
Information and steps on how to generate access token for a Teams user can be found in the
Microsoft Docs
-
On clicking the Login Teams User and Initialize SDK, if the Teams user email or password is not provided, Microsoft signin pop-up will be used
+
this.teamsUserOAuthLogin()}>
+ Login Teams user and Initialize SDK
+
- {
- (!this.state.showSpinner && !this.state.loggedIn) &&
-
-
-
- { this.teamsUserEmail = e.target.value }} />
- { this.teamsUserPassword = e.target.value }} />
-
-
-
-
-
this.teamsUserOAuthLogin()}>
- Login Teams user and Initialize SDK
-
-
-
-
- }
-
+ }
- }
- {
- this.state.loggedIn && this.state.isTeamsUser &&
-
-
-
Congrats! Teams User was successfully logged in. You are ready to start making calls!
-
Teams User logged in identity is: {this.state.communicationUserId}
- {
Usage is tagged with: {this.clientTag}
}
-
- }
- {
- this.state.loggedIn &&
-
-
-
Environment information
-
-
-
-
Current environment details
-
{`Operating system: ${this.state.environmentInfo?.environment?.platform}.`}
-
{`Browser: ${this.state.environmentInfo?.environment?.browser}.`}
-
{`Browser's version: ${this.state.environmentInfo?.environment?.browserVersion}.`}
-
{`Is the application loaded in many tabs: ${this.state.isCallClientActiveInAnotherTab}.`}
-
-
-
Environment support verification
-
{`Operating system supported: ${this.state.environmentInfo?.isSupportedPlatform}.`}
-
{`Browser supported: ${this.state.environmentInfo?.isSupportedBrowser}.`}
-
{`Browser's version supported: ${this.state.environmentInfo?.isSupportedBrowserVersion}.`}
-
{`Current environment supported: ${this.state.environmentInfo?.isSupportedEnvironment}.`}
-
-
+
+
+ }
+ {
+ this.state.loggedIn && this.state.isTeamsUser &&
+
+
+
Congrats! Teams User was successfully logged in. You are ready to start making calls!
+
Teams User logged in identity is: {this.state.communicationUserId}
+ {
Usage is tagged with: {this.clientTag}
}
+
+ }
+ {
+ this.state.loggedIn &&
+
+
+
Environment information
+
+
+
+
Current environment details
+
{`Operating system: ${this.state.environmentInfo?.environment?.platform}.`}
+
{`Browser: ${this.state.environmentInfo?.environment?.browser}.`}
+
{`Browser's version: ${this.state.environmentInfo?.environment?.browserVersion}.`}
+
{`Is the application loaded in many tabs: ${this.state.isCallClientActiveInAnotherTab}.`}
- }
+
+
Environment support verification
+
{`Operating system supported: ${this.state.environmentInfo?.isSupportedPlatform}.`}
+
{`Browser supported: ${this.state.environmentInfo?.isSupportedBrowser}.`}
+
{`Browser's version supported: ${this.state.environmentInfo?.isSupportedBrowserVersion}.`}
+
{`Current environment supported: ${this.state.environmentInfo?.isSupportedEnvironment}.`}
+
+
-
+ }
+
+
);
}
}
diff --git a/Project/src/MakeCall/MakeCall.js b/src/MakeCall/MakeCall.js
similarity index 98%
rename from Project/src/MakeCall/MakeCall.js
rename to src/MakeCall/MakeCall.js
index 949a9b43..88fd4f43 100644
--- a/Project/src/MakeCall/MakeCall.js
+++ b/src/MakeCall/MakeCall.js
@@ -1,1338 +1,1341 @@
-import React from "react";
-import { CallClient, LocalVideoStream, Features, CallAgentKind, VideoStreamRenderer } from '@azure/communication-calling';
-import { AzureCommunicationTokenCredential, createIdentifierFromRawId} from '@azure/communication-common';
-import {
- PrimaryButton,
- TextField,
- MessageBar,
- MessageBarType
-} from 'office-ui-fabric-react'
-import { Icon } from '@fluentui/react/lib/Icon';
-import IncomingCallCard from './IncomingCallCard';
-import CallCard from '../MakeCall/CallCard';
-import CallSurvey from '../MakeCall/CallSurvey';
-import Login from './Login';
-import MediaConstraint from './MediaConstraint';
-import { setLogLevel, AzureLogger } from '@azure/logger';
-import { inflate } from 'pako';
-export default class MakeCall extends React.Component {
- constructor(props) {
- super(props);
- this.callClient = null;
- this.callAgent = null;
- this.deviceManager = null;
- this.destinationUserIds = null;
- this.destinationPhoneIds = null;
- this.destinationGroup = null;
- this.meetingLink = null;
- this.meetingId = null;
- this.passcode = null;
- this.roomsId = null;
- this.threadId = null;
- this.messageId = null;
- this.organizerId = null;
- this.tenantId = null;
- this.callError = null;
- this.logBuffer = [];
- this.videoConstraints = null;
- this.tokenCredential = null;
- this.logInComponentRef = React.createRef();
-
- this.state = {
- id: undefined,
- loggedIn: false,
- isCallClientActiveInAnotherTab: false,
- call: undefined,
- callSurvey: undefined,
- incomingCall: undefined,
- showCallSampleCode: false,
- showMuteUnmuteSampleCode: false,
- showHoldUnholdCallSampleCode: false,
- showPreCallDiagnosticsSampleCode: false,
- showPreCallDiagnostcisResults: false,
- isPreCallDiagnosticsCallInProgress: false,
- selectedCameraDeviceId: null,
- selectedSpeakerDeviceId: null,
- selectedMicrophoneDeviceId: null,
- deviceManagerWarning: null,
- callError: null,
- ufdMessages: [],
- permissions: {
- audio: null,
- video: null
- },
- preCallDiagnosticsResults: {},
- isTeamsUser: false,
- identityMri: undefined
- };
-
- setInterval(() => {
- if (this.state.ufdMessages.length > 0) {
- this.setState({ ufdMessages: this.state.ufdMessages.slice(1) });
- }
- }, 10000);
-
- // override logger to be able to dowload logs locally
- AzureLogger.log = (...args) => {
- this.logBuffer.push(...args);
- if (args[0].startsWith('azure:ACS-calling:info')) {
- console.info(...args);
- } else if (args[0].startsWith('azure:ACS-calling:verbose')) {
- console.debug(...args);
- } else if (args[0].startsWith('azure:ACS-calling:warning')) {
- console.warn(...args);
- } else if (args[0].startsWith('azure:ACS-calling:error')) {
- console.error(...args);
- } else {
- console.log(...args);
- }
- };
- }
-
- handleMediaConstraint = (constraints) => {
- if (constraints.video) {
- this.videoConstraints = constraints.video;
- }
- }
-
- handleLogIn = async (userDetails) => {
- if (userDetails) {
- try {
- const tokenCredential = new AzureCommunicationTokenCredential(userDetails.token);
- this.tokenCredential = tokenCredential;
- setLogLevel('verbose');
-
- const proxyConfiguration = userDetails.proxy.useProxy ? { url: userDetails.proxy.url } : undefined;
- const turnConfiguration = userDetails.customTurn.useCustomTurn && !userDetails.customTurn.isLoading ? userDetails.customTurn.turn : undefined;
- this.callClient = new CallClient({
- diagnostics: {
- appName: 'azure-communication-services',
- appVersion: '1.3.1-beta.1',
- tags: ["javascript_calling_sdk",
- `#clientTag:${userDetails.clientTag}`]
- },
- networkConfiguration: {
- proxy: proxyConfiguration,
- turn: turnConfiguration
- }
- });
-
- this.deviceManager = await this.callClient.getDeviceManager();
- const permissions = await this.deviceManager.askDevicePermission({ audio: true, video: true });
- this.setState({permissions: permissions});
-
- this.setState({ isTeamsUser: userDetails.isTeamsUser});
- this.setState({ identityMri: createIdentifierFromRawId(userDetails.communicationUserId)})
- this.callAgent = this.state.isTeamsUser ?
- await this.callClient.createTeamsCallAgent(tokenCredential) :
- await this.callClient.createCallAgent(tokenCredential, { displayName: userDetails.displayName });
-
- window.callAgent = this.callAgent;
- window.videoStreamRenderer = VideoStreamRenderer;
- this.callAgent.on('callsUpdated', e => {
- console.log(`callsUpdated, added=${e.added}, removed=${e.removed}`);
-
- e.added.forEach(call => {
- this.setState({ call: call });
-
- const diagnosticChangedListener = (diagnosticInfo) => {
- const rmsg = `UFD Diagnostic changed:
- Diagnostic: ${diagnosticInfo.diagnostic}
- Value: ${diagnosticInfo.value}
- Value type: ${diagnosticInfo.valueType}`;
- if (this.state.ufdMessages.length > 0) {
- this.setState({ ufdMessages: [...this.state.ufdMessages, rmsg] });
- } else {
- this.setState({ ufdMessages: [rmsg] });
- }
-
-
- };
-
- call.feature(Features.UserFacingDiagnostics).media.on('diagnosticChanged', diagnosticChangedListener);
- call.feature(Features.UserFacingDiagnostics).network.on('diagnosticChanged', diagnosticChangedListener);
- });
-
- e.removed.forEach(call => {
- if (this.state.call && this.state.call === call) {
- this.displayCallEndReason(this.state.call.callEndReason);
- }
- });
- });
- this.callAgent.on('incomingCall', args => {
- const incomingCall = args.incomingCall;
- if (this.state.call) {
- incomingCall.reject();
- return;
- }
-
- this.setState({ incomingCall: incomingCall });
-
- incomingCall.on('callEnded', args => {
- this.displayCallEndReason(args.callEndReason);
- });
-
- });
- this.setState({ loggedIn: true });
- this.logInComponentRef.current.setCallAgent(this.callAgent);
- this.logInComponentRef.current.setCallClient(this.callClient);
- } catch (e) {
- console.error(e);
- }
- }
- }
-
- displayCallEndReason = (callEndReason) => {
- if (callEndReason.code !== 0 || callEndReason.subCode !== 0) {
- this.setState({ callSurvey: this.state.call, callError: `Call end reason: code: ${callEndReason.code}, subcode: ${callEndReason.subCode}` });
- }
-
- this.setState({ call: null, callSurvey: this.state.call, incomingCall: null });
- }
-
- placeCall = async (withVideo) => {
- try {
- let identitiesToCall = [];
- const userIdsArray = this.destinationUserIds.value.split(',');
- const phoneIdsArray = this.destinationPhoneIds.value.split(',');
-
- userIdsArray.forEach((userId, index) => {
- if (userId) {
- userId = userId.trim();
- if (userId === '8:echo123') {
- userId = { id: userId };
- }
- else {
- userId = createIdentifierFromRawId(userId);
- }
- if (!identitiesToCall.find(id => { return id === userId })) {
- identitiesToCall.push(userId);
- }
- }
- });
-
- phoneIdsArray.forEach((phoneNumberId, index) => {
- if (phoneNumberId) {
- phoneNumberId = phoneNumberId.trim();
- phoneNumberId = createIdentifierFromRawId(phoneNumberId);
- if (!identitiesToCall.find(id => { return id === phoneNumberId })) {
- identitiesToCall.push(phoneNumberId);
- }
- }
- });
-
- const callOptions = await this.getCallOptions({video: withVideo, micMuted: false});
-
- if (this.callAgent.kind === CallAgentKind.CallAgent && this.alternateCallerId.value !== '') {
- callOptions.alternateCallerId = { phoneNumber: this.alternateCallerId.value.trim() };
- }
-
- if (identitiesToCall.length > 1) {
- if (this.callAgent.kind === CallAgentKind.TeamsCallAgent && this.threadId === '') {
- throw new Error('Thread ID is needed to make Teams Group Call');
- } else {
- callOptions.threadId = this.threadId.value;
- }
- }
-
- this.callAgent.startCall(identitiesToCall, callOptions);
-
- } catch (e) {
- console.error('Failed to place a call', e);
- this.setState({ callError: 'Failed to place a call: ' + e });
- }
- };
-
- downloadLog = async () => {
- const date = new Date();
- const fileName = `logs-${date.toISOString().slice(0, 19)}.txt`;
- var element = document.createElement('a');
- element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(this.logBuffer.join('\n')));
- element.setAttribute('download', fileName);
-
- element.style.display = 'none';
- document.body.appendChild(element);
-
- element.click();
- document.body.removeChild(element);
- this.logBuffer = [];
- }
-
- downloadDebugInfoLogDump = async () => {
- const date = new Date();
- const fileName = `logs-${date.toISOString().slice(0, 19)}.txt`;
- var element = document.createElement('a');
- let newDebugInfo = null;
- try {
- let debugInfoFeature = this.callClient.feature(Features.DebugInfo);
- let debugInfo = debugInfoFeature.dumpDebugInfo();
- let debugInfoZippedDump = debugInfo.dump;
- let debugInfoDumpId = debugInfo.dumpId;
- newDebugInfo = {
- lastCallId: debugInfoFeature.lastCallId,
- lastLocalParticipantId: debugInfoFeature.lastLocalParticipantId,
- debugInfoDumpId:debugInfoDumpId,
- debugInfoDumpUnzipped: JSON.parse(inflate(debugInfoZippedDump, { to: 'string' })),
- }
- } catch (e) {
- console.error('ERROR, failed to dumpDebugInfo', e);
- }
- element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(newDebugInfo)));
- element.setAttribute('download', fileName);
-
- element.style.display = 'none';
- document.body.appendChild(element);
-
- element.click();
- document.body.removeChild(element);
- }
-
- joinGroup = async (withVideo) => {
- try {
- const callOptions = await this.getCallOptions({video: withVideo, micMuted: false});
- this.callAgent.join({ groupId: this.destinationGroup.value }, callOptions);
- } catch (e) {
- console.error('Failed to join a call', e);
- this.setState({ callError: 'Failed to join a call: ' + e });
- }
- };
-
- joinRooms = async (withVideo) => {
- try {
- const callOptions = await this.getCallOptions({video: withVideo, micMuted: false});
- this.callAgent.join({ roomId: this.roomsId.value }, callOptions);
- } catch (e) {
- console.error('Failed to join a call', e);
- this.setState({ callError: 'Failed to join a call: ' + e });
- }
- };
-
- joinTeamsMeeting = async (withVideo) => {
- try {
- const callOptions = await this.getCallOptions({video: withVideo, micMuted: false});
- if (this.meetingLink.value && !this.messageId.value && !this.threadId.value && this.tenantId && this.organizerId) {
- this.callAgent.join({ meetingLink: this.meetingLink.value }, callOptions);
-
- } else if (this.meetingId.value || this.passcode.value && !this.meetingLink.value && !this.messageId.value && !this.threadId.value && this.tenantId && this.organizerId) {
- this.callAgent.join({
- meetingId: this.meetingId.value,
- passcode: this.passcode.value
- }, callOptions);
- } else if (!this.meetingLink.value && this.messageId.value && this.threadId.value && this.tenantId && this.organizerId) {
- this.callAgent.join({
- messageId: this.messageId.value,
- threadId: this.threadId.value,
- tenantId: this.tenantId.value,
- organizerId: this.organizerId.value
- }, callOptions);
- } else {
- throw new Error('Please enter Teams meeting link or Teams meeting coordinate');
- }
- } catch (e) {
- console.error('Failed to join teams meeting:', e);
- this.setState({ callError: 'Failed to join teams meeting: ' + e });
- }
- }
-
- async getCallOptions(options) {
- let callOptions = {
- videoOptions: {
- localVideoStreams: undefined
- },
- audioOptions: {
- muted: !!options.micMuted
- }
- };
-
- let cameraWarning = undefined;
- let speakerWarning = undefined;
- let microphoneWarning = undefined;
-
- // On iOS, device permissions are lost after a little while, so re-ask for permissions
- const permissions = await this.deviceManager.askDevicePermission({ audio: true, video: true });
- this.setState({permissions: permissions});
-
- const cameras = await this.deviceManager.getCameras();
- const cameraDevice = cameras[0];
- if (cameraDevice && cameraDevice?.id !== 'camera:') {
- this.setState({
- selectedCameraDeviceId: cameraDevice?.id,
- cameraDeviceOptions: cameras.map(camera => { return { key: camera.id, text: camera.name } })
- });
- }
- if (!!options.video) {
- try {
- if (!cameraDevice || cameraDevice?.id === 'camera:') {
- throw new Error('No camera devices found.');
- } else if (cameraDevice) {
- callOptions.videoOptions = { localVideoStreams: [new LocalVideoStream(cameraDevice)] };
- if (this.videoConstraints) {
- callOptions.videoOptions.constraints = this.videoConstraints;
- }
- }
- } catch (e) {
- cameraWarning = e.message;
- }
- }
-
- try {
- const speakers = await this.deviceManager.getSpeakers();
- const speakerDevice = speakers[0];
- if (!speakerDevice || speakerDevice.id === 'speaker:') {
- throw new Error('No speaker devices found.');
- } else if (speakerDevice) {
- this.setState({
- selectedSpeakerDeviceId: speakerDevice.id,
- speakerDeviceOptions: speakers.map(speaker => { return { key: speaker.id, text: speaker.name } })
- });
- await this.deviceManager.selectSpeaker(speakerDevice);
- }
- } catch (e) {
- speakerWarning = e.message;
- }
-
- try {
- const microphones = await this.deviceManager.getMicrophones();
- const microphoneDevice = microphones[0];
- if (!microphoneDevice || microphoneDevice.id === 'microphone:') {
- throw new Error('No microphone devices found.');
- } else {
- this.setState({
- selectedMicrophoneDeviceId: microphoneDevice.id,
- microphoneDeviceOptions: microphones.map(microphone => { return { key: microphone.id, text: microphone.name } })
- });
- await this.deviceManager.selectMicrophone(microphoneDevice);
- }
- } catch (e) {
- microphoneWarning = e.message;
- }
-
- if (cameraWarning || speakerWarning || microphoneWarning) {
- this.setState({
- deviceManagerWarning:
- `${cameraWarning ? cameraWarning + ' ' : ''}
- ${speakerWarning ? speakerWarning + ' ' : ''}
- ${microphoneWarning ? microphoneWarning + ' ' : ''}`
- });
- }
-
- return callOptions;
- }
-
- handleCallSurveySubmitted() {
- this.setState({ callSurvey: null, call: null });
- }
-
- async runPreCallDiagnostics() {
- try {
- this.setState({
- showPreCallDiagnostcisResults: false,
- isPreCallDiagnosticsCall: true,
- preCallDiagnosticsResults: {}
- });
- const preCallDiagnosticsResult = await this.callClient.feature(Features.PreCallDiagnostics).startTest(this.tokenCredential);
-
- const deviceAccess = await preCallDiagnosticsResult.deviceAccess;
- this.setState({preCallDiagnosticsResults: {...this.state.preCallDiagnosticsResults, deviceAccess}});
-
- const deviceEnumeration = await preCallDiagnosticsResult.deviceEnumeration;
- this.setState({preCallDiagnosticsResults: {...this.state.preCallDiagnosticsResults, deviceEnumeration}});
-
- const inCallDiagnostics = await preCallDiagnosticsResult.inCallDiagnostics;
- this.setState({preCallDiagnosticsResults: {...this.state.preCallDiagnosticsResults, inCallDiagnostics}});
-
- const browserSupport = await preCallDiagnosticsResult.browserSupport;
- this.setState({preCallDiagnosticsResults: {...this.state.preCallDiagnosticsResults, browserSupport}});
-
- this.setState({
- showPreCallDiagnostcisResults: true,
- isPreCallDiagnosticsCall: false
- });
-
- } catch {
- throw new Error("Can't run Pre Call Diagnostics test. Please try again...");
- }
- }
-
- render() {
- const callSampleCode = `
-/******************************/
-/* Placing a call */
-/******************************/
-// Set up CallOptions
-const cameraDevice = this.callClient.getDeviceManager().getCameras()[0];
-const localVideoStream = new LocalVideoStream(cameraDevice);
-this.callOptions.videoOptions = { localVideoStreams: [localVideoStream] };
-
-// To place a 1:1 call to another ACS user
-const userId = { communicationUserId: 'ACS_USER_ID');
-this.currentCall = this.callAgent.startCall([userId], this.callOptions);
-
-// Place a 1:1 call to an ACS phone number. PSTN calling is currently in private preview.
-// When making PSTN calls, your Alternate Caller Id must be specified in the call options.
-const phoneNumber = { phoneNumber:
);
-this.callOptions.alternateCallerId = { phoneNumber: }
-this.currentCall = this.callAgent.startCall([phoneNumber], this.callOptions);
-
-// Place a 1:N call. Specify a multiple destinations
-this.currentCall = this.callAgent.startCall([userId1, phoneNumber], this.callOptions);
-
-/******************************/
-/* Receiving a call */
-/******************************/
-this.callAgent.on('incomingCall', async (args) => {
- // accept the incoming call
- const call = await args.incomingCall.accept();
-
- // or reject the incoming call
- args.incomingCall.reject();
-});
-
-/******************************/
-/* Joining a group call */
-/******************************/
-// Set up CallOptions
-const cameraDevice = this.callClient.deviceManager.getCameras()[0];
-const localVideoStream = new LocalVideoStream(cameraDevice);
-this.callOptions.videoOptions = { localVideoStreams: [localVideoStream] };
-
-// Join a group call
-this.currentCall = this.callAgent.join({groupId: }, this.callOptions);
-
-/*******************************/
-/* Joining a Teams meetings */
-/*******************************/
-// Join a Teams meeting using a meeting link. To get a Teams meeting link, go to the Teams meeting and
-// open the participants roster, then click on the 'Share Invite' button and then click on 'Copy link meeting' button.
-this.currentCall = this.callAgent.join({meetingLink: }, this.callOptions);
-// Join a Teams meeting using a meeting id.
-this.currentCall = this.callAgent.join({meetingId: , passcode (optional): }, this.callOptions);
-// Join a Teams meeting using meeting coordinates. Coordinates can be derived from the meeting link
-// Teams meeting link example
-const meetingLink = 'https://teams.microsoft.com/l/meetup-join/19:meeting_NjNiNzE3YzMtYzcxNi00ZGQ3LTk2YmYtMjNmOTE1MTVhM2Jl@thread.v2/0?context=%7B%22Tid%22:%2272f988bf-86f1-41af-91ab-2d7cd011db47%22,%22Oid%22:%227e353a91-0f71-4724-853b-b30ee4ca6a42%22%7D'
-const url = new URL(meetingLink);
-// Derive the coordinates (threadId, messageId, tenantId, and organizerId)
-const pathNameSplit = url.pathname.split('/');
-const threadId = decodeURIComponent(pathNameSplit[3]);
-const messageId = pathNameSplit[4];
-const meetingContext = JSON.parse(decodeURIComponent(url.search.replace('?context=', '')));
-const organizerId = meetingContext.Oid;
-const tenantId = meetingContext.Tid;
-this.currentCall = this.callAgent.join({
- threadId,
- messageId,
- tenantId,
- organizerId
- }, this.callOptions);
- `;
-
- const preCallDiagnosticsSampleCode = `
-//Get new token or use existing token.
-const response = (await fetch('getAcsUserAccessToken')).json();
-const token = response.token;
-const tokenCredential = new AzureCommunicationTokenCredential(token);
-
-// Start Pre Call diagnostics test
-const preCallDiagnosticsResult = await this.callClient.feature(Features.PreCallDiagnostics).startTest(tokenCredential);
-
-// Pre Call Diagnostics results
-const deviceAccess = await preCallDiagnosticsResult.deviceAccess;
-const audioDeviceAccess = deviceAccess.audio // boolean
-const videoDeviceAccess = deviceAccess.video // boolean
-
-const deviceEnumeration = await preCallDiagnosticsResult.deviceEnumeration;
-const microphone = deviceEnumeration.microphone // 'Available' | 'NotAvailable' | 'Unknown';
-const camera = deviceEnumeration.camera // 'Available' | 'NotAvailable' | 'Unknown';
-const speaker = deviceEnumeration.speaker // 'Available' | 'NotAvailable' | 'Unknown';
-
-const inCallDiagnostics = await preCallDiagnosticsResult.inCallDiagnostics;
-
-const callConnected = inCallDiagnostics.connected; // boolean
-
-const audioJitter = inCallDiagnostics.diagnostics.audio.jitter; // 'Bad' | 'Average' | 'Good' | 'Unknown';
-const audioPacketLoss = inCallDiagnostics.diagnostics.audio.packetLoss; // 'Bad' | 'Average' | 'Good' | 'Unknown';
-const audioRtt = inCallDiagnostics.diagnostics.audio.rtt; // 'Bad' | 'Average' | 'Good' | 'Unknown';
-
-const videoJitter = inCallDiagnostics.diagnostics.video.jitter; // 'Bad' | 'Average' | 'Good' | 'Unknown';
-const videoPacketLoss = inCallDiagnostics.diagnostics.video.packetLoss; // 'Bad' | 'Average' | 'Good' | 'Unknown';
-const videoRtt = inCallDiagnostics.diagnostics.video.rtt; // 'Bad' | 'Average' | 'Good' | 'Unknown';
-
-const brandWidth = inCallDiagnostics.bandWidth; // 'Bad' | 'Average' | 'Good' | 'Unknown';
-
-const browserSupport = await preCallDiagnosticsResult.browserSupport;
-const browser = browserSupport.browser; // 'Supported' | 'NotSupported' | 'Unknown';
-const os = browserSupport.os; // 'Supported' | 'NotSupported' | 'Unknown';
-
-const collector = (await preCallDiagnosticsResult.callMediaStatistics).createCollector({
- aggregationInterval: 200,
- dataPointsPerAggregation: 1,
-});
-collector.on("summaryReported", (mediaStats) => {
- console.log(mediaStats); // Get mediaStats summary for the test call.
-});
-
- `;
-
- const streamingSampleCode = `
-/************************************************/
-/* Local Video and Local Screen-sharing */
-/************************************************/
-// 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 cameraDevice = this.callClient.getDeviceManager().getCameras()[0];
-const localVideoStream = new LocalVideoStream(cameraDevice);
-await call.startVideo(localVideoStream);
-
-// To stop local video, pass the localVideoStream instance available in the
-// localVideoStreams collection
-await this.currentCall.stopVideo(localVideoStream);
-
-// You can use DeviceManager and Renderer 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 renderer = new Renderer(localVideoStream);
-const view = await renderer.createView();
-document.getElementById('someDiv').appendChild(view.target);
-
-// You can switch to a different camera device while video is being sent by invoking
-// switchSource() on a localVideoStream instance
-const cameraDevice1 = this.callClient.getDeviceManager().getCameras()[1];
-localVideoStream.switchSource(cameraDeivce1);
-
-// Handle 'localVideoStreamsUpdated' event
-this.currentCall.on('localVideoStreamsUpdated', e => {
- e.added.forEach(addedLocalVideoStream => { this.handleAddedLocalVideoStream(addedLocalVideoStream) });
- e.removed.forEach(removedLocalVideoStream => { this.handleRemovedLocalVideoStream(removedLocalVideoStream) });
-});
-
-// To start sharing your screen
-await this.currentCall.startScreenSharing();
-
-// To stop sharing your screen
-await this.currentCall.stopScreenSharing();
-
-// Handle 'isScreenSharingOnChanged' event
-this.currentCall.on('isScreenSharingOnChanged', this.handleIsScreenSharingOnChanged());
-
-
-
-
-/**************************************************************************************/
-/* Handling Video streams and Screen-sharing streams from remote participants */
-/**************************************************************************************/
-// Handle remote participant video and screen-sharing streams
-remoteParticipant.videoStreams.forEach(videoStream => subscribeToRemoteVideoStream(videoStream))
-
-// Handle remote participant 'videoStreamsUpdated' event. This is for videos and screen-shrings streams.
-remoteParticipant.on('videoStreamsUpdated', videoStreams => {
- videoStreams.added.forEach(addedStream => {
- subscribeToRemoteVideoStream(addedStream)
- });
-
- videoStreams.removed.forEach(removedStream => {
- unsubscribeFromRemoteVideoStream(removedStream);
- });
-});
-
-// Render remote streams on UI. Do this logic in a UI component.
-// Please refer to /src/MakeCall/StreamMedia.js of this app for an example of how to render streams on the UI:
-const subscribeToRemoteVideoStream = (stream) => {
- let componentContainer = document.getElementById(this.componentId);
- componentContainer.hidden = true;
-
- let renderer = new VideoStreamRenderer(stream);
- let view;
- let videoContainer;
-
- const renderStream = async () => {
- if(!view) {
- view = await renderer.createView();
- }
- videoContainer = document.getElementById(this.videoContainerId);
- if(!videoContainer?.hasChildNodes()) { videoContainer.appendChild(view.target); }
- }
-
- stream.on('isAvailableChanged', async () => {
- if (stream.isAvailable) {
- componentContainer.hidden = false;
- await renderStream();
- } else {
- componentContainer.hidden = true;
- }
- });
-
- if (stream.isAvailable) {
- componentContainer.hidden = false;
- await renderStream();
- }
-}
-
-
-
- `;
-
- const muteUnmuteSampleCode = `
-// To mute your microphone
-await this.currentCall.mute();
-
-// To unmute your microphone
-await this.currentCall.unmute();
-
-// Handle remote participant isMutedChanged event
-addedParticipant.on('isMutedChanged', () => {
- if(remoteParticipant.isMuted) {
- console.log('Remote participant is muted');
- } else {
- console.log('Remote participant is unmuted');
- }
-});
- `;
-
- const holdUnholdSampleCode = `
-/******************************/
-/* To hold the call */
-/******************************/
- // Call state changes when holding
- this.currentCall.on('stateChanged', () => {
- // Call state changes to 'LocalHold' or 'RemoteHold'
- console.log(this.currentCall.state);
- });
-
- // If you hold the Call, remote participant state changes to 'Hold'.
- // Handle remote participant stateChanged event
- addedParticipant.on('stateChanged', () => {
- console.log(addedParticipant.state); // 'Hold'
- });
-
- // If you want to hold the call use:
- await this.currentCall.hold();
-
-/******************************/
-/* To unhold the call */
-/******************************/
- // The Call state changes when unholding
- this.currentCall.on('stateChanged', () => {
- // Call state changes back to 'Connected'
- console.log(this.currentCall.state);
- });
-
- // Remote participant state changes to 'Connected'
- addedParticipant.on('stateChanged', () => {
- console.log(addedParticipant.state); // 'Connected'
- });
-
- // If you want to unhold the call use:
- await this.currentCall.resume();
- `;
-
- const deviceManagerSampleCode = `
-/*************************************/
-/* Device Manager */
-/*************************************/
-// Get the Device Manager.
-// The CallAgent must be initialized first in order to be able to access the DeviceManager.
-this.deviceManager = this.callClient.getDeviceManager();
-
-// Get list of devices
-const cameraDevices = await this.deviceManager.getCameras();
-const speakerDevices = await this.deviceManager.getSpeakers();
-const microphoneDevices = await this.deviceManager.getMicrophones();
-
-// Set microphone device and speaker device to use across the call stack.
-await this.deviceManager.selectSpeaker(speakerDevices[0]);
-await this.deviceManager.selectMicrophone(microphoneDevices[0]);
-// NOTE: Setting of video camera device to use is specified on CallAgent.startCall() and Call.join() APIs
-// by passing a LocalVideoStream into the options paramter.
-// To switch video camera device to use during call, use the LocalVideoStream.switchSource() method.
-
-// Get selected speaker and microphone
-const selectedSpeaker = this.deviceManager.selectedSpeaker;
-const selectedMicrophone = this.deviceManager.selectedMicrophone;
-
-// Handle videoDevicesUpdated event
-this.callClient.deviceManager.on('videoDevicesUpdated', e => {
- e.added.forEach(cameraDevice => { this.handleAddedCameraDevice(cameraDevice); });
- e.removed.forEach(removedCameraDevice => { this.handleRemovedCameraDevice(removeCameraDevice); });
-});
-
-// Handle audioDevicesUpdate event
-this.callClient.deviceManager.on('audioDevicesUpdated', e => {
- e.added.forEach(audioDevice => { this.handleAddedAudioDevice(audioDevice); });
- e.removed.forEach(removedAudioDevice => { this.handleRemovedAudioDevice(removedAudioDevice); });
-});
-
-// Handle selectedMicrophoneChanged event
-this.deviceManager.on('selectedMicrophoneChanged', () => { console.log(this.deviceManager.selectedMicrophone) });
-
-// Handle selectedSpeakerChanged event
-this.deviceManager.on('selectedSpeakerChanged', () => { console.log(this.deviceManager.selectedSpeaker) });
- `;
-
- // TODO: Create section component. Couldnt use the ExampleCard compoenent from uifabric because it is buggy,
- // when toggling their show/hide code functionality, videos dissapear from DOM.
-
- return (
-
-
- {
- this.state?.callSurvey &&
- this.handleCallSurveySubmitted()}
- call={this.state.callSurvey}
- />
- }
-
-
-
-
-
Placing and receiving calls
-
{`Permissions audio: ${this.state.permissions.audio} video: ${this.state.permissions.video}`}
-
-
-
-
-
-
-
this.setState({ showCallSampleCode: !this.state.showCallSampleCode })}>
-
-
-
-
-
Having provisioned an ACS Identity and initialized the SDK from the section above, you are now ready to place calls, join group calls, and receiving calls.
-
- {
- this.state.showCallSampleCode &&
-
-
- {callSampleCode}
-
-
- }
- {
- this.state.callError &&
-
- { this.setState({ callError: undefined }) }}
- dismissButtonAriaLabel="Close">
- {this.state.callError}
-
-
-
- }
- {
- this.state.deviceManagerWarning &&
-
{ this.setState({ deviceManagerWarning: undefined }) }}
- dismissButtonAriaLabel="Close">
- {this.state.deviceManagerWarning}
-
- }
- {
- this.state.ufdMessages.length > 0 &&
-
{ this.setState({ ufdMessages: [] }) }}
- dismissButtonAriaLabel="Close">
- {this.state.ufdMessages.map((msg, index) => {msg})}
-
- }
- {
- !this.state.incomingCall && !this.state.call && !this.state.callSurvey &&
-
-
-
-
-
-
- this.destinationUserIds = val} />
- this.destinationPhoneIds = val} />
- this.alternateCallerId = val} />
-
-
-
this.placeCall(false)}>
-
-
this.placeCall(true)}>
-
-
-
-
-
-
-
-
Join a Teams meeting
-
-
-
-
-
Enter meeting link
-
- this.meetingLink = val} />
-
-
Or enter meeting id (and) passcode
-
- this.meetingId = val} />
- this.passcode = val} />
-
-
Or enter meeting coordinates (Thread Id, Message Id, Organizer Id, and Tenant Id)
-
- this.threadId = val} />
- this.messageId = val} />
- this.organizerId = val} />
- this.tenantId = val} />
-
-
-
-
this.joinTeamsMeeting(false)}>
-
-
this.joinTeamsMeeting(true)}>
-
-
-
-
-
-
-
Join a group call
-
-
- this.destinationGroup = val} />
-
-
-
this.joinGroup(false)}>
-
-
this.joinGroup(true)}>
-
-
-
-
Join a Rooms call
-
-
- this.roomsId = val} />
-
-
-
this.joinRooms(false)}>
-
-
this.joinRooms(true)}>
-
-
-
-
-
-
-
Video Send Constraints
-
-
-
-
-
- }
- {
- this.state.call && this.state.isPreCallDiagnosticsCallInProgress &&
-
- Pre Call Diagnostics call in progress...
-
- }
- {
- this.state.call && !this.state.callSurvey && !this.state.isPreCallDiagnosticsCallInProgress &&
-
{ this.setState({ showCameraNotFoundWarning: show }) }}
- onShowSpeakerNotFoundWarning={(show) => { this.setState({ showSpeakerNotFoundWarning: show }) }}
- onShowMicrophoneNotFoundWarning={(show) => { this.setState({ showMicrophoneNotFoundWarning: show }) }} />
- }
- {
- this.state.incomingCall && !this.state.call &&
- await this.getCallOptions({ video: false, micMuted: false })}
- acceptCallMicrophoneUnmutedVideoOn={async () => await this.getCallOptions({ video: true, micMuted: false })}
- acceptCallMicrophoneMutedVideoOn={async () => await this.getCallOptions({ video: true, micMuted: true })}
- acceptCallMicrophoneMutedVideoOff={async () => await this.getCallOptions({ video: false, micMuted: true })}
- onReject={() => { this.setState({ incomingCall: undefined }) }} />
- }
-
-
-
-
-
-
Pre Call Diagnostics
-
-
this.runPreCallDiagnostics()}>
-
-
this.setState({ showPreCallDiagnosticsSampleCode: !this.state.showPreCallDiagnosticsSampleCode })}>
-
-
-
- {
- this.state.call && this.state.isPreCallDiagnosticsCallInProgress &&
-
- Pre Call Diagnostics call in progress...
-
-
- }
- {
- this.state.showPreCallDiagnostcisResults &&
-
- {
-
- {
- this.state.preCallDiagnosticsResults.deviceAccess &&
-
-
Device Permission:
-
-
Audio:
-
{this.state.preCallDiagnosticsResults.deviceAccess.audio.toString()}
-
-
-
Video:
-
{this.state.preCallDiagnosticsResults.deviceAccess.video.toString()}
-
-
- }
- {
- this.state.preCallDiagnosticsResults.deviceEnumeration &&
-
-
Device Access:
-
-
Microphone:
-
{this.state.preCallDiagnosticsResults.deviceEnumeration.microphone}
-
-
-
Camera:
-
{this.state.preCallDiagnosticsResults.deviceEnumeration.camera}
-
-
-
Speaker:
-
{this.state.preCallDiagnosticsResults.deviceEnumeration.speaker}
-
-
- }
- {
- this.state.preCallDiagnosticsResults.browserSupport &&
-
-
Browser Support:
-
-
OS:
-
{this.state.preCallDiagnosticsResults.browserSupport.os}
-
-
-
Browser:
-
{this.state.preCallDiagnosticsResults.browserSupport.browser}
-
-
- }
- {
- this.state.preCallDiagnosticsResults.inCallDiagnostics &&
-
-
Call Diagnostics:
-
-
-
Call Connected:
-
{this.state.preCallDiagnosticsResults.inCallDiagnostics.connected.toString()}
-
-
-
BandWidth:
-
{this.state.preCallDiagnosticsResults.inCallDiagnostics.bandWidth}
-
-
-
-
Audio Jitter:
-
{this.state.preCallDiagnosticsResults.inCallDiagnostics.diagnostics.audio.jitter}
-
-
-
Audio PacketLoss:
-
{this.state.preCallDiagnosticsResults.inCallDiagnostics.diagnostics.audio.packetLoss}
-
-
-
Audio Rtt:
-
{this.state.preCallDiagnosticsResults.inCallDiagnostics.diagnostics.audio.rtt}
-
-
-
-
Video Jitter:
-
{this.state.preCallDiagnosticsResults.inCallDiagnostics.diagnostics.video.jitter}
-
-
-
Video PacketLoss:
-
{this.state.preCallDiagnosticsResults.inCallDiagnostics.diagnostics.video.packetLoss}
-
-
-
Video Rtt:
-
{this.state.preCallDiagnosticsResults.inCallDiagnostics.diagnostics.video.rtt}
-
-
-
- }
-
- }
-
- }
- {
- this.state.showPreCallDiagnosticsSampleCode &&
-
-
- {
- preCallDiagnosticsSampleCode
- }
-
-
- }
-
-
-
-
-
-
Video, Screen sharing, and local video preview
-
-
this.setState({ showStreamingSampleCode: !this.state.showStreamingSampleCode })}>
-
-
-
- {
- this.state.showStreamingSampleCode &&
-
-
- {streamingSampleCode}
-
-
- }
-
- Video - try it out.
-
-
- From your current call, toggle your video on and off by clicking on the icon.
- When you start your video, remote participants can see your video by receiving a stream and rendering it in an HTML element.
-
-
-
- Screen sharing - try it out.
-
-
- From your current call, toggle your screen sharing on and off by clicking on the icon.
- When you start sharing your screen, remote participants can see your screen by receiving a stream and rendering it in an HTML element.
-
-
-
-
-
-
-
Mute / Unmute
-
-
this.setState({ showMuteUnmuteSampleCode: !this.state.showMuteUnmuteSampleCode })}>
-
-
-
- {
- this.state.showMuteUnmuteSampleCode &&
-
-
- {muteUnmuteSampleCode}
-
-
- }
-
- Try it out.
-
-
- From your current call, toggle your microphone on and off by clicking on the icon.
- When you mute or unmute your microphone, remote participants can receive an event about wether your micrphone is muted or unmuted.
-
-
-
-
-
-
-
Hold / Unhold
-
-
this.setState({ showHoldUnholdSampleCode: !this.state.showHoldUnholdSampleCode })}>
-
-
-
- {
- this.state.showHoldUnholdSampleCode &&
-
-
- {holdUnholdSampleCode}
-
-
- }
-
- Try it out.
-
-
- From your current call, toggle hold call and unhold call on by clicking on the icon.
- When you hold or unhold the call, remote participants can receive other participant state changed events. Also, the call state changes.
-
-
-
-
-
-
-
Device Manager
-
-
this.setState({ showDeviceManagerSampleCode: !this.state.showDeviceManagerSampleCode })}>
-
-
-
- {
- this.state.showDeviceManagerSampleCode &&
-
-
- {deviceManagerSampleCode}
-
-
- }
-
- Try it out.
-
-
- From your current call, click on the icon to open up the settings panel.
- The DeviceManager is used to select the devices (camera, microphone, and speakers) to use across the call stack and to preview your camera.
-
-
-
-
- );
- }
-}
+import React from "react";
+import { CallClient, LocalVideoStream, Features, CallAgentKind, VideoStreamRenderer } from '@azure/communication-calling';
+import { AzureCommunicationTokenCredential, createIdentifierFromRawId} from '@azure/communication-common';
+import {
+ PrimaryButton,
+ TextField,
+ MessageBar,
+ MessageBarType
+} from 'office-ui-fabric-react'
+import { Icon } from '@fluentui/react/lib/Icon';
+import IncomingCallCard from './IncomingCallCard';
+import CallCard from '../MakeCall/CallCard';
+import CallSurvey from '../MakeCall/CallSurvey';
+import Login from './Login';
+import MediaConstraint from './MediaConstraint';
+import { setLogLevel, AzureLogger } from '@azure/logger';
+import { inflate } from 'pako';
+export default class MakeCall extends React.Component {
+ constructor(props) {
+ super(props);
+ this.callClient = null;
+ this.callAgent = null;
+ this.deviceManager = null;
+ this.destinationUserIds = null;
+ this.destinationPhoneIds = null;
+ this.destinationGroup = null;
+ this.meetingLink = null;
+ this.meetingId = null;
+ this.passcode = null;
+ this.roomsId = null;
+ this.threadId = null;
+ this.messageId = null;
+ this.organizerId = null;
+ this.tenantId = null;
+ this.callError = null;
+ this.logBuffer = [];
+ this.videoConstraints = null;
+ this.tokenCredential = null;
+ this.logInComponentRef = React.createRef();
+
+ this.state = {
+ componentId: props.componentId,
+ loggedIn: false,
+ isCallClientActiveInAnotherTab: false,
+ call: undefined,
+ callSurvey: undefined,
+ incomingCall: undefined,
+ showCallSampleCode: false,
+ showMuteUnmuteSampleCode: false,
+ showHoldUnholdCallSampleCode: false,
+ showPreCallDiagnosticsSampleCode: false,
+ showPreCallDiagnostcisResults: false,
+ isPreCallDiagnosticsCallInProgress: false,
+ selectedCameraDeviceId: null,
+ selectedSpeakerDeviceId: null,
+ selectedMicrophoneDeviceId: null,
+ deviceManagerWarning: null,
+ callError: null,
+ ufdMessages: [],
+ permissions: {
+ audio: null,
+ video: null
+ },
+ preCallDiagnosticsResults: {},
+ isTeamsUser: false,
+ identityMri: undefined
+ };
+
+ setInterval(() => {
+ if (this.state.ufdMessages.length > 0) {
+ this.setState({ ufdMessages: this.state.ufdMessages.slice(1) });
+ }
+ }, 10000);
+
+ // override logger to be able to dowload logs locally
+ AzureLogger.log = (...args) => {
+ this.logBuffer.push(...args);
+ if (args[0].startsWith('azure:ACS-calling:info')) {
+ console.info(...args);
+ } else if (args[0].startsWith('azure:ACS-calling:verbose')) {
+ console.debug(...args);
+ } else if (args[0].startsWith('azure:ACS-calling:warning')) {
+ console.warn(...args);
+ } else if (args[0].startsWith('azure:ACS-calling:error')) {
+ console.error(...args);
+ } else {
+ console.log(...args);
+ }
+ };
+ }
+
+ handleMediaConstraint = (constraints) => {
+ if (constraints.video) {
+ this.videoConstraints = constraints.video;
+ }
+ }
+
+ handleLogIn = async (userDetails) => {
+ if (userDetails) {
+ try {
+ const tokenCredential = new AzureCommunicationTokenCredential(userDetails.token);
+ this.tokenCredential = tokenCredential;
+ setLogLevel('verbose');
+
+ const proxyConfiguration = userDetails.proxy.useProxy ? { url: userDetails.proxy.url } : undefined;
+ const turnConfiguration = userDetails.customTurn.useCustomTurn && !userDetails.customTurn.isLoading ? userDetails.customTurn.turn : undefined;
+ this.callClient = new CallClient({
+ diagnostics: {
+ appName: 'azure-communication-services',
+ appVersion: '1.3.1-beta.1',
+ tags: ["javascript_calling_sdk",
+ `#clientTag:${userDetails.clientTag}`]
+ },
+ networkConfiguration: {
+ proxy: proxyConfiguration,
+ turn: turnConfiguration
+ }
+ });
+
+ this.deviceManager = await this.callClient.getDeviceManager();
+ const permissions = await this.deviceManager.askDevicePermission({ audio: true, video: true });
+ this.setState({permissions: permissions});
+
+ this.setState({ isTeamsUser: userDetails.isTeamsUser});
+ this.setState({ identityMri: createIdentifierFromRawId(userDetails.communicationUserId)})
+ this.callAgent = this.state.isTeamsUser ?
+ await this.callClient.createTeamsCallAgent(tokenCredential) :
+ await this.callClient.createCallAgent(tokenCredential, { displayName: userDetails.displayName });
+
+ window.callAgent = this.callAgent;
+ window.videoStreamRenderer = VideoStreamRenderer;
+ this.callAgent.on('callsUpdated', e => {
+ console.log(`callsUpdated, added=${e.added}, removed=${e.removed}`);
+
+ e.added.forEach(call => {
+ this.setState({ call: call });
+
+ const diagnosticChangedListener = (diagnosticInfo) => {
+ const rmsg = `UFD Diagnostic changed:
+ Diagnostic: ${diagnosticInfo.diagnostic}
+ Value: ${diagnosticInfo.value}
+ Value type: ${diagnosticInfo.valueType}`;
+ if (this.state.ufdMessages.length > 0) {
+ this.setState({ ufdMessages: [...this.state.ufdMessages, rmsg] });
+ } else {
+ this.setState({ ufdMessages: [rmsg] });
+ }
+
+
+ };
+
+ call.feature(Features.UserFacingDiagnostics).media.on('diagnosticChanged', diagnosticChangedListener);
+ call.feature(Features.UserFacingDiagnostics).network.on('diagnosticChanged', diagnosticChangedListener);
+ });
+
+ e.removed.forEach(call => {
+ if (this.state.call && this.state.call === call) {
+ this.displayCallEndReason(this.state.call.callEndReason);
+ }
+ });
+ });
+ this.callAgent.on('incomingCall', args => {
+ const incomingCall = args.incomingCall;
+ if (this.state.call) {
+ incomingCall.reject();
+ return;
+ }
+
+ this.setState({ incomingCall: incomingCall });
+
+ incomingCall.on('callEnded', args => {
+ this.displayCallEndReason(args.callEndReason);
+ });
+
+ });
+ this.setState({ loggedIn: true });
+ this.logInComponentRef.current.setCallAgent(this.callAgent);
+ this.logInComponentRef.current.setCallClient(this.callClient);
+ } catch (e) {
+ console.error(e);
+ }
+ }
+ }
+
+ displayCallEndReason = (callEndReason) => {
+ if (callEndReason.code !== 0 || callEndReason.subCode !== 0) {
+ this.setState({ callSurvey: this.state.call, callError: `Call end reason: code: ${callEndReason.code}, subcode: ${callEndReason.subCode}` });
+ }
+
+ this.setState({ call: null, callSurvey: this.state.call, incomingCall: null });
+ }
+
+ placeCall = async (withVideo) => {
+ try {
+ let identitiesToCall = [];
+ const userIdsArray = this.destinationUserIds.value.split(',');
+ const phoneIdsArray = this.destinationPhoneIds.value.split(',');
+
+ userIdsArray.forEach((userId, index) => {
+ if (userId) {
+ userId = userId.trim();
+ if (userId === '8:echo123') {
+ userId = { id: userId };
+ }
+ else {
+ userId = createIdentifierFromRawId(userId);
+ }
+ if (!identitiesToCall.find(id => { return id === userId })) {
+ identitiesToCall.push(userId);
+ }
+ }
+ });
+
+ phoneIdsArray.forEach((phoneNumberId, index) => {
+ if (phoneNumberId) {
+ phoneNumberId = phoneNumberId.trim();
+ phoneNumberId = createIdentifierFromRawId(phoneNumberId);
+ if (!identitiesToCall.find(id => { return id === phoneNumberId })) {
+ identitiesToCall.push(phoneNumberId);
+ }
+ }
+ });
+
+ const callOptions = await this.getCallOptions({video: withVideo, micMuted: false});
+
+ if (this.callAgent.kind === CallAgentKind.CallAgent && this.alternateCallerId.value !== '') {
+ callOptions.alternateCallerId = { phoneNumber: this.alternateCallerId.value.trim() };
+ }
+
+ if (identitiesToCall.length > 1) {
+ if (this.callAgent.kind === CallAgentKind.TeamsCallAgent && this.threadId === '') {
+ throw new Error('Thread ID is needed to make Teams Group Call');
+ } else {
+ callOptions.threadId = this.threadId.value;
+ }
+ }
+
+ this.callAgent.startCall(identitiesToCall, callOptions);
+
+ } catch (e) {
+ console.error('Failed to place a call', e);
+ this.setState({ callError: 'Failed to place a call: ' + e });
+ }
+ };
+
+ downloadLog = async () => {
+ const date = new Date();
+ const fileName = `logs-${date.toISOString().slice(0, 19)}.txt`;
+ var element = document.createElement('a');
+ element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(this.logBuffer.join('\n')));
+ element.setAttribute('download', fileName);
+
+ element.style.display = 'none';
+ document.body.appendChild(element);
+
+ element.click();
+ document.body.removeChild(element);
+ this.logBuffer = [];
+ }
+
+ downloadDebugInfoLogDump = async () => {
+ const date = new Date();
+ const fileName = `logs-${date.toISOString().slice(0, 19)}.txt`;
+ var element = document.createElement('a');
+ let newDebugInfo = null;
+ try {
+ let debugInfoFeature = this.callClient.feature(Features.DebugInfo);
+ let debugInfo = debugInfoFeature.dumpDebugInfo();
+ let debugInfoZippedDump = debugInfo.dump;
+ let debugInfoDumpId = debugInfo.dumpId;
+ newDebugInfo = {
+ lastCallId: debugInfoFeature.lastCallId,
+ lastLocalParticipantId: debugInfoFeature.lastLocalParticipantId,
+ debugInfoDumpId:debugInfoDumpId,
+ debugInfoDumpUnzipped: JSON.parse(inflate(debugInfoZippedDump, { to: 'string' })),
+ }
+ } catch (e) {
+ console.error('ERROR, failed to dumpDebugInfo', e);
+ }
+ element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(newDebugInfo)));
+ element.setAttribute('download', fileName);
+
+ element.style.display = 'none';
+ document.body.appendChild(element);
+
+ element.click();
+ document.body.removeChild(element);
+ }
+
+ joinGroup = async (withVideo) => {
+ try {
+ const callOptions = await this.getCallOptions({video: withVideo, micMuted: false});
+ this.callAgent.join({ groupId: this.destinationGroup.value }, callOptions);
+ } catch (e) {
+ console.error('Failed to join a call', e);
+ this.setState({ callError: 'Failed to join a call: ' + e });
+ }
+ };
+
+ joinRooms = async (withVideo) => {
+ try {
+ const callOptions = await this.getCallOptions({video: withVideo, micMuted: false});
+ this.callAgent.join({ roomId: this.roomsId.value }, callOptions);
+ } catch (e) {
+ console.error('Failed to join a call', e);
+ this.setState({ callError: 'Failed to join a call: ' + e });
+ }
+ };
+
+ joinTeamsMeeting = async (withVideo) => {
+ try {
+ const callOptions = await this.getCallOptions({video: withVideo, micMuted: false});
+ if (this.meetingLink.value && !this.messageId.value && !this.threadId.value && this.tenantId && this.organizerId) {
+ this.callAgent.join({ meetingLink: this.meetingLink.value }, callOptions);
+
+ } else if (this.meetingId.value || this.passcode.value && !this.meetingLink.value && !this.messageId.value && !this.threadId.value && this.tenantId && this.organizerId) {
+ this.callAgent.join({
+ meetingId: this.meetingId.value,
+ passcode: this.passcode.value
+ }, callOptions);
+ } else if (!this.meetingLink.value && this.messageId.value && this.threadId.value && this.tenantId && this.organizerId) {
+ this.callAgent.join({
+ messageId: this.messageId.value,
+ threadId: this.threadId.value,
+ tenantId: this.tenantId.value,
+ organizerId: this.organizerId.value
+ }, callOptions);
+ } else {
+ throw new Error('Please enter Teams meeting link or Teams meeting coordinate');
+ }
+ } catch (e) {
+ console.error('Failed to join teams meeting:', e);
+ this.setState({ callError: 'Failed to join teams meeting: ' + e });
+ }
+ }
+
+ async getCallOptions(options) {
+ let callOptions = {
+ videoOptions: {
+ localVideoStreams: undefined
+ },
+ audioOptions: {
+ muted: !!options.micMuted
+ }
+ };
+
+ let cameraWarning = undefined;
+ let speakerWarning = undefined;
+ let microphoneWarning = undefined;
+
+ // On iOS, device permissions are lost after a little while, so re-ask for permissions
+ const permissions = await this.deviceManager.askDevicePermission({ audio: true, video: true });
+ this.setState({permissions: permissions});
+
+ const cameras = await this.deviceManager.getCameras();
+ const cameraDevice = cameras[0];
+ if (cameraDevice && cameraDevice?.id !== 'camera:') {
+ this.setState({
+ selectedCameraDeviceId: cameraDevice?.id,
+ cameraDeviceOptions: cameras.map(camera => { return { key: camera.id, text: camera.name } })
+ });
+ }
+ if (!!options.video) {
+ try {
+ if (!cameraDevice || cameraDevice?.id === 'camera:') {
+ throw new Error('No camera devices found.');
+ } else if (cameraDevice) {
+ callOptions.videoOptions = { localVideoStreams: [new LocalVideoStream(cameraDevice)] };
+ if (this.videoConstraints) {
+ callOptions.videoOptions.constraints = this.videoConstraints;
+ }
+ }
+ } catch (e) {
+ cameraWarning = e.message;
+ }
+ }
+
+ try {
+ const speakers = await this.deviceManager.getSpeakers();
+ const speakerDevice = speakers[0];
+ if (!speakerDevice || speakerDevice.id === 'speaker:') {
+ throw new Error('No speaker devices found.');
+ } else if (speakerDevice) {
+ this.setState({
+ selectedSpeakerDeviceId: speakerDevice.id,
+ speakerDeviceOptions: speakers.map(speaker => { return { key: speaker.id, text: speaker.name } })
+ });
+ await this.deviceManager.selectSpeaker(speakerDevice);
+ }
+ } catch (e) {
+ speakerWarning = e.message;
+ }
+
+ try {
+ const microphones = await this.deviceManager.getMicrophones();
+ const microphoneDevice = microphones[0];
+ if (!microphoneDevice || microphoneDevice.id === 'microphone:') {
+ throw new Error('No microphone devices found.');
+ } else {
+ this.setState({
+ selectedMicrophoneDeviceId: microphoneDevice.id,
+ microphoneDeviceOptions: microphones.map(microphone => { return { key: microphone.id, text: microphone.name } })
+ });
+ await this.deviceManager.selectMicrophone(microphoneDevice);
+ }
+ } catch (e) {
+ microphoneWarning = e.message;
+ }
+
+ if (cameraWarning || speakerWarning || microphoneWarning) {
+ this.setState({
+ deviceManagerWarning:
+ `${cameraWarning ? cameraWarning + ' ' : ''}
+ ${speakerWarning ? speakerWarning + ' ' : ''}
+ ${microphoneWarning ? microphoneWarning + ' ' : ''}`
+ });
+ }
+
+ return callOptions;
+ }
+
+ handleCallSurveySubmitted() {
+ this.setState({ callSurvey: null, call: null });
+ }
+
+ async runPreCallDiagnostics() {
+ try {
+ this.setState({
+ showPreCallDiagnostcisResults: false,
+ isPreCallDiagnosticsCall: true,
+ preCallDiagnosticsResults: {}
+ });
+ const preCallDiagnosticsResult = await this.callClient.feature(Features.PreCallDiagnostics).startTest(this.tokenCredential);
+
+ const deviceAccess = await preCallDiagnosticsResult.deviceAccess;
+ this.setState({preCallDiagnosticsResults: {...this.state.preCallDiagnosticsResults, deviceAccess}});
+
+ const deviceEnumeration = await preCallDiagnosticsResult.deviceEnumeration;
+ this.setState({preCallDiagnosticsResults: {...this.state.preCallDiagnosticsResults, deviceEnumeration}});
+
+ const inCallDiagnostics = await preCallDiagnosticsResult.inCallDiagnostics;
+ this.setState({preCallDiagnosticsResults: {...this.state.preCallDiagnosticsResults, inCallDiagnostics}});
+
+ const browserSupport = await preCallDiagnosticsResult.browserSupport;
+ this.setState({preCallDiagnosticsResults: {...this.state.preCallDiagnosticsResults, browserSupport}});
+
+ this.setState({
+ showPreCallDiagnostcisResults: true,
+ isPreCallDiagnosticsCall: false
+ });
+
+ } catch {
+ throw new Error("Can't run Pre Call Diagnostics test. Please try again...");
+ }
+ }
+
+ render() {
+ const callSampleCode = `
+/******************************/
+/* Placing a call */
+/******************************/
+// Set up CallOptions
+const cameraDevice = this.callClient.getDeviceManager().getCameras()[0];
+const localVideoStream = new LocalVideoStream(cameraDevice);
+this.callOptions.videoOptions = { localVideoStreams: [localVideoStream] };
+
+// To place a 1:1 call to another ACS user
+const userId = { communicationUserId: 'ACS_USER_ID');
+this.currentCall = this.callAgent.startCall([userId], this.callOptions);
+
+// Place a 1:1 call to an ACS phone number. PSTN calling is currently in private preview.
+// When making PSTN calls, your Alternate Caller Id must be specified in the call options.
+const phoneNumber = { phoneNumber: );
+this.callOptions.alternateCallerId = { phoneNumber: }
+this.currentCall = this.callAgent.startCall([phoneNumber], this.callOptions);
+
+// Place a 1:N call. Specify a multiple destinations
+this.currentCall = this.callAgent.startCall([userId1, phoneNumber], this.callOptions);
+
+/******************************/
+/* Receiving a call */
+/******************************/
+this.callAgent.on('incomingCall', async (args) => {
+ // accept the incoming call
+ const call = await args.incomingCall.accept();
+
+ // or reject the incoming call
+ args.incomingCall.reject();
+});
+
+/******************************/
+/* Joining a group call */
+/******************************/
+// Set up CallOptions
+const cameraDevice = this.callClient.deviceManager.getCameras()[0];
+const localVideoStream = new LocalVideoStream(cameraDevice);
+this.callOptions.videoOptions = { localVideoStreams: [localVideoStream] };
+
+// Join a group call
+this.currentCall = this.callAgent.join({groupId: }, this.callOptions);
+
+/*******************************/
+/* Joining a Teams meetings */
+/*******************************/
+// Join a Teams meeting using a meeting link. To get a Teams meeting link, go to the Teams meeting and
+// open the participants roster, then click on the 'Share Invite' button and then click on 'Copy link meeting' button.
+this.currentCall = this.callAgent.join({meetingLink: }, this.callOptions);
+// Join a Teams meeting using a meeting id.
+this.currentCall = this.callAgent.join({meetingId: , passcode (optional): }, this.callOptions);
+// Join a Teams meeting using meeting coordinates. Coordinates can be derived from the meeting link
+// Teams meeting link example
+const meetingLink = 'https://teams.microsoft.com/l/meetup-join/19:meeting_NjNiNzE3YzMtYzcxNi00ZGQ3LTk2YmYtMjNmOTE1MTVhM2Jl@thread.v2/0?context=%7B%22Tid%22:%2272f988bf-86f1-41af-91ab-2d7cd011db47%22,%22Oid%22:%227e353a91-0f71-4724-853b-b30ee4ca6a42%22%7D'
+const url = new URL(meetingLink);
+// Derive the coordinates (threadId, messageId, tenantId, and organizerId)
+const pathNameSplit = url.pathname.split('/');
+const threadId = decodeURIComponent(pathNameSplit[3]);
+const messageId = pathNameSplit[4];
+const meetingContext = JSON.parse(decodeURIComponent(url.search.replace('?context=', '')));
+const organizerId = meetingContext.Oid;
+const tenantId = meetingContext.Tid;
+this.currentCall = this.callAgent.join({
+ threadId,
+ messageId,
+ tenantId,
+ organizerId
+ }, this.callOptions);
+ `;
+
+ const preCallDiagnosticsSampleCode = `
+//Get new token or use existing token.
+const response = (await fetch('getAcsUserAccessToken')).json();
+const token = response.token;
+const tokenCredential = new AzureCommunicationTokenCredential(token);
+
+// Start Pre Call diagnostics test
+const preCallDiagnosticsResult = await this.callClient.feature(Features.PreCallDiagnostics).startTest(tokenCredential);
+
+// Pre Call Diagnostics results
+const deviceAccess = await preCallDiagnosticsResult.deviceAccess;
+const audioDeviceAccess = deviceAccess.audio // boolean
+const videoDeviceAccess = deviceAccess.video // boolean
+
+const deviceEnumeration = await preCallDiagnosticsResult.deviceEnumeration;
+const microphone = deviceEnumeration.microphone // 'Available' | 'NotAvailable' | 'Unknown';
+const camera = deviceEnumeration.camera // 'Available' | 'NotAvailable' | 'Unknown';
+const speaker = deviceEnumeration.speaker // 'Available' | 'NotAvailable' | 'Unknown';
+
+const inCallDiagnostics = await preCallDiagnosticsResult.inCallDiagnostics;
+
+const callConnected = inCallDiagnostics.connected; // boolean
+
+const audioJitter = inCallDiagnostics.diagnostics.audio.jitter; // 'Bad' | 'Average' | 'Good' | 'Unknown';
+const audioPacketLoss = inCallDiagnostics.diagnostics.audio.packetLoss; // 'Bad' | 'Average' | 'Good' | 'Unknown';
+const audioRtt = inCallDiagnostics.diagnostics.audio.rtt; // 'Bad' | 'Average' | 'Good' | 'Unknown';
+
+const videoJitter = inCallDiagnostics.diagnostics.video.jitter; // 'Bad' | 'Average' | 'Good' | 'Unknown';
+const videoPacketLoss = inCallDiagnostics.diagnostics.video.packetLoss; // 'Bad' | 'Average' | 'Good' | 'Unknown';
+const videoRtt = inCallDiagnostics.diagnostics.video.rtt; // 'Bad' | 'Average' | 'Good' | 'Unknown';
+
+const brandWidth = inCallDiagnostics.bandWidth; // 'Bad' | 'Average' | 'Good' | 'Unknown';
+
+const browserSupport = await preCallDiagnosticsResult.browserSupport;
+const browser = browserSupport.browser; // 'Supported' | 'NotSupported' | 'Unknown';
+const os = browserSupport.os; // 'Supported' | 'NotSupported' | 'Unknown';
+
+const collector = (await preCallDiagnosticsResult.callMediaStatistics).createCollector({
+ aggregationInterval: 200,
+ dataPointsPerAggregation: 1,
+});
+collector.on("summaryReported", (mediaStats) => {
+ console.log(mediaStats); // Get mediaStats summary for the test call.
+});
+
+ `;
+
+ const streamingSampleCode = `
+/************************************************/
+/* Local Video and Local Screen-sharing */
+/************************************************/
+// 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 cameraDevice = this.callClient.getDeviceManager().getCameras()[0];
+const localVideoStream = new LocalVideoStream(cameraDevice);
+await call.startVideo(localVideoStream);
+
+// To stop local video, pass the localVideoStream instance available in the
+// localVideoStreams collection
+await this.currentCall.stopVideo(localVideoStream);
+
+// You can use DeviceManager and Renderer 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 renderer = new Renderer(localVideoStream);
+const view = await renderer.createView();
+document.getElementById('someDiv').appendChild(view.target);
+
+// You can switch to a different camera device while video is being sent by invoking
+// switchSource() on a localVideoStream instance
+const cameraDevice1 = this.callClient.getDeviceManager().getCameras()[1];
+localVideoStream.switchSource(cameraDeivce1);
+
+// Handle 'localVideoStreamsUpdated' event
+this.currentCall.on('localVideoStreamsUpdated', e => {
+ e.added.forEach(addedLocalVideoStream => { this.handleAddedLocalVideoStream(addedLocalVideoStream) });
+ e.removed.forEach(removedLocalVideoStream => { this.handleRemovedLocalVideoStream(removedLocalVideoStream) });
+});
+
+// To start sharing your screen
+await this.currentCall.startScreenSharing();
+
+// To stop sharing your screen
+await this.currentCall.stopScreenSharing();
+
+// Handle 'isScreenSharingOnChanged' event
+this.currentCall.on('isScreenSharingOnChanged', this.handleIsScreenSharingOnChanged());
+
+
+
+
+/**************************************************************************************/
+/* Handling Video streams and Screen-sharing streams from remote participants */
+/**************************************************************************************/
+// Handle remote participant video and screen-sharing streams
+remoteParticipant.videoStreams.forEach(videoStream => subscribeToRemoteVideoStream(videoStream))
+
+// Handle remote participant 'videoStreamsUpdated' event. This is for videos and screen-shrings streams.
+remoteParticipant.on('videoStreamsUpdated', videoStreams => {
+ videoStreams.added.forEach(addedStream => {
+ subscribeToRemoteVideoStream(addedStream)
+ });
+
+ videoStreams.removed.forEach(removedStream => {
+ unsubscribeFromRemoteVideoStream(removedStream);
+ });
+});
+
+// Render remote streams on UI. Do this logic in a UI component.
+// Please refer to /src/MakeCall/StreamMedia.js of this app for an example of how to render streams on the UI:
+const subscribeToRemoteVideoStream = (stream) => {
+ let componentContainer = document.getElementById(this.componentId);
+ componentContainer.hidden = true;
+
+ let renderer = new VideoStreamRenderer(stream);
+ let view;
+ let videoContainer;
+
+ const renderStream = async () => {
+ if(!view) {
+ view = await renderer.createView();
+ }
+ videoContainer = document.getElementById(this.videoContainerId);
+ if(!videoContainer?.hasChildNodes()) { videoContainer.appendChild(view.target); }
+ }
+
+ stream.on('isAvailableChanged', async () => {
+ if (stream.isAvailable) {
+ componentContainer.hidden = false;
+ await renderStream();
+ } else {
+ componentContainer.hidden = true;
+ }
+ });
+
+ if (stream.isAvailable) {
+ componentContainer.hidden = false;
+ await renderStream();
+ }
+}
+
+
+
+ `;
+
+ const muteUnmuteSampleCode = `
+// To mute your microphone
+await this.currentCall.mute();
+
+// To unmute your microphone
+await this.currentCall.unmute();
+
+// Handle remote participant isMutedChanged event
+addedParticipant.on('isMutedChanged', () => {
+ if(remoteParticipant.isMuted) {
+ console.log('Remote participant is muted');
+ } else {
+ console.log('Remote participant is unmuted');
+ }
+});
+ `;
+
+ const holdUnholdSampleCode = `
+/******************************/
+/* To hold the call */
+/******************************/
+ // Call state changes when holding
+ this.currentCall.on('stateChanged', () => {
+ // Call state changes to 'LocalHold' or 'RemoteHold'
+ console.log(this.currentCall.state);
+ });
+
+ // If you hold the Call, remote participant state changes to 'Hold'.
+ // Handle remote participant stateChanged event
+ addedParticipant.on('stateChanged', () => {
+ console.log(addedParticipant.state); // 'Hold'
+ });
+
+ // If you want to hold the call use:
+ await this.currentCall.hold();
+
+/******************************/
+/* To unhold the call */
+/******************************/
+ // The Call state changes when unholding
+ this.currentCall.on('stateChanged', () => {
+ // Call state changes back to 'Connected'
+ console.log(this.currentCall.state);
+ });
+
+ // Remote participant state changes to 'Connected'
+ addedParticipant.on('stateChanged', () => {
+ console.log(addedParticipant.state); // 'Connected'
+ });
+
+ // If you want to unhold the call use:
+ await this.currentCall.resume();
+ `;
+
+ const deviceManagerSampleCode = `
+/*************************************/
+/* Device Manager */
+/*************************************/
+// Get the Device Manager.
+// The CallAgent must be initialized first in order to be able to access the DeviceManager.
+this.deviceManager = this.callClient.getDeviceManager();
+
+// Get list of devices
+const cameraDevices = await this.deviceManager.getCameras();
+const speakerDevices = await this.deviceManager.getSpeakers();
+const microphoneDevices = await this.deviceManager.getMicrophones();
+
+// Set microphone device and speaker device to use across the call stack.
+await this.deviceManager.selectSpeaker(speakerDevices[0]);
+await this.deviceManager.selectMicrophone(microphoneDevices[0]);
+// NOTE: Setting of video camera device to use is specified on CallAgent.startCall() and Call.join() APIs
+// by passing a LocalVideoStream into the options paramter.
+// To switch video camera device to use during call, use the LocalVideoStream.switchSource() method.
+
+// Get selected speaker and microphone
+const selectedSpeaker = this.deviceManager.selectedSpeaker;
+const selectedMicrophone = this.deviceManager.selectedMicrophone;
+
+// Handle videoDevicesUpdated event
+this.callClient.deviceManager.on('videoDevicesUpdated', e => {
+ e.added.forEach(cameraDevice => { this.handleAddedCameraDevice(cameraDevice); });
+ e.removed.forEach(removedCameraDevice => { this.handleRemovedCameraDevice(removeCameraDevice); });
+});
+
+// Handle audioDevicesUpdate event
+this.callClient.deviceManager.on('audioDevicesUpdated', e => {
+ e.added.forEach(audioDevice => { this.handleAddedAudioDevice(audioDevice); });
+ e.removed.forEach(removedAudioDevice => { this.handleRemovedAudioDevice(removedAudioDevice); });
+});
+
+// Handle selectedMicrophoneChanged event
+this.deviceManager.on('selectedMicrophoneChanged', () => { console.log(this.deviceManager.selectedMicrophone) });
+
+// Handle selectedSpeakerChanged event
+this.deviceManager.on('selectedSpeakerChanged', () => { console.log(this.deviceManager.selectedSpeaker) });
+ `;
+
+ // TODO: Create section component. Couldnt use the ExampleCard compoenent from uifabric because it is buggy,
+ // when toggling their show/hide code functionality, videos dissapear from DOM.
+
+ return (
+
+
+ {
+ this.state?.callSurvey &&
+ this.handleCallSurveySubmitted()}
+ call={this.state.callSurvey}
+ />
+ }
+
+
+
+
+
Placing and receiving calls
+
{`Permissions audio: ${this.state.permissions.audio} video: ${this.state.permissions.video}`}
+
+
+
+
+
+
+
this.setState({ showCallSampleCode: !this.state.showCallSampleCode })}>
+
+
+
+
+
Having provisioned an ACS Identity and initialized the SDK from the section above, you are now ready to place calls, join group calls, and receiving calls.
+
+ {
+ this.state.showCallSampleCode &&
+
+
+ {callSampleCode}
+
+
+ }
+ {
+ this.state.callError &&
+
+ { this.setState({ callError: undefined }) }}
+ dismissButtonAriaLabel="Close">
+ {this.state.callError}
+
+
+
+ }
+ {
+ this.state.deviceManagerWarning &&
+
{ this.setState({ deviceManagerWarning: undefined }) }}
+ dismissButtonAriaLabel="Close">
+ {this.state.deviceManagerWarning}
+
+ }
+ {
+ this.state.ufdMessages.length > 0 &&
+
{ this.setState({ ufdMessages: [] }) }}
+ dismissButtonAriaLabel="Close">
+ {this.state.ufdMessages.map((msg, index) => {msg})}
+
+ }
+ {
+ !this.state.incomingCall && !this.state.call && !this.state.callSurvey &&
+
+
+
+
+
+
+ this.destinationUserIds = val} />
+ this.destinationPhoneIds = val} />
+ this.alternateCallerId = val} />
+
+
+
this.placeCall(false)}>
+
+
this.placeCall(true)}>
+
+
+
+
+
+
+
+
Join a Teams meeting
+
+
+
+
+
Enter meeting link
+
+ this.meetingLink = val} />
+
+
Or enter meeting id (and) passcode
+
+ this.meetingId = val} />
+ this.passcode = val} />
+
+
Or enter meeting coordinates (Thread Id, Message Id, Organizer Id, and Tenant Id)
+
+ this.threadId = val} />
+ this.messageId = val} />
+ this.organizerId = val} />
+ this.tenantId = val} />
+
+
+
+
this.joinTeamsMeeting(false)}>
+
+
this.joinTeamsMeeting(true)}>
+
+
+
+
+
+
+
Join a group call
+
+
+ this.destinationGroup = val} />
+
+
+
this.joinGroup(false)}>
+
+
this.joinGroup(true)}>
+
+
+
+
Join a Rooms call
+
+
+ this.roomsId = val} />
+
+
+
this.joinRooms(false)}>
+
+
this.joinRooms(true)}>
+
+
+
+
+
+
+
Video Send Constraints
+
+
+
+
+
+ }
+ {
+ this.state.call && this.state.isPreCallDiagnosticsCallInProgress &&
+
+ Pre Call Diagnostics call in progress...
+
+ }
+ {
+ this.state.call && !this.state.callSurvey && !this.state.isPreCallDiagnosticsCallInProgress &&
+
{ this.setState({ showCameraNotFoundWarning: show }) }}
+ onShowSpeakerNotFoundWarning={(show) => { this.setState({ showSpeakerNotFoundWarning: show }) }}
+ onShowMicrophoneNotFoundWarning={(show) => { this.setState({ showMicrophoneNotFoundWarning: show }) }} />
+ }
+ {
+ this.state.incomingCall && !this.state.call &&
+ await this.getCallOptions({ video: false, micMuted: false })}
+ acceptCallMicrophoneUnmutedVideoOn={async () => await this.getCallOptions({ video: true, micMuted: false })}
+ acceptCallMicrophoneMutedVideoOn={async () => await this.getCallOptions({ video: true, micMuted: true })}
+ acceptCallMicrophoneMutedVideoOff={async () => await this.getCallOptions({ video: false, micMuted: true })}
+ onReject={() => { this.setState({ incomingCall: undefined }) }} />
+ }
+
+
+
+
+
+
Pre Call Diagnostics
+
+
this.runPreCallDiagnostics()}>
+
+
this.setState({ showPreCallDiagnosticsSampleCode: !this.state.showPreCallDiagnosticsSampleCode })}>
+
+
+
+ {
+ this.state.call && this.state.isPreCallDiagnosticsCallInProgress &&
+
+ Pre Call Diagnostics call in progress...
+
+
+ }
+ {
+ this.state.showPreCallDiagnostcisResults &&
+
+ {
+
+ {
+ this.state.preCallDiagnosticsResults.deviceAccess &&
+
+
Device Permission:
+
+
Audio:
+
{this.state.preCallDiagnosticsResults.deviceAccess.audio.toString()}
+
+
+
Video:
+
{this.state.preCallDiagnosticsResults.deviceAccess.video.toString()}
+
+
+ }
+ {
+ this.state.preCallDiagnosticsResults.deviceEnumeration &&
+
+
Device Access:
+
+
Microphone:
+
{this.state.preCallDiagnosticsResults.deviceEnumeration.microphone}
+
+
+
Camera:
+
{this.state.preCallDiagnosticsResults.deviceEnumeration.camera}
+
+
+
Speaker:
+
{this.state.preCallDiagnosticsResults.deviceEnumeration.speaker}
+
+
+ }
+ {
+ this.state.preCallDiagnosticsResults.browserSupport &&
+
+
Browser Support:
+
+
OS:
+
{this.state.preCallDiagnosticsResults.browserSupport.os}
+
+
+
Browser:
+
{this.state.preCallDiagnosticsResults.browserSupport.browser}
+
+
+ }
+ {
+ this.state.preCallDiagnosticsResults.inCallDiagnostics &&
+
+
Call Diagnostics:
+
+
+
Call Connected:
+
{this.state.preCallDiagnosticsResults.inCallDiagnostics.connected.toString()}
+
+
+
BandWidth:
+
{this.state.preCallDiagnosticsResults.inCallDiagnostics.bandWidth}
+
+
+
+
Audio Jitter:
+
{this.state.preCallDiagnosticsResults.inCallDiagnostics.diagnostics.audio.jitter}
+
+
+
Audio PacketLoss:
+
{this.state.preCallDiagnosticsResults.inCallDiagnostics.diagnostics.audio.packetLoss}
+
+
+
Audio Rtt:
+
{this.state.preCallDiagnosticsResults.inCallDiagnostics.diagnostics.audio.rtt}
+
+
+
+
Video Jitter:
+
{this.state.preCallDiagnosticsResults.inCallDiagnostics.diagnostics.video.jitter}
+
+
+
Video PacketLoss:
+
{this.state.preCallDiagnosticsResults.inCallDiagnostics.diagnostics.video.packetLoss}
+
+
+
Video Rtt:
+
{this.state.preCallDiagnosticsResults.inCallDiagnostics.diagnostics.video.rtt}
+
+
+
+ }
+
+ }
+
+ }
+ {
+ this.state.showPreCallDiagnosticsSampleCode &&
+
+
+ {
+ preCallDiagnosticsSampleCode
+ }
+
+
+ }
+
+
+
+
+
+
Video, Screen sharing, and local video preview
+
+
this.setState({ showStreamingSampleCode: !this.state.showStreamingSampleCode })}>
+
+
+
+ {
+ this.state.showStreamingSampleCode &&
+
+
+ {streamingSampleCode}
+
+
+ }
+
+ Video - try it out.
+
+
+ From your current call, toggle your video on and off by clicking on the icon.
+ When you start your video, remote participants can see your video by receiving a stream and rendering it in an HTML element.
+
+
+
+ Screen sharing - try it out.
+
+
+ From your current call, toggle your screen sharing on and off by clicking on the icon.
+ When you start sharing your screen, remote participants can see your screen by receiving a stream and rendering it in an HTML element.
+
+
+
+
+
+
+
Mute / Unmute
+
+
this.setState({ showMuteUnmuteSampleCode: !this.state.showMuteUnmuteSampleCode })}>
+
+
+
+ {
+ this.state.showMuteUnmuteSampleCode &&
+
+
+ {muteUnmuteSampleCode}
+
+
+ }
+
+ Try it out.
+
+
+ From your current call, toggle your microphone on and off by clicking on the icon.
+ When you mute or unmute your microphone, remote participants can receive an event about wether your micrphone is muted or unmuted.
+
+
+
+
+
+
+
Hold / Unhold
+
+
this.setState({ showHoldUnholdSampleCode: !this.state.showHoldUnholdSampleCode })}>
+
+
+
+ {
+ this.state.showHoldUnholdSampleCode &&
+
+
+ {holdUnholdSampleCode}
+
+
+ }
+
+ Try it out.
+
+
+ From your current call, toggle hold call and unhold call on by clicking on the icon.
+ When you hold or unhold the call, remote participants can receive other participant state changed events. Also, the call state changes.
+
+
+
+
+
+
+
Device Manager
+
+
this.setState({ showDeviceManagerSampleCode: !this.state.showDeviceManagerSampleCode })}>
+
+
+
+ {
+ this.state.showDeviceManagerSampleCode &&
+
+
+ {deviceManagerSampleCode}
+
+
+ }
+
+ Try it out.
+
+
+ From your current call, click on the icon to open up the settings panel.
+ The DeviceManager is used to select the devices (camera, microphone, and speakers) to use across the call stack and to preview your camera.
+
+
+
+
+ );
+ }
+}
diff --git a/Project/src/MakeCall/MediaConstraint.js b/src/MakeCall/MediaConstraint.js
similarity index 100%
rename from Project/src/MakeCall/MediaConstraint.js
rename to src/MakeCall/MediaConstraint.js
diff --git a/Project/src/MakeCall/NetworkConfiguration/ProxyConfiguration.js b/src/MakeCall/NetworkConfiguration/ProxyConfiguration.js
similarity index 100%
rename from Project/src/MakeCall/NetworkConfiguration/ProxyConfiguration.js
rename to src/MakeCall/NetworkConfiguration/ProxyConfiguration.js
diff --git a/Project/src/MakeCall/NetworkConfiguration/TurnConfiguration.js b/src/MakeCall/NetworkConfiguration/TurnConfiguration.js
similarity index 100%
rename from Project/src/MakeCall/NetworkConfiguration/TurnConfiguration.js
rename to src/MakeCall/NetworkConfiguration/TurnConfiguration.js
diff --git a/Project/src/MakeCall/ParticipantMenuOptions.js b/src/MakeCall/ParticipantMenuOptions.js
similarity index 100%
rename from Project/src/MakeCall/ParticipantMenuOptions.js
rename to src/MakeCall/ParticipantMenuOptions.js
diff --git a/Project/src/MakeCall/RawVideoAccess/CustomVideoEffects.js b/src/MakeCall/RawVideoAccess/CustomVideoEffects.js
similarity index 100%
rename from Project/src/MakeCall/RawVideoAccess/CustomVideoEffects.js
rename to src/MakeCall/RawVideoAccess/CustomVideoEffects.js
diff --git a/Project/src/MakeCall/RemoteParticipantCard.js b/src/MakeCall/RemoteParticipantCard.js
similarity index 100%
rename from Project/src/MakeCall/RemoteParticipantCard.js
rename to src/MakeCall/RemoteParticipantCard.js
diff --git a/Project/src/MakeCall/Section.js b/src/MakeCall/Section.js
similarity index 100%
rename from Project/src/MakeCall/Section.js
rename to src/MakeCall/Section.js
diff --git a/Project/src/MakeCall/StarRating.js b/src/MakeCall/StarRating.js
similarity index 100%
rename from Project/src/MakeCall/StarRating.js
rename to src/MakeCall/StarRating.js
diff --git a/Project/src/MakeCall/StreamRenderer.js b/src/MakeCall/StreamRenderer.js
similarity index 100%
rename from Project/src/MakeCall/StreamRenderer.js
rename to src/MakeCall/StreamRenderer.js
diff --git a/Project/src/MakeCall/VideoEffects/VideoEffectsContainer.js b/src/MakeCall/VideoEffects/VideoEffectsContainer.js
similarity index 100%
rename from Project/src/MakeCall/VideoEffects/VideoEffectsContainer.js
rename to src/MakeCall/VideoEffects/VideoEffectsContainer.js
diff --git a/Project/src/MakeCall/VideoEffects/VideoEffectsImagePicker.js b/src/MakeCall/VideoEffects/VideoEffectsImagePicker.js
similarity index 100%
rename from Project/src/MakeCall/VideoEffects/VideoEffectsImagePicker.js
rename to src/MakeCall/VideoEffects/VideoEffectsImagePicker.js
diff --git a/Project/src/MakeCall/VideoReceiveStats.js b/src/MakeCall/VideoReceiveStats.js
similarity index 100%
rename from Project/src/MakeCall/VideoReceiveStats.js
rename to src/MakeCall/VideoReceiveStats.js
diff --git a/Project/src/MakeCall/VolumeVisualizer.js b/src/MakeCall/VolumeVisualizer.js
similarity index 100%
rename from Project/src/MakeCall/VolumeVisualizer.js
rename to src/MakeCall/VolumeVisualizer.js
diff --git a/Project/src/Utils/Utils.js b/src/Utils/Utils.js
similarity index 97%
rename from Project/src/Utils/Utils.js
rename to src/Utils/Utils.js
index ed1dfd35..b708ce0c 100644
--- a/Project/src/Utils/Utils.js
+++ b/src/Utils/Utils.js
@@ -1,150 +1,150 @@
-import {
- isCommunicationUserIdentifier,
- isPhoneNumberIdentifier,
- isMicrosoftTeamsUserIdentifier,
- isUnknownIdentifier,
- createIdentifierFromRawId
-} from '@azure/communication-common';
-import { PublicClientApplication } from "@azure/msal-browser";
-import { authConfig, authScopes } from "../../oAuthConfig"
-import axios from 'axios';
-
-export const utils = {
- getAppServiceUrl: () => {
- return window.location.origin;
- },
- getCommunicationUserToken: async (communicationUserId) => {
- let response = await axios({
- url: 'getCommunicationUserToken',
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- data: communicationUserId ? JSON.stringify({communicationUserId}) : undefined
- })
- if (response.status === 200) {
- return response.data;
- }
- throw new Error('Failed to get ACS User Access token');
- },
- getCommunicationUserTokenForOneSignalRegistrationToken: async (oneSignalRegistrationToken) => {
- let response = await axios({
- url: 'getCommunicationUserTokenForOneSignalRegistrationToken',
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- data: JSON.stringify({oneSignalRegistrationToken})
- });
- if (response.status === 200) {
- return response.data;
- }
- throw new Error('Failed to get ACS User Acccess token for the given OneSignal Registration Token');
- },
- getOneSignalRegistrationTokenForCommunicationUserToken: async (token, communicationUserId) => {
- let response = await axios({
- url: 'getOneSignalRegistrationTokenForCommunicationUserToken',
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- data: JSON.stringify({token, communicationUserId})
- });
- if (response.status === 200) {
- return response.data;
- }
- throw new Error('Failed to get ACS User Acccess token for the given OneSignal Registration Token');
- },
- teamsPopupLogin: async () => {
- const oAuthObj = new PublicClientApplication(authConfig);
- const popupLoginRespoonse = await oAuthObj.loginPopup({scopes: authScopes.popUpLogin});
- const response = await axios({
- url: 'teamsPopupLogin',
- method: 'POST',
- headers: {
- 'Accept': 'application/json, text/plain, */*',
- 'Content-type': 'application/json'
- },
- data: JSON.stringify({
- aadToken: popupLoginRespoonse.accessToken,
- userObjectId: popupLoginRespoonse.uniqueId
- })
- });
- if (response.status === 200) {
- return response.data;
- }
- throw new Error('Failed to get Teams User Acccess token');
- },
- teamsM365Login: async (email, password) => {
- const response = await axios({
- url: 'teamsM365Login',
- method: 'POST',
- headers: {
- 'Accept': 'application/json, text/plain, */*',
- 'Content-type': 'application/json'
- },
- data: JSON.stringify({email, password })
- })
- if (response.status === 200) {
- return response.data;
- }
- throw new Error('Failed to get Teams User Acccess token');
- },
- getIdentifierText: (identifier) => {
- if (isCommunicationUserIdentifier(identifier)) {
- return identifier.communicationUserId;
- } else if (isPhoneNumberIdentifier(identifier)) {
- return identifier.phoneNumber;
- } else if (isMicrosoftTeamsUserIdentifier(identifier)) {
- return identifier.microsoftTeamsUserId;
- } else if (isUnknownIdentifier(identifier) && identifier.id === '8:echo123'){
- return 'Echo Bot';
- } else {
- return 'Unknown Identifier';
- }
- },
- getSizeInBytes(str) {
- return new Blob([str]).size;
- },
- getRemoteParticipantObjFromIdentifier(call, identifier) {
- switch(identifier.kind) {
- case 'communicationUser': {
- return call.remoteParticipants.find(rm => {
- return rm.identifier.communicationUserId === identifier.communicationUserId
- });
- }
- case 'microsoftTeamsUser': {
- return call.remoteParticipants.find(rm => {
- return rm.identifier.microsoftTeamsUserId === identifier.microsoftTeamsUserId
- });
- }
- case 'phoneNumber': {
- return call.remoteParticipants.find(rm => {
- return rm.identifier.phoneNumber === identifier.phoneNumber
- });
- }
- case 'unknown': {
- return call.remoteParticipants.find(rm => {
- return rm.identifier.id === identifier.id
- });
- }
- }
- },
- isParticipantSpotlighted(participantId, spotlightState) {
- if (!participantId || !spotlightState) { return false }
- let rtn = spotlightState.find(element => this.getIdentifierText(element.identifier) === this.getIdentifierText(participantId));
- return !!rtn
-
- },
- isParticipantHandRaised(participantId, raisedHandState) {
- if (!participantId || !raisedHandState) { return false }
- let rtn = raisedHandState.find(element => this.getIdentifierText(element.identifier) === this.getIdentifierText(participantId));
- return !!rtn
- },
- getParticipantPublishStates(participantId, publishedStates) {
- let states = {isSpotlighted: false, isHandRaised: false}
- states.isSpotlighted = this.isParticipantSpotlighted(participantId, publishedStates.spotlight)
- states.isHandRaised = this.isParticipantHandRaised(participantId, publishedStates.raiseHand)
- return states
- }
-}
+import {
+ isCommunicationUserIdentifier,
+ isPhoneNumberIdentifier,
+ isMicrosoftTeamsUserIdentifier,
+ isUnknownIdentifier,
+ createIdentifierFromRawId
+} from '@azure/communication-common';
+import { PublicClientApplication } from "@azure/msal-browser";
+import { authConfig, authScopes } from "../../oAuthConfig"
+import axios from 'axios';
+
+export const utils = {
+ getAppServiceUrl: () => {
+ return window.location.origin;
+ },
+ getCommunicationUserToken: async (communicationUserId) => {
+ let response = await axios({
+ url: 'getCommunicationUserToken',
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ data: communicationUserId ? JSON.stringify({communicationUserId}) : undefined
+ })
+ if (response.status === 200) {
+ return response.data;
+ }
+ throw new Error('Failed to get ACS User Access token');
+ },
+ getCommunicationUserTokenForOneSignalRegistrationToken: async (oneSignalRegistrationToken) => {
+ let response = await axios({
+ url: 'getCommunicationUserTokenForOneSignalRegistrationToken',
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ data: JSON.stringify({oneSignalRegistrationToken})
+ });
+ if (response.status === 200) {
+ return response.data;
+ }
+ throw new Error('Failed to get ACS User Acccess token for the given OneSignal Registration Token');
+ },
+ getOneSignalRegistrationTokenForCommunicationUserToken: async (token, communicationUserId) => {
+ let response = await axios({
+ url: 'getOneSignalRegistrationTokenForCommunicationUserToken',
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ data: JSON.stringify({token, communicationUserId})
+ });
+ if (response.status === 200) {
+ return response.data;
+ }
+ throw new Error('Failed to get ACS User Acccess token for the given OneSignal Registration Token');
+ },
+ teamsPopupLogin: async () => {
+ const oAuthObj = new PublicClientApplication(authConfig);
+ const popupLoginRespoonse = await oAuthObj.loginPopup({scopes: authScopes.popUpLogin});
+ const response = await axios({
+ url: 'teamsPopupLogin',
+ method: 'POST',
+ headers: {
+ 'Accept': 'application/json, text/plain, */*',
+ 'Content-type': 'application/json'
+ },
+ data: JSON.stringify({
+ aadToken: popupLoginRespoonse.accessToken,
+ userObjectId: popupLoginRespoonse.uniqueId
+ })
+ });
+ if (response.status === 200) {
+ return response.data;
+ }
+ throw new Error('Failed to get Teams User Acccess token');
+ },
+ teamsM365Login: async (email, password) => {
+ const response = await axios({
+ url: 'teamsM365Login',
+ method: 'POST',
+ headers: {
+ 'Accept': 'application/json, text/plain, */*',
+ 'Content-type': 'application/json'
+ },
+ data: JSON.stringify({email, password })
+ })
+ if (response.status === 200) {
+ return response.data;
+ }
+ throw new Error('Failed to get Teams User Acccess token');
+ },
+ getIdentifierText: (identifier) => {
+ if (isCommunicationUserIdentifier(identifier)) {
+ return identifier.communicationUserId;
+ } else if (isPhoneNumberIdentifier(identifier)) {
+ return identifier.phoneNumber;
+ } else if (isMicrosoftTeamsUserIdentifier(identifier)) {
+ return identifier.microsoftTeamsUserId;
+ } else if (isUnknownIdentifier(identifier) && identifier.id === '8:echo123'){
+ return 'Echo Bot';
+ } else {
+ return 'Unknown Identifier';
+ }
+ },
+ getSizeInBytes(str) {
+ return new Blob([str]).size;
+ },
+ getRemoteParticipantObjFromIdentifier(call, identifier) {
+ switch(identifier.kind) {
+ case 'communicationUser': {
+ return call.remoteParticipants.find(rm => {
+ return rm.identifier.communicationUserId === identifier.communicationUserId
+ });
+ }
+ case 'microsoftTeamsUser': {
+ return call.remoteParticipants.find(rm => {
+ return rm.identifier.microsoftTeamsUserId === identifier.microsoftTeamsUserId
+ });
+ }
+ case 'phoneNumber': {
+ return call.remoteParticipants.find(rm => {
+ return rm.identifier.phoneNumber === identifier.phoneNumber
+ });
+ }
+ case 'unknown': {
+ return call.remoteParticipants.find(rm => {
+ return rm.identifier.id === identifier.id
+ });
+ }
+ }
+ },
+ isParticipantSpotlighted(participantId, spotlightState) {
+ if (!participantId || !spotlightState) { return false }
+ let rtn = spotlightState.find(element => this.getIdentifierText(element.identifier) === this.getIdentifierText(participantId));
+ return !!rtn
+
+ },
+ isParticipantHandRaised(participantId, raisedHandState) {
+ if (!participantId || !raisedHandState) { return false }
+ let rtn = raisedHandState.find(element => this.getIdentifierText(element.identifier) === this.getIdentifierText(participantId));
+ return !!rtn
+ },
+ getParticipantPublishStates(participantId, publishedStates) {
+ let states = {isSpotlighted: false, isHandRaised: false}
+ states.isSpotlighted = this.isParticipantSpotlighted(participantId, publishedStates.spotlight)
+ states.isHandRaised = this.isParticipantHandRaised(participantId, publishedStates.raiseHand)
+ return states
+ }
+}
diff --git a/Project/src/index.js b/src/index.js
similarity index 100%
rename from Project/src/index.js
rename to src/index.js
diff --git a/Project/src/serviceWorker.js b/src/serviceWorker.js
similarity index 100%
rename from Project/src/serviceWorker.js
rename to src/serviceWorker.js
diff --git a/Project/webpack.config.js b/webpack.config.js
similarity index 100%
rename from Project/webpack.config.js
rename to webpack.config.js