This application includes some other nice-to-have features, such as:
- Screensharing
- A 'vanity mirror' that shows your local video stream
- Mic picker
- Cam picker
- Phone Dial out
You will need to set up Bandwidth Applications and have phone numbers associated with these application, and point the callback URL on these applications to the messaging and voice endpoints on the server running this app. ngrok
is highly recommended for local prototyping.
This app runs an HTTP server that listens for requests from browsers to get connection information. This connection information tells a browser the unique ID it should use to join a Room.
The server connects to Bandwidth's HTTP WebRTC API, which it will use to create a session and participant IDs. This example leverages our Node SDK to make the WebRTC calls.
The web browser will also use a websocket managed by the WebRTC browser SDK to handle signaling to the WebRTC API - this is all handled by a prepackaged Javascript SDK. Once more than one browser has joined the conference, they will be able to talk to each other.
Note: Unless you are running on
localhost
, you will need to use HTTPS. Most modern browsers require a secure context when accessing cameras and microphones.
There are two main components to building out this solution: the server side which handles call control and the client side which enables the participant.
The only file here is app.py
which includes the Bandwidth WebRTC sdk.
There are several endpoints that are exposed by this file for browser consumption:
/joinCall
allows a browser to join a call/startPSTNCall
dials out to a phone and has it join an active call/endPSTNCall
is NOT exposed in this example browser interface/idLookup
allows a browser to lookup a caller's/participant's name based on their participant_id
There are also some endpoints not intended for the browser
/Callbacks/answer
is called by the Bandwidth Voice system to determine what to do with an initiated call that has been answered/endSession
would be called to terminate a session, this could be called by some control system.
The meat of /joinCall
is a call to the addParticipantToRoom()
function which does the following:
- Gets a session id - either by creating a session or by getting the
session_id
from an internal list ofsession_id
s stored byroom_name
- Creates a new participant via
createParticipant()
, returning a token used for later authentication - Calls
addParticipantToSession()
with a session level subscription - doing this means that everyone is subscribed to this participant and this participant is subscribed to everyone else. As people are added or removed subscriptions will be managed for you. - Returns the participant token (from
createParticipant()
) to the web user that is needed to start connect to the WebRTC server and start streaming
The /startPSTNCall
works in a similar fashion to /joinCall
except it initiates a call via the Voice API and rather than passing back a token, it stores it for later use when the call is answered - as seen in /Callbacks/answer
.
/endSession
ends the session by removing everyone from it. As a reminder, sessions are only billed when media is flowing and all sessions are automatically ended and purged after 9 hours.
There is a bit more going on in the JavaScript side, much of this isn't needed to get a basic session going. But since we are creating a basic (but admitidly ugly) video meeting system, there is more going on. We're not going to go into the details that aren't pertinent to setting up WebRTC sessions here.
There is one html file, index.html
with very little going on, it just sets the stage. main.js
handles most of the meeting logic and webrtc_mgr.js
handles most audio and video work and interacting with the WebRTC SDK.
The most important elements of getting a browser user online are:
- Getting your participant token from your Call Control server application (above), this is accomplished in the
getOnline()
function. - Next
startSreaming()
, which callsbandwidthRtc.connect();
- this establishes a connection with the WebRTC servers - After the browser is connected, some work is done to setup our
constraints
which tell the browser which devices to use and any constraints around the encodings, rates, resolutions, etc. of the media - Once that is all set, we can start flowing media out by calling
bandwidthRtc.publish()
with the constraints we just created
There is one other section that is of particular importance here, it's this section:
window.addEventListener("load", (event) => {
bandwidthRtc.onStreamAvailable((rtcStream) => {
connectStream(rtcStream);
});
bandwidthRtc.onStreamUnavailable((endpointId) => {
disconnectEndpoint(endpointId);
});
});
This section creates the listeners that will fire whenever a new stream is attached or disconnected from this browser. The implementation here creates a new <video>
element and places it within the DOM. We add each stream to a list of connected streams as well.
You'll note that in the disconnectEndPoint
function we also check if this was the last person we were connected to. If so, we tell the user we are all done. This isn't necessary, but may be useful if you are doing small group calls and want to inform the user that the call is over.
The following configuration variables need to be set, start by coping the config.ini.default
file to config.ini
. For more information about each variable. Read more about each variable on the Security & Credentials Documentation Page.
Note that we have hardcoded the FROM and TO here. You may wish to make your FROM numbers dynamic based on the customer you are serving (or not!). The TO will almost definitely be dynamic, but in order to reduce accidental dialing or abuse, we have hardcoded it here. You can easily make this a dynamic variable, doing so is evident from looking at the code.
Variable | Description | Example |
---|---|---|
BW_ACCOUNT_ID |
Your Bandwidth Account Id | 539525 |
BW_USERNAME |
Your Bandwidth API Username | johnDoe |
BW_PASSWORD |
Your Bandwidth API Password | hunter22 |
BW_VOICE_APPLICATION_ID |
Your Bandwidth Voice application ID | acd1575d-b0f7-4274-95ee-e942a286df8c |
BASE_CALLBACK_URL |
The url for your dev server, without a trailing / | https://e8b0c1c2a03e.ngrok.io/ |
BW_NUMBER |
The "From" caller Id number for your call | +13428675309 |
Run the following commands to get started
pip install -r requirements.txt
cp config.ini.default config.ini
# edit config.ini per the description above
ngrok http 5000
# update your config.ini to have the ngrok url that is shown on screen
# you'll need to do this every time you restart ngrok if you want callbacks to work
# leave that open, in a new terminal...
python app.py
- go to http://localhost:5000 and you will see a basic call interface
- Click "Get Online" to connect your browser to the media server
- Click on "Dial Out" to initiate the PSTN call from the server side and add the PSTN call to the session, this will also transfer the call to the WebRTC session once it has been answered.
- Click "End Call" to remove the participant from the session and end the PSTN call
- Have Bandwidth Account
- Have Python (3.7.6+) Installed (along with pip)
- Have ngrok installed
The code for this example is fairly well documented, so please take a look through it to understand how it works. But here are a few pointers on flow
The path that you take to add people into a WebRTC call is as follows:
-
Enter your name (or leave the default)
-
Enter a room name (or leave the default, or it can be set in the query string)
-
Select your devices from the list, which is autodetected on page load
-
Click "Join Your Meeting" to get a token for your browser, get you connected to our media server, and start media flowing from the browser
-
Do the same in another browser, with the same room name of course
-
Start two other browsers with a different room name
-
You can also share your screen
To Dial out to a call:
- click the Dial Out link at the bottom
- It will ask for a phone number and name for the participant
- The demo passes the number info to the server, but ignores it in favor of a value set in a config file
- this can easily be changed to use the number provided; that number will need to be e.164 formatted - meaning it starts with +1 and contains no spaces, hyphens, or other chars, e.g. +15558675309
- You can preset a name for the room by putting the query param
room
in the query string of the url - try http://localhost:5000?room=test%20room - You can autostart all the attendees muted by changing the
start_muted_audio
variable totrue
at the top ofpublic/webrtc_mgr.js
- Note that an unmute button isn't provided in this example though
- However there are javascript functions in
public/webrtc_mgr.js
for muting and unmuting both audio and video
- There are facilities fo muting audio and video in the webrtc_mgr.js file
- There is an ability to play ringing in the browser (natively in JS) while awaiting your first connection