Proposed Pull Request Change

title description services author manager ms.service ms.date ms.topic ms.custom ms.author
include file include file azure-communication-services mrayyan alexokun azure-communication-services 06/02/2025 include include file t-siddiquim
📄 Document Links
GitHub View on GitHub Microsoft Learn View on Microsoft Learn
Raw New Markdown
Generating updated version of doc...
Rendered New Markdown
Generating updated version of doc...
+0 -0
+0 -0
--- title: include file description: include file services: azure-communication-services author: mrayyan manager: alexokun ms.service: azure-communication-services ms.date: 06/02/2025 ms.topic: include ms.custom: include file ms.author: t-siddiquim --- > [!NOTE] > You can access rooms using the [Azure Communication Services UI Library](https://azure.github.io/communication-ui-library/?path=/docs/concepts-rooms--docs). The UI Library enables developers to add a call client that is Rooms enabled into their application with only a couple lines of code. ## Implement the sample app To implement the code needed to join participants to a rooms call, download from GitHub the [Room Call sample app](https://github.com/Azure-Samples/communication-services-javascript-quickstarts/tree/main/calling-rooms-quickstart). ## Web Prerequisites - You need to have [Node.js 18](https://nodejs.org/dist/v18.18.0/). You can use the Microsoft Installer (MSI) to install it. ## Set up the project ### Create a new Node.js application Open your terminal or command window create a new directory for your app, and navigate to it. ```console mkdir calling-rooms-quickstart && cd calling-rooms-quickstart ``` Run `npm init -y` to create a **package.json** file with default settings. ```console npm init -y ``` ### Install the package Use the `npm install` command to install the Azure Communication Services Calling SDK for JavaScript. > [!IMPORTANT] > This quickstart uses the Azure Communication Services Calling SDK version `1.14.1`. The ability to join a room call and display the roles of call participants is available in the Calling JavaScript SDK for web browsers [version 1.13.1](https://www.npmjs.com/package/@azure/communication-calling/v/1.13.1) and above. ```console npm install @azure/communication-common --save npm install @azure/communication-calling@1.14.1 --save ``` ### Set up the app framework This article uses webpack to bundle the application assets. Run the following command to install the `webpack`, `webpack-cli`, and `webpack-dev-server` npm packages and list them as development dependencies in your `package.json`: ```console npm install copy-webpack-plugin@^11.0.0 webpack@^5.88.2 webpack-cli@^5.1.4 webpack-dev-server@^4.15.1 --save-dev ``` Here's the code: Create an `index.html` file in the root directory of your project. Use this file to configure a basic layout that enables the user to join a rooms call. ```html <!-- index.html--> <!DOCTYPE html> <html> <head> <title>Azure Communication Services - Rooms Call Sample</title> <link rel="stylesheet" type="text/css" href="styles.css"/> </head> <body> <h4>Azure Communication Services - Rooms Call Sample</h4> <input id="user-access-token" type="text" placeholder="User access token" style="margin-bottom:1em; width: 500px;"/> <button id="initialize-call-agent" type="button">Initialize Call Agent</button> <br> <br> <input id="acs-room-id" type="text" placeholder="Enter Room Id" style="margin-bottom:1em; width: 500px; display: block;"/> <button id="join-room-call-button" type="button" disabled="true">Join Room Call</button> <button id="hangup-call-button" type="button" disabled="true">Hang up Call</button> <button id="start-video-button" type="button" disabled="true">Start Video</button> <button id="stop-video-button" type="button" disabled="true">Stop Video</button> <br> <br> <div id="connectedLabel" style="color: #13bb13;" hidden>Room Call is connected!</div> <br> <div id="remoteVideosGallery" style="width: 40%;" hidden>Remote participants' video streams:</div> <br> <div id="localVideoContainer" style="width: 30%;" hidden>Local video stream:</div> <!-- points to the bundle generated from client.js --> <script src="./main.js"></script> </body> </html> ``` Create a file in the root directory of your project called `index.js` for the application logic. Add the following code to `index.js`: ```JavaScript // Make sure to install the necessary dependencies const { CallClient, VideoStreamRenderer, LocalVideoStream } = require('@azure/communication-calling'); const { AzureCommunicationTokenCredential } = require('@azure/communication-common'); const { AzureLogger, setLogLevel } = require("@azure/logger"); // Set the log level and output setLogLevel('verbose'); AzureLogger.log = (...args) => { console.log(...args); }; // Calling web sdk objects let callAgent; let deviceManager; let call; let localVideoStream; let localVideoStreamRenderer; // UI widgets let userAccessToken = document.getElementById('user-access-token'); let acsRoomId = document.getElementById('acs-room-id'); let initializeCallAgentButton = document.getElementById('initialize-call-agent'); let startCallButton = document.getElementById('join-room-call-button'); let hangUpCallButton = document.getElementById('hangup-call-button'); let startVideoButton = document.getElementById('start-video-button'); let stopVideoButton = document.getElementById('stop-video-button'); let connectedLabel = document.getElementById('connectedLabel'); let remoteVideosGallery = document.getElementById('remoteVideosGallery'); let localVideoContainer = document.getElementById('localVideoContainer'); /** * Using the CallClient, initialize a CallAgent instance with a CommunicationUserCredential which enable us to join a rooms call. */ initializeCallAgentButton.onclick = async () => { try { const callClient = new CallClient(); tokenCredential = new AzureCommunicationTokenCredential(userAccessToken.value.trim()); callAgent = await callClient.createCallAgent(tokenCredential) // Set up a camera device to use. deviceManager = await callClient.getDeviceManager(); await deviceManager.askDevicePermission({ video: true }); await deviceManager.askDevicePermission({ audio: true }); startCallButton.disabled = false; initializeCallAgentButton.disabled = true; } catch(error) { console.error(error); } } startCallButton.onclick = async () => { try { const localVideoStream = await createLocalVideoStream(); const videoOptions = localVideoStream ? { localVideoStreams: [localVideoStream] } : undefined; const roomCallLocator = { roomId: acsRoomId.value.trim() }; call = callAgent.join(roomCallLocator, { videoOptions }); // Subscribe to the call's properties and events. subscribeToCall(call); } catch (error) { console.error(error); } } /** * Subscribe to a call obj. * Listen for property changes and collection updates. */ subscribeToCall = (call) => { try { // Inspect the initial call.id value. console.log(`Call Id: ${call.id}`); //Subscribe to call's 'idChanged' event for value changes. call.on('idChanged', () => { console.log(`Call Id changed: ${call.id}`); }); // Inspect the initial call.state value. console.log(`Call state: ${call.state}`); // Subscribe to call's 'stateChanged' event for value changes. call.on('stateChanged', async () => { console.log(`Call state changed: ${call.state}`); if(call.state === 'Connected') { connectedLabel.hidden = false; startCallButton.disabled = true; hangUpCallButton.disabled = false; startVideoButton.disabled = false; stopVideoButton.disabled = false; remoteVideosGallery.hidden = false; } else if (call.state === 'Disconnected') { connectedLabel.hidden = true; startCallButton.disabled = false; hangUpCallButton.disabled = true; startVideoButton.disabled = true; stopVideoButton.disabled = true; remoteVideosGallery.hidden = true; console.log(`Call ended, call end reason={code=${call.callEndReason.code}, subCode=${call.callEndReason.subCode}}`); } }); call.on('isLocalVideoStartedChanged', () => { console.log(`isLocalVideoStarted changed: ${call.isLocalVideoStarted}`); }); console.log(`isLocalVideoStarted: ${call.isLocalVideoStarted}`); call.localVideoStreams.forEach(async (lvs) => { localVideoStream = lvs; await displayLocalVideoStream(); }); call.on('localVideoStreamsUpdated', e => { e.added.forEach(async (lvs) => { localVideoStream = lvs; await displayLocalVideoStream(); }); e.removed.forEach(lvs => { removeLocalVideoStream(); }); }); // Inspect the call's current remote participants and subscribe to them. call.remoteParticipants.forEach(remoteParticipant => { subscribeToRemoteParticipant(remoteParticipant); }); // Subscribe to the call's 'remoteParticipantsUpdated' event to be // notified when new participants are added to the call or removed from the call. call.on('remoteParticipantsUpdated', e => { // Subscribe to new remote participants that are added to the call. e.added.forEach(remoteParticipant => { subscribeToRemoteParticipant(remoteParticipant) }); // Unsubscribe from participants that are removed from the call e.removed.forEach(remoteParticipant => { console.log('Remote participant removed from the call.'); }); }); } catch (error) { console.error(error); } } /** * Subscribe to a remote participant obj. * Listen for property changes and collection updates. */ subscribeToRemoteParticipant = (remoteParticipant) => { try { // Inspect the initial remoteParticipant.state value. console.log(`Remote participant state: ${remoteParticipant.state}`); // Subscribe to remoteParticipant's 'stateChanged' event for value changes. remoteParticipant.on('stateChanged', () => { console.log(`Remote participant state changed: ${remoteParticipant.state}`); }); // Inspect the remoteParticipants's current videoStreams and subscribe to them. remoteParticipant.videoStreams.forEach(remoteVideoStream => { subscribeToRemoteVideoStream(remoteVideoStream) }); // Subscribe to the remoteParticipant's 'videoStreamsUpdated' event to be // notified when the remoteParticipant adds new videoStreams and removes video streams. remoteParticipant.on('videoStreamsUpdated', e => { // Subscribe to new remote participant's video streams that were added. e.added.forEach(remoteVideoStream => { subscribeToRemoteVideoStream(remoteVideoStream) }); // Unsubscribe from remote participant's video streams that were removed. e.removed.forEach(remoteVideoStream => { console.log('Remote participant video stream was removed.'); }) }); } catch (error) { console.error(error); } } /** * Subscribe to a remote participant's remote video stream obj. * You have to subscribe to the 'isAvailableChanged' event to render the remoteVideoStream. If the 'isAvailable' property * changes to 'true', a remote participant is sending a stream. Whenever availability of a remote stream changes * you can choose to destroy the whole 'Renderer', a specific 'RendererView' or keep them, but this will result in displaying blank video frame. */ subscribeToRemoteVideoStream = async (remoteVideoStream) => { let renderer = new VideoStreamRenderer(remoteVideoStream); let view; let remoteVideoContainer = document.createElement('div'); remoteVideoContainer.className = 'remote-video-container'; const createView = async () => { // Create a renderer view for the remote video stream. view = await renderer.createView(); // Attach the renderer view to the UI. remoteVideoContainer.appendChild(view.target); remoteVideosGallery.appendChild(remoteVideoContainer); } // Remote participant has switched video on/off remoteVideoStream.on('isAvailableChanged', async () => { try { if (remoteVideoStream.isAvailable) { await createView(); } else { view.dispose(); remoteVideosGallery.removeChild(remoteVideoContainer); } } catch (e) { console.error(e); } }); // Remote participant has video on initially. if (remoteVideoStream.isAvailable) { try { await createView(); } catch (e) { console.error(e); } } } /** * Start your local video stream. * This will send your local video stream to remote participants so they can view it. */ startVideoButton.onclick = async () => { try { const localVideoStream = await createLocalVideoStream(); await call.startVideo(localVideoStream); } catch (error) { console.error(error); } } /** * Stop your local video stream. * This will stop your local video stream from being sent to remote participants. */ stopVideoButton.onclick = async () => { try { await call.stopVideo(localVideoStream); } catch (error) { console.error(error); } } /** * To render a LocalVideoStream, you need to 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. */ createLocalVideoStream = async () => { const camera = (await deviceManager.getCameras())[0]; if (camera) { return new LocalVideoStream(camera); } else { console.error(`No camera device found on the system`); } } /** * Display your local video stream preview in your UI */ displayLocalVideoStream = async () => { try { localVideoStreamRenderer = new VideoStreamRenderer(localVideoStream); const view = await localVideoStreamRenderer.createView(); localVideoContainer.hidden = false; localVideoContainer.appendChild(view.target); } catch (error) { console.error(error); } } /** * Remove your local video stream preview from your UI */ removeLocalVideoStream = async() => { try { localVideoStreamRenderer.dispose(); localVideoContainer.hidden = true; } catch (error) { console.error(error); } } /** * End current room call */ hangUpCallButton.addEventListener("click", async () => { await call.hangUp(); }); ``` ## Add the webpack local server code Create a file in the root directory of your project called **webpack.config.js** to contain the local server logic for this quickstart. Add the following code to **webpack.config.js**: ```javascript const path = require('path'); const CopyPlugin = require("copy-webpack-plugin"); module.exports = { mode: 'development', entry: './index.js', output: { filename: 'main.js', path: path.resolve(__dirname, 'dist'), }, devServer: { static: { directory: path.join(__dirname, './') }, }, plugins: [ new CopyPlugin({ patterns: [ './index.html' ] }), ] }; ``` ## Run the code Use the `webpack-dev-server` to build and run your app. Run the following command to bundle the application host in a local webserver: ```console `npx webpack serve --config webpack.config.js` ``` 1. Open your browser navigate to http://localhost:8080/. 2. On the first input field, enter a valid user access token. 3. Click on the "Initialize Call Agent" and enter your Room ID. 4. Click **Join Room Call**. You successfully joined a Rooms call! ## Understanding joining a Room call All the code that you added to the sample app enabled you to successfully start and join a room call. Here's more information about what more methods/handlers you can access for Rooms to extend functionality in your application. To display the role of the local or remote call participants, subscribe to the handler as follows. ```js // Subscribe to changes for your role in a call const callRoleChangedHandler = () => { console.log(call.role); }; call.on('roleChanged', callRoleChangedHandler); // Subscribe to role changes for remote participants const subscribeToRemoteParticipant = (remoteParticipant) => { remoteParticipant.on('roleChanged', () => { console.log(remoteParticipant.role); }); } ``` For more information about roles of room call participants, see [Rooms API for structured meetings](../../../concepts/rooms/room-concept.md#predefined-participant-roles-and-permissions).
Success! Branch created successfully. Create Pull Request on GitHub
Error: