git
use the following commands:$ git clone https://github.com/miguelgrinberg/flask-twilio-video $ git checkout only-video-sharing
TWILIO_ACCOUNT_SID="<enter your Twilio account SID here>" TWILIO_API_KEY_SID="<enter your Twilio API key here>" TWILIO_API_KEY_SECRET="<enter your Twilio API secret here>"
$ python -m venv venv $ source venv/bin/activate (venv) $ pip install -r requirements.txt
$ python -m venv venv $ venv\Scripts\activate (venv) $ pip install -r requirements.txt
pip
, the Python package installer, to install the Python packages (dependencies) used by this application. These packages are:FLASK_APP=app.py FLASK_ENV=development
(venv) $ flask run * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 247-567-386
(venv) $ ngrok http 5000
<!doctype html> <html> <head> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css') }}"> </head> <body> <h1> Twilio Video Chat : Dominant Speaker Detection </h1> <form> <label for="username">Name: </label> <input type="text" name="username" id="username"> <button id="join_leave">Join call</button> </form> <p id="count">Disconnected.</p> <div id="container" class="container"> <div id="local" class="participant"><div></div><div class="nameLabel">Me</div></div> <!-- more participants will be added dynamically here --> </div> <script src="//media.twiliocdn.com/sdk/js/video/releases/2.3.0/twilio-video.min.js"></script> <script src="{{ url_for('static', filename='app.js') }}"></script> </body> </html>
nameLabel
class:.container { margin-top: 20px; width: 100%; display: flex; flex-wrap: wrap; } .participant { margin-bottom: 5px; margin-right: 5px; } .participant div { text-align: center; } .participant div:first-child { width: 240px; height: 180px; background-color: #ccc; border: 1px solid black; } .participant video { width: 100%; height: 100%; } .nameLabel { background-color: #ebebeb; border-style: solid; }
nameLabel
for styling the name tag of the participant. We have also set a default background-color
attribute to it. Moving forward we will be using this background-color
property to highlight the dominant speaker.background-color
attribute of their name tag to highlight them. But before that, we want a way to uniquely identify the name tags of all the remote participants in our video call.participantConnected(...)
function from the app.js file in the static directory, where we already have the reference to the remote participant who is joining the video call. Let’s have a look at the updated participantConnected(...)
function. Changes and additions are highlighted.function participantConnected(participant) { let participantDiv = document.createElement('div'); participantDiv.setAttribute('id', participant.sid); participantDiv.setAttribute('class', 'participant'); let tracksDiv = document.createElement('div'); participantDiv.appendChild(tracksDiv); let labelDiv = document.createElement('div'); labelDiv.innerHTML = participant.identity; // Add formatting to name of participant labelDiv.setAttribute('class', 'nameLabel'); // Add unique SID to the name tag labelDiv.setAttribute('id', 'N_' + participant.sid); participantDiv.appendChild(labelDiv); container.appendChild(participantDiv); participant.tracks.forEach(publication => { if (publication.isSubscribed) trackSubscribed(tracksDiv, publication.track); }); participant.on('trackSubscribed', track => trackSubscribed(tracksDiv, track)); participant.on('trackUnsubscribed', trackUnsubscribed); updateParticipantCount(); };
nameLabel
class to the participant’s name tag for a consistent styling across all the participants. Further, each name tag is uniquely identified with the help of the id
attribute that holds the remote participant’s SID.dominantSpeakerChanged
event is emitted. The remote participant with the loudest RemoteAudioTrack
is considered a dominant speaker.connect(...)
function from the base application to capture and handle this event. Below is the updated connect(...)
function with all the additions and modifications highlighted.function connect(username) { let promise = new Promise((resolve, reject) => { // get a token from the back end fetch('/login', { method: 'POST', body: JSON.stringify({'username': username}) }).then(res => res.json()).then(data => { // join video call return Twilio.Video.connect(data.token, {dominantSpeaker: true}); }).then(_room => { room = _room; room.participants.forEach(participantConnected); room.on('dominantSpeakerChanged', participant => { handleSpeakerChange(participant); }); room.on('participantConnected', participantConnected); room.on('participantDisconnected', participantDisconnected); connected = true; updateParticipantCount(); resolve(); }).catch(() => { reject(); }); }); return promise; };
dominantSpeakerChanged
events we need to set the property dominantSpeaker
to true
inside the Twilio.Video.connect()
call.handleSpeakerChange(...)
every time we receive the dominantSpeakerChanged
events from the room. Let’s add the code for this event handler in app.js:function handleSpeakerChange(participant) { removeDominantSpeaker(); if (participant !== null) assignDominantSpeaker(participant); }
dominantSpeakerChanged
event is received, we need to remove the highlight from the previous speaker and add it to the current dominant speaker. We are using a removeDominantSpeaker()
auxiliary function to remove the name tag highlight from the last dominant speaker and assignDominantSpeaker()
to highlight the new one.setLabelColor(...)
to set the background-color
of a label element. Below are the implementations of these auxiliary functions:let lastSpeakerSID = null; // add this at the top with the other variable declarations function setLabelColor(label, color) { if (label !== null) { label.style.backgroundColor = color; } } function removeDominantSpeaker() { let speakerNameLabel; speakerNameLabel = document.getElementById(lastSpeakerSID); setLabelColor(speakerNameLabel, "#ebebeb"); // default color } function assignDominantSpeaker(participant) { let domSpeakerNameLabel; lastSpeakerSID = "N_" + participant.sid; domSpeakerNameLabel = document.getElementById(lastSpeakerSID); setLabelColor(domSpeakerNameLabel, "#b5e7a0"); // green color }
removeDominantSpeaker()
function we are using the lastSpeakerSID
to reference the name tag of the last speaker to set its background-color
to default.assignDominantSpeaker()
function we fetch the name tag of the participant and set its background-color
to green to indicate that the participant is currently the dominant speaker.